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.
493 494 495 496 497 498 |
# File 'lib/space_architect/harness.rb', line 493 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.
484 485 486 487 488 |
# File 'lib/space_architect/harness.rb', line 484 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
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 |
# File 'lib/space_architect/harness.rb', line 500 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
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/space_architect/harness.rb', line 532 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 |