Class: Space::Architect::Harness::PiHarness
- Inherits:
-
Object
- Object
- Space::Architect::Harness::PiHarness
- Defined in:
- lib/space_architect/harness.rb
Overview
pi -p --mode json, session redirected into the lane's build dir via --session-dir
(pi otherwise auto-saves under ~/.pi/agent/sessions/, organized by mangled CWD).
Constant Summary collapse
- TIMEOUT_EXIT_CODE =
124
Class Method Summary collapse
-
.translate_thinking(level, force: false) ⇒ Object
pi's --thinking accepts the full normalized vocabulary unchanged — pi's own per-model thinkingLevelMap clamps further; architect does not clamp for pi.
Instance Method Summary collapse
-
#initialize(model:, max_turns:, bin: nil, config_dir:, effort: nil) ⇒ PiHarness
constructor
max_turns is accepted for interface parity with the other harnesses but is intentionally absent from argv — pi has no turn cap; the wall-clock timeout (below) is the only bound on a run.
- #run(prompt_path:, run_log_path:, chdir:, timeout: nil) ⇒ Object
- #run_detached(prompt_path:, run_log_path:, chdir:) ⇒ Object
Constructor Details
#initialize(model:, max_turns:, bin: nil, config_dir:, effort: nil) ⇒ PiHarness
max_turns is accepted for interface parity with the other harnesses but is intentionally absent from argv — pi has no turn cap; the wall-clock timeout (below) is the only bound on a run.
443 444 445 446 447 448 |
# File 'lib/space_architect/harness.rb', line 443 def initialize(model:, max_turns:, bin: nil, config_dir:, effort: nil) @model = model @bin = bin || ENV.fetch("ARCHITECT_PI_BIN", "pi") @config_dir = Pathname.new(config_dir) @effort = effort end |
Class Method Details
.translate_thinking(level, force: false) ⇒ Object
pi's --thinking accepts the full normalized vocabulary unchanged — pi's own per-model thinkingLevelMap clamps further; architect does not clamp for pi.
434 435 436 437 438 |
# File 'lib/space_architect/harness.rb', line 434 def self.translate_thinking(level, force: false) return [nil, nil] if level.nil? return [level, "thinking: force --effort=#{level} (unmodified, may be rejected)"] if force [level, nil] end |
Instance Method Details
#run(prompt_path:, run_log_path:, chdir:, timeout: nil) ⇒ Object
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
# File 'lib/space_architect/harness.rb', line 450 def run(prompt_path:, run_log_path:, chdir:, timeout: nil, **) prompt_path = Pathname.new(prompt_path) run_log_path = Pathname.new(run_log_path) File.open(prompt_path, "r") do |prompt_io| File.open(run_log_path, "w") do |log| Sync do child = Async::Process::Child.new(*argv, chdir: chdir.to_s, in: prompt_io, out: log, err: log) timed_out = false timeout_task = nil # Same TERM→grace→KILL escalation as ClaudeCodeHarness — with: Async::Process::Child's # #wait_thread ensure goes straight to KILL, so a concurrent fiber does the escalation. if timeout && timeout > 0 timeout_task = Async(transient: true) do sleep timeout timed_out = true Process.kill("TERM", -child.pid) rescue nil sleep 0.5 Process.kill("KILL", -child.pid) rescue nil end end status = child.wait timeout_task&.stop timed_out ? TIMEOUT_EXIT_CODE : status.exitstatus end end end end |
#run_detached(prompt_path:, run_log_path:, chdir:) ⇒ Object
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/space_architect/harness.rb', line 482 def run_detached(prompt_path:, run_log_path:, chdir:) prompt_path = Pathname.new(prompt_path) run_log_path = Pathname.new(run_log_path) prompt_io = File.open(prompt_path, "r") log = File.open(run_log_path, "w") begin pid = Process.spawn(*argv, chdir: chdir.to_s, pgroup: true, in: prompt_io, out: log, err: log) Process.detach(pid) ensure prompt_io.close log.close end pid end |