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.
448 449 450 451 452 453 |
# File 'lib/space_architect/harness.rb', line 448 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.
439 440 441 442 443 |
# File 'lib/space_architect/harness.rb', line 439 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
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 481 482 483 484 485 |
# File 'lib/space_architect/harness.rb', line 455 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
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
# File 'lib/space_architect/harness.rb', line 487 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 |