@openrig/cli 0.6.11 → 0.6.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/daemon/assets/claude-statusline-context.cjs +2 -0
- package/daemon/assets/guidance/openrig-start.md +2 -1
- package/daemon/assets/plugins/openrig-core/.claude-plugin/plugin.json +1 -1
- package/daemon/assets/plugins/openrig-core/.codex-plugin/plugin.json +1 -1
- package/daemon/assets/plugins/openrig-core/README.md +30 -7
- package/daemon/assets/plugins/openrig-core/hooks/claude.json +7 -7
- package/daemon/assets/plugins/openrig-core/hooks/codex.json +2 -2
- package/daemon/assets/plugins/openrig-core/hooks/scripts/activity-relay.cjs +77 -0
- package/daemon/assets/plugins/openrig-core/hooks/scripts/compaction-restore-bridge.cjs +23 -0
- package/daemon/assets/plugins/openrig-core/hooks/scripts/refocus-health.cjs +4 -2
- package/daemon/assets/plugins/openrig-core/hooks/scripts/refocus.cjs +69 -16
- package/daemon/assets/plugins/openrig-core/skills/claude-compaction-restore/scripts/precompact-hook.mjs +24 -0
- package/daemon/assets/plugins/openrig-core/skills/delegating-work/SKILL.md +25 -5
- package/daemon/assets/plugins/openrig-core/skills/forming-an-openrig-mental-model/SKILL.md +4 -2
- package/daemon/assets/plugins/openrig-core/skills/mission-slice-sop/SKILL.md +10 -1
- package/daemon/assets/plugins/openrig-core/skills/openrig-skills/SKILL.md +1 -1
- package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +172 -11
- package/daemon/assets/plugins/openrig-core/skills/orienting-to-an-inherited-seat/SKILL.md +7 -4
- package/daemon/assets/plugins/openrig-core/skills/queue-handoff/SKILL.md +8 -0
- package/daemon/assets/plugins/openrig-core/skills/retiring-and-inheriting-a-seat/SKILL.md +5 -2
- package/daemon/assets/plugins/openrig-core/skills/session-compaction-and-restore/SKILL.md +19 -0
- package/daemon/assets/skills/rigs/SKILL.md +55 -9
- package/daemon/context-packs/help/manifest.yaml +1 -1
- package/daemon/context-packs/onboarding-width/manifest.yaml +1 -1
- package/daemon/context-packs/onboarding-width/public-what-you-can-do.md +106 -37
- package/daemon/context-packs/reference/getting-started.md +39 -6
- package/daemon/context-packs/reference/manifest.yaml +1 -1
- package/daemon/context-packs/reference/rig-spec.md +64 -3
- package/daemon/context-packs/reference/whats-new.md +167 -146
- package/daemon/context-packs/skills/agent-operated-software/manifest.yaml +1 -1
- package/daemon/context-packs/skills/agent-operated-workflows/manifest.yaml +1 -1
- package/daemon/context-packs/skills/applying-a-permission-policy/manifest.yaml +1 -1
- package/daemon/context-packs/skills/claude-compaction-restore/manifest.yaml +1 -1
- package/daemon/context-packs/skills/claude-compaction-restore/scripts/precompact-hook.mjs +24 -0
- package/daemon/context-packs/skills/core/agent-starters/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/agent-startup-and-context-ingestion/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/cross-host-rig-commands/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/human-in-the-loop/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/messaging-the-human/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-architect/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-cmux/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-herdr/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-skills/SKILL.md +1 -1
- package/daemon/context-packs/skills/core/openrig-skills/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-software-factory/SKILL.md +0 -1
- package/daemon/context-packs/skills/core/openrig-software-factory/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-software-factory/references/worked-example.md +7 -4
- package/daemon/context-packs/skills/core/openrig-upgrade/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/rig-bundles-and-shareable-artifacts/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/rig-lifecycle/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/session-source-fork/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/specification-system/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/topology-mutation-and-seat-management/SKILL.md +1 -1
- package/daemon/context-packs/skills/core/topology-mutation-and-seat-management/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/watchdog/manifest.yaml +1 -1
- package/daemon/context-packs/skills/delegating-work/SKILL.md +25 -5
- package/daemon/context-packs/skills/delegating-work/manifest.yaml +3 -3
- package/daemon/context-packs/skills/forming-an-openrig-mental-model/SKILL.md +4 -2
- package/daemon/context-packs/skills/forming-an-openrig-mental-model/manifest.yaml +1 -1
- package/daemon/context-packs/skills/loading-addressable-markdown/manifest.yaml +1 -1
- package/daemon/context-packs/skills/mission-slice-sop/SKILL.md +10 -1
- package/daemon/context-packs/skills/mission-slice-sop/manifest.yaml +1 -1
- package/daemon/context-packs/skills/openrig-operating-model/manifest.yaml +1 -1
- package/daemon/context-packs/skills/openrig-user/SKILL.md +172 -11
- package/daemon/context-packs/skills/openrig-user/manifest.yaml +1 -1
- package/daemon/context-packs/skills/orienting-to-an-inherited-seat/SKILL.md +7 -4
- package/daemon/context-packs/skills/orienting-to-an-inherited-seat/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/backlog-capture/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/context-builder/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/exec-summary/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/office-hours/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/plan-review/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/requirements-writer/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/ui-mockup/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/development-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/orchestration-team/SKILL.md +1 -1
- package/daemon/context-packs/skills/pods/orchestration-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/oversight-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/review-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/agent-browser/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/context-engineering/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/dogfood/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/frontend-design/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/systematic-debugging/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/test-driven-development/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/verification-before-completion/manifest.yaml +1 -1
- package/daemon/context-packs/skills/queue-handoff/SKILL.md +8 -0
- package/daemon/context-packs/skills/queue-handoff/manifest.yaml +1 -1
- package/daemon/context-packs/skills/refocusing/manifest.yaml +1 -1
- package/daemon/context-packs/skills/retiring-and-inheriting-a-seat/SKILL.md +5 -2
- package/daemon/context-packs/skills/retiring-and-inheriting-a-seat/manifest.yaml +1 -1
- package/daemon/context-packs/skills/seat-continuity-and-handover/manifest.yaml +1 -1
- package/daemon/context-packs/skills/session-compaction-and-restore/SKILL.md +19 -0
- package/daemon/context-packs/skills/session-compaction-and-restore/manifest.yaml +1 -1
- package/daemon/context-packs/skills/software-for-agents/manifest.yaml +1 -1
- package/daemon/context-packs/world-example/manifest.yaml +1 -1
- package/daemon/context-packs/world-public/manifest.yaml +1 -1
- package/daemon/dist/adapters/bounded-probe-exec.d.ts +45 -0
- package/daemon/dist/adapters/bounded-probe-exec.d.ts.map +1 -0
- package/daemon/dist/adapters/bounded-probe-exec.js +204 -0
- package/daemon/dist/adapters/bounded-probe-exec.js.map +1 -0
- package/daemon/dist/adapters/claude-code-adapter.d.ts +17 -1
- package/daemon/dist/adapters/claude-code-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/claude-code-adapter.js +173 -32
- package/daemon/dist/adapters/claude-code-adapter.js.map +1 -1
- package/daemon/dist/adapters/claude-resume.d.ts.map +1 -1
- package/daemon/dist/adapters/claude-resume.js +14 -3
- package/daemon/dist/adapters/claude-resume.js.map +1 -1
- package/daemon/dist/adapters/codex-resume.d.ts +5 -0
- package/daemon/dist/adapters/codex-resume.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-resume.js +17 -3
- package/daemon/dist/adapters/codex-resume.js.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts +38 -1
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.js +137 -5
- package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/community-harness-runtime-adapter.d.ts +46 -0
- package/daemon/dist/adapters/community-harness-runtime-adapter.d.ts.map +1 -0
- package/daemon/dist/adapters/community-harness-runtime-adapter.js +102 -0
- package/daemon/dist/adapters/community-harness-runtime-adapter.js.map +1 -0
- package/daemon/dist/adapters/compose-services-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/compose-services-adapter.js +2 -1
- package/daemon/dist/adapters/compose-services-adapter.js.map +1 -1
- package/daemon/dist/adapters/kernel-authority.d.ts +8 -1
- package/daemon/dist/adapters/kernel-authority.d.ts.map +1 -1
- package/daemon/dist/adapters/kernel-authority.js +35 -10
- package/daemon/dist/adapters/kernel-authority.js.map +1 -1
- package/daemon/dist/adapters/omp-rpc-frame.d.ts +51 -0
- package/daemon/dist/adapters/omp-rpc-frame.d.ts.map +1 -0
- package/daemon/dist/adapters/omp-rpc-frame.js +153 -0
- package/daemon/dist/adapters/omp-rpc-frame.js.map +1 -0
- package/daemon/dist/adapters/pi-openrouter-charge.d.ts +67 -0
- package/daemon/dist/adapters/pi-openrouter-charge.d.ts.map +1 -0
- package/daemon/dist/adapters/pi-openrouter-charge.js +228 -0
- package/daemon/dist/adapters/pi-openrouter-charge.js.map +1 -0
- package/daemon/dist/adapters/pi-readiness.d.ts +81 -5
- package/daemon/dist/adapters/pi-readiness.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-readiness.js +193 -27
- package/daemon/dist/adapters/pi-readiness.js.map +1 -1
- package/daemon/dist/adapters/pi-resume.d.ts +4 -0
- package/daemon/dist/adapters/pi-resume.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-resume.js +2 -0
- package/daemon/dist/adapters/pi-resume.js.map +1 -1
- package/daemon/dist/adapters/pi-runner-protocol.d.ts +4 -0
- package/daemon/dist/adapters/pi-runner-protocol.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-runner-protocol.js +5 -0
- package/daemon/dist/adapters/pi-runner-protocol.js.map +1 -1
- package/daemon/dist/adapters/pi-runner.d.ts +67 -2
- package/daemon/dist/adapters/pi-runner.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-runner.js +333 -13
- package/daemon/dist/adapters/pi-runner.js.map +1 -1
- package/daemon/dist/adapters/pi-runtime-adapter.d.ts +16 -1
- package/daemon/dist/adapters/pi-runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-runtime-adapter.js +17 -0
- package/daemon/dist/adapters/pi-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/preflight-exec.d.ts +3 -0
- package/daemon/dist/adapters/preflight-exec.d.ts.map +1 -1
- package/daemon/dist/adapters/preflight-exec.js +15 -7
- package/daemon/dist/adapters/preflight-exec.js.map +1 -1
- package/daemon/dist/adapters/replace-file.d.ts +8 -0
- package/daemon/dist/adapters/replace-file.d.ts.map +1 -0
- package/daemon/dist/adapters/replace-file.js +32 -0
- package/daemon/dist/adapters/replace-file.js.map +1 -0
- package/daemon/dist/adapters/tmux-control.d.ts +119 -0
- package/daemon/dist/adapters/tmux-control.d.ts.map +1 -0
- package/daemon/dist/adapters/tmux-control.js +547 -0
- package/daemon/dist/adapters/tmux-control.js.map +1 -0
- package/daemon/dist/adapters/tmux-exec.d.ts +18 -4
- package/daemon/dist/adapters/tmux-exec.d.ts.map +1 -1
- package/daemon/dist/adapters/tmux-exec.js +98 -17
- package/daemon/dist/adapters/tmux-exec.js.map +1 -1
- package/daemon/dist/adapters/tmux.d.ts +52 -15
- package/daemon/dist/adapters/tmux.d.ts.map +1 -1
- package/daemon/dist/adapters/tmux.js +232 -58
- package/daemon/dist/adapters/tmux.js.map +1 -1
- package/daemon/dist/build-info.js +3 -3
- package/daemon/dist/composer-prompts-surface.d.ts +2 -0
- package/daemon/dist/composer-prompts-surface.d.ts.map +1 -0
- package/daemon/dist/composer-prompts-surface.js +2 -0
- package/daemon/dist/composer-prompts-surface.js.map +1 -0
- package/daemon/dist/db/all-migrations.d.ts.map +1 -1
- package/daemon/dist/db/all-migrations.js +24 -0
- package/daemon/dist/db/all-migrations.js.map +1 -1
- package/daemon/dist/db/migrations/100_typing_guard_modes.d.ts +3 -0
- package/daemon/dist/db/migrations/100_typing_guard_modes.d.ts.map +1 -0
- package/daemon/dist/db/migrations/100_typing_guard_modes.js +10 -0
- package/daemon/dist/db/migrations/100_typing_guard_modes.js.map +1 -0
- package/daemon/dist/db/migrations/104_node_effort_choice.d.ts +3 -0
- package/daemon/dist/db/migrations/104_node_effort_choice.d.ts.map +1 -0
- package/daemon/dist/db/migrations/104_node_effort_choice.js +10 -0
- package/daemon/dist/db/migrations/104_node_effort_choice.js.map +1 -0
- package/daemon/dist/db/migrations/105_usage_samples_client_cost.d.ts +9 -0
- package/daemon/dist/db/migrations/105_usage_samples_client_cost.d.ts.map +1 -0
- package/daemon/dist/db/migrations/105_usage_samples_client_cost.js +14 -0
- package/daemon/dist/db/migrations/105_usage_samples_client_cost.js.map +1 -0
- package/daemon/dist/db/migrations/107_pi_usage_entries.d.ts +10 -0
- package/daemon/dist/db/migrations/107_pi_usage_entries.d.ts.map +1 -0
- package/daemon/dist/db/migrations/107_pi_usage_entries.js +38 -0
- package/daemon/dist/db/migrations/107_pi_usage_entries.js.map +1 -0
- package/daemon/dist/db/migrations/108_pi_usage_charges.d.ts +12 -0
- package/daemon/dist/db/migrations/108_pi_usage_charges.d.ts.map +1 -0
- package/daemon/dist/db/migrations/108_pi_usage_charges.js +39 -0
- package/daemon/dist/db/migrations/108_pi_usage_charges.js.map +1 -0
- package/daemon/dist/db/migrations/109_mode_posture_coexist.d.ts +5 -0
- package/daemon/dist/db/migrations/109_mode_posture_coexist.d.ts.map +1 -0
- package/daemon/dist/db/migrations/109_mode_posture_coexist.js +11 -0
- package/daemon/dist/db/migrations/109_mode_posture_coexist.js.map +1 -0
- package/daemon/dist/db/migrations/110_workflow_instance_scope.d.ts +10 -0
- package/daemon/dist/db/migrations/110_workflow_instance_scope.d.ts.map +1 -0
- package/daemon/dist/db/migrations/110_workflow_instance_scope.js +14 -0
- package/daemon/dist/db/migrations/110_workflow_instance_scope.js.map +1 -0
- package/daemon/dist/db/migrations/111_seat_env.d.ts +6 -0
- package/daemon/dist/db/migrations/111_seat_env.d.ts.map +1 -0
- package/daemon/dist/db/migrations/111_seat_env.js +11 -0
- package/daemon/dist/db/migrations/111_seat_env.js.map +1 -0
- package/daemon/dist/db/migrations/112_queue_transitions_ts_qitem.d.ts +3 -0
- package/daemon/dist/db/migrations/112_queue_transitions_ts_qitem.d.ts.map +1 -0
- package/daemon/dist/db/migrations/112_queue_transitions_ts_qitem.js +14 -0
- package/daemon/dist/db/migrations/112_queue_transitions_ts_qitem.js.map +1 -0
- package/daemon/dist/db/migrations/113_applied_launch_daemon_opt_out.d.ts +3 -0
- package/daemon/dist/db/migrations/113_applied_launch_daemon_opt_out.d.ts.map +1 -0
- package/daemon/dist/db/migrations/113_applied_launch_daemon_opt_out.js +9 -0
- package/daemon/dist/db/migrations/113_applied_launch_daemon_opt_out.js.map +1 -0
- package/daemon/dist/db/migrations/114_session_agent_app.d.ts +3 -0
- package/daemon/dist/db/migrations/114_session_agent_app.d.ts.map +1 -0
- package/daemon/dist/db/migrations/114_session_agent_app.js +11 -0
- package/daemon/dist/db/migrations/114_session_agent_app.js.map +1 -0
- package/daemon/dist/db/migrations/115_thread_seat_map_channel_key.d.ts +3 -0
- package/daemon/dist/db/migrations/115_thread_seat_map_channel_key.d.ts.map +1 -0
- package/daemon/dist/db/migrations/115_thread_seat_map_channel_key.js +28 -0
- package/daemon/dist/db/migrations/115_thread_seat_map_channel_key.js.map +1 -0
- package/daemon/dist/domain/activity-taxonomy.d.ts +13 -5
- package/daemon/dist/domain/activity-taxonomy.d.ts.map +1 -1
- package/daemon/dist/domain/activity-taxonomy.js +14 -2
- package/daemon/dist/domain/activity-taxonomy.js.map +1 -1
- package/daemon/dist/domain/agent-activity-store.d.ts +2 -0
- package/daemon/dist/domain/agent-activity-store.d.ts.map +1 -1
- package/daemon/dist/domain/agent-activity-store.js +14 -8
- package/daemon/dist/domain/agent-activity-store.js.map +1 -1
- package/daemon/dist/domain/agent-images/agent-image-library-service.js +1 -1
- package/daemon/dist/domain/agent-images/agent-image-library-service.js.map +1 -1
- package/daemon/dist/domain/agent-images/manifest-parser.js +1 -1
- package/daemon/dist/domain/agent-images/manifest-parser.js.map +1 -1
- package/daemon/dist/domain/agent-manifest.d.ts.map +1 -1
- package/daemon/dist/domain/agent-manifest.js +21 -3
- package/daemon/dist/domain/agent-manifest.js.map +1 -1
- package/daemon/dist/domain/applied-launch-observation-store.d.ts +2 -0
- package/daemon/dist/domain/applied-launch-observation-store.d.ts.map +1 -1
- package/daemon/dist/domain/applied-launch-observation-store.js +27 -3
- package/daemon/dist/domain/applied-launch-observation-store.js.map +1 -1
- package/daemon/dist/domain/bootstrap-orchestrator.d.ts +3 -0
- package/daemon/dist/domain/bootstrap-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/bootstrap-orchestrator.js +49 -16
- package/daemon/dist/domain/bootstrap-orchestrator.js.map +1 -1
- package/daemon/dist/domain/built-in-runtimes.d.ts +10 -0
- package/daemon/dist/domain/built-in-runtimes.d.ts.map +1 -0
- package/daemon/dist/domain/built-in-runtimes.js +14 -0
- package/daemon/dist/domain/built-in-runtimes.js.map +1 -0
- package/daemon/dist/domain/bundle-archive.d.ts +3 -1
- package/daemon/dist/domain/bundle-archive.d.ts.map +1 -1
- package/daemon/dist/domain/bundle-archive.js +5 -3
- package/daemon/dist/domain/bundle-archive.js.map +1 -1
- package/daemon/dist/domain/bundle-behaviour.d.ts +20 -0
- package/daemon/dist/domain/bundle-behaviour.d.ts.map +1 -1
- package/daemon/dist/domain/bundle-behaviour.js +57 -1
- package/daemon/dist/domain/bundle-behaviour.js.map +1 -1
- package/daemon/dist/domain/bundle-integrity.d.ts +5 -2
- package/daemon/dist/domain/bundle-integrity.d.ts.map +1 -1
- package/daemon/dist/domain/bundle-integrity.js +39 -8
- package/daemon/dist/domain/bundle-integrity.js.map +1 -1
- package/daemon/dist/domain/bundle-source-resolver.d.ts +3 -1
- package/daemon/dist/domain/bundle-source-resolver.d.ts.map +1 -1
- package/daemon/dist/domain/bundle-source-resolver.js +7 -2
- package/daemon/dist/domain/bundle-source-resolver.js.map +1 -1
- package/daemon/dist/domain/bundle-types.d.ts +6 -0
- package/daemon/dist/domain/bundle-types.d.ts.map +1 -1
- package/daemon/dist/domain/bundle-types.js +26 -0
- package/daemon/dist/domain/bundle-types.js.map +1 -1
- package/daemon/dist/domain/chat-repository.d.ts.map +1 -1
- package/daemon/dist/domain/chat-repository.js +3 -2
- package/daemon/dist/domain/chat-repository.js.map +1 -1
- package/daemon/dist/domain/claim-service.d.ts +6 -0
- package/daemon/dist/domain/claim-service.d.ts.map +1 -1
- package/daemon/dist/domain/claim-service.js +20 -1
- package/daemon/dist/domain/claim-service.js.map +1 -1
- package/daemon/dist/domain/claude-compaction-enforcer.d.ts +63 -2
- package/daemon/dist/domain/claude-compaction-enforcer.d.ts.map +1 -1
- package/daemon/dist/domain/claude-compaction-enforcer.js +154 -16
- package/daemon/dist/domain/claude-compaction-enforcer.js.map +1 -1
- package/daemon/dist/domain/claude-core-plugin-env.d.ts +26 -0
- package/daemon/dist/domain/claude-core-plugin-env.d.ts.map +1 -0
- package/daemon/dist/domain/claude-core-plugin-env.js +86 -0
- package/daemon/dist/domain/claude-core-plugin-env.js.map +1 -0
- package/daemon/dist/domain/claude-managed-launch.d.ts +13 -6
- package/daemon/dist/domain/claude-managed-launch.d.ts.map +1 -1
- package/daemon/dist/domain/claude-managed-launch.js +58 -5
- package/daemon/dist/domain/claude-managed-launch.js.map +1 -1
- package/daemon/dist/domain/codex-thread-id.d.ts +22 -3
- package/daemon/dist/domain/codex-thread-id.d.ts.map +1 -1
- package/daemon/dist/domain/codex-thread-id.js +65 -16
- package/daemon/dist/domain/codex-thread-id.js.map +1 -1
- package/daemon/dist/domain/community-harness-presets.d.ts +38 -0
- package/daemon/dist/domain/community-harness-presets.d.ts.map +1 -0
- package/daemon/dist/domain/community-harness-presets.js +155 -0
- package/daemon/dist/domain/community-harness-presets.js.map +1 -0
- package/daemon/dist/domain/composer-prompts.d.ts +118 -0
- package/daemon/dist/domain/composer-prompts.d.ts.map +1 -0
- package/daemon/dist/domain/composer-prompts.js +165 -0
- package/daemon/dist/domain/composer-prompts.js.map +1 -0
- package/daemon/dist/domain/context-monitor.d.ts +3 -0
- package/daemon/dist/domain/context-monitor.d.ts.map +1 -1
- package/daemon/dist/domain/context-monitor.js +14 -2
- package/daemon/dist/domain/context-monitor.js.map +1 -1
- package/daemon/dist/domain/context-packs/context-pack-library-service.js +2 -2
- package/daemon/dist/domain/context-packs/context-pack-library-service.js.map +1 -1
- package/daemon/dist/domain/context-packs/manifest-parser.js +1 -1
- package/daemon/dist/domain/context-packs/manifest-parser.js.map +1 -1
- package/daemon/dist/domain/context-usage-store.d.ts +4 -0
- package/daemon/dist/domain/context-usage-store.d.ts.map +1 -1
- package/daemon/dist/domain/context-usage-store.js +1 -0
- package/daemon/dist/domain/context-usage-store.js.map +1 -1
- package/daemon/dist/domain/current-work.js +2 -2
- package/daemon/dist/domain/current-work.js.map +1 -1
- package/daemon/dist/domain/draft-aware-delivery.d.ts +32 -0
- package/daemon/dist/domain/draft-aware-delivery.d.ts.map +1 -0
- package/daemon/dist/domain/draft-aware-delivery.js +156 -0
- package/daemon/dist/domain/draft-aware-delivery.js.map +1 -0
- package/daemon/dist/domain/event-bus.d.ts +10 -1
- package/daemon/dist/domain/event-bus.d.ts.map +1 -1
- package/daemon/dist/domain/event-bus.js +25 -0
- package/daemon/dist/domain/event-bus.js.map +1 -1
- package/daemon/dist/domain/execution-view.d.ts.map +1 -1
- package/daemon/dist/domain/execution-view.js +76 -10
- package/daemon/dist/domain/execution-view.js.map +1 -1
- package/daemon/dist/domain/frontmatter.d.ts +11 -0
- package/daemon/dist/domain/frontmatter.d.ts.map +1 -0
- package/daemon/dist/domain/frontmatter.js +20 -0
- package/daemon/dist/domain/frontmatter.js.map +1 -0
- package/daemon/dist/domain/gateway/gateway-subsystem.d.ts +11 -0
- package/daemon/dist/domain/gateway/gateway-subsystem.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/gateway-subsystem.js +7 -0
- package/daemon/dist/domain/gateway/gateway-subsystem.js.map +1 -1
- package/daemon/dist/domain/gateway/human-registry.d.ts +6 -1
- package/daemon/dist/domain/gateway/human-registry.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/human-registry.js +7 -0
- package/daemon/dist/domain/gateway/human-registry.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/bot-inbox.d.ts +54 -0
- package/daemon/dist/domain/gateway/slack/bot-inbox.d.ts.map +1 -0
- package/daemon/dist/domain/gateway/slack/bot-inbox.js +86 -0
- package/daemon/dist/domain/gateway/slack/bot-inbox.js.map +1 -0
- package/daemon/dist/domain/gateway/slack/capabilities.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/capabilities.js +1 -0
- package/daemon/dist/domain/gateway/slack/capabilities.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/channel-map.d.ts +11 -1
- package/daemon/dist/domain/gateway/slack/channel-map.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/channel-map.js +31 -6
- package/daemon/dist/domain/gateway/slack/channel-map.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/config.d.ts +14 -0
- package/daemon/dist/domain/gateway/slack/config.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/config.js +28 -0
- package/daemon/dist/domain/gateway/slack/config.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/forwarding.d.ts +32 -0
- package/daemon/dist/domain/gateway/slack/forwarding.d.ts.map +1 -0
- package/daemon/dist/domain/gateway/slack/forwarding.js +76 -0
- package/daemon/dist/domain/gateway/slack/forwarding.js.map +1 -0
- package/daemon/dist/domain/gateway/slack/inbound.d.ts +99 -1
- package/daemon/dist/domain/gateway/slack/inbound.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/inbound.js +325 -7
- package/daemon/dist/domain/gateway/slack/inbound.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/message.d.ts +28 -0
- package/daemon/dist/domain/gateway/slack/message.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/message.js +105 -4
- package/daemon/dist/domain/gateway/slack/message.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/outbound-driver.d.ts +3 -0
- package/daemon/dist/domain/gateway/slack/outbound-driver.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/outbound-driver.js +1 -0
- package/daemon/dist/domain/gateway/slack/outbound-driver.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/queue-access.d.ts +1 -1
- package/daemon/dist/domain/gateway/slack/queue-access.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-api.d.ts +10 -0
- package/daemon/dist/domain/gateway/slack/slack-api.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-api.js +13 -0
- package/daemon/dist/domain/gateway/slack/slack-api.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts +2 -0
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.js +12 -2
- package/daemon/dist/domain/gateway/slack/slack-delivery.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts +3 -0
- package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js +133 -20
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/state-store.d.ts +9 -2
- package/daemon/dist/domain/gateway/slack/state-store.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/state-store.js +24 -2
- package/daemon/dist/domain/gateway/slack/state-store.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/thread-routing.d.ts +3 -0
- package/daemon/dist/domain/gateway/slack/thread-routing.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/thread-routing.js +18 -7
- package/daemon/dist/domain/gateway/slack/thread-routing.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/thread-seat-map.d.ts +17 -6
- package/daemon/dist/domain/gateway/slack/thread-seat-map.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/thread-seat-map.js +41 -23
- package/daemon/dist/domain/gateway/slack/thread-seat-map.js.map +1 -1
- package/daemon/dist/domain/history-query.d.ts.map +1 -1
- package/daemon/dist/domain/history-query.js +2 -1
- package/daemon/dist/domain/history-query.js.map +1 -1
- package/daemon/dist/domain/home-isolation.d.ts +18 -0
- package/daemon/dist/domain/home-isolation.d.ts.map +1 -0
- package/daemon/dist/domain/home-isolation.js +75 -0
- package/daemon/dist/domain/home-isolation.js.map +1 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts +22 -0
- package/daemon/dist/domain/hosts/remote-daemon-http.d.ts.map +1 -1
- package/daemon/dist/domain/hosts/remote-daemon-http.js +43 -3
- package/daemon/dist/domain/hosts/remote-daemon-http.js.map +1 -1
- package/daemon/dist/domain/inbox-handler.d.ts.map +1 -1
- package/daemon/dist/domain/inbox-handler.js +34 -32
- package/daemon/dist/domain/inbox-handler.js.map +1 -1
- package/daemon/dist/domain/mission-control/notification-adapter-ntfy.js +12 -2
- package/daemon/dist/domain/mission-control/notification-adapter-ntfy.js.map +1 -1
- package/daemon/dist/domain/mission-control/notification-dispatcher.d.ts.map +1 -1
- package/daemon/dist/domain/mission-control/notification-dispatcher.js +2 -1
- package/daemon/dist/domain/mission-control/notification-dispatcher.js.map +1 -1
- package/daemon/dist/domain/model-divergence/current-generation-record.d.ts +63 -0
- package/daemon/dist/domain/model-divergence/current-generation-record.d.ts.map +1 -1
- package/daemon/dist/domain/model-divergence/current-generation-record.js +130 -1
- package/daemon/dist/domain/model-divergence/current-generation-record.js.map +1 -1
- package/daemon/dist/domain/model-divergence/effective-model-readers.d.ts +42 -0
- package/daemon/dist/domain/model-divergence/effective-model-readers.d.ts.map +1 -1
- package/daemon/dist/domain/model-divergence/effective-model-readers.js +160 -5
- package/daemon/dist/domain/model-divergence/effective-model-readers.js.map +1 -1
- package/daemon/dist/domain/model-divergence/model-divergence-monitor.d.ts +8 -2
- package/daemon/dist/domain/model-divergence/model-divergence-monitor.d.ts.map +1 -1
- package/daemon/dist/domain/model-divergence/model-divergence-monitor.js +10 -5
- package/daemon/dist/domain/model-divergence/model-divergence-monitor.js.map +1 -1
- package/daemon/dist/domain/native-process-lineage.d.ts +49 -0
- package/daemon/dist/domain/native-process-lineage.d.ts.map +1 -1
- package/daemon/dist/domain/native-process-lineage.js +129 -15
- package/daemon/dist/domain/native-process-lineage.js.map +1 -1
- package/daemon/dist/domain/native-resume-probe.d.ts +3 -1
- package/daemon/dist/domain/native-resume-probe.d.ts.map +1 -1
- package/daemon/dist/domain/native-resume-probe.js +43 -6
- package/daemon/dist/domain/native-resume-probe.js.map +1 -1
- package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
- package/daemon/dist/domain/node-inventory.js +26 -9
- package/daemon/dist/domain/node-inventory.js.map +1 -1
- package/daemon/dist/domain/node-launcher.d.ts +18 -0
- package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/node-launcher.js +60 -2
- package/daemon/dist/domain/node-launcher.js.map +1 -1
- package/daemon/dist/domain/openrouter-charge.d.ts +42 -0
- package/daemon/dist/domain/openrouter-charge.d.ts.map +1 -0
- package/daemon/dist/domain/openrouter-charge.js +57 -0
- package/daemon/dist/domain/openrouter-charge.js.map +1 -0
- package/daemon/dist/domain/parked-query.d.ts +5 -0
- package/daemon/dist/domain/parked-query.d.ts.map +1 -1
- package/daemon/dist/domain/parked-query.js +9 -2
- package/daemon/dist/domain/parked-query.js.map +1 -1
- package/daemon/dist/domain/periodic-snapshot-scheduler.d.ts.map +1 -1
- package/daemon/dist/domain/periodic-snapshot-scheduler.js +27 -3
- package/daemon/dist/domain/periodic-snapshot-scheduler.js.map +1 -1
- package/daemon/dist/domain/permission-drift.d.ts +2 -0
- package/daemon/dist/domain/permission-drift.d.ts.map +1 -1
- package/daemon/dist/domain/permission-drift.js.map +1 -1
- package/daemon/dist/domain/permission-policy/policy-spec.d.ts.map +1 -1
- package/daemon/dist/domain/permission-policy/policy-spec.js +5 -7
- package/daemon/dist/domain/permission-policy/policy-spec.js.map +1 -1
- package/daemon/dist/domain/pi-usage-entries-store.d.ts +41 -0
- package/daemon/dist/domain/pi-usage-entries-store.d.ts.map +1 -0
- package/daemon/dist/domain/pi-usage-entries-store.js +173 -0
- package/daemon/dist/domain/pi-usage-entries-store.js.map +1 -0
- package/daemon/dist/domain/pi-usage-facts.d.ts +90 -0
- package/daemon/dist/domain/pi-usage-facts.d.ts.map +1 -0
- package/daemon/dist/domain/pi-usage-facts.js +165 -0
- package/daemon/dist/domain/pi-usage-facts.js.map +1 -0
- package/daemon/dist/domain/pod-bundle-assembler.d.ts +2 -0
- package/daemon/dist/domain/pod-bundle-assembler.d.ts.map +1 -1
- package/daemon/dist/domain/pod-bundle-assembler.js +4 -0
- package/daemon/dist/domain/pod-bundle-assembler.js.map +1 -1
- package/daemon/dist/domain/policies/artifact-pool-helpers.d.ts.map +1 -1
- package/daemon/dist/domain/policies/artifact-pool-helpers.js +6 -4
- package/daemon/dist/domain/policies/artifact-pool-helpers.js.map +1 -1
- package/daemon/dist/domain/profile-resolver.d.ts +2 -0
- package/daemon/dist/domain/profile-resolver.d.ts.map +1 -1
- package/daemon/dist/domain/profile-resolver.js +4 -0
- package/daemon/dist/domain/profile-resolver.js.map +1 -1
- package/daemon/dist/domain/progress/progress-indexer.js +1 -1
- package/daemon/dist/domain/progress/progress-indexer.js.map +1 -1
- package/daemon/dist/domain/project-lifecycle-compiler.d.ts +4 -0
- package/daemon/dist/domain/project-lifecycle-compiler.d.ts.map +1 -1
- package/daemon/dist/domain/project-lifecycle-compiler.js +63 -3
- package/daemon/dist/domain/project-lifecycle-compiler.js.map +1 -1
- package/daemon/dist/domain/proof/judgments.d.ts.map +1 -1
- package/daemon/dist/domain/proof/judgments.js +31 -2
- package/daemon/dist/domain/proof/judgments.js.map +1 -1
- package/daemon/dist/domain/provider/claude-auth-status-reader.d.ts +33 -0
- package/daemon/dist/domain/provider/claude-auth-status-reader.d.ts.map +1 -0
- package/daemon/dist/domain/provider/claude-auth-status-reader.js +195 -0
- package/daemon/dist/domain/provider/claude-auth-status-reader.js.map +1 -0
- package/daemon/dist/domain/provider/claude-usage-windows.d.ts +68 -0
- package/daemon/dist/domain/provider/claude-usage-windows.d.ts.map +1 -0
- package/daemon/dist/domain/provider/claude-usage-windows.js +260 -0
- package/daemon/dist/domain/provider/claude-usage-windows.js.map +1 -0
- package/daemon/dist/domain/provider/codex-auth-reader.d.ts +14 -0
- package/daemon/dist/domain/provider/codex-auth-reader.d.ts.map +1 -1
- package/daemon/dist/domain/provider/codex-auth-reader.js +41 -12
- package/daemon/dist/domain/provider/codex-auth-reader.js.map +1 -1
- package/daemon/dist/domain/provider/codex-usage-reader.d.ts +2 -0
- package/daemon/dist/domain/provider/codex-usage-reader.d.ts.map +1 -1
- package/daemon/dist/domain/provider/codex-usage-reader.js +3 -1
- package/daemon/dist/domain/provider/codex-usage-reader.js.map +1 -1
- package/daemon/dist/domain/provider/codex-usage-windows.d.ts +10 -49
- package/daemon/dist/domain/provider/codex-usage-windows.d.ts.map +1 -1
- package/daemon/dist/domain/provider/codex-usage-windows.js +15 -173
- package/daemon/dist/domain/provider/codex-usage-windows.js.map +1 -1
- package/daemon/dist/domain/provider/provider-seat-accounts.d.ts +18 -0
- package/daemon/dist/domain/provider/provider-seat-accounts.d.ts.map +1 -0
- package/daemon/dist/domain/provider/provider-seat-accounts.js +159 -0
- package/daemon/dist/domain/provider/provider-seat-accounts.js.map +1 -0
- package/daemon/dist/domain/provider/provider-service-impl.d.ts +7 -2
- package/daemon/dist/domain/provider/provider-service-impl.d.ts.map +1 -1
- package/daemon/dist/domain/provider/provider-service-impl.js +20 -2
- package/daemon/dist/domain/provider/provider-service-impl.js.map +1 -1
- package/daemon/dist/domain/provider/provider-service.d.ts +4 -2
- package/daemon/dist/domain/provider/provider-service.d.ts.map +1 -1
- package/daemon/dist/domain/provider/provider-types.d.ts +72 -4
- package/daemon/dist/domain/provider/provider-types.d.ts.map +1 -1
- package/daemon/dist/domain/provider/reactive-tap.js +2 -0
- package/daemon/dist/domain/provider/reactive-tap.js.map +1 -1
- package/daemon/dist/domain/provider/subscription-moves.d.ts +77 -0
- package/daemon/dist/domain/provider/subscription-moves.d.ts.map +1 -0
- package/daemon/dist/domain/provider/subscription-moves.js +140 -0
- package/daemon/dist/domain/provider/subscription-moves.js.map +1 -0
- package/daemon/dist/domain/provider/usage-across-hosts.d.ts +162 -0
- package/daemon/dist/domain/provider/usage-across-hosts.d.ts.map +1 -0
- package/daemon/dist/domain/provider/usage-across-hosts.js +563 -0
- package/daemon/dist/domain/provider/usage-across-hosts.js.map +1 -0
- package/daemon/dist/domain/provider/usage-windows.d.ts +88 -0
- package/daemon/dist/domain/provider/usage-windows.d.ts.map +1 -0
- package/daemon/dist/domain/provider/usage-windows.js +189 -0
- package/daemon/dist/domain/provider/usage-windows.js.map +1 -0
- package/daemon/dist/domain/queue-owner.d.ts +6 -0
- package/daemon/dist/domain/queue-owner.d.ts.map +1 -1
- package/daemon/dist/domain/queue-owner.js +22 -0
- package/daemon/dist/domain/queue-owner.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +89 -14
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +331 -86
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/queue-stuck-sweep.d.ts.map +1 -1
- package/daemon/dist/domain/queue-stuck-sweep.js +9 -21
- package/daemon/dist/domain/queue-stuck-sweep.js.map +1 -1
- package/daemon/dist/domain/queue-transition-log.d.ts +23 -2
- package/daemon/dist/domain/queue-transition-log.d.ts.map +1 -1
- package/daemon/dist/domain/queue-transition-log.js +98 -39
- package/daemon/dist/domain/queue-transition-log.js.map +1 -1
- package/daemon/dist/domain/queue-wake-ladder.d.ts +2 -2
- package/daemon/dist/domain/queue-wake-ladder.d.ts.map +1 -1
- package/daemon/dist/domain/queue-wake-ladder.js +27 -8
- package/daemon/dist/domain/queue-wake-ladder.js.map +1 -1
- package/daemon/dist/domain/queue-wake-repository.d.ts +7 -1
- package/daemon/dist/domain/queue-wake-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-wake-repository.js +54 -5
- package/daemon/dist/domain/queue-wake-repository.js.map +1 -1
- package/daemon/dist/domain/reply-to-choice.d.ts +2 -0
- package/daemon/dist/domain/reply-to-choice.d.ts.map +1 -1
- package/daemon/dist/domain/reply-to-choice.js +2 -0
- package/daemon/dist/domain/reply-to-choice.js.map +1 -1
- package/daemon/dist/domain/restore-check-service.d.ts.map +1 -1
- package/daemon/dist/domain/restore-check-service.js +3 -2
- package/daemon/dist/domain/restore-check-service.js.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.js +78 -14
- package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
- package/daemon/dist/domain/restore-plan-preview.d.ts.map +1 -1
- package/daemon/dist/domain/restore-plan-preview.js +23 -19
- package/daemon/dist/domain/restore-plan-preview.js.map +1 -1
- package/daemon/dist/domain/restore-policy.d.ts +26 -0
- package/daemon/dist/domain/restore-policy.d.ts.map +1 -0
- package/daemon/dist/domain/restore-policy.js +22 -0
- package/daemon/dist/domain/restore-policy.js.map +1 -0
- package/daemon/dist/domain/restore-preconditions.d.ts +2 -0
- package/daemon/dist/domain/restore-preconditions.d.ts.map +1 -1
- package/daemon/dist/domain/restore-preconditions.js +6 -1
- package/daemon/dist/domain/restore-preconditions.js.map +1 -1
- package/daemon/dist/domain/restore-runtime-compatibility.d.ts +3 -0
- package/daemon/dist/domain/restore-runtime-compatibility.d.ts.map +1 -0
- package/daemon/dist/domain/restore-runtime-compatibility.js +7 -0
- package/daemon/dist/domain/restore-runtime-compatibility.js.map +1 -0
- package/daemon/dist/domain/resume-metadata-refresher.d.ts +10 -5
- package/daemon/dist/domain/resume-metadata-refresher.d.ts.map +1 -1
- package/daemon/dist/domain/resume-metadata-refresher.js +109 -21
- package/daemon/dist/domain/resume-metadata-refresher.js.map +1 -1
- package/daemon/dist/domain/resume-token-capture.d.ts +1 -1
- package/daemon/dist/domain/resume-token-capture.d.ts.map +1 -1
- package/daemon/dist/domain/resume-token-capture.js +7 -1
- package/daemon/dist/domain/resume-token-capture.js.map +1 -1
- package/daemon/dist/domain/review/compose.d.ts.map +1 -1
- package/daemon/dist/domain/review/compose.js +5 -4
- package/daemon/dist/domain/review/compose.js.map +1 -1
- package/daemon/dist/domain/review/gather.d.ts +2 -1
- package/daemon/dist/domain/review/gather.d.ts.map +1 -1
- package/daemon/dist/domain/review/gather.js +25 -17
- package/daemon/dist/domain/review/gather.js.map +1 -1
- package/daemon/dist/domain/rig-lifecycle-service.d.ts +31 -8
- package/daemon/dist/domain/rig-lifecycle-service.d.ts.map +1 -1
- package/daemon/dist/domain/rig-lifecycle-service.js +253 -98
- package/daemon/dist/domain/rig-lifecycle-service.js.map +1 -1
- package/daemon/dist/domain/rig-mode/rig-mode-store.d.ts +11 -1
- package/daemon/dist/domain/rig-mode/rig-mode-store.d.ts.map +1 -1
- package/daemon/dist/domain/rig-mode/rig-mode-store.js +69 -48
- package/daemon/dist/domain/rig-mode/rig-mode-store.js.map +1 -1
- package/daemon/dist/domain/rig-repository.d.ts +18 -0
- package/daemon/dist/domain/rig-repository.d.ts.map +1 -1
- package/daemon/dist/domain/rig-repository.js +52 -1
- package/daemon/dist/domain/rig-repository.js.map +1 -1
- package/daemon/dist/domain/rig-teardown.d.ts +4 -1
- package/daemon/dist/domain/rig-teardown.d.ts.map +1 -1
- package/daemon/dist/domain/rig-teardown.js +40 -2
- package/daemon/dist/domain/rig-teardown.js.map +1 -1
- package/daemon/dist/domain/rigspec-codec.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-codec.js +7 -0
- package/daemon/dist/domain/rigspec-codec.js.map +1 -1
- package/daemon/dist/domain/rigspec-exporter.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-exporter.js +4 -0
- package/daemon/dist/domain/rigspec-exporter.js.map +1 -1
- package/daemon/dist/domain/rigspec-instantiator.d.ts +3 -0
- package/daemon/dist/domain/rigspec-instantiator.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-instantiator.js +28 -5
- package/daemon/dist/domain/rigspec-instantiator.js.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.js +18 -5
- package/daemon/dist/domain/rigspec-preflight.js.map +1 -1
- package/daemon/dist/domain/rigspec-schema.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-schema.js +24 -2
- package/daemon/dist/domain/rigspec-schema.js.map +1 -1
- package/daemon/dist/domain/runtime-adapter.d.ts +11 -0
- package/daemon/dist/domain/runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/domain/runtime-adapter.js.map +1 -1
- package/daemon/dist/domain/runtime-verifier.d.ts +15 -1
- package/daemon/dist/domain/runtime-verifier.d.ts.map +1 -1
- package/daemon/dist/domain/runtime-verifier.js +28 -4
- package/daemon/dist/domain/runtime-verifier.js.map +1 -1
- package/daemon/dist/domain/scope/logical-checkbox.js +1 -1
- package/daemon/dist/domain/scope/logical-checkbox.js.map +1 -1
- package/daemon/dist/domain/scope/scope-view-projection.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-view-projection.js +23 -10
- package/daemon/dist/domain/scope/scope-view-projection.js.map +1 -1
- package/daemon/dist/domain/seat-activity-service.d.ts +9 -2
- package/daemon/dist/domain/seat-activity-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-activity-service.js +100 -15
- package/daemon/dist/domain/seat-activity-service.js.map +1 -1
- package/daemon/dist/domain/seat-attention-reconciler.d.ts +3 -0
- package/daemon/dist/domain/seat-attention-reconciler.d.ts.map +1 -1
- package/daemon/dist/domain/seat-attention-reconciler.js +114 -7
- package/daemon/dist/domain/seat-attention-reconciler.js.map +1 -1
- package/daemon/dist/domain/seat-delivery-guard.d.ts +36 -1
- package/daemon/dist/domain/seat-delivery-guard.d.ts.map +1 -1
- package/daemon/dist/domain/seat-delivery-guard.js +255 -22
- package/daemon/dist/domain/seat-delivery-guard.js.map +1 -1
- package/daemon/dist/domain/seat-effort.d.ts +115 -0
- package/daemon/dist/domain/seat-effort.d.ts.map +1 -0
- package/daemon/dist/domain/seat-effort.js +233 -0
- package/daemon/dist/domain/seat-effort.js.map +1 -0
- package/daemon/dist/domain/seat-env.d.ts +37 -0
- package/daemon/dist/domain/seat-env.d.ts.map +1 -0
- package/daemon/dist/domain/seat-env.js +94 -0
- package/daemon/dist/domain/seat-env.js.map +1 -0
- package/daemon/dist/domain/seat-handover-service.d.ts +5 -0
- package/daemon/dist/domain/seat-handover-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-handover-service.js +22 -4
- package/daemon/dist/domain/seat-handover-service.js.map +1 -1
- package/daemon/dist/domain/seat-identity-reconciler.d.ts.map +1 -1
- package/daemon/dist/domain/seat-identity-reconciler.js +19 -2
- package/daemon/dist/domain/seat-identity-reconciler.js.map +1 -1
- package/daemon/dist/domain/seat-lifecycle-service.d.ts +119 -5
- package/daemon/dist/domain/seat-lifecycle-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-lifecycle-service.js +318 -23
- package/daemon/dist/domain/seat-lifecycle-service.js.map +1 -1
- package/daemon/dist/domain/seat-restart-in-place.d.ts +13 -2
- package/daemon/dist/domain/seat-restart-in-place.d.ts.map +1 -1
- package/daemon/dist/domain/seat-restart-in-place.js +23 -4
- package/daemon/dist/domain/seat-restart-in-place.js.map +1 -1
- package/daemon/dist/domain/seat-structural-activity-service.d.ts +6 -2
- package/daemon/dist/domain/seat-structural-activity-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-structural-activity-service.js +45 -9
- package/daemon/dist/domain/seat-structural-activity-service.js.map +1 -1
- package/daemon/dist/domain/session-registry.d.ts +13 -1
- package/daemon/dist/domain/session-registry.d.ts.map +1 -1
- package/daemon/dist/domain/session-registry.js +29 -2
- package/daemon/dist/domain/session-registry.js.map +1 -1
- package/daemon/dist/domain/session-transport.d.ts +108 -1
- package/daemon/dist/domain/session-transport.d.ts.map +1 -1
- package/daemon/dist/domain/session-transport.js +1116 -129
- package/daemon/dist/domain/session-transport.js.map +1 -1
- package/daemon/dist/domain/slices/slice-detail-projector.d.ts +2 -0
- package/daemon/dist/domain/slices/slice-detail-projector.d.ts.map +1 -1
- package/daemon/dist/domain/slices/slice-detail-projector.js +16 -4
- package/daemon/dist/domain/slices/slice-detail-projector.js.map +1 -1
- package/daemon/dist/domain/slices/slice-indexer.d.ts +10 -0
- package/daemon/dist/domain/slices/slice-indexer.d.ts.map +1 -1
- package/daemon/dist/domain/slices/slice-indexer.js +84 -4
- package/daemon/dist/domain/slices/slice-indexer.js.map +1 -1
- package/daemon/dist/domain/snapshot-repository.d.ts +3 -0
- package/daemon/dist/domain/snapshot-repository.d.ts.map +1 -1
- package/daemon/dist/domain/snapshot-repository.js +19 -2
- package/daemon/dist/domain/snapshot-repository.js.map +1 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.d.ts +1 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.d.ts.map +1 -1
- package/daemon/dist/domain/spec-library-workflow-scanner.js +24 -21
- package/daemon/dist/domain/spec-library-workflow-scanner.js.map +1 -1
- package/daemon/dist/domain/spec-validation-advisory.js +1 -1
- package/daemon/dist/domain/spec-validation-advisory.js.map +1 -1
- package/daemon/dist/domain/startup-orchestrator.d.ts +31 -3
- package/daemon/dist/domain/startup-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/startup-orchestrator.js +218 -60
- package/daemon/dist/domain/startup-orchestrator.js.map +1 -1
- package/daemon/dist/domain/startup-submission-evidence.d.ts +13 -3
- package/daemon/dist/domain/startup-submission-evidence.d.ts.map +1 -1
- package/daemon/dist/domain/startup-submission-evidence.js +34 -25
- package/daemon/dist/domain/startup-submission-evidence.js.map +1 -1
- package/daemon/dist/domain/startup-validation.d.ts.map +1 -1
- package/daemon/dist/domain/startup-validation.js +3 -0
- package/daemon/dist/domain/startup-validation.js.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.d.ts +7 -1
- package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.js +5 -1
- package/daemon/dist/domain/successor-session-launcher.js.map +1 -1
- package/daemon/dist/domain/text-cut.d.ts +4 -0
- package/daemon/dist/domain/text-cut.d.ts.map +1 -0
- package/daemon/dist/domain/text-cut.js +7 -0
- package/daemon/dist/domain/text-cut.js.map +1 -0
- package/daemon/dist/domain/time-bound-advisory.d.ts +5 -0
- package/daemon/dist/domain/time-bound-advisory.d.ts.map +1 -0
- package/daemon/dist/domain/time-bound-advisory.js +25 -0
- package/daemon/dist/domain/time-bound-advisory.js.map +1 -0
- package/daemon/dist/domain/transcript-capture.d.ts +7 -0
- package/daemon/dist/domain/transcript-capture.d.ts.map +1 -1
- package/daemon/dist/domain/transcript-capture.js +22 -1
- package/daemon/dist/domain/transcript-capture.js.map +1 -1
- package/daemon/dist/domain/transcript-rotation.d.ts +12 -4
- package/daemon/dist/domain/transcript-rotation.d.ts.map +1 -1
- package/daemon/dist/domain/transcript-rotation.js +185 -23
- package/daemon/dist/domain/transcript-rotation.js.map +1 -1
- package/daemon/dist/domain/transcript-store.d.ts +1 -1
- package/daemon/dist/domain/transcript-store.d.ts.map +1 -1
- package/daemon/dist/domain/transcript-store.js +8 -4
- package/daemon/dist/domain/transcript-store.js.map +1 -1
- package/daemon/dist/domain/types.d.ts +53 -3
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/usage-samples-store.d.ts +8 -0
- package/daemon/dist/domain/usage-samples-store.d.ts.map +1 -1
- package/daemon/dist/domain/usage-samples-store.js +24 -5
- package/daemon/dist/domain/usage-samples-store.js.map +1 -1
- package/daemon/dist/domain/usage-series.d.ts +154 -2
- package/daemon/dist/domain/usage-series.d.ts.map +1 -1
- package/daemon/dist/domain/usage-series.js +208 -9
- package/daemon/dist/domain/usage-series.js.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts +1 -1
- package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
- package/daemon/dist/domain/user-settings/settings-store.js +37 -1
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/watchdog-jobs-repository.d.ts +3 -1
- package/daemon/dist/domain/watchdog-jobs-repository.d.ts.map +1 -1
- package/daemon/dist/domain/watchdog-jobs-repository.js +13 -1
- package/daemon/dist/domain/watchdog-jobs-repository.js.map +1 -1
- package/daemon/dist/domain/watchdog-policy-engine.d.ts +1 -0
- package/daemon/dist/domain/watchdog-policy-engine.d.ts.map +1 -1
- package/daemon/dist/domain/watchdog-policy-engine.js +31 -2
- package/daemon/dist/domain/watchdog-policy-engine.js.map +1 -1
- package/daemon/dist/domain/workflow-exception-escalation.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-exception-escalation.js +3 -1
- package/daemon/dist/domain/workflow-exception-escalation.js.map +1 -1
- package/daemon/dist/domain/workflow-exception-readiness.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-exception-readiness.js +7 -3
- package/daemon/dist/domain/workflow-exception-readiness.js.map +1 -1
- package/daemon/dist/domain/workflow-instance-store.d.ts +11 -0
- package/daemon/dist/domain/workflow-instance-store.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-instance-store.js +18 -4
- package/daemon/dist/domain/workflow-instance-store.js.map +1 -1
- package/daemon/dist/domain/workflow-projector.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-projector.js +5 -3
- package/daemon/dist/domain/workflow-projector.js.map +1 -1
- package/daemon/dist/domain/workflow-rig-placeholder.d.ts +24 -0
- package/daemon/dist/domain/workflow-rig-placeholder.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-rig-placeholder.js +43 -0
- package/daemon/dist/domain/workflow-rig-placeholder.js.map +1 -0
- package/daemon/dist/domain/workflow-runtime.d.ts +10 -1
- package/daemon/dist/domain/workflow-runtime.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-runtime.js +26 -13
- package/daemon/dist/domain/workflow-runtime.js.map +1 -1
- package/daemon/dist/domain/workflow-scope.d.ts +50 -0
- package/daemon/dist/domain/workflow-scope.d.ts.map +1 -0
- package/daemon/dist/domain/workflow-scope.js +129 -0
- package/daemon/dist/domain/workflow-scope.js.map +1 -0
- package/daemon/dist/domain/workflow-spec-cache.d.ts +10 -0
- package/daemon/dist/domain/workflow-spec-cache.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-spec-cache.js +13 -0
- package/daemon/dist/domain/workflow-spec-cache.js.map +1 -1
- package/daemon/dist/domain/workflow-types.d.ts +4 -0
- package/daemon/dist/domain/workflow-types.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-types.js.map +1 -1
- package/daemon/dist/domain/workflow-validator.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-validator.js +3 -1
- package/daemon/dist/domain/workflow-validator.js.map +1 -1
- package/daemon/dist/domain/workspace/project-read.d.ts +2 -0
- package/daemon/dist/domain/workspace/project-read.d.ts.map +1 -1
- package/daemon/dist/domain/workspace/project-read.js +4 -0
- package/daemon/dist/domain/workspace/project-read.js.map +1 -1
- package/daemon/dist/index.d.ts.map +1 -1
- package/daemon/dist/index.js +10 -0
- package/daemon/dist/index.js.map +1 -1
- package/daemon/dist/lib/pane-envelope.js +1 -1
- package/daemon/dist/lib/pane-envelope.js.map +1 -1
- package/daemon/dist/middleware/json-body-error.d.ts +4 -3
- package/daemon/dist/middleware/json-body-error.d.ts.map +1 -1
- package/daemon/dist/middleware/json-body-error.js +18 -4
- package/daemon/dist/middleware/json-body-error.js.map +1 -1
- package/daemon/dist/routes/activity.d.ts.map +1 -1
- package/daemon/dist/routes/activity.js +78 -0
- package/daemon/dist/routes/activity.js.map +1 -1
- package/daemon/dist/routes/bundles.d.ts.map +1 -1
- package/daemon/dist/routes/bundles.js +39 -16
- package/daemon/dist/routes/bundles.js.map +1 -1
- package/daemon/dist/routes/community-harnesses.d.ts +3 -0
- package/daemon/dist/routes/community-harnesses.d.ts.map +1 -0
- package/daemon/dist/routes/community-harnesses.js +18 -0
- package/daemon/dist/routes/community-harnesses.js.map +1 -0
- package/daemon/dist/routes/compaction.d.ts +3 -0
- package/daemon/dist/routes/compaction.d.ts.map +1 -1
- package/daemon/dist/routes/compaction.js +14 -2
- package/daemon/dist/routes/compaction.js.map +1 -1
- package/daemon/dist/routes/events.js +26 -12
- package/daemon/dist/routes/events.js.map +1 -1
- package/daemon/dist/routes/gateway.d.ts +3 -0
- package/daemon/dist/routes/gateway.d.ts.map +1 -1
- package/daemon/dist/routes/gateway.js +15 -0
- package/daemon/dist/routes/gateway.js.map +1 -1
- package/daemon/dist/routes/hosts.d.ts.map +1 -1
- package/daemon/dist/routes/hosts.js +2 -1
- package/daemon/dist/routes/hosts.js.map +1 -1
- package/daemon/dist/routes/provider.d.ts +11 -1
- package/daemon/dist/routes/provider.d.ts.map +1 -1
- package/daemon/dist/routes/provider.js +182 -10
- package/daemon/dist/routes/provider.js.map +1 -1
- package/daemon/dist/routes/query-limit.d.ts +1 -0
- package/daemon/dist/routes/query-limit.d.ts.map +1 -1
- package/daemon/dist/routes/query-limit.js +1 -1
- package/daemon/dist/routes/query-limit.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +44 -3
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/rig-mode.d.ts.map +1 -1
- package/daemon/dist/routes/rig-mode.js +66 -11
- package/daemon/dist/routes/rig-mode.js.map +1 -1
- package/daemon/dist/routes/scope-audit.d.ts.map +1 -1
- package/daemon/dist/routes/scope-audit.js +10 -0
- package/daemon/dist/routes/scope-audit.js.map +1 -1
- package/daemon/dist/routes/seat.d.ts.map +1 -1
- package/daemon/dist/routes/seat.js +63 -12
- package/daemon/dist/routes/seat.js.map +1 -1
- package/daemon/dist/routes/sessions.js +1 -1
- package/daemon/dist/routes/startup.d.ts.map +1 -1
- package/daemon/dist/routes/startup.js +9 -2
- package/daemon/dist/routes/startup.js.map +1 -1
- package/daemon/dist/routes/telemetry.d.ts.map +1 -1
- package/daemon/dist/routes/telemetry.js +13 -3
- package/daemon/dist/routes/telemetry.js.map +1 -1
- package/daemon/dist/routes/transcripts.d.ts.map +1 -1
- package/daemon/dist/routes/transcripts.js +14 -5
- package/daemon/dist/routes/transcripts.js.map +1 -1
- package/daemon/dist/routes/transport.d.ts.map +1 -1
- package/daemon/dist/routes/transport.js +13 -2
- package/daemon/dist/routes/transport.js.map +1 -1
- package/daemon/dist/routes/up.d.ts.map +1 -1
- package/daemon/dist/routes/up.js +4 -0
- package/daemon/dist/routes/up.js.map +1 -1
- package/daemon/dist/routes/workflow.d.ts.map +1 -1
- package/daemon/dist/routes/workflow.js +50 -1
- package/daemon/dist/routes/workflow.js.map +1 -1
- package/daemon/dist/server.d.ts.map +1 -1
- package/daemon/dist/server.js +4 -1
- package/daemon/dist/server.js.map +1 -1
- package/daemon/dist/spec-validation-surface.d.ts +1 -1
- package/daemon/dist/spec-validation-surface.d.ts.map +1 -1
- package/daemon/dist/spec-validation-surface.js +1 -1
- package/daemon/dist/spec-validation-surface.js.map +1 -1
- package/daemon/dist/startup.d.ts +3 -1
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +140 -54
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/dist/workflow-scope-surface.d.ts +2 -0
- package/daemon/dist/workflow-scope-surface.d.ts.map +1 -0
- package/daemon/dist/workflow-scope-surface.js +2 -0
- package/daemon/dist/workflow-scope-surface.js.map +1 -0
- package/daemon/docs/reference/agent-spec.md +4 -2
- package/daemon/docs/reference/agent-startup-guide.md +95 -6
- package/daemon/docs/reference/claude-status-line.md +69 -0
- package/daemon/docs/reference/getting-started.md +39 -6
- package/daemon/docs/reference/keeping-a-watch.md +102 -5
- package/daemon/docs/reference/project-workspace.md +70 -1
- package/daemon/docs/reference/publishing-a-rig-bundle.md +6 -1
- package/daemon/docs/reference/refocus-channel.md +41 -5
- package/daemon/docs/reference/rig-bundle.md +26 -2
- package/daemon/docs/reference/rig-spec.md +64 -3
- package/daemon/docs/reference/schemas/bundle-behaviour.v1.schema.json +105 -2
- package/daemon/docs/reference/scoped-operating-posture.md +12 -2
- package/daemon/docs/reference/sdlc-conventions.md +39 -1
- package/daemon/docs/reference/seat-delivery.md +127 -0
- package/daemon/docs/reference/slack-app-setup.md +141 -13
- package/daemon/docs/reference/topology-naming.md +9 -0
- package/daemon/docs/reference/whats-new.md +167 -146
- package/daemon/specs/agents/shared/skills/core/openrig-skills/SKILL.md +1 -1
- package/daemon/specs/agents/shared/skills/core/openrig-software-factory/SKILL.md +0 -1
- package/daemon/specs/agents/shared/skills/core/openrig-software-factory/references/worked-example.md +7 -4
- package/daemon/specs/agents/shared/skills/core/topology-mutation-and-seat-management/SKILL.md +1 -1
- package/daemon/specs/agents/shared/skills/pods/orchestration-team/SKILL.md +1 -1
- package/daemon/specs/rigs/launch/demo/CULTURE.md +1 -1
- package/daemon/specs/rigs/launch/factory/CULTURE.md +1 -1
- package/daemon/specs/rigs/launch/factory/topology/rig/CRAFT.md +2 -2
- package/daemon/specs/rigs/launch/first-project/CULTURE.md +2 -1
- package/daemon/specs/rigs/launch/first-project-claude/CULTURE.md +2 -1
- package/daemon/specs/rigs/launch/first-project-mixed/CULTURE.md +2 -1
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +9 -2
- package/daemon/specs/rigs/launch/starter/CULTURE.md +2 -1
- package/daemon/specs/rigs/preview/product-team/CULTURE.md +1 -1
- package/dist/bin-wrapper.d.ts +2 -1
- package/dist/bin-wrapper.d.ts.map +1 -1
- package/dist/bin-wrapper.js +61 -4
- package/dist/bin-wrapper.js.map +1 -1
- package/dist/build-info.js +3 -3
- package/dist/bundle-behaviour.d.ts.map +1 -1
- package/dist/bundle-behaviour.js +22 -0
- package/dist/bundle-behaviour.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -3
- package/dist/client.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +1 -1
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +16 -5
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/bootstrap.d.ts.map +1 -1
- package/dist/commands/bootstrap.js +9 -5
- package/dist/commands/bootstrap.js.map +1 -1
- package/dist/commands/bundle.d.ts.map +1 -1
- package/dist/commands/bundle.js +8 -0
- package/dist/commands/bundle.js.map +1 -1
- package/dist/commands/capture.d.ts +4 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +15 -2
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/community-harness.d.ts +4 -0
- package/dist/commands/community-harness.d.ts.map +1 -0
- package/dist/commands/community-harness.js +48 -0
- package/dist/commands/community-harness.js.map +1 -0
- package/dist/commands/compact.d.ts.map +1 -1
- package/dist/commands/compact.js +8 -4
- package/dist/commands/compact.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +8 -2
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/crash-cart.d.ts.map +1 -1
- package/dist/commands/crash-cart.js +3 -5
- package/dist/commands/crash-cart.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +7 -5
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +2 -1
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/launch.d.ts +1 -0
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +61 -10
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/policy.d.ts +2 -1
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +66 -7
- package/dist/commands/policy.js.map +1 -1
- package/dist/commands/provider.d.ts +2 -0
- package/dist/commands/provider.d.ts.map +1 -1
- package/dist/commands/provider.js +336 -49
- package/dist/commands/provider.js.map +1 -1
- package/dist/commands/ps.d.ts +11 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +40 -6
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +59 -10
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/requirements.d.ts.map +1 -1
- package/dist/commands/requirements.js +8 -5
- package/dist/commands/requirements.js.map +1 -1
- package/dist/commands/restore.js +1 -1
- package/dist/commands/restore.js.map +1 -1
- package/dist/commands/rig-mode.d.ts.map +1 -1
- package/dist/commands/rig-mode.js +106 -18
- package/dist/commands/rig-mode.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +47 -9
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/seat.d.ts.map +1 -1
- package/dist/commands/seat.js +131 -20
- package/dist/commands/seat.js.map +1 -1
- package/dist/commands/send.d.ts +2 -2
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +79 -12
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/slack.d.ts.map +1 -1
- package/dist/commands/slack.js +9 -4
- package/dist/commands/slack.js.map +1 -1
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +77 -3
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +60 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +58 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/usage.d.ts +12 -0
- package/dist/commands/usage.d.ts.map +1 -1
- package/dist/commands/usage.js +107 -0
- package/dist/commands/usage.js.map +1 -1
- package/dist/commands/watchdog.d.ts.map +1 -1
- package/dist/commands/watchdog.js +20 -7
- package/dist/commands/watchdog.js.map +1 -1
- package/dist/commands/whoami.d.ts +36 -0
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +125 -7
- package/dist/commands/whoami.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +53 -5
- package/dist/commands/workflow.js.map +1 -1
- package/dist/config-store.d.ts +14 -1
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +50 -1
- package/dist/config-store.js.map +1 -1
- package/dist/daemon-lifecycle.d.ts +2 -2
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +5 -4
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/host-registry.d.ts.map +1 -1
- package/dist/host-registry.js +6 -3
- package/dist/host-registry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -7
- package/dist/index.js.map +1 -1
- package/dist/lazy-program.d.ts +9 -0
- package/dist/lazy-program.d.ts.map +1 -0
- package/dist/lazy-program.js +111 -0
- package/dist/lazy-program.js.map +1 -0
- package/dist/lib/bundle-check.js +2 -2
- package/dist/lib/bundle-check.js.map +1 -1
- package/dist/lib/bundle-source.d.ts +1 -0
- package/dist/lib/bundle-source.d.ts.map +1 -1
- package/dist/lib/bundle-source.js +1 -0
- package/dist/lib/bundle-source.js.map +1 -1
- package/dist/lib/codex-auth.d.ts.map +1 -1
- package/dist/lib/codex-auth.js +17 -1
- package/dist/lib/codex-auth.js.map +1 -1
- package/dist/lib/context-install.js +1 -1
- package/dist/lib/context-install.js.map +1 -1
- package/dist/lib/file-transfer.js +1 -1
- package/dist/lib/file-transfer.js.map +1 -1
- package/dist/lib/frontmatter.d.ts +11 -0
- package/dist/lib/frontmatter.d.ts.map +1 -0
- package/dist/lib/frontmatter.js +20 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/permission-policy/policy-spec.d.ts.map +1 -1
- package/dist/lib/permission-policy/policy-spec.js +5 -7
- package/dist/lib/permission-policy/policy-spec.js.map +1 -1
- package/dist/lib/scope/logical-checkbox.js +1 -1
- package/dist/lib/scope/logical-checkbox.js.map +1 -1
- package/dist/lib/scope/scope-fs.d.ts +8 -0
- package/dist/lib/scope/scope-fs.d.ts.map +1 -1
- package/dist/lib/scope/scope-fs.js +50 -1
- package/dist/lib/scope/scope-fs.js.map +1 -1
- package/dist/lib/work-install.d.ts.map +1 -1
- package/dist/lib/work-install.js +3 -13
- package/dist/lib/work-install.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +113 -0
- package/dist/mcp-server.js.map +1 -1
- package/dist/program-root.d.ts +5 -0
- package/dist/program-root.d.ts.map +1 -0
- package/dist/program-root.js +11 -0
- package/dist/program-root.js.map +1 -0
- package/dist/queue-read-compact.d.ts +29 -0
- package/dist/queue-read-compact.d.ts.map +1 -0
- package/dist/queue-read-compact.js +74 -0
- package/dist/queue-read-compact.js.map +1 -0
- package/dist/remote-host-ops.d.ts +21 -0
- package/dist/remote-host-ops.d.ts.map +1 -1
- package/dist/remote-host-ops.js +25 -12
- package/dist/remote-host-ops.js.map +1 -1
- package/package.json +1 -1
- package/tui/dist/commands/registry.d.ts.map +1 -1
- package/tui/dist/commands/registry.js +3 -0
- package/tui/dist/commands/registry.js.map +1 -1
- package/tui/dist/config/config-model.d.ts.map +1 -1
- package/tui/dist/config/config-model.js +6 -1
- package/tui/dist/config/config-model.js.map +1 -1
- package/tui/dist/cost/cost-model.d.ts +12 -0
- package/tui/dist/cost/cost-model.d.ts.map +1 -0
- package/tui/dist/cost/cost-model.js +141 -0
- package/tui/dist/cost/cost-model.js.map +1 -0
- package/tui/dist/cost/cost-read.d.ts +19 -0
- package/tui/dist/cost/cost-read.d.ts.map +1 -0
- package/tui/dist/cost/cost-read.js +4 -0
- package/tui/dist/cost/cost-read.js.map +1 -0
- package/tui/dist/daemon-client.d.ts +13 -0
- package/tui/dist/daemon-client.d.ts.map +1 -1
- package/tui/dist/daemon-client.js +77 -1
- package/tui/dist/daemon-client.js.map +1 -1
- package/tui/dist/detail.d.ts +2 -0
- package/tui/dist/detail.d.ts.map +1 -1
- package/tui/dist/detail.js +5 -0
- package/tui/dist/detail.js.map +1 -1
- package/tui/dist/execution/execution-model.d.ts.map +1 -1
- package/tui/dist/execution/execution-model.js +38 -18
- package/tui/dist/execution/execution-model.js.map +1 -1
- package/tui/dist/helper-cwd.d.ts +3 -0
- package/tui/dist/helper-cwd.d.ts.map +1 -0
- package/tui/dist/helper-cwd.js +16 -0
- package/tui/dist/helper-cwd.js.map +1 -0
- package/tui/dist/hydrate.d.ts.map +1 -1
- package/tui/dist/hydrate.js +32 -1
- package/tui/dist/hydrate.js.map +1 -1
- package/tui/dist/input.d.ts.map +1 -1
- package/tui/dist/input.js +2 -1
- package/tui/dist/input.js.map +1 -1
- package/tui/dist/local-reading.d.ts.map +1 -1
- package/tui/dist/local-reading.js +2 -1
- package/tui/dist/local-reading.js.map +1 -1
- package/tui/dist/main.js +6 -5
- package/tui/dist/main.js.map +1 -1
- package/tui/dist/pulse/render-pulse.d.ts.map +1 -1
- package/tui/dist/pulse/render-pulse.js +7 -1
- package/tui/dist/pulse/render-pulse.js.map +1 -1
- package/tui/dist/reading.js +1 -1
- package/tui/dist/reading.js.map +1 -1
- package/tui/dist/render.d.ts.map +1 -1
- package/tui/dist/render.js +10 -1
- package/tui/dist/render.js.map +1 -1
- package/tui/dist/scopes/scopes-model.d.ts +6 -1
- package/tui/dist/scopes/scopes-model.d.ts.map +1 -1
- package/tui/dist/scopes/scopes-model.js +33 -13
- package/tui/dist/scopes/scopes-model.js.map +1 -1
- package/tui/dist/sections.d.ts.map +1 -1
- package/tui/dist/sections.js +4 -1
- package/tui/dist/sections.js.map +1 -1
- package/tui/dist/socket-server.d.ts.map +1 -1
- package/tui/dist/socket-server.js +2 -1
- package/tui/dist/socket-server.js.map +1 -1
- package/tui/dist/specs/launch.d.ts.map +1 -1
- package/tui/dist/specs/launch.js +2 -1
- package/tui/dist/specs/launch.js.map +1 -1
- package/tui/dist/startup.js +1 -1
- package/tui/dist/startup.js.map +1 -1
- package/tui/dist/state.d.ts.map +1 -1
- package/tui/dist/state.js +8 -4
- package/tui/dist/state.js.map +1 -1
- package/tui/dist/terminals/open-window.d.ts.map +1 -1
- package/tui/dist/terminals/open-window.js +2 -1
- package/tui/dist/terminals/open-window.js.map +1 -1
- package/tui/dist/types.d.ts +9 -0
- package/tui/dist/types.d.ts.map +1 -1
- package/tui/dist/usage/usage-hosts-model.d.ts +17 -0
- package/tui/dist/usage/usage-hosts-model.d.ts.map +1 -0
- package/tui/dist/usage/usage-hosts-model.js +73 -0
- package/tui/dist/usage/usage-hosts-model.js.map +1 -0
- package/tui/dist/usage/usage-model.d.ts +39 -0
- package/tui/dist/usage/usage-model.d.ts.map +1 -0
- package/tui/dist/usage/usage-model.js +161 -0
- package/tui/dist/usage/usage-model.js.map +1 -0
- package/ui/dist/assets/{index-DhmxKuZf.js → index-Cfbtfd9G.js} +2 -2
- package/ui/dist/index.html +1 -1
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
import stringWidth from "string-width";
|
|
1
2
|
import { CODEX_TURN_STATUS_PATTERN } from "./codex-pane-patterns.js";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { inspectStartupStagedText, startupSubmissionEvidence } from "./startup-submission-evidence.js";
|
|
4
|
+
import { COLLAPSED_PASTE, composerRegion, isComposerFooter, inspectStartupStagedText, QUEUED_PLACEHOLDER, startupSubmissionEvidence } from "./startup-submission-evidence.js";
|
|
5
|
+
import { composerContainsOwnedText, composerNumberedChoicePattern, composerPromptClassForRuntime, composerPromptIsAmbiguous, composerSelectionPrefixPattern, COMPOSER_DRAFT_PATTERN, COMPOSER_EMPTY_CODEX_PLACEHOLDER_PATTERN as CODEX_EMPTY_COMPOSER_PATTERN, COMPOSER_EMPTY_PATTERN, COMPOSER_PROMPT_CLASS, COMPOSER_SELECTION_PATTERN, COMPOSER_SELECTION_SCAN_PATTERN, findComposerInputLineIndex, stripComposerPromptGlyph, readComposerPromptLine, COMPOSER_COLLAPSED_PASTE_PATTERN, } from "./composer-prompts.js";
|
|
4
6
|
export { inspectStartupStagedText } from "./startup-submission-evidence.js";
|
|
5
7
|
import { OutboxHandler } from "./outbox-handler.js";
|
|
8
|
+
import { DraftAwareDelivery } from "./draft-aware-delivery.js";
|
|
6
9
|
import { claudeResumeRotation } from "./session-registry.js";
|
|
7
|
-
import { latestHookWaitsOnPerson } from "./agent-activity-store.js";
|
|
10
|
+
import { latestHookPermissionPending, latestHookWaitsOnPerson } from "./agent-activity-store.js";
|
|
8
11
|
import { wrapPaneEnvelope, appendDeliveredSegment } from "../lib/pane-envelope.js";
|
|
9
12
|
import { SeatIdentityStore } from "./seat-identity-store.js";
|
|
10
13
|
import { isShellForeground } from "./shell-classifier.js";
|
|
11
14
|
import { observeClaudeDelivery, verifyCodexPaneProcess } from "./native-process-lineage.js";
|
|
12
15
|
import { hashSentText } from "./capture-observer.js";
|
|
16
|
+
import { isAttentionRequiredReadinessCode } from "./runtime-adapter.js";
|
|
17
|
+
import { getLatestStartupFailedEvent, hasClaudeResumeIdentityWork, isRecoverableStartupFailure } from "./startup-orchestrator.js";
|
|
13
18
|
// OPR.0.4.1.10 — send-readiness freshness. The runtime-hook store keeps a 5min freshness for activity
|
|
14
19
|
// DISPLAY, but "safe to send NOW" needs a tight window: a stale "idle" read must not authorize a send
|
|
15
20
|
// into what may since have become a prompt. Beyond this window the hook is ignored for send-readiness
|
|
@@ -20,13 +25,18 @@ import { hashSentText } from "./capture-observer.js";
|
|
|
20
25
|
// window comfortably spans one inter-tool gap (so a mid-turn reading stays trusted) while refusing to
|
|
21
26
|
// authorize a send from a reading tens of seconds old. (EXA: agtx#14, daintree#3938.)
|
|
22
27
|
const SEND_READINESS_FRESHNESS_MS = 15_000;
|
|
28
|
+
// transport.verify_input_box: each extra input-box read gives up after this long and reads `unavailable`. A deadline
|
|
29
|
+
// budget, not a guarantee: the pre-Enter read comes after the existing 200 ms wait and can add about this much before
|
|
30
|
+
// Enter, and the post-Enter process read about this much before the reply. A read that misses it leaves the send
|
|
31
|
+
// unconfirmed, and Enter is still sent once.
|
|
32
|
+
const INPUT_BOX_READ_DEADLINE_MS = 500;
|
|
23
33
|
// Mid-work detection patterns (cheap heuristics)
|
|
24
34
|
const MID_WORK_PATTERNS = [
|
|
25
35
|
/[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/, // spinner chars
|
|
26
36
|
/Working/,
|
|
27
37
|
/^[✶✢✳✻✽·]\s+\S.*(?:…|\.{3})\s+\([^)]*\bthinking\)$/m,
|
|
28
38
|
/esc to interrupt/,
|
|
29
|
-
|
|
39
|
+
COMPOSER_SELECTION_SCAN_PATTERN, // trust/consent prompt choices (e.g. '› 1. Yes, continue')
|
|
30
40
|
];
|
|
31
41
|
// Idle-prompt patterns: empty prompt line (no typed text after the char).
|
|
32
42
|
// Lines like '❯ Working on a task.' have text after the prompt char and
|
|
@@ -37,13 +47,12 @@ const MID_WORK_PATTERNS = [
|
|
|
37
47
|
// normally sits above it, but Codex hides that row while it streams assistant output. So the
|
|
38
48
|
// placeholder counts as idle only through MID_WORK_PATTERNS here, and classifySendReadiness
|
|
39
49
|
// never lets a placeholder-only verdict override a display-fresh running/needs_input hook.
|
|
40
|
-
const CODEX_EMPTY_COMPOSER_PATTERN = /^›\s+Ask Codex to do anything\s*$/;
|
|
41
50
|
const IDLE_PROMPT_PATTERNS = [
|
|
42
|
-
|
|
51
|
+
COMPOSER_EMPTY_PATTERN, // prompt char + optional whitespace + end-of-line only
|
|
43
52
|
CODEX_EMPTY_COMPOSER_PATTERN,
|
|
44
53
|
];
|
|
45
54
|
const PROMPT_DRAFT_PATTERNS = [
|
|
46
|
-
|
|
55
|
+
COMPOSER_DRAFT_PATTERN,
|
|
47
56
|
];
|
|
48
57
|
// Claude's permission-mode footers: default, accept edits, bypass, auto and plan, optionally after a
|
|
49
58
|
// vim-mode marker such as "-- INSERT --" (#808). A suffix such as "· 1 shell" can follow the mode.
|
|
@@ -133,15 +142,20 @@ const CLAUDE_LIVE_STATUS_PATTERN = /^[✶✢✳✻✽·*]\s+\S[^(]*(?:…|\.{3})
|
|
|
133
142
|
// ("✻ Crunched for 2s") have no trailing ellipsis. Only callers that must not send into a busy
|
|
134
143
|
// pane opt in (ClassifyPaneOptions.timerlessStatusIsLive); ordinary send readiness is unchanged.
|
|
135
144
|
const CLAUDE_TIMERLESS_STATUS_PATTERN = /^[✶✢✳✻✽·*]\s+\S.*(?:…|\.{3})$/;
|
|
145
|
+
/** The current Claude footer follows the input frame; earlier examples are history or draft text. */
|
|
146
|
+
function findClaudeFooterIndex(lines) {
|
|
147
|
+
let bar = lines.length - 1;
|
|
148
|
+
while (bar >= 0 && (!lines[bar].trim() || CLAUDE_STATUS_WARNINGS.some(pattern => pattern.test(lines[bar].trim()))))
|
|
149
|
+
bar--;
|
|
150
|
+
return bar;
|
|
151
|
+
}
|
|
136
152
|
function findClaudeComposer(paneContent, options = {}) {
|
|
137
153
|
// Preserve columns: a multiline draft may contain indented border/prompt text.
|
|
138
154
|
// This classifier scans at most 20 physical lines; captures can be taller.
|
|
139
155
|
// Exhausting the scan without reaching the status head is unknown, not idle.
|
|
140
156
|
const lines = paneContent.split("\n").slice(-20)
|
|
141
157
|
.map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
|
|
142
|
-
|
|
143
|
-
while (bar >= 0 && CLAUDE_STATUS_WARNINGS.some((pattern) => pattern.test(lines[bar].trim())))
|
|
144
|
-
bar--;
|
|
158
|
+
const bar = findClaudeFooterIndex(lines);
|
|
145
159
|
const supportedWarningFooter = lines[bar]?.trim() === "⏵⏵ accept edits on (shift+tab to cycle) · ← for agents";
|
|
146
160
|
const modeFooter = CLAUDE_MODE_FOOTER.test(lines[bar]?.trim() ?? "");
|
|
147
161
|
let indent = /^([ \t]*)─{3,}$/.exec(lines[bar - 1] ?? "")?.[1];
|
|
@@ -154,7 +168,8 @@ function findClaudeComposer(paneContent, options = {}) {
|
|
|
154
168
|
if (bar < 2 || !CLAUDE_ANCHORED_MODE_FOOTER.test(lines[bar].trim()))
|
|
155
169
|
return null;
|
|
156
170
|
prompt = lines[bar - 1] ?? "";
|
|
157
|
-
|
|
171
|
+
const input = readComposerPromptLine(prompt, composerPromptClassForRuntime("claude-code"));
|
|
172
|
+
indent = input && !input.text.trim() ? input.indent : undefined;
|
|
158
173
|
if (indent === undefined)
|
|
159
174
|
return null;
|
|
160
175
|
statusStart = bar - 2;
|
|
@@ -162,7 +177,7 @@ function findClaudeComposer(paneContent, options = {}) {
|
|
|
162
177
|
else {
|
|
163
178
|
const upper = lines[bar - 3] ?? "";
|
|
164
179
|
if (!upper.startsWith(indent) || !/^─{3,}(?: .+ ─+)?$/.test(upper.slice(indent.length)) ||
|
|
165
|
-
!prompt.startsWith(`${indent}❯`) ||
|
|
180
|
+
!prompt.startsWith(`${indent}❯`) || !readComposerPromptLine(prompt, composerPromptClassForRuntime("claude-code")))
|
|
166
181
|
return null;
|
|
167
182
|
}
|
|
168
183
|
let liveStatus = null;
|
|
@@ -188,6 +203,163 @@ function findClaudeComposer(paneContent, options = {}) {
|
|
|
188
203
|
}
|
|
189
204
|
return { text: prompt.slice(indent.length), bar: lines[bar].trim(), framed, hasWarnings: bar < lines.length - 1, supportedWarningFooter, modeFooter, headSeen, liveStatus };
|
|
190
205
|
}
|
|
206
|
+
const unknown = () => ({ state: "unknown", rows: [], columns: 0, cursorAtEnd: false, collapsedPaste: null });
|
|
207
|
+
const rule = (line) => /^[─━═-]{6,}(?: .+ [─━═-]+)?$/.test(line.trim());
|
|
208
|
+
const codexFooter = (line) => isComposerFooter(line)
|
|
209
|
+
|| /(?:\b\d+% context left\b|\bContext \[)/.test(line);
|
|
210
|
+
/** Preserve faint styling so autocomplete ghosts can be separated from authored
|
|
211
|
+
* text. Color channel values must never be interpreted as style codes. */
|
|
212
|
+
function styledTextLines(screen) {
|
|
213
|
+
let faint = false;
|
|
214
|
+
return screen.replace(/\r\n/g, "\n").split("\n").map(raw => {
|
|
215
|
+
let text = "", offset = 0;
|
|
216
|
+
const faintCells = [];
|
|
217
|
+
const append = (value) => { text += value; faintCells.push(...Array(value.length).fill(faint)); };
|
|
218
|
+
for (const match of raw.matchAll(/\x1b\[([0-9;:]*)m/g)) {
|
|
219
|
+
append(raw.slice(offset, match.index));
|
|
220
|
+
const codes = match[1].split(";");
|
|
221
|
+
for (let i = 0; i < codes.length; i++) {
|
|
222
|
+
const code = codes[i];
|
|
223
|
+
if (/^(?:38|48|58):/.test(code))
|
|
224
|
+
continue; // Colon-form color is one parameter.
|
|
225
|
+
if (code === "38" || code === "48" || code === "58") {
|
|
226
|
+
const skip = codes[i + 1] === "5" ? 2 : codes[i + 1] === "2" ? (codes[i + 2] === "" ? 5 : 4) : 0;
|
|
227
|
+
// Color channels are not style codes. Ambiguous compound sequences
|
|
228
|
+
// provide no faint-style proof, rather than interpreting an RGB 2 as dim.
|
|
229
|
+
if (!skip || i + skip !== codes.length - 1) {
|
|
230
|
+
faint = false;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
i += skip;
|
|
234
|
+
}
|
|
235
|
+
else if (code === "" || code === "0" || code === "22")
|
|
236
|
+
faint = false;
|
|
237
|
+
else if (code === "2")
|
|
238
|
+
faint = true;
|
|
239
|
+
}
|
|
240
|
+
offset = match.index + match[0].length;
|
|
241
|
+
}
|
|
242
|
+
append(raw.slice(offset));
|
|
243
|
+
return { text, faintCells };
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
/** Inspect only the visible input containing the current cursor. Activity, hook
|
|
247
|
+
* freshness and text elsewhere in the pane are not evidence that input is empty.
|
|
248
|
+
*/
|
|
249
|
+
export function inspectComposerInput(snapshot, runtime = null, owned) {
|
|
250
|
+
const promptClass = composerPromptClassForRuntime(runtime);
|
|
251
|
+
if (!snapshot || snapshot.inMode)
|
|
252
|
+
return unknown();
|
|
253
|
+
const { x, y, width, height } = snapshot.cursor;
|
|
254
|
+
if (![x, y, width, height].every(Number.isInteger) || x < 0 || y < 0 || x >= width || y >= height || width < 3 || height < 1)
|
|
255
|
+
return unknown();
|
|
256
|
+
const styled = styledTextLines(snapshot.screen);
|
|
257
|
+
const lines = styled.map(line => line.text);
|
|
258
|
+
if (lines.at(-1) === "")
|
|
259
|
+
lines.pop();
|
|
260
|
+
if (lines.length > height || !lines[y])
|
|
261
|
+
return unknown();
|
|
262
|
+
const footerAt = findClaudeFooterIndex(lines);
|
|
263
|
+
const footer = lines[footerAt]?.trim() ?? "";
|
|
264
|
+
let claudeEnd = footerAt - 1;
|
|
265
|
+
while (claudeEnd >= 0 && !lines[claudeEnd].trim())
|
|
266
|
+
claudeEnd--;
|
|
267
|
+
const candidates = [];
|
|
268
|
+
for (let start = 0; start <= y; start++) {
|
|
269
|
+
const prompt = readComposerPromptLine(lines[start] ?? "", promptClass);
|
|
270
|
+
if (!prompt || !/^ *$/.test(prompt.indent))
|
|
271
|
+
continue;
|
|
272
|
+
const numbered = composerSelectionPrefixPattern(promptClass).test(lines[start].trimStart()) || /^\d+[.)]\s/.test(prompt.text);
|
|
273
|
+
const prefix = prompt.prefix;
|
|
274
|
+
let end = -1;
|
|
275
|
+
if (prompt.glyph === "❯") {
|
|
276
|
+
// Share the main reader's terminal footer anchor. A quoted frame within a
|
|
277
|
+
// multiline draft cannot supply either the closing rule or its indentation.
|
|
278
|
+
const alignedRule = (line) => rule(line) && line.length - line.trimStart().length === prompt.indent.length;
|
|
279
|
+
if ((!isComposerFooter(footer) && !CLAUDE_MODE_FOOTER.test(footer))
|
|
280
|
+
|| !alignedRule(lines[start - 1] ?? "") || !alignedRule(lines[claudeEnd] ?? ""))
|
|
281
|
+
continue;
|
|
282
|
+
end = claudeEnd;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
286
|
+
if (lines[i].trim())
|
|
287
|
+
continue;
|
|
288
|
+
const tail = lines.slice(i + 1).filter(line => line.trim());
|
|
289
|
+
if (lines[i + 1]?.trim() && tail.length === 1 && codexFooter(tail[0].trim())) {
|
|
290
|
+
end = i;
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (end <= y || x < prefix)
|
|
296
|
+
continue;
|
|
297
|
+
const rows = [prompt.text];
|
|
298
|
+
let valid = true;
|
|
299
|
+
for (let i = start + 1; i < end; i++) {
|
|
300
|
+
const line = lines[i];
|
|
301
|
+
if (line.trim() && !line.startsWith(" ".repeat(prefix))) {
|
|
302
|
+
valid = false;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
rows.push(line.slice(prefix));
|
|
306
|
+
}
|
|
307
|
+
if (!valid)
|
|
308
|
+
continue;
|
|
309
|
+
// A suggestion is a wholly dim suffix beginning at the current cursor.
|
|
310
|
+
// A literal placeholder or text before a moved cursor remains authored input.
|
|
311
|
+
const cursorRow = y - start;
|
|
312
|
+
let ghost;
|
|
313
|
+
const currentRow = rows[cursorRow];
|
|
314
|
+
let cursorOffset = x === prefix ? 0 : -1;
|
|
315
|
+
let offset = 0;
|
|
316
|
+
for (const character of currentRow) {
|
|
317
|
+
offset += character.length;
|
|
318
|
+
if (stringWidth(currentRow.slice(0, offset)) === x - prefix)
|
|
319
|
+
cursorOffset = offset;
|
|
320
|
+
}
|
|
321
|
+
if (cursorOffset >= 0) {
|
|
322
|
+
const suffix = rows.slice(cursorRow).map((row, i) => ({
|
|
323
|
+
text: row.slice(i === 0 ? cursorOffset : 0),
|
|
324
|
+
faint: styled[y + i].faintCells.slice(prefix + (i === 0 ? cursorOffset : 0), prefix + row.length),
|
|
325
|
+
}));
|
|
326
|
+
if (suffix.some(row => row.text.length > 0) && suffix.every(row => row.faint.every(Boolean))) {
|
|
327
|
+
ghost = { line: y, offset: prefix + cursorOffset, continuationRows: rows.length - cursorRow - 1 };
|
|
328
|
+
rows.splice(cursorRow + 1);
|
|
329
|
+
rows[cursorRow] = currentRow.slice(0, cursorOffset);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const visible = rows.map(line => line.trimEnd());
|
|
333
|
+
// The cursor bounds authored whitespace on the last input row. Captures may
|
|
334
|
+
// omit trailing blank cells or include screen padding; neither changes the bytes to submit.
|
|
335
|
+
if (cursorRow === rows.length - 1) {
|
|
336
|
+
const cellWidth = stringWidth(rows[cursorRow]);
|
|
337
|
+
if (x - prefix >= cellWidth)
|
|
338
|
+
visible[cursorRow] = rows[cursorRow] + " ".repeat(x - prefix - cellWidth);
|
|
339
|
+
else if (cursorOffset >= 0 && !rows[cursorRow].slice(cursorOffset).trim())
|
|
340
|
+
visible[cursorRow] = rows[cursorRow].slice(0, cursorOffset);
|
|
341
|
+
}
|
|
342
|
+
const atStart = y === start && x === prefix;
|
|
343
|
+
const empty = atStart && rows.length === 1 && rows[0] === "";
|
|
344
|
+
const last = visible.length - 1;
|
|
345
|
+
const cursorAtEnd = y === start + last && x === prefix + stringWidth(visible[last] ?? "");
|
|
346
|
+
const label = visible.length === 1 && COMPOSER_COLLAPSED_PASTE_PATTERN.test(visible[0]) ? visible[0] : null;
|
|
347
|
+
const frame = JSON.stringify([prompt.glyph, prefix, width, footer]);
|
|
348
|
+
const input = { state: empty ? "empty" : "text", rows: visible, columns: width - prefix, cursorAtEnd, collapsedPaste: label, frame,
|
|
349
|
+
...(ghost ? { ghost } : {}) };
|
|
350
|
+
// A numbered body is allowed only as this operation's exact text in its
|
|
351
|
+
// previously empty composer. A newly observed menu gains no such authority.
|
|
352
|
+
if (numbered && !(owned?.frame === frame && composerContainsOwnedText(input, owned.text)))
|
|
353
|
+
continue;
|
|
354
|
+
if (owned?.frame === frame && cursorAtEnd && visible.join("\n").includes("[Pasted text #")
|
|
355
|
+
&& hasExpectedStagedText(`${prompt.glyph} ${visible.join("\n")}`, owned.text, runtime)) {
|
|
356
|
+
input.collapsedPaste = visible.join("\n");
|
|
357
|
+
input.pasteMatchesExpected = true;
|
|
358
|
+
}
|
|
359
|
+
candidates.push(input);
|
|
360
|
+
}
|
|
361
|
+
return candidates.length === 1 ? candidates[0] : unknown();
|
|
362
|
+
}
|
|
191
363
|
function findPromptDraftBeforeFooter(paneContent) {
|
|
192
364
|
const rawLines = paneContent.split("\n").map((line) => line.trimEnd());
|
|
193
365
|
let lastLineIndex = rawLines.length - 1;
|
|
@@ -204,8 +376,9 @@ function findPromptDraftBeforeFooter(paneContent) {
|
|
|
204
376
|
if (priorLine.trim().length === 0)
|
|
205
377
|
return null;
|
|
206
378
|
const priorTrimmed = priorLine.trim();
|
|
207
|
-
const
|
|
208
|
-
const
|
|
379
|
+
const prompt = readComposerPromptLine(priorLine);
|
|
380
|
+
const looksLikeDraft = prompt !== null && prompt.text.trim().length > 0;
|
|
381
|
+
const looksLikeSelection = COMPOSER_SELECTION_PATTERN.test(priorTrimmed);
|
|
209
382
|
if (!looksLikeDraft || looksLikeSelection)
|
|
210
383
|
return null;
|
|
211
384
|
return truncateEvidence(priorTrimmed);
|
|
@@ -282,7 +455,25 @@ function draftComposerLine(lines, verdict) {
|
|
|
282
455
|
return null;
|
|
283
456
|
return lines[last].trim();
|
|
284
457
|
}
|
|
458
|
+
/** A community harness's screen never proves it's idle: a quiet prompt can be an approval or a pause, so its idle
|
|
459
|
+
* reading becomes unknown. Positive work and attention are kept. `communityHarness` is the seat's recorded selection
|
|
460
|
+
* (`communityHarnessOf`); without it the classification is returned as it is. */
|
|
461
|
+
export function communityHarnessPaneActivity(classification, communityHarness) {
|
|
462
|
+
return communityHarness && classification.state === "agent_idle"
|
|
463
|
+
? { state: "unknown", reason: "community_harness_no_idle_signal", evidence: classification.evidence }
|
|
464
|
+
: classification;
|
|
465
|
+
}
|
|
285
466
|
export function classifyPaneActivity(paneContent, options = {}) {
|
|
467
|
+
if (options.composerSnapshot) {
|
|
468
|
+
const snapshot = options.composerSnapshot;
|
|
469
|
+
const { ghost } = inspectComposerInput(snapshot, options.runtime);
|
|
470
|
+
const lines = styledTextLines(snapshot.screen).map(line => line.text);
|
|
471
|
+
if (ghost) {
|
|
472
|
+
lines[ghost.line] = lines[ghost.line].slice(0, ghost.offset);
|
|
473
|
+
lines.splice(ghost.line + 1, ghost.continuationRows);
|
|
474
|
+
}
|
|
475
|
+
paneContent = lines.join("\n");
|
|
476
|
+
}
|
|
286
477
|
const lastNonBlank = trimPaneLines(paneContent);
|
|
287
478
|
if (lastNonBlank.length === 0) {
|
|
288
479
|
return { state: "unknown", reason: "empty_capture", evidence: null };
|
|
@@ -307,7 +498,7 @@ export function classifyPaneActivity(paneContent, options = {}) {
|
|
|
307
498
|
oldQuestionEnd >= 0 && oldQuestionEnd < promptScanLines.length - 1
|
|
308
499
|
? promptScanLines.slice(oldQuestionEnd + 1) : promptScanLines;
|
|
309
500
|
const selectionPromptEvidence = findCurrentClaudeQuestion(paneContent) ??
|
|
310
|
-
findPatternEvidence(selectionLines, [
|
|
501
|
+
findPatternEvidence(selectionLines, [COMPOSER_SELECTION_SCAN_PATTERN]);
|
|
311
502
|
if (selectionPromptEvidence) {
|
|
312
503
|
return {
|
|
313
504
|
state: "attention",
|
|
@@ -514,10 +705,12 @@ export async function probeSessionActivity(input) {
|
|
|
514
705
|
};
|
|
515
706
|
const captureSeq = observed ? nextCaptureSeq++ : 0;
|
|
516
707
|
try {
|
|
517
|
-
const
|
|
708
|
+
const snapshot = input.composerSnapshot ? await input.composerSnapshot().catch(() => null) : null;
|
|
709
|
+
const paneContent = snapshot?.screen ?? await tmuxAdapter.capturePaneContent(sessionName, 20);
|
|
518
710
|
const capturedAt = new Date().toISOString();
|
|
519
711
|
const classifyOptions = { timerlessStatusIsLive: input.timerlessStatusIsLive === true };
|
|
520
|
-
let classification = classifyPaneActivity(paneContent ?? "", classifyOptions
|
|
712
|
+
let classification = communityHarnessPaneActivity(classifyPaneActivity(paneContent ?? "", { ...classifyOptions,
|
|
713
|
+
composerSnapshot: snapshot ?? undefined, runtime }), input.communityHarness);
|
|
521
714
|
// Claude renders its prompt suggestion faint, which the plain capture loses. When a Claude pane reads
|
|
522
715
|
// as a draft, one capture with styling is classified on its own. Newer attention or work there is
|
|
523
716
|
// kept. A faint suggestion counts only when it's the same composer text this capture read as a draft;
|
|
@@ -625,7 +818,9 @@ function verificationSnippet(text) {
|
|
|
625
818
|
/** Count whole visible envelope blocks, not prefixes of longer messages.
|
|
626
819
|
* A new envelope, prompt or same-column output ends an indented user block.
|
|
627
820
|
* Whitespace is compared only after retaining boundaries and quote indentation. */
|
|
628
|
-
function countEnvelopeOccurrences(transcript, expected) {
|
|
821
|
+
function countEnvelopeOccurrences(transcript, expected, promptClass) {
|
|
822
|
+
const promptPrefix = new RegExp(`^[${promptClass}]\\s+`);
|
|
823
|
+
const promptBoundary = new RegExp(`^[${promptClass}](?:\\s|$)`);
|
|
629
824
|
const lines = transcript.split("\n");
|
|
630
825
|
let count = 0;
|
|
631
826
|
let block = null;
|
|
@@ -640,8 +835,8 @@ function countEnvelopeOccurrences(transcript, expected) {
|
|
|
640
835
|
for (let i = 0; i < lines.length; i++) {
|
|
641
836
|
const line = lines[i];
|
|
642
837
|
const indent = /^[ \t]*/.exec(line)[0].length;
|
|
643
|
-
const prompted =
|
|
644
|
-
const header = line.slice(indent).replace(
|
|
838
|
+
const prompted = promptPrefix.test(line.slice(indent));
|
|
839
|
+
const header = line.slice(indent).replace(promptPrefix, "");
|
|
645
840
|
const atBoundary = (!block && !inAgentOutput) || indent <= blockIndent;
|
|
646
841
|
const candidate = normalizeVerificationText([header, ...lines.slice(i + 1, i + 5)].join("\n"));
|
|
647
842
|
const envelopeHeader = /^From: [^\n]+\nTo: [^\n]+\n(?:Sent: [^\n]+\n)?---\n/.test(candidate);
|
|
@@ -660,7 +855,7 @@ function countEnvelopeOccurrences(transcript, expected) {
|
|
|
660
855
|
finish();
|
|
661
856
|
inAgentOutput = true;
|
|
662
857
|
}
|
|
663
|
-
else if (atBoundary &&
|
|
858
|
+
else if (atBoundary && promptBoundary.test(line.slice(indent))) {
|
|
664
859
|
finish();
|
|
665
860
|
}
|
|
666
861
|
else if (block) {
|
|
@@ -673,15 +868,16 @@ function countEnvelopeOccurrences(transcript, expected) {
|
|
|
673
868
|
/** The last prompt marker owns the current input region. Never count it or its
|
|
674
869
|
* continuation lines as submitted transcript, in either capture. If a capture
|
|
675
870
|
* ends at a historical prompt with no later input marker, leave it unconfirmed. */
|
|
676
|
-
function verificationTranscript(content) {
|
|
871
|
+
function verificationTranscript(content, promptClass) {
|
|
872
|
+
const indentedPrompt = new RegExp(`^([ \\t]*)[${promptClass}]`);
|
|
677
873
|
const lines = (content ?? "").replace(/\r\n/g, "\n").split("\n");
|
|
678
|
-
let inputAt =
|
|
874
|
+
let inputAt = findComposerInputLineIndex(lines, promptClass);
|
|
679
875
|
if (inputAt >= 0) {
|
|
680
876
|
let indent = /^[ \t]*/.exec(lines[inputAt])[0].length;
|
|
681
877
|
// Claude indents draft continuation lines. A quoted prompt on one of them
|
|
682
878
|
// belongs to the enclosing input, not a new transcript/composer boundary.
|
|
683
879
|
for (let i = inputAt - 1; i >= 0; i--) {
|
|
684
|
-
const prompt =
|
|
880
|
+
const prompt = indentedPrompt.exec(lines[i]);
|
|
685
881
|
if (prompt && prompt[1].length < indent) {
|
|
686
882
|
inputAt = i;
|
|
687
883
|
indent = prompt[1].length;
|
|
@@ -715,33 +911,36 @@ export function scopeForTarget(target, recipients) {
|
|
|
715
911
|
}
|
|
716
912
|
/** The existing submit-only identity check, also used to inspect startup's own paste.
|
|
717
913
|
* A false result is no matching staged evidence, not proof of model consumption. */
|
|
718
|
-
export function hasExpectedStagedText(pane, expected) {
|
|
914
|
+
export function hasExpectedStagedText(pane, expected, runtime = null) {
|
|
719
915
|
const norm = (s) => s.replace(/\s+/g, "");
|
|
916
|
+
// The composer glyph set is scoped to the runtime when known: a Claude draft's
|
|
917
|
+
// continuation line can start with a Codex glyph, and taking that line as the
|
|
918
|
+
// input marker would misread the staged text. Unknown runtimes keep the union.
|
|
919
|
+
const promptClass = composerPromptClassForRuntime(runtime);
|
|
720
920
|
// Round-2 (r2 HIGH-1): the evidence must be the CURRENT ACTIVE INPUT and must identify
|
|
721
921
|
// THIS piece — stale scrollback can carry an old placeholder while a LATER interactive
|
|
722
922
|
// prompt owns the input, and an Enter there approves the prompt. So:
|
|
723
923
|
// 1. Only the pane's LAST input-marker line counts (the current input; everything above
|
|
724
924
|
// is history).
|
|
725
|
-
// 2. A numbered-option line (`❯ 1.
|
|
925
|
+
// 2. A numbered-option line (`❯ 1. …`, `› 1.Yes`) is a PROMPT SELECTION, never staged
|
|
926
|
+
// input: refuse.
|
|
726
927
|
// 3. A pasted-text placeholder is identity-qualified: "[Pasted text #N +X lines]" counts
|
|
727
928
|
// only when X matches the expected piece's own line count (±1 for a trailing newline).
|
|
728
929
|
// More than one placeholder is COALESCED staging (several pieces, one Enter): refuse.
|
|
729
930
|
// 4. Otherwise the line must carry the content's own head (24 normalized chars — a short
|
|
730
931
|
// paste renders inline, possibly truncated).
|
|
731
932
|
const paneLines = (pane ?? "").split("\n");
|
|
732
|
-
|
|
733
|
-
for (let i = paneLines.length - 1; i >= 0; i--) {
|
|
734
|
-
if (paneLines[i].trimStart().startsWith("❯")) {
|
|
735
|
-
currentInputAt = i;
|
|
736
|
-
break;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
933
|
+
const currentInputAt = findComposerInputLineIndex(paneLines, promptClass);
|
|
739
934
|
let stagedEvidence = false;
|
|
740
935
|
if (currentInputAt >= 0) {
|
|
936
|
+
// Unknown runtime: a composer block that mixes glyph kinds has no runtime to
|
|
937
|
+
// say which line is live, so fail closed exactly as the pre-union check did.
|
|
938
|
+
if (composerPromptIsAmbiguous(paneLines, currentInputAt, promptClass))
|
|
939
|
+
return false;
|
|
741
940
|
const inputLine = paneLines[currentInputAt].trimStart();
|
|
742
|
-
// The region is the last
|
|
743
|
-
// line) or pane end — wrapped input continues below the marker; everything
|
|
744
|
-
// marker is history and everything below the separator is hint-bar chrome.
|
|
941
|
+
// The region is the last composer-marker line through the input box's closing separator
|
|
942
|
+
// (a box-drawing line) or pane end — wrapped input continues below the marker; everything
|
|
943
|
+
// ABOVE the marker is history and everything below the separator is hint-bar chrome.
|
|
745
944
|
let regionEnd = paneLines.length;
|
|
746
945
|
for (let i = currentInputAt + 1; i < paneLines.length; i++) {
|
|
747
946
|
const t = paneLines[i].trim();
|
|
@@ -751,7 +950,7 @@ export function hasExpectedStagedText(pane, expected) {
|
|
|
751
950
|
}
|
|
752
951
|
}
|
|
753
952
|
const region = paneLines.slice(currentInputAt, regionEnd).join("\n");
|
|
754
|
-
if (
|
|
953
|
+
if (!composerSelectionPrefixPattern(promptClass).test(inputLine)) {
|
|
755
954
|
// Round-3 (r2 R2 HIGH-1, specimen-pinned): Claude renders ONE staged piece as MANY
|
|
756
955
|
// placeholders whose displayed counts are SEGMENT sizes (sum ≤ source lines), followed
|
|
757
956
|
// by the piece's own literal tail wrapped across pane lines — and the placeholder
|
|
@@ -777,7 +976,7 @@ export function hasExpectedStagedText(pane, expected) {
|
|
|
777
976
|
// or one that is not the piece's own suffix, FAIL CLOSED — the TUI did not expose
|
|
778
977
|
// enough content to identify the staged state, and a bare Enter is never guessed.
|
|
779
978
|
const chrome = /paste again to expand|ctrl\+g to edit( in Vim)?/gi;
|
|
780
|
-
const residual = norm(regionFlat.replace(placeholderRe, "").replace(chrome, ""))
|
|
979
|
+
const residual = stripComposerPromptGlyph(norm(regionFlat.replace(placeholderRe, "").replace(chrome, "")), promptClass);
|
|
781
980
|
const pieceNorm = norm(expected);
|
|
782
981
|
const sum = counts.reduce((a, b) => a + b, 0);
|
|
783
982
|
// Round-5 (r2 R4 HIGH-1): the suffix anchor is JOINED to the opaque prefix. The
|
|
@@ -814,23 +1013,466 @@ export function hasExpectedStagedText(pane, expected) {
|
|
|
814
1013
|
function promptAnswerStaged(pane, answer, runtime) {
|
|
815
1014
|
if (!pane || !answer.trim() || /[\r\n\x1b]/.test(answer))
|
|
816
1015
|
return false;
|
|
1016
|
+
// Fail closed for an unknown runtime: with no scoped glyph set there is no way to
|
|
1017
|
+
// say which prompt line is the live input (the pre-shared-matcher check did the same).
|
|
1018
|
+
if (runtime !== "codex" && runtime !== "claude-code")
|
|
1019
|
+
return false;
|
|
1020
|
+
const promptClass = composerPromptClassForRuntime(runtime);
|
|
817
1021
|
const lines = pane.split("\n");
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1022
|
+
// Only the LAST prompt line of this runtime's glyph set can be the current input;
|
|
1023
|
+
// everything above it is transcript history, and another harness's glyph is not ours.
|
|
1024
|
+
const inputIndex = findComposerInputLineIndex(lines, promptClass);
|
|
1025
|
+
if (inputIndex < 0)
|
|
1026
|
+
return false;
|
|
1027
|
+
const input = lines[inputIndex].trimStart();
|
|
1028
|
+
// A numbered choice (`.` or `)`), e.g. `» 1)` or `❯ 1.`, is a menu selection, never the answer.
|
|
1029
|
+
if (composerNumberedChoicePattern(promptClass).test(input))
|
|
1030
|
+
return false;
|
|
1031
|
+
// Where the input ends: Codex closes the composer on a blank line, Claude Code on a rule line.
|
|
1032
|
+
const end = lines.findIndex((line, index) => index > inputIndex && (runtime === "codex"
|
|
1033
|
+
? line.trim() === "" : /^[─═-]{10,}$/.test(line.trim())));
|
|
1034
|
+
return end > inputIndex && lines.slice(inputIndex, end).join("\n").trimStart().slice(1).trim() === answer.trim();
|
|
1035
|
+
}
|
|
1036
|
+
// Codex 0.160 input box: the last prompt line plus indented continuation rows, one blank row, then a footer
|
|
1037
|
+
// of at most two rows: the `<model> · <~ or /path>` status line, optionally followed by one known hint row.
|
|
1038
|
+
const CODEX_STATUS_LINE = /^ {2}[^\s·][^·]* · [~/]/;
|
|
1039
|
+
const CODEX_HINT_ROW = /(?:^|\s)\? for shortcuts(?:\s|$)|⚠ \d+ warnings? · f2 to view\s*$/;
|
|
1040
|
+
// Codex shows a long paste as this label until it is submitted. Matched on the normalized body.
|
|
1041
|
+
const CODEX_COLLAPSED_PASTE = /^\[PastedContent\d+chars\]$/;
|
|
1042
|
+
/** The current Codex input box: its normalized text ("" when empty), its rows as drawn (the prompt glyph removed)
|
|
1043
|
+
* and where its prompt row is, or null when the layout is not the recognized one (another footer, a selector, no
|
|
1044
|
+
* blank separator, an older TUI). */
|
|
1045
|
+
function codexInputBox(pane) {
|
|
1046
|
+
const lines = pane.split("\n");
|
|
1047
|
+
let last = lines.length - 1;
|
|
1048
|
+
while (last >= 0 && !lines[last].trim())
|
|
1049
|
+
last--;
|
|
1050
|
+
let footerStart = last;
|
|
1051
|
+
while (footerStart > 0 && lines[footerStart - 1].trim())
|
|
1052
|
+
footerStart--;
|
|
1053
|
+
const footer = lines.slice(footerStart, last + 1);
|
|
1054
|
+
if (last < 0 || footer.length > 2 || !CODEX_STATUS_LINE.test(footer[0])
|
|
1055
|
+
|| !footer.slice(1).every((row) => CODEX_HINT_ROW.test(row)))
|
|
1056
|
+
return null;
|
|
1057
|
+
// promptAnswerStaged also ends a Codex input at a blank row.
|
|
1058
|
+
const separator = footerStart - 1;
|
|
1059
|
+
if (separator < 1 || lines[separator].trim())
|
|
1060
|
+
return null;
|
|
1061
|
+
const promptClass = composerPromptClassForRuntime("codex");
|
|
1062
|
+
const inputAt = findComposerInputLineIndex(lines.slice(0, separator), promptClass);
|
|
1063
|
+
if (inputAt < 0 || composerNumberedChoicePattern(promptClass).test(lines[inputAt].trimStart()))
|
|
1064
|
+
return null;
|
|
1065
|
+
const rows = lines.slice(inputAt, separator);
|
|
1066
|
+
// Only indented continuation rows belong to the input; anything else is not an input box.
|
|
1067
|
+
if (rows.slice(1).some((row) => row.trim() && !/^\s/.test(row)))
|
|
1068
|
+
return null;
|
|
1069
|
+
if (CODEX_EMPTY_COMPOSER_PATTERN.test(rows[0].trimStart()) && rows.slice(1).every((row) => !row.trim())) {
|
|
1070
|
+
return { text: "", rows: [], lines, inputAt };
|
|
1071
|
+
}
|
|
1072
|
+
return { text: rows.join("\n").trimStart().slice(1).replace(/\s+/g, ""), rows: stripBoxGlyph(rows), lines, inputAt };
|
|
1073
|
+
}
|
|
1074
|
+
/** A box's rows with the prompt glyph taken off the first row (`❯`, `›`), as composerRegion does for its body. */
|
|
1075
|
+
function stripBoxGlyph(rows) {
|
|
1076
|
+
return rows.map((row, i) => (i === 0 ? row.trimStart().slice(1) : row));
|
|
1077
|
+
}
|
|
1078
|
+
// Bounds of a string's terminal width. Upper: a non-ASCII character may take two cells. Lower: a printable ASCII
|
|
1079
|
+
// character takes exactly one cell, and any other character is counted as none (a combining mark, a variation
|
|
1080
|
+
// selector or a joiner takes none, and an emoji sequence of several code points takes two cells in all).
|
|
1081
|
+
const widthAtMost = (text) => [...text].reduce((cells, ch) => cells + (ch.charCodeAt(0) < 0x80 ? 1 : 2), 0);
|
|
1082
|
+
const widthAtLeast = (text) => [...text].filter((ch) => ch >= " " && ch <= "~").length;
|
|
1083
|
+
// Pairs of characters that no wrapper separates across rows inside a word that fits in a row, under the conditions
|
|
1084
|
+
// rowBreakIsSpace adds. Claude Code 2.1.296 wraps its input with wrap-ansi, which breaks only at spaces. Codex 0.160.0
|
|
1085
|
+
// wraps it with textwrap's FirstFit, its UAX #14 word separator and its hyphen splitter, then re-passes the rows
|
|
1086
|
+
// (tui/src/bottom_pane/textarea/wrapping.rs at rust-v0.160.0), which also break with no space drawn after a hyphen
|
|
1087
|
+
// or a slash, between ideographs and at other UAX #14 break opportunities. UAX #14 has none between two ASCII
|
|
1088
|
+
// letters or digits (LB23, LB25, LB28), nor between a printable ASCII character and a following hyphen, slash,
|
|
1089
|
+
// closing bracket or `.,:;!?` (LB13, LB15d, LB21).
|
|
1090
|
+
const ASCII_LETTER_OR_DIGIT = /^[A-Za-z0-9]$/;
|
|
1091
|
+
const ASCII_VISIBLE = /^[!-~]$/;
|
|
1092
|
+
const ASCII_NO_BREAK_BEFORE = /^[-/.,:;!?)\]}]$/;
|
|
1093
|
+
// A word of ASCII letters, digits, `'` and `.,:;`, which has no UAX #14 break opportunity inside it (LB13, LB19, LB23,
|
|
1094
|
+
// LB25, LB28, LB29): Codex's re-pass keeps such a word whole when it starts on a row that already holds text.
|
|
1095
|
+
const ASCII_UNBROKEN_WORD = /^[A-Za-z0-9'.,:;]+$/;
|
|
1096
|
+
/** Whether the break between two drawn rows of a box must be whitespace of the box's text, given a lower bound of
|
|
1097
|
+
* the box's width. Rows are measured as drawn, the box's own indentation included (an upper bound of their width);
|
|
1098
|
+
* words are split at ASCII spaces only, which neither TUI breaks inside a word. `ownIndent` is how many cells of
|
|
1099
|
+
* `row` the box draws itself (1 after the prompt glyph, 2 on a continuation row). Either:
|
|
1100
|
+
* - the next row's first word would have fit on this row after a space: both wrappers fill a row first
|
|
1101
|
+
* (wrap-ansi and FirstFit are greedy), so where the rows follow the wrapper's own lines the box breaks its rows
|
|
1102
|
+
* there only at a line break of its text. Residual (review-safety-3 F1, from a model of the rust-v0.160.0 source,
|
|
1103
|
+
* not a live capture): on Codex, once a row holding only indentation has absorbed and split a word (below), the
|
|
1104
|
+
* re-pass can push a later row early at a hyphen, since its keep-whole check measures a hyphenated compound as one
|
|
1105
|
+
* word, so a short row can be followed by a fragment that would have fit with no whitespace between them, and this
|
|
1106
|
+
* rule counts that break as a space. Every such box in that model also holds the earlier absorbed split, which
|
|
1107
|
+
* only this rule reads and which it rejects (the row is full), so the box reads `ambiguous`, never `exact`. That
|
|
1108
|
+
* relies on the row-start check below, which the tests pin; or
|
|
1109
|
+
* - no wrapper breaks between the two characters (ASCII_NO_BREAK_BEFORE above), the two words they end and begin,
|
|
1110
|
+
* joined, fit in a row, the row's last word has no break opportunity inside it (ASCII_UNBROKEN_WORD), and the
|
|
1111
|
+
* row's text starts at its first cell with a printable ASCII character. Codex 0.160.0 keeps a word that fits in a
|
|
1112
|
+
* row whole only where the word starts on a row that already holds text, after whitespace or at one of textwrap's
|
|
1113
|
+
* breaks: a row holding only indentation absorbs the next word and splits it where the row fills ("Partial
|
|
1114
|
+
* whitespace rows absorb the next word"), and a word after a hyphen or a slash in a row that took the next
|
|
1115
|
+
* textwrap line is split the same way. So on Codex a split between two letters is no space at all on a row that
|
|
1116
|
+
* starts with indentation, or after a break opportunity inside the row's last word; only the first rule above
|
|
1117
|
+
* reads such a break. */
|
|
1118
|
+
function rowBreakIsSpace(row, next, width, ownIndent) {
|
|
1119
|
+
const firstWord = next.trim().split(" ")[0];
|
|
1120
|
+
if (widthAtMost(row.trimEnd()) + 1 + widthAtMost(firstWord) <= width)
|
|
1121
|
+
return true;
|
|
1122
|
+
if (row.slice(0, ownIndent).trim() || !ASCII_VISIBLE.test(row.slice(ownIndent, ownIndent + 1)))
|
|
1123
|
+
return false;
|
|
1124
|
+
const lastWord = row.trim().split(" ").at(-1);
|
|
1125
|
+
if (!ASCII_UNBROKEN_WORD.test(lastWord))
|
|
1126
|
+
return false;
|
|
1127
|
+
const before = lastWord.at(-1);
|
|
1128
|
+
const after = next.trimStart()[0] ?? "";
|
|
1129
|
+
const noBreak = (ASCII_LETTER_OR_DIGIT.test(before) && ASCII_LETTER_OR_DIGIT.test(after))
|
|
1130
|
+
|| (ASCII_VISIBLE.test(before) && ASCII_NO_BREAK_BEFORE.test(after));
|
|
1131
|
+
return noBreak && widthAtMost(lastWord + firstWord) <= width;
|
|
1132
|
+
}
|
|
1133
|
+
/** Whether a Claude Code or Codex box, given as its rows, holds exactly `text` with its word boundaries kept.
|
|
1134
|
+
* Whitespace inside a row is compared as one space (runs and Unicode spaces count the same, so `now here` is not
|
|
1135
|
+
* `nowhere`), and letters are compared as they are (case included). A row break counts as the text's space only
|
|
1136
|
+
* where it must be whitespace of the box's text (rowBreakIsSpace), measured against the box's widest row in
|
|
1137
|
+
* printable ASCII cells (widthAtLeast), a lower bound of the box's width whatever else the rows hold. Any other
|
|
1138
|
+
* break may fall inside a word of the box's text, or be how the box draws different spacing, so the reading is
|
|
1139
|
+
* `ambiguous`, never `exact`, and so is a break that falls inside a word of the sent text. */
|
|
1140
|
+
function boxHoldsText(rows, text) {
|
|
1141
|
+
// The box draws its first row one cell after the prompt glyph, and its continuation rows two cells in.
|
|
1142
|
+
const drawn = rows.flatMap((row, index) => (row.trim() ? [{ row, ownIndent: index === 0 ? 1 : 2 }] : []));
|
|
1143
|
+
const shown = drawn.map(({ row }) => row.trim().replace(/\s+/g, " "));
|
|
1144
|
+
const sent = text.trim().replace(/\s+/g, " ");
|
|
1145
|
+
// Measured on the rows as drawn: whitespace compares as a space, but U+FEFF, which `\s` matches, takes no cell.
|
|
1146
|
+
const width = Math.max(0, ...drawn.map(({ row }) => widthAtLeast(row.trim())));
|
|
1147
|
+
let at = 0;
|
|
1148
|
+
let ambiguous = false;
|
|
1149
|
+
for (let i = 0; i < shown.length; i++) {
|
|
1150
|
+
const row = shown[i];
|
|
1151
|
+
if (!sent.startsWith(row, at))
|
|
1152
|
+
return "other";
|
|
1153
|
+
at += row.length;
|
|
1154
|
+
if (i === shown.length - 1)
|
|
1155
|
+
break;
|
|
1156
|
+
if (sent[at] === " ") {
|
|
1157
|
+
at += 1;
|
|
1158
|
+
if (!rowBreakIsSpace(drawn[i].row, drawn[i + 1].row, width, drawn[i].ownIndent))
|
|
1159
|
+
ambiguous = true;
|
|
1160
|
+
}
|
|
1161
|
+
else {
|
|
1162
|
+
ambiguous = true;
|
|
1163
|
+
}
|
|
829
1164
|
}
|
|
830
|
-
|
|
1165
|
+
if (at !== sent.length)
|
|
1166
|
+
return "other";
|
|
1167
|
+
return ambiguous ? "ambiguous" : "exact";
|
|
1168
|
+
}
|
|
1169
|
+
/** The row of Codex's running-turn status (`• Working (… esc to interrupt)`) above its input box, or -1. */
|
|
1170
|
+
function codexTurnStatusAt(box) {
|
|
1171
|
+
for (let i = box.inputAt - 1; i >= 0; i--)
|
|
1172
|
+
if (CODEX_TURN_STATUS_PATTERN.test(box.lines[i]))
|
|
1173
|
+
return i;
|
|
1174
|
+
return -1;
|
|
1175
|
+
}
|
|
1176
|
+
/** Whether a Claude Code frame shows a running turn: `esc to interrupt` in the footer under its input box, or a
|
|
1177
|
+
* live status row above the box (timer-less rows included): in the two rows above the box's opening rule, where
|
|
1178
|
+
* Claude Code 2.1.296 draws its status row over a one-row margin whatever the box's height, or as
|
|
1179
|
+
* `findClaudeComposer` reads it above a one-row box. Claude Code queues a message submitted during a turn, and it
|
|
1180
|
+
* labels the queue only while a per-user hint counter is low, so an unlabelled queued message leaves the same
|
|
1181
|
+
* empty box as a submit. With a statusLine configured (OpenRig installs one on every seat) and while the box holds
|
|
1182
|
+
* text, the footer never draws `esc to interrupt`, so the status row is the running signal, but only while it is
|
|
1183
|
+
* drawn: Claude Code hides it while streamed text is shown (SEAT_IDLE_SHAPES.claude.statusSlot). So this reading
|
|
1184
|
+
* can only say a turn ran; a turn it misses is ruled out, if at all, by the seat reading before the paste. */
|
|
1185
|
+
function claudeTurnRunning(pane) {
|
|
1186
|
+
const { markerLine, closingRuleLine } = composerRegion(pane, "claude-code");
|
|
1187
|
+
if (closingRuleLine === null || markerLine === null)
|
|
1188
|
+
return false;
|
|
1189
|
+
const lines = pane.split("\n");
|
|
1190
|
+
if (lines.slice(closingRuleLine).some((row) => /esc to interrupt/.test(row)))
|
|
1191
|
+
return true;
|
|
1192
|
+
// markerLine is 1-based: the box's first row is lines[markerLine - 1] and its opening rule the row above it.
|
|
1193
|
+
const statusSlot = lines.slice(Math.max(0, markerLine - 4), Math.max(0, markerLine - 2)).map((row) => row.trim());
|
|
1194
|
+
if (statusSlot.some((row) => CLAUDE_LIVE_STATUS_PATTERN.test(row) || CLAUDE_TIMERLESS_STATUS_PATTERN.test(row)))
|
|
1195
|
+
return true;
|
|
1196
|
+
return findClaudeComposer(pane, { timerlessStatusIsLive: true })?.liveStatus != null;
|
|
1197
|
+
}
|
|
1198
|
+
// Claude's faint `Try "…"` suggestion in an empty box. The capture is unstyled, so it is read as other text and
|
|
1199
|
+
// never as the sent text, even when the text is the suggestion itself.
|
|
1200
|
+
const CLAUDE_TRY_SUGGESTION = /^Try"[^"]*"$/;
|
|
1201
|
+
// Codex 0.160 lists messages waiting for a running turn above its input box under one of these headers, with
|
|
1202
|
+
// `↳ <text>` rows (tui/src/bottom_pane/pending_input_preview.rs). Like Claude's queued label, such a row is generic:
|
|
1203
|
+
// it says a message waits, not which one.
|
|
1204
|
+
const CODEX_PENDING_INPUT_HEADER = /^• (?:Messages to be submitted (?:after next tool call|at end of turn)|Queued follow-up inputs)\b/;
|
|
1205
|
+
const CODEX_PENDING_INPUT_ROW = new RegExp(`${CODEX_PENDING_INPUT_HEADER.source}|^↳ `);
|
|
1206
|
+
const BRAILLE_SPINNER = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/;
|
|
1207
|
+
// Rows that rule out an idle seat wherever they are in the capture (compared trimmed). A transcript row that
|
|
1208
|
+
// happens to match also reads not-idle, which only leaves the send unconfirmed.
|
|
1209
|
+
const SEAT_NOT_IDLE_ROWS = {
|
|
1210
|
+
"claude-code": [CLAUDE_LIVE_STATUS_PATTERN, CLAUDE_TIMERLESS_STATUS_PATTERN, /esc to interrupt/, /paste again to expand/,
|
|
1211
|
+
/Press up to edit queued messages/, BRAILLE_SPINNER],
|
|
1212
|
+
codex: [CODEX_TURN_STATUS_PATTERN, /esc to interrupt/, CODEX_PENDING_INPUT_ROW, BRAILLE_SPINNER],
|
|
1213
|
+
};
|
|
1214
|
+
// Codex 0.160's completion row (tui/src/history_cell/separators.rs, FinalMessageSeparator::label at rust-v0.160.0):
|
|
1215
|
+
// `Worked for <elapsed>`, then ` • ` and the completion time (an optional date for another day, then the host's
|
|
1216
|
+
// 12- or 24-hour clock, tui/src/clock_format.rs), then optional runtime metrics, each after ` • `, in source order.
|
|
1217
|
+
// A live completion always has both the elapsed time and the time (chatwidget/completion.rs), so both are required.
|
|
1218
|
+
// `<1s`, `{s}s`, `{m}m {s}s` or `{h}h {m}m {s}s`, with minutes and seconds below 60 and no leading zeros.
|
|
1219
|
+
const CODEX_SIXTY = "(?:[0-9]|[1-5][0-9])";
|
|
1220
|
+
const CODEX_NONZERO_SIXTY = "(?:[1-9]|[1-5][0-9])";
|
|
1221
|
+
const CODEX_ELAPSED = `(?:<1s|${CODEX_NONZERO_SIXTY}s|${CODEX_NONZERO_SIXTY}m ${CODEX_SIXTY}s|[1-9][0-9]*h ${CODEX_SIXTY}m ${CODEX_SIXTY}s)`;
|
|
1222
|
+
// `%b %-d at ` (this year) or `%b %-d, %Y at ` (another year); nothing for today.
|
|
1223
|
+
const CODEX_DATE = "(?:(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:[1-9]|[12][0-9]|3[01])(?:, [0-9]{4})? at )?";
|
|
1224
|
+
// `%-I:%M %p` (TwelveHour) or `%H:%M` (TwentyFourHour).
|
|
1225
|
+
const CODEX_CLOCK = "(?:(?:[1-9]|1[0-2]):[0-5][0-9] [AP]M|(?:[01][0-9]|2[0-3]):[0-5][0-9])";
|
|
1226
|
+
// runtime_metrics_label: format_duration_ms gives `<n>.<d>s` from one second, `<n>ms` below it.
|
|
1227
|
+
const CODEX_MS = "(?:[0-9]+\\.[0-9]s|[0-9]+ms)";
|
|
1228
|
+
const CODEX_TTFT_TBT = `(?:${CODEX_MS} \\(iapi\\)(?: ${CODEX_MS} \\(service\\))?|${CODEX_MS} \\(service\\))`;
|
|
1229
|
+
// A metric is shown only for a count above zero; pluralize() gives the singular for exactly one.
|
|
1230
|
+
const CODEX_MANY = "(?:[2-9]|[1-9][0-9]+)";
|
|
1231
|
+
const CODEX_COUNT = "[1-9][0-9]*";
|
|
1232
|
+
const CODEX_METRICS = [
|
|
1233
|
+
`Local tools: (?:1 call|${CODEX_MANY} calls) \\(${CODEX_MS}\\)`,
|
|
1234
|
+
`Inference: (?:1 call|${CODEX_MANY} calls) \\(${CODEX_MS}\\)`,
|
|
1235
|
+
`WebSocket: ${CODEX_COUNT} events send \\(${CODEX_MS}\\)`,
|
|
1236
|
+
`(?:Stream: 1 event|Streams: ${CODEX_MANY} events) \\(${CODEX_MS}\\)`,
|
|
1237
|
+
`${CODEX_COUNT} events received \\(${CODEX_MS}\\)`,
|
|
1238
|
+
`Responses API overhead: ${CODEX_MS}`,
|
|
1239
|
+
`Responses API inference: ${CODEX_MS}`,
|
|
1240
|
+
`TTFT: ${CODEX_TTFT_TBT}`,
|
|
1241
|
+
`TBT: ${CODEX_TTFT_TBT}`,
|
|
1242
|
+
].map((metric) => `(?: • ${metric})?`).join("");
|
|
1243
|
+
const CODEX_COMPLETION_ROW = new RegExp(`^ {2}Worked for ${CODEX_ELAPSED} • ${CODEX_DATE}${CODEX_CLOCK}${CODEX_METRICS}$`);
|
|
1244
|
+
const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1245
|
+
// Claude Code 2.1.296's completion row, which ends a finished turn in the transcript (the bundle's turn-duration
|
|
1246
|
+
// row): the `✻` glyph in a two-cell column at column 0, then `<verb> for <duration> · done <time>`. The verb is one
|
|
1247
|
+
// of the bundle's past-tense list, picked from the turn's id. The duration is the bundle's duration format:
|
|
1248
|
+
// `<s>s` below a minute (`0.0s` below 1 ms), `<m>m <s>s`, `<h>h <m>m <s>s`, `<d>d <h>h <m>m`, without leading
|
|
1249
|
+
// zeros. The time is the clock of a turn that finished today in the en-US forms of the bundle's time-format
|
|
1250
|
+
// presets: `auto` (en-US) and `12-hour` (`5:24 PM`), `24-hour` (`17:24`, `09:05`), `24-hour-utc` (`17:24Z`); real
|
|
1251
|
+
// frames of OpenRig-launched seats show the 12-hour form. Anything after the time
|
|
1252
|
+
// (a still-running note, hidden-message or budget notes), a row without `· done`, another locale, a custom
|
|
1253
|
+
// pattern or the weekday and date of an earlier day is not on this list.
|
|
1254
|
+
const CLAUDE_COMPLETION_VERBS = ["Baked", "Brewed", "Churned", "Cogitated", "Cooked", "Crunched", "Saut\u00e9ed", "Worked"];
|
|
1255
|
+
const CLAUDE_SIXTY = "(?:[0-9]|[1-5][0-9])";
|
|
1256
|
+
const CLAUDE_NONZERO_SIXTY = "(?:[1-9]|[1-5][0-9])";
|
|
1257
|
+
const CLAUDE_HOURS = "(?:[0-9]|1[0-9]|2[0-3])";
|
|
1258
|
+
const CLAUDE_NONZERO_HOURS = "(?:[1-9]|1[0-9]|2[0-3])";
|
|
1259
|
+
const CLAUDE_DURATION = `(?:0\\.0s|${CLAUDE_SIXTY}s|${CLAUDE_NONZERO_SIXTY}m ${CLAUDE_SIXTY}s`
|
|
1260
|
+
+ `|${CLAUDE_NONZERO_HOURS}h ${CLAUDE_SIXTY}m ${CLAUDE_SIXTY}s|[1-9][0-9]*d ${CLAUDE_HOURS}h ${CLAUDE_SIXTY}m)`;
|
|
1261
|
+
const CLAUDE_CLOCK = "(?:(?:[1-9]|1[0-2]):[0-5][0-9] [AP]M|(?:[01][0-9]|2[0-3]):[0-5][0-9]Z?)";
|
|
1262
|
+
const CLAUDE_COMPLETION_ROW = new RegExp(`^✻ (?:${CLAUDE_COMPLETION_VERBS.join("|")}) for ${CLAUDE_DURATION} · done ${CLAUDE_CLOCK}$`);
|
|
1263
|
+
/** The idle allow-list. Each entry comes from real Claude Code 2.1.296 and Codex 0.160.0 frames
|
|
1264
|
+
* (test/fixtures/input-box, including idle seats OpenRig launched: openrig-seat-*), or, where no frame was captured,
|
|
1265
|
+
* from the Claude Code 2.1.296 bundle, the Codex 0.160.0 source or OpenRig's own launch code, as each entry says; a
|
|
1266
|
+
* frame that matches none of them is not proven idle. */
|
|
1267
|
+
const SEAT_IDLE_SHAPES = {
|
|
1268
|
+
claude: {
|
|
1269
|
+
// The box's opening rule. Plain (the captured frames: a seat started without a name), or carrying this seat's
|
|
1270
|
+
// own session name: OpenRig launches every Claude seat with `--name <session>` (claude-code-adapter.ts), which
|
|
1271
|
+
// Claude Code draws on the box's top border as `─…─ <name> ─`, as wide as the closing rule (2.1.296 bundle:
|
|
1272
|
+
// the prompt-border banner draws the rule, the name padded by one space each side, then one `─`). Real frames
|
|
1273
|
+
// of OpenRig seats from earlier Claude Code versions end it with `──` (session-transport.test.ts,
|
|
1274
|
+
// fixtures/walk-staged-specimen), so one or two are accepted; real 2.1.296 OpenRig seats (openrig-seat-*) draw
|
|
1275
|
+
// one. Any other name, or a truncated one, is unknown.
|
|
1276
|
+
openingRules: {
|
|
1277
|
+
plain: /^─{10,}$/,
|
|
1278
|
+
named: (sessionName) => new RegExp(`^─+ ${escapeRegExp(sessionName)} ─{1,2}$`),
|
|
1279
|
+
},
|
|
1280
|
+
// The footer under an empty box when no turn runs, one per permission posture claudePostureFlag can launch
|
|
1281
|
+
// (yolo-mode.ts: --dangerously-skip-permissions, or --permission-mode with any mode Claude Code 2.1.296 offers).
|
|
1282
|
+
// Bypass and accept edits are real (openrig-seat-*; bypass also after-enter-15s.J, idle-fresh-suggestion). The
|
|
1283
|
+
// others are drawn by the 2.1.296 bundle's footer (the mode's symbol and indicator plus ` on`,
|
|
1284
|
+
// ` (shift+tab to cycle)` for every mode but default, then ` · ← for agents` while the box is empty); accept edits
|
|
1285
|
+
// also matches a real 2.1.220 capture (claude-warning-footer-readiness.test.ts). OpenRig's statusLine makes the footer drop its hints
|
|
1286
|
+
// (`? for shortcuts`, `esc to interrupt`), so these rows are the same whether or not a turn runs: the status
|
|
1287
|
+
// slot above the box (`statusSlot` below) is what tells a running turn apart.
|
|
1288
|
+
footers: {
|
|
1289
|
+
bypassPermissions: "⏵⏵ bypass permissions on (shift+tab to cycle) · ← for agents",
|
|
1290
|
+
acceptEdits: "⏵⏵ accept edits on (shift+tab to cycle) · ← for agents",
|
|
1291
|
+
auto: "⏵⏵ auto mode on (shift+tab to cycle) · ← for agents",
|
|
1292
|
+
dontAsk: "⏵⏵ don't ask on (shift+tab to cycle) · ← for agents",
|
|
1293
|
+
plan: "⏸ plan mode on (shift+tab to cycle) · ← for agents",
|
|
1294
|
+
default: "⏸ manual mode on · ← for agents",
|
|
1295
|
+
},
|
|
1296
|
+
// The rows OpenRig's statusLine collector (provisionContextCollector, assets/claude-statusline-context.cjs) draws
|
|
1297
|
+
// between the closing rule and the footer: none. It prints nothing to stdout, and Claude Code 2.1.296 draws no
|
|
1298
|
+
// row for an empty statusLine in the classic renderer OpenRig launches with (CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN=1).
|
|
1299
|
+
// A statusLine that prints anything (a person's own command) is not on this list.
|
|
1300
|
+
statusLineRows: [],
|
|
1301
|
+
// The one row between the box's two rules: empty (after-enter-15s.J), or the Try suggestion (idle-fresh-suggestion).
|
|
1302
|
+
boxes: [/^❯$/, /^❯\s+Try "[^"]*"$/],
|
|
1303
|
+
// The row just above the opening rule: blank (after-enter-15s.J), the effort row (idle-fresh-suggestion), or a
|
|
1304
|
+
// tmux notice (cleared-without-enter.J, staged-short).
|
|
1305
|
+
notices: ["", "◐ medium · /effort",
|
|
1306
|
+
"tmux detected · scroll with PgUp/PgDn · or add 'set -g mouse on' to ~/.tmux.conf for wheel scroll",
|
|
1307
|
+
"tmux focus-events off · add 'set -g focus-events on' to ~/.tmux.conf and reattach for focus tracking"],
|
|
1308
|
+
// The status slot, the row above that, read together with the rows above it when it is blank: the first
|
|
1309
|
+
// non-blank row at or above the slot must be one of these. With OpenRig's statusLine the footer shows no running
|
|
1310
|
+
// signal, so this slot is what tells a running turn apart, and the 2.1.296 bundle says what a running turn leaves
|
|
1311
|
+
// there. Its REPL draws the live status row (the spinner) only while
|
|
1312
|
+
// `isLoading && (!hasDisplayedStreamingText || tailBlank || …)`: once streamed text with a complete line is on
|
|
1313
|
+
// screen it hides the spinner, and draws nothing in its place (the slot component renders only a retry banner,
|
|
1314
|
+
// or null), so the last streamed row moves down into the slot. That row is never the completion row: Claude Code
|
|
1315
|
+
// draws a message behind a two-cell `●` column (continuation rows two spaces in), the user's echo after `❯ `,
|
|
1316
|
+
// tool output indented. But the bundle does not show that the last streamed row is never blank (the streamed
|
|
1317
|
+
// markdown is laid out in a column with a one-row gap, and its unfinished last line is hidden), so a blank slot
|
|
1318
|
+
// under transcript text is not proof of an idle seat and reads unknown. A blank slot counts only where nothing
|
|
1319
|
+
// of a newer turn can be under the row it sits below:
|
|
1320
|
+
// - finishedTurn: the completion row at column 0 (CLAUDE_COMPLETION_ROW). In the classic renderer OpenRig
|
|
1321
|
+
// launches Claude with, the box follows the transcript, so an idle seat that has finished a turn shows that
|
|
1322
|
+
// turn's completion row in the slot itself (openrig-seat-*: real OpenRig-launched seats); in the fullscreen
|
|
1323
|
+
// renderer the box is pinned to the bottom and blank rows sit between them (after-enter-15s.J). A newer turn
|
|
1324
|
+
// draws its echo, its status row or its `●` rows under the completion row.
|
|
1325
|
+
// - freshSession: the last row of the welcome banner of Claude Code v2.1.296, before any turn (idle-fresh-
|
|
1326
|
+
// suggestion, cleared-without-enter.J).
|
|
1327
|
+
// Any other row reads unknown.
|
|
1328
|
+
statusSlot: {
|
|
1329
|
+
finishedTurn: (lines, at) => CLAUDE_COMPLETION_ROW.test(lines[at] ?? ""),
|
|
1330
|
+
freshSession: (lines, at) => at >= 2 && /^ ▝▝ {3}▝▝ {3}[~/]/.test(lines[at])
|
|
1331
|
+
&& /^▝▜██████▀ {2}\S/.test(lines[at - 1]) && /^ ▐▛███▛█ {3}Claude Code v2\.1\.296$/.test(lines[at - 2]),
|
|
1332
|
+
},
|
|
1333
|
+
},
|
|
1334
|
+
codex: {
|
|
1335
|
+
// The hint row under the status line of an empty box (product-path-idle.J, idle).
|
|
1336
|
+
hints: [/^\? for shortcuts$/, /^\? for shortcuts\s+⚠ \d+ warnings? · f2 to view$/],
|
|
1337
|
+
// The last non-blank row above the empty box: the completion row Codex adds when a turn completes
|
|
1338
|
+
// (after-enter-15s.J; its grammar is CODEX_COMPLETION_ROW above), or the greeting under the session header
|
|
1339
|
+
// before any turn (idle, product-path-idle.J, cleared-without-enter.J). A running or streaming turn draws rows
|
|
1340
|
+
// below either.
|
|
1341
|
+
above: {
|
|
1342
|
+
finishedTurn: (lines, at) => CODEX_COMPLETION_ROW.test(lines[at] ?? ""),
|
|
1343
|
+
freshSession: (lines, at) => {
|
|
1344
|
+
if (!/^ {2}\S/.test(lines[at] ?? ""))
|
|
1345
|
+
return false;
|
|
1346
|
+
let i = at - 1;
|
|
1347
|
+
while (i >= 0 && !lines[i].trim())
|
|
1348
|
+
i--;
|
|
1349
|
+
return i >= 2 && i < at - 1 && /^ {2}permissions: \S/.test(lines[i]) && /^ {5}[~/]/.test(lines[i - 1])
|
|
1350
|
+
&& /^ {2}>_ OpenAI Codex \(v0\.160\.0\)$/.test(lines[i - 2]);
|
|
1351
|
+
},
|
|
1352
|
+
},
|
|
1353
|
+
},
|
|
1354
|
+
};
|
|
1355
|
+
function claudeSeatIdle(lines, sessionName) {
|
|
1356
|
+
const idle = SEAT_IDLE_SHAPES.claude;
|
|
1357
|
+
let footer = lines.length - 1;
|
|
1358
|
+
while (footer >= 0 && !lines[footer].trim())
|
|
1359
|
+
footer--;
|
|
1360
|
+
const closing = footer - 1 - idle.statusLineRows.length;
|
|
1361
|
+
if (closing < 4)
|
|
1362
|
+
return false;
|
|
1363
|
+
const width = (row) => [...row].length;
|
|
1364
|
+
const plainRule = (row) => idle.openingRules.plain.test(row);
|
|
1365
|
+
const openingRule = (row) => plainRule(row)
|
|
1366
|
+
|| (!!sessionName && idle.openingRules.named(sessionName).test(row) && width(row) === width(lines[closing]));
|
|
1367
|
+
return Object.values(idle.footers).includes(lines[footer].trim())
|
|
1368
|
+
&& idle.statusLineRows.every((row, i) => lines[closing + 1 + i].trim() === row) && plainRule(lines[closing])
|
|
1369
|
+
&& idle.boxes.some((box) => box.test(lines[closing - 1])) && openingRule(lines[closing - 2])
|
|
1370
|
+
&& idle.notices.includes(lines[closing - 3].trim()) && claudeStatusSlotIdle(lines, closing - 4);
|
|
831
1371
|
}
|
|
1372
|
+
/** The status slot (see SEAT_IDLE_SHAPES.claude.statusSlot): its own row, or, when it is blank, the first non-blank
|
|
1373
|
+
* row above it. */
|
|
1374
|
+
function claudeStatusSlotIdle(lines, slot) {
|
|
1375
|
+
let at = slot;
|
|
1376
|
+
while (at >= 0 && !lines[at].trim())
|
|
1377
|
+
at--;
|
|
1378
|
+
return at >= 0 && Object.values(SEAT_IDLE_SHAPES.claude.statusSlot).some((shape) => shape(lines, at));
|
|
1379
|
+
}
|
|
1380
|
+
function codexSeatIdle(pane) {
|
|
1381
|
+
const box = codexInputBox(pane);
|
|
1382
|
+
if (box === null || box.text)
|
|
1383
|
+
return false;
|
|
1384
|
+
const lines = box.lines.map((row) => row.trimEnd());
|
|
1385
|
+
let hint = lines.length - 1;
|
|
1386
|
+
while (hint >= 0 && !lines[hint].trim())
|
|
1387
|
+
hint--;
|
|
1388
|
+
if (!SEAT_IDLE_SHAPES.codex.hints.some((row) => row.test(lines[hint].trim())))
|
|
1389
|
+
return false;
|
|
1390
|
+
let above = box.inputAt - 1;
|
|
1391
|
+
while (above >= 0 && !lines[above].trim())
|
|
1392
|
+
above--;
|
|
1393
|
+
return Object.values(SEAT_IDLE_SHAPES.codex.above).some((shape) => shape(lines, above));
|
|
1394
|
+
}
|
|
1395
|
+
// The client versions the idle layouts and box readers were recorded from, and the rows in which each client names
|
|
1396
|
+
// the version it runs: Claude Code's welcome banner (`Claude Code v2.1.296`), Codex's session header
|
|
1397
|
+
// (`>_ OpenAI Codex (v0.160.0)`) and its update notice, whose first version is the running one
|
|
1398
|
+
// (`Update available! 0.160.0 -> 0.162.1`). These are only what the frames show; nothing here discovers a version. A frame where one of them names another version is not read as these
|
|
1399
|
+
// layouts. Most frames name no version (the banner and header scroll away), and then a matching layout is evidence
|
|
1400
|
+
// about the layout only, not proof of the client version: another version that draws the same rows reads the same.
|
|
1401
|
+
const RECORDED_CLIENT_VERSION = { "claude-code": "2.1.296", codex: "0.160.0" };
|
|
1402
|
+
const VISIBLE_CLIENT_VERSION = {
|
|
1403
|
+
"claude-code": [/Claude Code v(\d[\w.+-]*)/g],
|
|
1404
|
+
codex: [/OpenAI Codex \(v(\d[\w.+-]*)\)/g, /Update available! (\d[\w.+-]*) -> /g],
|
|
1405
|
+
};
|
|
1406
|
+
/** Whether a frame visibly names a client version other than the recorded one (see RECORDED_CLIENT_VERSION). */
|
|
1407
|
+
function namesAnotherClientVersion(pane, runtime) {
|
|
1408
|
+
return VISIBLE_CLIENT_VERSION[runtime].some((pattern) => [...pane.matchAll(pattern)].some((match) => match[1] !== RECORDED_CLIENT_VERSION[runtime]));
|
|
1409
|
+
}
|
|
1410
|
+
/** The seat before the paste (see SeatReading), read from the capture the transport already takes before it.
|
|
1411
|
+
* `sessionName` is the seat's own session name, which a Claude Code seat launched by OpenRig draws on its box.
|
|
1412
|
+
* A frame that names another client version is `unknown`. */
|
|
1413
|
+
export function readSeatBeforePaste(pane, runtime, sessionName) {
|
|
1414
|
+
if (!pane?.trim())
|
|
1415
|
+
return "unknown";
|
|
1416
|
+
const lines = pane.split("\n").map((row) => row.trimEnd());
|
|
1417
|
+
if (lines.some((row) => SEAT_NOT_IDLE_ROWS[runtime].some((pattern) => pattern.test(row.trim()))))
|
|
1418
|
+
return "not-idle";
|
|
1419
|
+
if (namesAnotherClientVersion(pane, runtime))
|
|
1420
|
+
return "unknown";
|
|
1421
|
+
return (runtime === "codex" ? codexSeatIdle(pane) : claudeSeatIdle(lines, sessionName)) ? "idle" : "unknown";
|
|
1422
|
+
}
|
|
1423
|
+
/** What a Claude Code or Codex input box shows, read from the input box only (never the transcript). Labels and
|
|
1424
|
+
* placeholders (Claude's queued label and Try suggestion, collapsed pastes, Codex's empty-box placeholder) are
|
|
1425
|
+
* checked before the sent text, so none of them can read as `exact`. The sent text is `exact` only with its word
|
|
1426
|
+
* boundaries kept, and `ambiguous` where the box's row breaks leave them uncertain (boxHoldsText). A frame that
|
|
1427
|
+
* names another client version reads `unrecognized` (RECORDED_CLIENT_VERSION).
|
|
1428
|
+
*
|
|
1429
|
+
* An empty box also reads `queued` where the frames say the message may wait behind a turn: Codex's
|
|
1430
|
+
* pending-input rows above the box, rows between Codex's running-turn status row and the box, or
|
|
1431
|
+
* `beforeEnter` (the frame read just before Enter) showing a running turn (Codex's status row, or
|
|
1432
|
+
* `claudeTurnRunning`). These readings decide the verdict: verified needs `empty` after Enter. For a seat that
|
|
1433
|
+
* was not idle before the paste they only say why the send is unconfirmed (readSeatBeforePaste already rules it
|
|
1434
|
+
* out), but for a seat that was idle they are the only check that rejects a message queued behind a turn that
|
|
1435
|
+
* started after the pre-paste capture and shows in the frame read before Enter.
|
|
1436
|
+
* Used only to report a send with transport.verify_input_box on. */
|
|
1437
|
+
export function readInputBox(pane, runtime, text, beforeEnter) {
|
|
1438
|
+
if (!pane?.trim())
|
|
1439
|
+
return "unavailable";
|
|
1440
|
+
if (namesAnotherClientVersion(pane, runtime))
|
|
1441
|
+
return "unrecognized";
|
|
1442
|
+
if (runtime === "codex") {
|
|
1443
|
+
const box = codexInputBox(pane);
|
|
1444
|
+
if (box === null)
|
|
1445
|
+
return "unrecognized";
|
|
1446
|
+
if (!box.text) {
|
|
1447
|
+
const before = beforeEnter ? codexInputBox(beforeEnter) : null;
|
|
1448
|
+
const status = codexTurnStatusAt(box);
|
|
1449
|
+
const rowsBehindTurn = status >= 0 && box.lines.slice(status + 1, box.inputAt).some((row) => row.trim());
|
|
1450
|
+
const pendingInput = box.lines.slice(0, box.inputAt).some((row) => CODEX_PENDING_INPUT_ROW.test(row.trim()));
|
|
1451
|
+
return pendingInput || rowsBehindTurn || (before !== null && codexTurnStatusAt(before) >= 0) ? "queued" : "empty";
|
|
1452
|
+
}
|
|
1453
|
+
if (CODEX_COLLAPSED_PASTE.test(box.text))
|
|
1454
|
+
return "hidden";
|
|
1455
|
+
return boxHoldsText(box.rows, text);
|
|
1456
|
+
}
|
|
1457
|
+
const { body, markerLine, closingRuleLine } = composerRegion(pane, runtime);
|
|
1458
|
+
if (body === null)
|
|
1459
|
+
return "unrecognized";
|
|
1460
|
+
if (!body)
|
|
1461
|
+
return beforeEnter && claudeTurnRunning(beforeEnter) ? "queued" : "empty";
|
|
1462
|
+
if (body === QUEUED_PLACEHOLDER)
|
|
1463
|
+
return "queued";
|
|
1464
|
+
if (COLLAPSED_PASTE.test(body))
|
|
1465
|
+
return "hidden";
|
|
1466
|
+
if (CLAUDE_TRY_SUGGESTION.test(body))
|
|
1467
|
+
return "other";
|
|
1468
|
+
// composerRegion's body has every space removed (the startup guard's matcher, unchanged); identity reads the rows.
|
|
1469
|
+
return boxHoldsText(stripBoxGlyph(pane.split("\n").slice(markerLine - 1, closingRuleLine - 1)), text);
|
|
1470
|
+
}
|
|
1471
|
+
/** The evidence of a verified send whose path does not read the input box (an audited prompt answer). */
|
|
1472
|
+
const INPUT_BOX_UNCHECKED = { seat: "unchecked", before: "unchecked", after: "unchecked", sameProcess: false };
|
|
832
1473
|
export class SessionTransport {
|
|
833
1474
|
db;
|
|
1475
|
+
guardedDelivery;
|
|
834
1476
|
rigRepo;
|
|
835
1477
|
sessionRegistry;
|
|
836
1478
|
tmuxAdapter;
|
|
@@ -844,6 +1486,9 @@ export class SessionTransport {
|
|
|
844
1486
|
activityEndpointFile;
|
|
845
1487
|
captureObserver;
|
|
846
1488
|
listProcesses;
|
|
1489
|
+
runtimeAdapters;
|
|
1490
|
+
inFlightReconciliations = new Map();
|
|
1491
|
+
verifyInputBox;
|
|
847
1492
|
constructor(deps) {
|
|
848
1493
|
this.db = deps.db;
|
|
849
1494
|
this.rigRepo = deps.rigRepo;
|
|
@@ -859,6 +1504,17 @@ export class SessionTransport {
|
|
|
859
1504
|
this.activityEndpointFile = deps.activityEndpointFile ?? (() => null);
|
|
860
1505
|
this.captureObserver = deps.captureObserver;
|
|
861
1506
|
this.listProcesses = deps.listProcesses;
|
|
1507
|
+
this.runtimeAdapters = deps.runtimeAdapters ?? {};
|
|
1508
|
+
this.verifyInputBox = deps.verifyInputBox;
|
|
1509
|
+
const guard = this.tmuxAdapter.deliveryGuard;
|
|
1510
|
+
if (guard && this.db.prepare("PRAGMA table_info(outbox_entries)").all().some(c => c.name === "guard_delivery"))
|
|
1511
|
+
this.guardedDelivery = new DraftAwareDelivery(this.db, guard, (session, text, opts) => this.sendUnguarded(session, text, opts));
|
|
1512
|
+
}
|
|
1513
|
+
configureGuardedWakes(applicable) {
|
|
1514
|
+
this.guardedDelivery?.configureWakes(applicable);
|
|
1515
|
+
}
|
|
1516
|
+
attachRuntimeAdapters(adapters) {
|
|
1517
|
+
this.runtimeAdapters = { ...this.runtimeAdapters, ...adapters };
|
|
862
1518
|
}
|
|
863
1519
|
/** Freeze the registration and binding before asking tmux about this name. */
|
|
864
1520
|
absenceProbeTarget(sessionName) {
|
|
@@ -1132,17 +1788,30 @@ export class SessionTransport {
|
|
|
1132
1788
|
const pref = guard.preference(target.nodeId);
|
|
1133
1789
|
return pref.desired || pref.effective ? target : null;
|
|
1134
1790
|
}
|
|
1791
|
+
draftAwareTarget(sessionName) {
|
|
1792
|
+
const guard = this.tmuxAdapter.deliveryGuard;
|
|
1793
|
+
const target = guard?.maybeTarget(sessionName);
|
|
1794
|
+
if (!guard || !target)
|
|
1795
|
+
return null;
|
|
1796
|
+
const policy = guard.configuration(target.nodeId);
|
|
1797
|
+
return policy.desired.mode === "draft-aware" || policy.effective.mode === "draft-aware" ? target : null;
|
|
1798
|
+
}
|
|
1135
1799
|
/** Deliver text and Enter to the resolved recipient under the send guards.
|
|
1136
1800
|
* With verification requested, compare rendered message counts, excluding
|
|
1137
1801
|
* agent input; a confirmed render does not prove agent consumption. */
|
|
1138
1802
|
async send(sessionName, text, opts) {
|
|
1139
1803
|
const guard = this.tmuxAdapter.deliveryGuard;
|
|
1140
|
-
if (!guard)
|
|
1141
|
-
|
|
1804
|
+
if (!guard) {
|
|
1805
|
+
const result = await this.sendUnguarded(sessionName, text, opts);
|
|
1806
|
+
if (result.ok && result.sent !== false) {
|
|
1807
|
+
await this.reconcileStartupReadiness(sessionName).catch(() => { });
|
|
1808
|
+
}
|
|
1809
|
+
return result;
|
|
1810
|
+
}
|
|
1142
1811
|
const outbox = new OutboxHandler(this.db);
|
|
1143
1812
|
const ids = opts?.committedOutboxIds ?? [opts?.deliveryId ?? `guard-send-${randomUUID()}`];
|
|
1144
|
-
const retainedResult = () => ({ ok: true, sessionName, outcome: "retained", sent: false, verified: false,
|
|
1145
|
-
outboxIds: ids, reason
|
|
1813
|
+
const retainedResult = (reason = "typing_guard_enabled") => ({ ok: true, sessionName, outcome: "retained", sent: false, verified: false,
|
|
1814
|
+
outboxIds: ids, reason, warning: `Retained, not delivered. Inspect with rig seat held-messages ${sessionName}; changing protection does not replay held messages.` });
|
|
1146
1815
|
try {
|
|
1147
1816
|
if (opts?.committedOutboxIds) {
|
|
1148
1817
|
const target = guard.target(sessionName);
|
|
@@ -1155,6 +1824,9 @@ export class SessionTransport {
|
|
|
1155
1824
|
}
|
|
1156
1825
|
}
|
|
1157
1826
|
}
|
|
1827
|
+
const existing = this.guardedDelivery?.existing(sessionName, text, opts ?? {}, ids);
|
|
1828
|
+
if (existing)
|
|
1829
|
+
return existing;
|
|
1158
1830
|
// Idempotent readback also after disabling: an old retained ID never becomes a new send.
|
|
1159
1831
|
// (P2: this readback is NOT a new retention and is outside the retained_no_write seam.)
|
|
1160
1832
|
if (!opts?.committedOutboxIds && opts?.deliveryId) {
|
|
@@ -1204,7 +1876,16 @@ export class SessionTransport {
|
|
|
1204
1876
|
sendOpts = { ...opts, preWaitedIdle: pre.evidence, preWaitBound: pre.bound };
|
|
1205
1877
|
}
|
|
1206
1878
|
}
|
|
1207
|
-
|
|
1879
|
+
const result = await guard.operation(sessionName, async () => {
|
|
1880
|
+
if (this.guardedDelivery && !sendOpts?.submitOnly && guard.needsInputCheck(sessionName)) {
|
|
1881
|
+
return this.guardedDelivery.send(sessionName, text, sendOpts ?? {}, ids);
|
|
1882
|
+
}
|
|
1883
|
+
const prior = this.guardedDelivery?.existing(sessionName, text, sendOpts ?? {}, ids);
|
|
1884
|
+
return prior ?? this.sendUnguarded(sessionName, text, sendOpts);
|
|
1885
|
+
}, async (target) => {
|
|
1886
|
+
const prior = this.guardedDelivery?.existing(sessionName, text, sendOpts ?? {}, ids);
|
|
1887
|
+
if (prior)
|
|
1888
|
+
return prior;
|
|
1208
1889
|
if (opts?.submitOnly)
|
|
1209
1890
|
return { ok: false, sessionName, sent: false, reason: "typing_guard_enabled", error: "Typing guard prevents submit-only; no Enter was sent." };
|
|
1210
1891
|
this.db.transaction(() => {
|
|
@@ -1214,6 +1895,7 @@ export class SessionTransport {
|
|
|
1214
1895
|
throw new Error("Committed wake target/ID mismatch");
|
|
1215
1896
|
outbox.retain(prior ? { ...prior, outboxId: id, tags: prior.tags ?? undefined, auditPointer: prior.auditPointer ?? undefined } : {
|
|
1216
1897
|
outboxId: id, senderSession: opts?.actorSession ?? "unknown", destinationSession: sessionName, body: text, auditPointer: opts?.auditPointer,
|
|
1898
|
+
identityProvenance: opts?.identityProvenance,
|
|
1217
1899
|
}, target, !!opts?.committedOutboxIds);
|
|
1218
1900
|
}
|
|
1219
1901
|
})();
|
|
@@ -1234,8 +1916,24 @@ export class SessionTransport {
|
|
|
1234
1916
|
}
|
|
1235
1917
|
return retainedResult();
|
|
1236
1918
|
});
|
|
1919
|
+
if (result.ok && result.sent !== false) {
|
|
1920
|
+
await this.reconcileStartupReadiness(sessionName).catch(() => { });
|
|
1921
|
+
}
|
|
1922
|
+
return result;
|
|
1237
1923
|
}
|
|
1238
1924
|
catch (error) {
|
|
1925
|
+
// A refusal thrown after the text was written (for example a guard check before Enter) is not "nothing sent".
|
|
1926
|
+
if (error.inputWritten === true) {
|
|
1927
|
+
// Only a throw before the Enter key reached the adapter positively means Enter was never sent.
|
|
1928
|
+
const enter = error.enterAttempted === true ? "the submission (Enter) outcome is unconfirmed" : "Enter was not sent";
|
|
1929
|
+
return { ok: false, sessionName, sent: true, outcome: "failed", inputPhase: "written", reason: error.code ?? "submit_refused",
|
|
1930
|
+
error: `Text was written to '${sessionName}' but ${enter}: ${error.message}` };
|
|
1931
|
+
}
|
|
1932
|
+
// A throw after the text write was issued: whether it landed is unknown, so it is not "nothing sent" either.
|
|
1933
|
+
if (error.inputAttempted === true) {
|
|
1934
|
+
return { ok: false, sessionName, outcome: "failed", inputPhase: "attempted", reason: error.code ?? "delivery_indeterminate",
|
|
1935
|
+
error: `A text write to '${sessionName}' was issued, but its result was lost: ${error.message}` };
|
|
1936
|
+
}
|
|
1239
1937
|
return { ok: false, sessionName, sent: false, reason: error.code ?? "guard_unavailable", error: error.message };
|
|
1240
1938
|
}
|
|
1241
1939
|
}
|
|
@@ -1261,7 +1959,10 @@ export class SessionTransport {
|
|
|
1261
1959
|
if (!preTarget)
|
|
1262
1960
|
return null;
|
|
1263
1961
|
const prePref = guard.preference(preTarget.nodeId);
|
|
1264
|
-
|
|
1962
|
+
// Hold mode retains in the lease, and draft-aware mode reads the input there first and returns its
|
|
1963
|
+
// immediate result (held, or refused with its reason): neither takes the lease-free wait. A
|
|
1964
|
+
// draft-aware send whose input is empty waits for idle inside the lease, as before this wait existed.
|
|
1965
|
+
if (prePref.effective || prePref.desired || prePref.desiredMode === "draft-aware" || prePref.effectiveMode === "draft-aware")
|
|
1265
1966
|
return null;
|
|
1266
1967
|
meta = this.getSessionMeta(sessionName);
|
|
1267
1968
|
bound = guard.target(sessionName);
|
|
@@ -1357,6 +2058,7 @@ export class SessionTransport {
|
|
|
1357
2058
|
reason: preWait.reason,
|
|
1358
2059
|
error: preWait.error,
|
|
1359
2060
|
sent: false,
|
|
2061
|
+
inputPhase: "not_reached",
|
|
1360
2062
|
activity: preWait.activity,
|
|
1361
2063
|
waitedMs: preWait.waitedMs,
|
|
1362
2064
|
attempts: preWait.attempts,
|
|
@@ -1388,6 +2090,31 @@ export class SessionTransport {
|
|
|
1388
2090
|
runtimeAdvisory = `runtime: ${observation.detail}; delivery proceeds without verified native identity.`;
|
|
1389
2091
|
return null;
|
|
1390
2092
|
};
|
|
2093
|
+
// #331: with mouse on, scrolling a seat pane puts it in tmux copy mode. A paste still reaches
|
|
2094
|
+
// the input there, but copy mode swallows the Enter, so the send would report ok while the
|
|
2095
|
+
// text sits unsubmitted. Leave the mode once (send-keys -X cancel) and deliver; fail before
|
|
2096
|
+
// typing anything only if the pane still reads as in a mode. An unknown answer keeps today's path.
|
|
2097
|
+
const readPaneInMode = async () => {
|
|
2098
|
+
try {
|
|
2099
|
+
return typeof this.tmuxAdapter.isPaneInMode === "function" ? await this.tmuxAdapter.isPaneInMode(sessionName) : null;
|
|
2100
|
+
}
|
|
2101
|
+
catch {
|
|
2102
|
+
return null;
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
const leavePaneMode = async (outcome) => {
|
|
2106
|
+
if (await readPaneInMode() !== true)
|
|
2107
|
+
return null;
|
|
2108
|
+
try {
|
|
2109
|
+
if (typeof this.tmuxAdapter.cancelPaneMode === "function")
|
|
2110
|
+
await this.tmuxAdapter.cancelPaneMode(sessionName);
|
|
2111
|
+
}
|
|
2112
|
+
catch { /* the re-read below decides */ }
|
|
2113
|
+
if (await readPaneInMode() !== true)
|
|
2114
|
+
return null;
|
|
2115
|
+
return { ok: false, sessionName, sent: false, reason: "pane_in_mode",
|
|
2116
|
+
error: `'${sessionName}' is in a tmux mode (such as copy mode) and could not leave it, so Enter would not reach the agent. ${outcome}` };
|
|
2117
|
+
};
|
|
1391
2118
|
// S01/S02 P2 observation context, frozen at attempt entry before any await.
|
|
1392
2119
|
const observed = this.captureObserver ? {
|
|
1393
2120
|
attemptId: randomUUID(),
|
|
@@ -1507,7 +2234,7 @@ export class SessionTransport {
|
|
|
1507
2234
|
if (!opts.requireFullStagedText || !opts.onStartupMismatch)
|
|
1508
2235
|
return;
|
|
1509
2236
|
try {
|
|
1510
|
-
const evidence = startupSubmissionEvidence(pane, expected, opts.submitOnlyCaptureLines ?? 50);
|
|
2237
|
+
const evidence = startupSubmissionEvidence(pane, expected, opts.submitOnlyCaptureLines ?? 50, runtime);
|
|
1511
2238
|
if (evidence)
|
|
1512
2239
|
opts.onStartupMismatch(evidence);
|
|
1513
2240
|
}
|
|
@@ -1522,8 +2249,8 @@ export class SessionTransport {
|
|
|
1522
2249
|
throw error; // Preserve the existing guarded/unguarded error handling.
|
|
1523
2250
|
}
|
|
1524
2251
|
const staged = opts.requireFullStagedText
|
|
1525
|
-
? inspectStartupStagedText(pane, expected) === "staged"
|
|
1526
|
-
: hasExpectedStagedText(pane, expected);
|
|
2252
|
+
? inspectStartupStagedText(pane, expected, runtime) === "staged"
|
|
2253
|
+
: hasExpectedStagedText(pane, expected, runtime);
|
|
1527
2254
|
if (!staged) {
|
|
1528
2255
|
recordMismatch(pane);
|
|
1529
2256
|
return {
|
|
@@ -1533,13 +2260,21 @@ export class SessionTransport {
|
|
|
1533
2260
|
error: `submitOnly refused: the pane of '${sessionName}' does not show the expected staged text — pressing Enter here could drive something else entirely. Nothing was submitted.`,
|
|
1534
2261
|
};
|
|
1535
2262
|
}
|
|
2263
|
+
this.tmuxAdapter.deliveryGuard?.expectStagedInput(sessionName, expected);
|
|
1536
2264
|
const targetFailure = await checkClaudeTarget();
|
|
1537
2265
|
if (targetFailure)
|
|
1538
2266
|
return targetFailure;
|
|
2267
|
+
const copyModeFailure = await leavePaneMode("Nothing was submitted.");
|
|
2268
|
+
if (copyModeFailure)
|
|
2269
|
+
return copyModeFailure;
|
|
1539
2270
|
const submitResult = await this.runStage("session_transport.submit", () => this.tmuxAdapter.sendKeys(sessionName, ["Enter"]), (result) => result.ok ? "ok" : "failed");
|
|
1540
2271
|
if (!submitResult.ok) {
|
|
1541
2272
|
return { ok: false, sessionName, reason: "submit_failed", outcome: "failed", error: `submitOnly: Enter did not land on '${sessionName}': ${submitResult.message}` };
|
|
1542
2273
|
}
|
|
2274
|
+
try {
|
|
2275
|
+
opts.onInputEffect?.("submitted");
|
|
2276
|
+
}
|
|
2277
|
+
catch { /* Observation cannot change an input result. */ }
|
|
1543
2278
|
return observe({ ok: true, sessionName, outcome: "rendered-unconfirmed", submitOnly: true });
|
|
1544
2279
|
}
|
|
1545
2280
|
// #1084: when send() already waited lease-free (preWaitedIdle), the wait
|
|
@@ -1566,6 +2301,7 @@ export class SessionTransport {
|
|
|
1566
2301
|
reason: waitResult.reason,
|
|
1567
2302
|
error: waitResult.error,
|
|
1568
2303
|
sent: false,
|
|
2304
|
+
inputPhase: "not_reached",
|
|
1569
2305
|
...waitEvidence,
|
|
1570
2306
|
};
|
|
1571
2307
|
}
|
|
@@ -1623,6 +2359,7 @@ export class SessionTransport {
|
|
|
1623
2359
|
reason: rewait.reason,
|
|
1624
2360
|
error: rewait.error,
|
|
1625
2361
|
sent: false,
|
|
2362
|
+
inputPhase: "not_reached",
|
|
1626
2363
|
...waitEvidence,
|
|
1627
2364
|
};
|
|
1628
2365
|
}
|
|
@@ -1654,6 +2391,7 @@ export class SessionTransport {
|
|
|
1654
2391
|
ok: false,
|
|
1655
2392
|
sessionName,
|
|
1656
2393
|
sent: false,
|
|
2394
|
+
inputPhase: "not_reached",
|
|
1657
2395
|
reason: late.reason,
|
|
1658
2396
|
error: late.error,
|
|
1659
2397
|
activity: recheck,
|
|
@@ -1680,7 +2418,7 @@ export class SessionTransport {
|
|
|
1680
2418
|
// the positive-picker guard (FR-4 — the footgun separation). The advisory is carried on the
|
|
1681
2419
|
// success result via `warning` so the honest telemetry is surfaced.
|
|
1682
2420
|
let sendAdvisory;
|
|
1683
|
-
|
|
2421
|
+
const promptOverride = waitForIdleMs === undefined && Boolean(opts?.dangerouslyInteract);
|
|
1684
2422
|
if (waitForIdleMs === undefined) {
|
|
1685
2423
|
const readiness = await this.classifySendReadiness({
|
|
1686
2424
|
sessionName,
|
|
@@ -1688,42 +2426,37 @@ export class SessionTransport {
|
|
|
1688
2426
|
attachmentType: sessionMeta.attachmentType,
|
|
1689
2427
|
binding: observed?.binding,
|
|
1690
2428
|
});
|
|
1691
|
-
//
|
|
1692
|
-
//
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
// cannot be audited so an unauditable override never sends). Otherwise refuse with the
|
|
1698
|
-
// proceed-path. This is the ONLY state --dangerously-interact bypasses.
|
|
1699
|
-
if (opts?.dangerouslyInteract) {
|
|
1700
|
-
if (!opts.reason || opts.reason.trim().length === 0) {
|
|
1701
|
-
return {
|
|
1702
|
-
ok: false,
|
|
1703
|
-
sessionName,
|
|
1704
|
-
reason: "dangerously_interact_requires_reason",
|
|
1705
|
-
error: "--dangerously-interact requires --reason explaining why the prompt is being driven. No text was sent.",
|
|
1706
|
-
};
|
|
1707
|
-
}
|
|
1708
|
-
const audit = this.recordPromptOverride({
|
|
2429
|
+
// Explicit interaction selects key input even when readiness cannot recognize the prompt.
|
|
2430
|
+
// Only bypassing a positive picker requires a successful audit; other states already sent.
|
|
2431
|
+
if (opts?.dangerouslyInteract) {
|
|
2432
|
+
if (readiness.state === "needs_input" && (!opts.reason || opts.reason.trim().length === 0)) {
|
|
2433
|
+
return {
|
|
2434
|
+
ok: false,
|
|
1709
2435
|
sessionName,
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
});
|
|
1714
|
-
if (!audit.ok) {
|
|
1715
|
-
return {
|
|
1716
|
-
ok: false,
|
|
1717
|
-
sessionName,
|
|
1718
|
-
reason: "prompt_override_audit_unavailable",
|
|
1719
|
-
activity: readiness,
|
|
1720
|
-
error: `Refused: --dangerously-interact requires an auditable override record, which could not be persisted (${audit.reason}). No text was sent.`,
|
|
1721
|
-
};
|
|
1722
|
-
}
|
|
1723
|
-
// Audited answers use unbracketed input; a choice may submit itself.
|
|
1724
|
-
promptOverride = true;
|
|
2436
|
+
reason: "dangerously_interact_requires_reason",
|
|
2437
|
+
error: "--dangerously-interact requires --reason explaining why the prompt is being driven. No text was sent.",
|
|
2438
|
+
};
|
|
1725
2439
|
}
|
|
1726
|
-
|
|
2440
|
+
const audit = this.recordPromptOverride({
|
|
2441
|
+
sessionName,
|
|
2442
|
+
readiness,
|
|
2443
|
+
actorSession: opts.actorSession ?? null,
|
|
2444
|
+
overrideReason: opts.reason ?? null,
|
|
2445
|
+
});
|
|
2446
|
+
if (!audit.ok && readiness.state === "needs_input") {
|
|
2447
|
+
return {
|
|
2448
|
+
ok: false,
|
|
2449
|
+
sessionName,
|
|
2450
|
+
reason: "prompt_override_audit_unavailable",
|
|
2451
|
+
activity: readiness,
|
|
2452
|
+
error: `Refused: --dangerously-interact requires an auditable override record, which could not be persisted (${audit.reason}). No text was sent.`,
|
|
2453
|
+
};
|
|
2454
|
+
}
|
|
2455
|
+
if (!audit.ok)
|
|
2456
|
+
sendAdvisory = "prompt-override: audit unavailable (" + audit.reason + "); input sent anyway.";
|
|
2457
|
+
}
|
|
2458
|
+
if (readiness.state === "needs_input") {
|
|
2459
|
+
if (!promptOverride) {
|
|
1727
2460
|
return {
|
|
1728
2461
|
ok: false,
|
|
1729
2462
|
sessionName,
|
|
@@ -1741,7 +2474,7 @@ export class SessionTransport {
|
|
|
1741
2474
|
// surfaced without ever blocking communication. Hooks are advisory telemetry, not authority
|
|
1742
2475
|
// over whether agents can talk.
|
|
1743
2476
|
const linkDiagnosis = await this.diagnoseProducerLink(sessionName);
|
|
1744
|
-
sendAdvisory = `producer-link: ${linkDiagnosis} — activity could not be determined (${readiness.reason}); sent anyway (telemetry is advisory)
|
|
2477
|
+
sendAdvisory = [sendAdvisory, `producer-link: ${linkDiagnosis} — activity could not be determined (${readiness.reason}); sent anyway (telemetry is advisory).`].filter(Boolean).join(" ");
|
|
1745
2478
|
// fall through to the send below.
|
|
1746
2479
|
}
|
|
1747
2480
|
else if (readiness.state === "running") {
|
|
@@ -1750,11 +2483,15 @@ export class SessionTransport {
|
|
|
1750
2483
|
// attach it on the success result + PROCEED (mirrors the unknown inversion). --force is now a
|
|
1751
2484
|
// no-op here (the option is kept for back-compat). needs_input (positive picker) remains the
|
|
1752
2485
|
// ONLY hard refuse; unknown/stale/missing already proceed-with-advisory above.
|
|
1753
|
-
sendAdvisory =
|
|
2486
|
+
sendAdvisory = [sendAdvisory, "target pane appears mid-task; sent anyway (busy is advisory, not a block)."].filter(Boolean).join(" ");
|
|
1754
2487
|
// fall through to the send below.
|
|
1755
2488
|
}
|
|
1756
2489
|
// idle (or running/unknown — now advisory-and-proceed) → proceed to send.
|
|
1757
2490
|
}
|
|
2491
|
+
// transport.verify_input_box: a verified Claude Code or Codex send reports on input-box evidence. The
|
|
2492
|
+
// setting is read only for those sends; everything else keeps today's path.
|
|
2493
|
+
const inputBoxRuntime = opts?.verify && (runtime === "claude-code" || runtime === "codex") && this.inputBoxSettingOn()
|
|
2494
|
+
? runtime : null;
|
|
1758
2495
|
if (opts?.verify) {
|
|
1759
2496
|
const captureSeq = observed ? nextCaptureSeq++ : 0;
|
|
1760
2497
|
try {
|
|
@@ -1780,51 +2517,99 @@ export class SessionTransport {
|
|
|
1780
2517
|
const targetFailure = await checkClaudeTarget();
|
|
1781
2518
|
if (targetFailure)
|
|
1782
2519
|
return observe(targetFailure);
|
|
2520
|
+
const copyModeFailure = await leavePaneMode("No text was sent.");
|
|
2521
|
+
if (copyModeFailure)
|
|
2522
|
+
return observe({ ...copyModeFailure, ...(waitMode ? waitEvidence : {}) });
|
|
1783
2523
|
// 3. Deliver ordinary messages as paste, audited prompt answers as key input.
|
|
1784
2524
|
if (observed)
|
|
1785
2525
|
observed.sentHash = hashSentText(text);
|
|
2526
|
+
// Set once the text write has been handed to the adapter: from then on a throw is not "nothing sent".
|
|
2527
|
+
let writeIssued = false;
|
|
2528
|
+
const tagIssued = (error) => {
|
|
2529
|
+
if (writeIssued && error && typeof error === "object")
|
|
2530
|
+
error.inputAttempted = true;
|
|
2531
|
+
throw error;
|
|
2532
|
+
};
|
|
1786
2533
|
const textResult = await this.runStage("session_transport.send_text", () => {
|
|
1787
2534
|
opts?.beforeWrite?.();
|
|
2535
|
+
writeIssued = true;
|
|
1788
2536
|
if (promptOverride)
|
|
1789
2537
|
return this.tmuxAdapter.sendText(sessionName, text, opts?.beforeWrite, { bracketed: false });
|
|
1790
2538
|
return opts?.beforeWrite ? this.tmuxAdapter.sendText(sessionName, text, opts.beforeWrite) : this.tmuxAdapter.sendText(sessionName, text);
|
|
1791
|
-
}, (result) => result.ok ? "ok" : "failed");
|
|
2539
|
+
}, (result) => result.ok ? "ok" : "failed").catch(tagIssued);
|
|
1792
2540
|
if (!textResult.ok) {
|
|
2541
|
+
// A draft refusal is the guard holding the write before any input; it is not an attempted write.
|
|
2542
|
+
const policyRefusal = typeof textResult.code === "string" && textResult.code.startsWith("draft_");
|
|
1793
2543
|
return observe({
|
|
1794
2544
|
ok: false,
|
|
1795
2545
|
sessionName,
|
|
1796
|
-
reason: "send_failed",
|
|
2546
|
+
reason: policyRefusal ? textResult.code : "send_failed",
|
|
1797
2547
|
outcome: "failed",
|
|
2548
|
+
...(policyRefusal ? {} : { inputPhase: "attempted" }),
|
|
1798
2549
|
error: `Failed to send text to '${sessionName}': ${textResult.message}`,
|
|
1799
|
-
...(waitMode ? { sent: false, ...waitEvidence } : {}),
|
|
2550
|
+
...(waitMode || policyRefusal ? { sent: false, ...waitEvidence } : {}),
|
|
1800
2551
|
});
|
|
1801
2552
|
}
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
2553
|
+
try {
|
|
2554
|
+
opts?.onInputEffect?.("pasted");
|
|
2555
|
+
}
|
|
2556
|
+
catch { /* Observation cannot change a completed paste. */ }
|
|
2557
|
+
// From the successful paste to Enter the text may sit in the seat's input. Any throw in this span is
|
|
2558
|
+
// tagged inputWritten, so no caller reports it as unsent; a step added here later stays covered.
|
|
2559
|
+
let boxBefore = null;
|
|
2560
|
+
let submitResult;
|
|
2561
|
+
let enterIssued = false; // set as the Enter key is handed to the adapter
|
|
2562
|
+
try {
|
|
2563
|
+
// 4. Wait 200ms (spike-proven delay)
|
|
2564
|
+
await this.sleep(200);
|
|
2565
|
+
// With transport.verify_input_box on, one bounded read of the input box and its pane process before
|
|
2566
|
+
// Enter (a 500 ms budget after the 200 ms wait). It only reports: nothing here decides whether Enter is sent.
|
|
2567
|
+
// The seat is read from the existing --verify capture taken before the paste (the whole screen); no new read.
|
|
2568
|
+
if (inputBoxRuntime && !promptOverride) {
|
|
2569
|
+
const read = await this.readWithinDeadline("session_transport.input_box_capture", () => Promise.all([
|
|
2570
|
+
this.tmuxAdapter.getPanePid(sessionName).catch(() => null),
|
|
2571
|
+
this.tmuxAdapter.capturePaneContent(sessionName, 30).catch(() => null),
|
|
2572
|
+
]));
|
|
2573
|
+
boxBefore = { seat: readSeatBeforePaste(preVerifyContent, inputBoxRuntime, sessionName),
|
|
2574
|
+
reading: readInputBox(read?.[1] ?? null, inputBoxRuntime, text), pid: read?.[0] ?? null, pane: read?.[1] ?? null };
|
|
2575
|
+
}
|
|
1808
2576
|
if (runtime === "claude-code" && bindingChanged())
|
|
1809
2577
|
return observe(changedRecipient(true));
|
|
1810
|
-
if (
|
|
1811
|
-
|
|
1812
|
-
|
|
2578
|
+
if (promptOverride) {
|
|
2579
|
+
const pane = await this.runStage("session_transport.prompt_override_pre_submit_capture", () => this.tmuxAdapter.capturePaneContent(sessionName, 50)).catch(() => null);
|
|
2580
|
+
if (runtime === "claude-code" && bindingChanged())
|
|
2581
|
+
return observe(changedRecipient(true));
|
|
2582
|
+
if (!promptAnswerStaged(pane, text, runtime)) {
|
|
2583
|
+
return observe({ ok: true, sessionName, sent: true, verified: false, outcome: "rendered-unconfirmed", promptInteraction: "unverified",
|
|
2584
|
+
...(inputBoxRuntime ? { inputBox: INPUT_BOX_UNCHECKED } : {}),
|
|
2585
|
+
warning: [sendAdvisory, "prompt-override: answer sent as unbracketed input; submission unverified. No trailing Enter: the complete answer is not visibly staged (it may have been consumed, the prompt changed, or observation is unavailable)."].filter(Boolean).join(" ") });
|
|
2586
|
+
}
|
|
2587
|
+
sendAdvisory = [sendAdvisory, "prompt-override: answer sent as unbracketed input; Enter submitted the complete still-staged answer."].filter(Boolean).join(" ");
|
|
2588
|
+
}
|
|
2589
|
+
// 5. Submit (Enter)
|
|
2590
|
+
submitResult = await this.runStage("session_transport.submit", () => { opts?.beforeWrite?.(); enterIssued = true; return opts?.beforeWrite ? this.tmuxAdapter.sendKeys(sessionName, ["Enter"], opts.beforeWrite) : this.tmuxAdapter.sendKeys(sessionName, ["Enter"]); }, (result) => result.ok ? "ok" : "failed");
|
|
2591
|
+
if (!submitResult.ok) {
|
|
2592
|
+
const policyRefusal = typeof submitResult.code === "string" && submitResult.code.startsWith("draft_");
|
|
2593
|
+
return observe({
|
|
2594
|
+
ok: false,
|
|
2595
|
+
sessionName,
|
|
2596
|
+
reason: policyRefusal ? submitResult.code : "submit_failed",
|
|
2597
|
+
outcome: "failed",
|
|
2598
|
+
inputPhase: "written",
|
|
2599
|
+
error: `Text is in '${sessionName}', but the Enter reported a failure, so the submission outcome is unconfirmed. The agent may need manual attention.`,
|
|
2600
|
+
...(waitMode || policyRefusal ? { sent: true, ...waitEvidence } : {}),
|
|
2601
|
+
});
|
|
1813
2602
|
}
|
|
1814
|
-
sendAdvisory = "prompt-override: answer sent as unbracketed input; Enter submitted the complete still-staged answer.";
|
|
1815
2603
|
}
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
ok: false,
|
|
1821
|
-
sessionName,
|
|
1822
|
-
reason: "submit_failed",
|
|
1823
|
-
outcome: "failed",
|
|
1824
|
-
error: `Text is visible in '${sessionName}' but was not submitted (Enter failed). The agent may need manual attention.`,
|
|
1825
|
-
...(waitMode ? { sent: true, ...waitEvidence } : {}),
|
|
1826
|
-
});
|
|
2604
|
+
catch (error) {
|
|
2605
|
+
if (error && typeof error === "object")
|
|
2606
|
+
Object.assign(error, { inputWritten: true, enterAttempted: enterIssued });
|
|
2607
|
+
throw error;
|
|
1827
2608
|
}
|
|
2609
|
+
try {
|
|
2610
|
+
opts?.onInputEffect?.("submitted");
|
|
2611
|
+
}
|
|
2612
|
+
catch { /* Observation cannot change a completed submission. */ }
|
|
1828
2613
|
const interaction = promptOverride ? { promptInteraction: "enter-sent" } : {};
|
|
1829
2614
|
// 6. Verify if requested. At this point text + Enter BOTH succeeded, so the
|
|
1830
2615
|
// message LANDED; the capture only re-confirms the render. Not re-confirming
|
|
@@ -1842,23 +2627,77 @@ export class SessionTransport {
|
|
|
1842
2627
|
// its original prefix-count verification even while a command is running.
|
|
1843
2628
|
const terminal = runtime === "terminal";
|
|
1844
2629
|
const { snippet, envelope } = terminal ? { snippet: text.slice(0, 40), envelope: false } : verificationSnippet(text);
|
|
2630
|
+
// Only Codex gains »; all other runtimes keep the existing boundaries.
|
|
2631
|
+
const promptClass = runtime === "codex" ? COMPOSER_PROMPT_CLASS : "❯›";
|
|
1845
2632
|
const transcript = terminal ? (pane) => pane ?? ""
|
|
1846
|
-
:
|
|
1847
|
-
|
|
2633
|
+
: (pane) => envelope ? verificationTranscript(pane, promptClass)
|
|
2634
|
+
: normalizeVerificationText(verificationTranscript(pane, promptClass));
|
|
2635
|
+
const count = envelope ? (pane, expected) => countEnvelopeOccurrences(pane, expected, promptClass) : countOccurrences;
|
|
1848
2636
|
const preCount = count(transcript(preVerifyContent), snippet);
|
|
1849
2637
|
const postCount = count(transcript(content), snippet);
|
|
1850
|
-
|
|
1851
|
-
|
|
2638
|
+
// An audited prompt answer reads no box (no extra read is added for it), so it says so: unchecked, and
|
|
2639
|
+
// unconfirmed; it never falls back to the count below.
|
|
2640
|
+
const inputBox = boxBefore && inputBoxRuntime
|
|
2641
|
+
? await this.inputBoxEvidence(sessionName, sessionMeta, boxBefore, readInputBox(content, inputBoxRuntime, text, boxBefore.pane))
|
|
2642
|
+
: inputBoxRuntime && promptOverride ? INPUT_BOX_UNCHECKED : undefined;
|
|
2643
|
+
// With the setting on, only a seat proven idle before the paste and input-box evidence verify: exact before
|
|
2644
|
+
// Enter, empty after, same process. A not-idle or unknown seat always reads unconfirmed.
|
|
2645
|
+
const verified = inputBoxRuntime
|
|
2646
|
+
? inputBox?.seat === "idle" && inputBox.before === "exact" && inputBox.after === "empty" && inputBox.sameProcess
|
|
2647
|
+
: postCount > preCount;
|
|
2648
|
+
return observe({ ok: true, sessionName, ...interaction, verified, outcome: verified ? "delivered" : "rendered-unconfirmed", ...(inputBox ? { inputBox } : {}), ...(sendAdvisory ? { warning: sendAdvisory } : {}), ...(waitMode ? { sent: true, ...waitEvidence } : {}) });
|
|
1852
2649
|
}
|
|
1853
2650
|
catch {
|
|
1854
2651
|
if (observed && observed.post.state === "not_reached") {
|
|
1855
2652
|
observed.post = { state: "unavailable", cause: "capture_error", capturedAt: this.now().toISOString(), captureSeq };
|
|
1856
2653
|
}
|
|
1857
|
-
|
|
2654
|
+
const inputBox = boxBefore
|
|
2655
|
+
? { seat: boxBefore.seat, before: boxBefore.reading, after: "unavailable", sameProcess: false }
|
|
2656
|
+
: inputBoxRuntime && promptOverride ? INPUT_BOX_UNCHECKED : undefined;
|
|
2657
|
+
return observe({ ok: true, sessionName, ...interaction, verified: false, outcome: "rendered-unconfirmed", ...(inputBox ? { inputBox } : {}), ...(sendAdvisory ? { warning: sendAdvisory } : {}), ...(waitMode ? { sent: true, ...waitEvidence } : {}) });
|
|
1858
2658
|
}
|
|
1859
2659
|
}
|
|
1860
2660
|
return observe({ ok: true, sessionName, ...interaction, ...(sendAdvisory ? { warning: sendAdvisory } : {}), ...(waitMode ? { sent: true, ...waitEvidence } : {}) });
|
|
1861
2661
|
}
|
|
2662
|
+
inputBoxSettingOn() {
|
|
2663
|
+
try {
|
|
2664
|
+
return this.verifyInputBox?.() === true;
|
|
2665
|
+
}
|
|
2666
|
+
catch {
|
|
2667
|
+
return false;
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
/** One input-box read, timed by the slow-op recorder and raced against the deadline. Null on timeout or
|
|
2671
|
+
* error; an abandoned read is ignored. A read never decides whether Enter is sent. */
|
|
2672
|
+
async readWithinDeadline(site, read) {
|
|
2673
|
+
let timer;
|
|
2674
|
+
try {
|
|
2675
|
+
const pending = this.runStage(site, read);
|
|
2676
|
+
pending.catch(() => undefined);
|
|
2677
|
+
return await Promise.race([
|
|
2678
|
+
pending,
|
|
2679
|
+
new Promise((resolve) => { timer = setTimeout(() => resolve(null), INPUT_BOX_READ_DEADLINE_MS); }),
|
|
2680
|
+
]);
|
|
2681
|
+
}
|
|
2682
|
+
catch {
|
|
2683
|
+
return null;
|
|
2684
|
+
}
|
|
2685
|
+
finally {
|
|
2686
|
+
if (timer !== undefined)
|
|
2687
|
+
clearTimeout(timer);
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
/** After the post-Enter capture: the same pane process (pid read before Enter and now) and the same binding. */
|
|
2691
|
+
async inputBoxEvidence(sessionName, meta, before, after) {
|
|
2692
|
+
const pid = await this.readWithinDeadline("session_transport.input_box_pane_pid", () => this.tmuxAdapter.getPanePid(sessionName));
|
|
2693
|
+
let sameBinding = false;
|
|
2694
|
+
try {
|
|
2695
|
+
const now = this.getSessionMeta(sessionName);
|
|
2696
|
+
sameBinding = now.nodeId === meta.nodeId && now.pane === meta.pane && now.occupant === meta.occupant;
|
|
2697
|
+
}
|
|
2698
|
+
catch { /* unknown binding is not the same binding */ }
|
|
2699
|
+
return { seat: before.seat, before: before.reading, after, sameProcess: before.pid !== null && pid !== null && pid === before.pid && sameBinding };
|
|
2700
|
+
}
|
|
1862
2701
|
runStage(site, fn, classify) {
|
|
1863
2702
|
return this.slowOpRecorder?.runStage
|
|
1864
2703
|
? this.slowOpRecorder.runStage(site, fn, classify)
|
|
@@ -2015,10 +2854,16 @@ export class SessionTransport {
|
|
|
2015
2854
|
}
|
|
2016
2855
|
async classifySendReadiness(input) {
|
|
2017
2856
|
const now = this.now();
|
|
2857
|
+
const composerSnapshot = this.tmuxAdapter.deliveryGuard?.needsInputCheck(input.sessionName)
|
|
2858
|
+
? () => this.tmuxAdapter.captureComposerSnapshot(input.binding?.pane ?? input.sessionName) : undefined;
|
|
2018
2859
|
const hookActivity = this.agentActivityStore?.getLatestForNode({
|
|
2019
2860
|
sessionName: input.sessionName,
|
|
2020
2861
|
now,
|
|
2021
2862
|
});
|
|
2863
|
+
const permissionPending = latestHookPermissionPending(hookActivity, input.runtime);
|
|
2864
|
+
if (hookActivity && permissionPending && this.hookFreshForSend(hookActivity, now)) {
|
|
2865
|
+
return { ...hookActivity, state: "needs_input" };
|
|
2866
|
+
}
|
|
2022
2867
|
// Pane-only readiness never lets a hook authorise a send, but a fresh hook showing work or a
|
|
2023
2868
|
// waiting person still refuses one, as it does for every other send.
|
|
2024
2869
|
if (input.readinessFromPaneOnly &&
|
|
@@ -2035,7 +2880,9 @@ export class SessionTransport {
|
|
|
2035
2880
|
if (!input.readinessFromPaneOnly &&
|
|
2036
2881
|
hookActivity &&
|
|
2037
2882
|
hookActivity.evidenceSource === "runtime_hook" &&
|
|
2038
|
-
hookActivity.stale !== true
|
|
2883
|
+
hookActivity.stale !== true &&
|
|
2884
|
+
hookActivity.reason !== "permission_request_pending" &&
|
|
2885
|
+
hookActivity.reason !== "permission_prompt_cleared") {
|
|
2039
2886
|
// Fresh hook (within the 15s send window): authoritative for any state.
|
|
2040
2887
|
if (this.hookFreshForSend(hookActivity, now)) {
|
|
2041
2888
|
return hookActivity;
|
|
@@ -2055,6 +2902,7 @@ export class SessionTransport {
|
|
|
2055
2902
|
// vetoes (→ refuse); an unknown pane (the flaky-Codex case this trust
|
|
2056
2903
|
// exists for) or a clean idle pane does NOT veto → trust the stale hook.
|
|
2057
2904
|
const paneVeto = await probeSessionActivity({
|
|
2905
|
+
composerSnapshot,
|
|
2058
2906
|
sessionName: input.sessionName,
|
|
2059
2907
|
runtime: input.runtime,
|
|
2060
2908
|
attachmentType: input.attachmentType,
|
|
@@ -2070,6 +2918,7 @@ export class SessionTransport {
|
|
|
2070
2918
|
}
|
|
2071
2919
|
}
|
|
2072
2920
|
const probe = await probeSessionActivity({
|
|
2921
|
+
composerSnapshot,
|
|
2073
2922
|
sessionName: input.sessionName,
|
|
2074
2923
|
runtime: input.runtime,
|
|
2075
2924
|
attachmentType: input.attachmentType,
|
|
@@ -2093,6 +2942,9 @@ export class SessionTransport {
|
|
|
2093
2942
|
(hookActivity.state === "running" || hookActivity.state === "needs_input")) {
|
|
2094
2943
|
return hookActivity;
|
|
2095
2944
|
}
|
|
2945
|
+
if (probe.state === "unknown" && permissionPending) {
|
|
2946
|
+
return { ...probe, state: "needs_input", reason: "permission_request_unverified" };
|
|
2947
|
+
}
|
|
2096
2948
|
// A latest hook saying the seat waits on a person (approval, picker or elicitation) stays positive
|
|
2097
2949
|
// picker evidence past the send window, and past the store's freshness window: nothing newer was
|
|
2098
2950
|
// recorded, so nothing answered it. The pane alone can miss a picker it has no signature for (a
|
|
@@ -2219,10 +3071,9 @@ export class SessionTransport {
|
|
|
2219
3071
|
}
|
|
2220
3072
|
return `a recent activity hook exists but the live pane probe could not confirm idle (possible identity mismatch between the seat env, the DB, and the stored payload)`;
|
|
2221
3073
|
}
|
|
2222
|
-
//
|
|
2223
|
-
//
|
|
2224
|
-
//
|
|
2225
|
-
// overrideReason distinct from the classifier's detectedReason/evidenceSource (not overloaded).
|
|
3074
|
+
// Record actor/reason with the actual readiness, never a fabricated prompt classification.
|
|
3075
|
+
// Report an unavailable sink to the caller: a positive-picker override still fails closed,
|
|
3076
|
+
// while other readiness states keep their existing accepted-send behavior with an advisory.
|
|
2226
3077
|
recordPromptOverride(input) {
|
|
2227
3078
|
if (!this.eventBus)
|
|
2228
3079
|
return { ok: false, reason: "audit_unconfigured" };
|
|
@@ -2299,6 +3150,7 @@ export class SessionTransport {
|
|
|
2299
3150
|
error: `Could not capture pane content for '${sessionName}'.`,
|
|
2300
3151
|
};
|
|
2301
3152
|
}
|
|
3153
|
+
await this.reconcileStartupReadiness(sessionName).catch(() => { });
|
|
2302
3154
|
return { ok: true, sessionName, content, lines };
|
|
2303
3155
|
}
|
|
2304
3156
|
async broadcast(target, text, opts) {
|
|
@@ -2352,5 +3204,140 @@ export class SessionTransport {
|
|
|
2352
3204
|
results,
|
|
2353
3205
|
};
|
|
2354
3206
|
}
|
|
3207
|
+
getReconciliationSnapshot(sessionName) {
|
|
3208
|
+
return this.db.prepare(`
|
|
3209
|
+
SELECT n.id as node_id, r.id as rig_id, s.id as session_id, s.session_name, n.runtime, n.cwd,
|
|
3210
|
+
s.startup_status, s.status as session_status, s.startup_completed_at,
|
|
3211
|
+
s.resume_type, s.resume_token,
|
|
3212
|
+
b.tmux_session as binding_session, b.tmux_window as binding_window, b.tmux_pane as binding_pane,
|
|
3213
|
+
b.updated_at as binding_updated_at,
|
|
3214
|
+
(SELECT generation_uuid FROM occupant_tenures WHERE node_id = n.id ORDER BY generation_ordinal DESC LIMIT 1) as occupant_generation,
|
|
3215
|
+
(SELECT id FROM sessions WHERE node_id = n.id ORDER BY id DESC LIMIT 1) as latest_session_id,
|
|
3216
|
+
(SELECT MAX(seq) FROM events e WHERE e.node_id = n.id AND e.type IN ('node.startup_pending', 'node.startup_ready', 'node.startup_failed')) as startup_event_seq
|
|
3217
|
+
FROM sessions s
|
|
3218
|
+
JOIN nodes n ON n.id = s.node_id
|
|
3219
|
+
JOIN rigs r ON r.id = n.rig_id
|
|
3220
|
+
LEFT JOIN bindings b ON b.node_id = n.id
|
|
3221
|
+
WHERE s.session_name = ?
|
|
3222
|
+
ORDER BY s.id DESC LIMIT 1
|
|
3223
|
+
`).get(sessionName);
|
|
3224
|
+
}
|
|
3225
|
+
/**
|
|
3226
|
+
* Reconcile non-ready startup state after successful session interaction (Refs #7).
|
|
3227
|
+
*
|
|
3228
|
+
* If an active session is in a non-ready startup state (`failed` or `attention_required`),
|
|
3229
|
+
* re-check readiness via the runtime adapter with currentness verification and atomic commit.
|
|
3230
|
+
*/
|
|
3231
|
+
async reconcileStartupReadiness(sessionName) {
|
|
3232
|
+
const existing = this.inFlightReconciliations.get(sessionName);
|
|
3233
|
+
if (existing)
|
|
3234
|
+
return existing;
|
|
3235
|
+
const promise = this.doReconcileStartupReadiness(sessionName);
|
|
3236
|
+
this.inFlightReconciliations.set(sessionName, promise);
|
|
3237
|
+
try {
|
|
3238
|
+
return await promise;
|
|
3239
|
+
}
|
|
3240
|
+
finally {
|
|
3241
|
+
this.inFlightReconciliations.delete(sessionName);
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
async doReconcileStartupReadiness(sessionName) {
|
|
3245
|
+
const snapshot = this.getReconciliationSnapshot(sessionName);
|
|
3246
|
+
if (!snapshot)
|
|
3247
|
+
return false;
|
|
3248
|
+
if (snapshot.startup_status !== "failed" && snapshot.startup_status !== "attention_required")
|
|
3249
|
+
return false;
|
|
3250
|
+
if (snapshot.session_status !== "running")
|
|
3251
|
+
return false;
|
|
3252
|
+
if (snapshot.session_id !== snapshot.latest_session_id)
|
|
3253
|
+
return false;
|
|
3254
|
+
if (!isRecoverableStartupFailure(this.db, snapshot.node_id, snapshot.session_id))
|
|
3255
|
+
return false;
|
|
3256
|
+
const checker = this.runtimeAdapters[snapshot.runtime];
|
|
3257
|
+
if (!checker?.checkReady)
|
|
3258
|
+
return false;
|
|
3259
|
+
const hasClaudeIdentityWork = (nodeId, sessionId, runtime) => {
|
|
3260
|
+
if (runtime !== "claude-code")
|
|
3261
|
+
return false;
|
|
3262
|
+
const failedEvent = getLatestStartupFailedEvent(this.db, nodeId, sessionId);
|
|
3263
|
+
if (!failedEvent || failedEvent.requestedResumeToken === undefined || failedEvent.requestedResumeType === undefined) {
|
|
3264
|
+
return true;
|
|
3265
|
+
}
|
|
3266
|
+
return hasClaudeResumeIdentityWork(this.sessionRegistry, sessionId, runtime, failedEvent.requestedResumeToken, failedEvent.requestedResumeType);
|
|
3267
|
+
};
|
|
3268
|
+
const probeBinding = {
|
|
3269
|
+
id: `session-transport:${snapshot.session_id}`,
|
|
3270
|
+
nodeId: snapshot.node_id,
|
|
3271
|
+
attachmentType: "tmux",
|
|
3272
|
+
tmuxSession: sessionName,
|
|
3273
|
+
tmuxWindow: null,
|
|
3274
|
+
tmuxPane: null,
|
|
3275
|
+
cmuxWorkspace: null,
|
|
3276
|
+
cmuxSurface: null,
|
|
3277
|
+
updatedAt: "",
|
|
3278
|
+
cwd: snapshot.cwd ?? "",
|
|
3279
|
+
};
|
|
3280
|
+
let timeoutTimer;
|
|
3281
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
3282
|
+
timeoutTimer = setTimeout(() => reject(new Error("readiness probe timeout")), 5000);
|
|
3283
|
+
});
|
|
3284
|
+
let readiness;
|
|
3285
|
+
try {
|
|
3286
|
+
readiness = await Promise.race([checker.checkReady(probeBinding), timeoutPromise]);
|
|
3287
|
+
}
|
|
3288
|
+
catch {
|
|
3289
|
+
return false;
|
|
3290
|
+
}
|
|
3291
|
+
finally {
|
|
3292
|
+
if (timeoutTimer)
|
|
3293
|
+
clearTimeout(timeoutTimer);
|
|
3294
|
+
}
|
|
3295
|
+
let emittedReady = false;
|
|
3296
|
+
this.db.transaction(() => {
|
|
3297
|
+
const current = this.getReconciliationSnapshot(sessionName);
|
|
3298
|
+
if (!current || JSON.stringify(current) !== JSON.stringify(snapshot))
|
|
3299
|
+
return;
|
|
3300
|
+
if (!isRecoverableStartupFailure(this.db, snapshot.node_id, snapshot.session_id))
|
|
3301
|
+
return;
|
|
3302
|
+
const currentIdentityWork = hasClaudeIdentityWork(current.node_id, current.session_id, current.runtime);
|
|
3303
|
+
if (readiness.ready) {
|
|
3304
|
+
if (currentIdentityWork) {
|
|
3305
|
+
this.db.prepare(`
|
|
3306
|
+
UPDATE sessions
|
|
3307
|
+
SET startup_status = 'attention_required'
|
|
3308
|
+
WHERE id = ? AND startup_status = 'failed'
|
|
3309
|
+
`).run(snapshot.session_id);
|
|
3310
|
+
return;
|
|
3311
|
+
}
|
|
3312
|
+
const result = this.db.prepare(`
|
|
3313
|
+
UPDATE sessions
|
|
3314
|
+
SET startup_status = 'ready',
|
|
3315
|
+
startup_completed_at = ?
|
|
3316
|
+
WHERE id = ? AND startup_status IN ('failed', 'attention_required')
|
|
3317
|
+
`).run(new Date().toISOString(), snapshot.session_id);
|
|
3318
|
+
if (result.changes > 0) {
|
|
3319
|
+
emittedReady = true;
|
|
3320
|
+
}
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
if (isAttentionRequiredReadinessCode(readiness.code)) {
|
|
3324
|
+
this.db.prepare(`
|
|
3325
|
+
UPDATE sessions
|
|
3326
|
+
SET startup_status = 'attention_required'
|
|
3327
|
+
WHERE id = ? AND startup_status = 'failed'
|
|
3328
|
+
`).run(snapshot.session_id);
|
|
3329
|
+
}
|
|
3330
|
+
}).immediate();
|
|
3331
|
+
if (emittedReady && this.eventBus) {
|
|
3332
|
+
this.eventBus.emit({
|
|
3333
|
+
type: "node.startup_ready",
|
|
3334
|
+
rigId: snapshot.rig_id,
|
|
3335
|
+
nodeId: snapshot.node_id,
|
|
3336
|
+
previousStatus: snapshot.startup_status,
|
|
3337
|
+
});
|
|
3338
|
+
return true;
|
|
3339
|
+
}
|
|
3340
|
+
return false;
|
|
3341
|
+
}
|
|
2355
3342
|
}
|
|
2356
3343
|
//# sourceMappingURL=session-transport.js.map
|