@openrig/cli 0.6.9 → 0.6.11
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 +2 -2
- package/daemon/assets/onboarding/02-self-and-competent-action.md +4 -3
- 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/hooks/scripts/activity-relay.cjs +42 -2
- package/daemon/assets/plugins/openrig-core/skills/openrig-skills/SKILL.md +1 -1
- package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +7 -1
- package/daemon/assets/plugins/openrig-core/skills/refocusing/scripts/trace-to-root.py +6 -1
- package/daemon/assets/skills/rigs/SKILL.md +7 -5
- 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 +64 -14
- package/daemon/context-packs/reference/getting-started.md +10 -6
- package/daemon/context-packs/reference/manifest.yaml +1 -1
- package/daemon/context-packs/reference/rig-spec.md +2 -2
- package/daemon/context-packs/reference/whats-new.md +151 -74
- 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/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 +3 -3
- package/daemon/context-packs/skills/core/openrig-software-factory/manifest.yaml +1 -1
- 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 +2 -2
- 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/manifest.yaml +1 -1
- 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/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 +7 -1
- package/daemon/context-packs/skills/openrig-user/manifest.yaml +1 -1
- 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/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/manifest.yaml +1 -1
- package/daemon/context-packs/skills/refocusing/manifest.yaml +1 -1
- package/daemon/context-packs/skills/refocusing/scripts/trace-to-root.py +6 -1
- 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/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/claude-code-adapter.d.ts +0 -14
- package/daemon/dist/adapters/claude-code-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/claude-code-adapter.js +43 -80
- package/daemon/dist/adapters/claude-code-adapter.js.map +1 -1
- package/daemon/dist/adapters/claude-resume-launch.d.ts +4 -0
- package/daemon/dist/adapters/claude-resume-launch.d.ts.map +1 -0
- package/daemon/dist/adapters/claude-resume-launch.js +13 -0
- package/daemon/dist/adapters/claude-resume-launch.js.map +1 -0
- package/daemon/dist/adapters/claude-resume.d.ts +2 -1
- package/daemon/dist/adapters/claude-resume.d.ts.map +1 -1
- package/daemon/dist/adapters/claude-resume.js +9 -4
- package/daemon/dist/adapters/claude-resume.js.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts +35 -0
- package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
- package/daemon/dist/adapters/codex-runtime-adapter.js +94 -44
- package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/pi-runner.d.ts.map +1 -1
- package/daemon/dist/adapters/pi-runner.js +6 -3
- package/daemon/dist/adapters/pi-runner.js.map +1 -1
- package/daemon/dist/adapters/pi-runtime-adapter.js +1 -1
- package/daemon/dist/adapters/pi-runtime-adapter.js.map +1 -1
- package/daemon/dist/adapters/tmux-server.d.ts +15 -0
- package/daemon/dist/adapters/tmux-server.d.ts.map +1 -0
- package/daemon/dist/adapters/tmux-server.js +58 -0
- package/daemon/dist/adapters/tmux-server.js.map +1 -0
- package/daemon/dist/adapters/tmux.d.ts +26 -1
- package/daemon/dist/adapters/tmux.d.ts.map +1 -1
- package/daemon/dist/adapters/tmux.js +130 -7
- package/daemon/dist/adapters/tmux.js.map +1 -1
- package/daemon/dist/build-info.js +3 -3
- package/daemon/dist/db/all-migrations.d.ts.map +1 -1
- package/daemon/dist/db/all-migrations.js +6 -0
- package/daemon/dist/db/all-migrations.js.map +1 -1
- package/daemon/dist/db/migrations/099_queue_items_recovery_tags.d.ts +3 -0
- package/daemon/dist/db/migrations/099_queue_items_recovery_tags.d.ts.map +1 -0
- package/daemon/dist/db/migrations/099_queue_items_recovery_tags.js +19 -0
- package/daemon/dist/db/migrations/099_queue_items_recovery_tags.js.map +1 -0
- package/daemon/dist/db/migrations/101_claude_resume_rotation.d.ts +3 -0
- package/daemon/dist/db/migrations/101_claude_resume_rotation.d.ts.map +1 -0
- package/daemon/dist/db/migrations/101_claude_resume_rotation.js +19 -0
- package/daemon/dist/db/migrations/101_claude_resume_rotation.js.map +1 -0
- package/daemon/dist/db/migrations/103_session_stop_intent.d.ts +4 -0
- package/daemon/dist/db/migrations/103_session_stop_intent.d.ts.map +1 -0
- package/daemon/dist/db/migrations/103_session_stop_intent.js +9 -0
- package/daemon/dist/db/migrations/103_session_stop_intent.js.map +1 -0
- package/daemon/dist/domain/active-occupant.d.ts +14 -0
- package/daemon/dist/domain/active-occupant.d.ts.map +1 -1
- package/daemon/dist/domain/active-occupant.js +17 -0
- package/daemon/dist/domain/active-occupant.js.map +1 -1
- package/daemon/dist/domain/agent-activity-store.js +3 -0
- package/daemon/dist/domain/agent-activity-store.js.map +1 -1
- package/daemon/dist/domain/bootstrap-repository.js +1 -1
- package/daemon/dist/domain/bootstrap-repository.js.map +1 -1
- package/daemon/dist/domain/chat-repository.d.ts +3 -0
- package/daemon/dist/domain/chat-repository.d.ts.map +1 -1
- package/daemon/dist/domain/chat-repository.js +15 -4
- package/daemon/dist/domain/chat-repository.js.map +1 -1
- package/daemon/dist/domain/claude-managed-launch.d.ts +1 -1
- package/daemon/dist/domain/claude-managed-launch.d.ts.map +1 -1
- package/daemon/dist/domain/claude-managed-launch.js +3 -2
- package/daemon/dist/domain/claude-managed-launch.js.map +1 -1
- package/daemon/dist/domain/cmux-layout-service.d.ts.map +1 -1
- package/daemon/dist/domain/cmux-layout-service.js +2 -1
- package/daemon/dist/domain/cmux-layout-service.js.map +1 -1
- package/daemon/dist/domain/codex-git-add-dirs.js +1 -1
- package/daemon/dist/domain/codex-git-add-dirs.js.map +1 -1
- package/daemon/dist/domain/codex-network-default.d.ts +3 -0
- package/daemon/dist/domain/codex-network-default.d.ts.map +1 -1
- package/daemon/dist/domain/codex-network-default.js +1 -1
- package/daemon/dist/domain/codex-network-default.js.map +1 -1
- package/daemon/dist/domain/codex-pane-patterns.d.ts +2 -0
- package/daemon/dist/domain/codex-pane-patterns.d.ts.map +1 -0
- package/daemon/dist/domain/codex-pane-patterns.js +5 -0
- package/daemon/dist/domain/codex-pane-patterns.js.map +1 -0
- package/daemon/dist/domain/context-monitor.d.ts.map +1 -1
- package/daemon/dist/domain/context-monitor.js +46 -22
- package/daemon/dist/domain/context-monitor.js.map +1 -1
- package/daemon/dist/domain/context-usage-store.d.ts.map +1 -1
- package/daemon/dist/domain/context-usage-store.js +6 -1
- package/daemon/dist/domain/context-usage-store.js.map +1 -1
- package/daemon/dist/domain/crash-cart-conductor.d.ts.map +1 -1
- package/daemon/dist/domain/crash-cart-conductor.js +3 -1
- package/daemon/dist/domain/crash-cart-conductor.js.map +1 -1
- package/daemon/dist/domain/cwd-resolution.d.ts +8 -0
- package/daemon/dist/domain/cwd-resolution.d.ts.map +1 -1
- package/daemon/dist/domain/cwd-resolution.js +24 -3
- package/daemon/dist/domain/cwd-resolution.js.map +1 -1
- package/daemon/dist/domain/files/file-read.d.ts.map +1 -1
- package/daemon/dist/domain/files/file-read.js +11 -3
- package/daemon/dist/domain/files/file-read.js.map +1 -1
- package/daemon/dist/domain/gateway/connections-projection.d.ts +1 -1
- package/daemon/dist/domain/gateway/slack/channel-recovery.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/channel-recovery.js +4 -3
- package/daemon/dist/domain/gateway/slack/channel-recovery.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/inbound.d.ts +21 -2
- package/daemon/dist/domain/gateway/slack/inbound.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/inbound.js +70 -11
- package/daemon/dist/domain/gateway/slack/inbound.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts +4 -0
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.js +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js +7 -4
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js.map +1 -1
- package/daemon/dist/domain/history-query.d.ts.map +1 -1
- package/daemon/dist/domain/history-query.js +8 -6
- package/daemon/dist/domain/history-query.js.map +1 -1
- package/daemon/dist/domain/human-questions.d.ts +4 -1
- package/daemon/dist/domain/human-questions.d.ts.map +1 -1
- package/daemon/dist/domain/human-questions.js.map +1 -1
- package/daemon/dist/domain/install-engine.js +2 -2
- package/daemon/dist/domain/install-engine.js.map +1 -1
- package/daemon/dist/domain/native-process-lineage.d.ts +57 -4
- package/daemon/dist/domain/native-process-lineage.d.ts.map +1 -1
- package/daemon/dist/domain/native-process-lineage.js +277 -42
- package/daemon/dist/domain/native-process-lineage.js.map +1 -1
- package/daemon/dist/domain/node-cmux-service.d.ts.map +1 -1
- package/daemon/dist/domain/node-cmux-service.js +2 -1
- package/daemon/dist/domain/node-cmux-service.js.map +1 -1
- package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
- package/daemon/dist/domain/node-inventory.js +71 -18
- package/daemon/dist/domain/node-inventory.js.map +1 -1
- package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/node-launcher.js +2 -1
- package/daemon/dist/domain/node-launcher.js.map +1 -1
- package/daemon/dist/domain/pane-cutover.d.ts +27 -0
- package/daemon/dist/domain/pane-cutover.d.ts.map +1 -0
- package/daemon/dist/domain/pane-cutover.js +37 -0
- package/daemon/dist/domain/pane-cutover.js.map +1 -0
- package/daemon/dist/domain/parent-session-env.d.ts +8 -0
- package/daemon/dist/domain/parent-session-env.d.ts.map +1 -0
- package/daemon/dist/domain/parent-session-env.js +37 -0
- package/daemon/dist/domain/parent-session-env.js.map +1 -0
- package/daemon/dist/domain/policies/parked-owner-consumer.d.ts +11 -0
- package/daemon/dist/domain/policies/parked-owner-consumer.d.ts.map +1 -1
- package/daemon/dist/domain/policies/parked-owner-consumer.js +81 -11
- package/daemon/dist/domain/policies/parked-owner-consumer.js.map +1 -1
- package/daemon/dist/domain/profile-resolver.d.ts.map +1 -1
- package/daemon/dist/domain/profile-resolver.js +2 -3
- package/daemon/dist/domain/profile-resolver.js.map +1 -1
- package/daemon/dist/domain/progress/progress-indexer.d.ts.map +1 -1
- package/daemon/dist/domain/progress/progress-indexer.js +19 -0
- package/daemon/dist/domain/progress/progress-indexer.js.map +1 -1
- package/daemon/dist/domain/proof/judgments.d.ts +10 -5
- package/daemon/dist/domain/proof/judgments.d.ts.map +1 -1
- package/daemon/dist/domain/proof/judgments.js +27 -19
- package/daemon/dist/domain/proof/judgments.js.map +1 -1
- package/daemon/dist/domain/provider/codex-limit-banner.d.ts +40 -0
- package/daemon/dist/domain/provider/codex-limit-banner.d.ts.map +1 -0
- package/daemon/dist/domain/provider/codex-limit-banner.js +168 -0
- package/daemon/dist/domain/provider/codex-limit-banner.js.map +1 -0
- package/daemon/dist/domain/provider/codex-usage-reader.d.ts +31 -0
- package/daemon/dist/domain/provider/codex-usage-reader.d.ts.map +1 -0
- package/daemon/dist/domain/provider/codex-usage-reader.js +115 -0
- package/daemon/dist/domain/provider/codex-usage-reader.js.map +1 -0
- package/daemon/dist/domain/provider/codex-usage-windows.d.ts +78 -0
- package/daemon/dist/domain/provider/codex-usage-windows.d.ts.map +1 -0
- package/daemon/dist/domain/provider/codex-usage-windows.js +259 -0
- package/daemon/dist/domain/provider/codex-usage-windows.js.map +1 -0
- package/daemon/dist/domain/provider/provider-service-impl.d.ts +10 -0
- package/daemon/dist/domain/provider/provider-service-impl.d.ts.map +1 -1
- package/daemon/dist/domain/provider/provider-service-impl.js +27 -1
- package/daemon/dist/domain/provider/provider-service-impl.js.map +1 -1
- package/daemon/dist/domain/provider/provider-service.d.ts +5 -0
- package/daemon/dist/domain/provider/provider-service.d.ts.map +1 -1
- package/daemon/dist/domain/provider/provider-types.d.ts +65 -0
- package/daemon/dist/domain/provider/provider-types.d.ts.map +1 -1
- package/daemon/dist/domain/ps-projection.d.ts +5 -2
- package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
- package/daemon/dist/domain/ps-projection.js +29 -26
- package/daemon/dist/domain/ps-projection.js.map +1 -1
- package/daemon/dist/domain/queue-recovery.d.ts.map +1 -1
- package/daemon/dist/domain/queue-recovery.js +10 -3
- package/daemon/dist/domain/queue-recovery.js.map +1 -1
- package/daemon/dist/domain/queue-repository.d.ts +68 -13
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +72 -43
- 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 +26 -9
- package/daemon/dist/domain/queue-stuck-sweep.js.map +1 -1
- package/daemon/dist/domain/queue-wake-ladder.d.ts +17 -0
- package/daemon/dist/domain/queue-wake-ladder.d.ts.map +1 -1
- package/daemon/dist/domain/queue-wake-ladder.js +49 -3
- package/daemon/dist/domain/queue-wake-ladder.js.map +1 -1
- package/daemon/dist/domain/restore-check-service.d.ts +8 -0
- package/daemon/dist/domain/restore-check-service.d.ts.map +1 -1
- package/daemon/dist/domain/restore-check-service.js +12 -3
- package/daemon/dist/domain/restore-check-service.js.map +1 -1
- package/daemon/dist/domain/restore-guidance.d.ts +39 -0
- package/daemon/dist/domain/restore-guidance.d.ts.map +1 -0
- package/daemon/dist/domain/restore-guidance.js +163 -0
- package/daemon/dist/domain/restore-guidance.js.map +1 -0
- package/daemon/dist/domain/restore-orchestrator.d.ts +10 -1
- package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.js +192 -9
- package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
- package/daemon/dist/domain/restore-plan-preview.d.ts +4 -0
- package/daemon/dist/domain/restore-plan-preview.d.ts.map +1 -1
- package/daemon/dist/domain/restore-plan-preview.js +14 -3
- package/daemon/dist/domain/restore-plan-preview.js.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.d.ts.map +1 -1
- package/daemon/dist/domain/rigspec-preflight.js +7 -6
- package/daemon/dist/domain/rigspec-preflight.js.map +1 -1
- package/daemon/dist/domain/runtime-adapter.d.ts +3 -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/scope/scope-view-projection.d.ts +4 -2
- package/daemon/dist/domain/scope/scope-view-projection.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-view-projection.js +11 -11
- package/daemon/dist/domain/scope/scope-view-projection.js.map +1 -1
- package/daemon/dist/domain/seat-attention-reconciler.d.ts +5 -2
- package/daemon/dist/domain/seat-attention-reconciler.d.ts.map +1 -1
- package/daemon/dist/domain/seat-attention-reconciler.js +18 -4
- package/daemon/dist/domain/seat-attention-reconciler.js.map +1 -1
- package/daemon/dist/domain/seat-identity-reconciler.d.ts.map +1 -1
- package/daemon/dist/domain/seat-identity-reconciler.js +7 -2
- package/daemon/dist/domain/seat-identity-reconciler.js.map +1 -1
- package/daemon/dist/domain/seat-lifecycle-service.d.ts +117 -2
- package/daemon/dist/domain/seat-lifecycle-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-lifecycle-service.js +335 -74
- package/daemon/dist/domain/seat-lifecycle-service.js.map +1 -1
- package/daemon/dist/domain/seat-restart-in-place.d.ts +292 -0
- package/daemon/dist/domain/seat-restart-in-place.d.ts.map +1 -0
- package/daemon/dist/domain/seat-restart-in-place.js +895 -0
- package/daemon/dist/domain/seat-restart-in-place.js.map +1 -0
- package/daemon/dist/domain/seat-status-service.d.ts +2 -1
- package/daemon/dist/domain/seat-status-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-status-service.js +1 -0
- package/daemon/dist/domain/seat-status-service.js.map +1 -1
- package/daemon/dist/domain/seat-structural-activity-service.d.ts +9 -1
- package/daemon/dist/domain/seat-structural-activity-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-structural-activity-service.js +27 -4
- package/daemon/dist/domain/seat-structural-activity-service.js.map +1 -1
- package/daemon/dist/domain/seat-switch-client-service.d.ts.map +1 -1
- package/daemon/dist/domain/seat-switch-client-service.js +2 -1
- package/daemon/dist/domain/seat-switch-client-service.js.map +1 -1
- package/daemon/dist/domain/session-fingerprinter.d.ts.map +1 -1
- package/daemon/dist/domain/session-fingerprinter.js +9 -5
- package/daemon/dist/domain/session-fingerprinter.js.map +1 -1
- package/daemon/dist/domain/session-registry.d.ts +49 -0
- package/daemon/dist/domain/session-registry.d.ts.map +1 -1
- package/daemon/dist/domain/session-registry.js +92 -5
- package/daemon/dist/domain/session-registry.js.map +1 -1
- package/daemon/dist/domain/session-transport.d.ts +46 -4
- package/daemon/dist/domain/session-transport.d.ts.map +1 -1
- package/daemon/dist/domain/session-transport.js +492 -26
- package/daemon/dist/domain/session-transport.js.map +1 -1
- package/daemon/dist/domain/startup-orchestrator.d.ts +10 -0
- package/daemon/dist/domain/startup-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/startup-orchestrator.js +41 -4
- package/daemon/dist/domain/startup-orchestrator.js.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.d.ts +3 -7
- package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -1
- package/daemon/dist/domain/successor-session-launcher.js +6 -32
- package/daemon/dist/domain/successor-session-launcher.js.map +1 -1
- package/daemon/dist/domain/terminal/terminal-service.d.ts +5 -1
- package/daemon/dist/domain/terminal/terminal-service.d.ts.map +1 -1
- package/daemon/dist/domain/terminal/terminal-service.js +30 -4
- package/daemon/dist/domain/terminal/terminal-service.js.map +1 -1
- package/daemon/dist/domain/terminal/view-composer.d.ts +6 -0
- package/daemon/dist/domain/terminal/view-composer.d.ts.map +1 -1
- package/daemon/dist/domain/terminal/view-composer.js +2 -1
- package/daemon/dist/domain/terminal/view-composer.js.map +1 -1
- package/daemon/dist/domain/transcript-store.js +1 -1
- package/daemon/dist/domain/transcript-store.js.map +1 -1
- package/daemon/dist/domain/types.d.ts +38 -0
- package/daemon/dist/domain/types.d.ts.map +1 -1
- package/daemon/dist/domain/types.js +5 -0
- package/daemon/dist/domain/types.js.map +1 -1
- package/daemon/dist/domain/usage-series.d.ts.map +1 -1
- package/daemon/dist/domain/usage-series.js +9 -4
- 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 +44 -0
- package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
- package/daemon/dist/domain/view-projector.d.ts +4 -0
- package/daemon/dist/domain/view-projector.d.ts.map +1 -1
- package/daemon/dist/domain/view-projector.js +19 -4
- package/daemon/dist/domain/view-projector.js.map +1 -1
- package/daemon/dist/domain/wake-resolver.js +2 -2
- package/daemon/dist/domain/wake-resolver.js.map +1 -1
- package/daemon/dist/domain/watchdog-jobs-repository.d.ts.map +1 -1
- package/daemon/dist/domain/watchdog-jobs-repository.js +19 -3
- package/daemon/dist/domain/watchdog-jobs-repository.js.map +1 -1
- package/daemon/dist/domain/whoami-service.d.ts +4 -1
- package/daemon/dist/domain/whoami-service.d.ts.map +1 -1
- package/daemon/dist/domain/whoami-service.js +25 -19
- package/daemon/dist/domain/whoami-service.js.map +1 -1
- package/daemon/dist/domain/workflow-runtime.d.ts +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 +19 -8
- 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 +6 -0
- package/daemon/dist/index.js.map +1 -1
- package/daemon/dist/routes/activity.d.ts.map +1 -1
- package/daemon/dist/routes/activity.js +33 -1
- package/daemon/dist/routes/activity.js.map +1 -1
- package/daemon/dist/routes/chat.d.ts.map +1 -1
- package/daemon/dist/routes/chat.js +29 -4
- package/daemon/dist/routes/chat.js.map +1 -1
- package/daemon/dist/routes/projects.d.ts.map +1 -1
- package/daemon/dist/routes/projects.js +5 -1
- package/daemon/dist/routes/projects.js.map +1 -1
- package/daemon/dist/routes/proof.d.ts.map +1 -1
- package/daemon/dist/routes/proof.js +99 -10
- package/daemon/dist/routes/proof.js.map +1 -1
- package/daemon/dist/routes/provider.d.ts.map +1 -1
- package/daemon/dist/routes/provider.js +31 -0
- package/daemon/dist/routes/provider.js.map +1 -1
- package/daemon/dist/routes/query-limit.d.ts +19 -0
- package/daemon/dist/routes/query-limit.d.ts.map +1 -0
- package/daemon/dist/routes/query-limit.js +24 -0
- package/daemon/dist/routes/query-limit.js.map +1 -0
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +35 -12
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/rig-cmux.js +8 -10
- package/daemon/dist/routes/rig-cmux.js.map +1 -1
- package/daemon/dist/routes/rigs.d.ts.map +1 -1
- package/daemon/dist/routes/rigs.js +11 -7
- package/daemon/dist/routes/rigs.js.map +1 -1
- package/daemon/dist/routes/scopes.d.ts.map +1 -1
- package/daemon/dist/routes/scopes.js +5 -3
- package/daemon/dist/routes/scopes.js.map +1 -1
- package/daemon/dist/routes/seat.d.ts.map +1 -1
- package/daemon/dist/routes/seat.js +32 -0
- package/daemon/dist/routes/seat.js.map +1 -1
- package/daemon/dist/routes/stream.d.ts.map +1 -1
- package/daemon/dist/routes/stream.js +5 -1
- package/daemon/dist/routes/stream.js.map +1 -1
- package/daemon/dist/routes/up.d.ts.map +1 -1
- package/daemon/dist/routes/up.js +2 -1
- package/daemon/dist/routes/up.js.map +1 -1
- package/daemon/dist/routes/views.d.ts.map +1 -1
- package/daemon/dist/routes/views.js +7 -1
- package/daemon/dist/routes/views.js.map +1 -1
- package/daemon/dist/startup.d.ts.map +1 -1
- package/daemon/dist/startup.js +30 -1
- package/daemon/dist/startup.js.map +1 -1
- package/daemon/docs/reference/agent-startup-guide.md +19 -0
- package/daemon/docs/reference/getting-started.md +10 -6
- package/daemon/docs/reference/keeping-a-watch.md +156 -0
- package/daemon/docs/reference/rig-spec.md +2 -2
- package/daemon/docs/reference/sdlc-conventions.md +11 -1
- package/daemon/docs/reference/slack-app-setup.md +12 -4
- package/daemon/docs/reference/whats-new.md +151 -74
- package/daemon/specs/agents/development/qa/guidance/role.md +9 -0
- package/daemon/specs/agents/shared/skills/core/openrig-skills/SKILL.md +1 -1
- package/daemon/specs/agents/shared/skills/core/topology-mutation-and-seat-management/SKILL.md +2 -2
- package/daemon/specs/rigs/launch/factory/CULTURE.md +13 -0
- package/daemon/specs/rigs/launch/factory/guidance/lead-first-move.md +18 -5
- package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +20 -1
- package/daemon/specs/rigs/launch/starter/CULTURE.md +13 -0
- package/daemon/specs/rigs/launch/starter/guidance/lead-first-move.md +18 -5
- package/dist/build-info.js +3 -3
- package/dist/cli-error.d.ts.map +1 -1
- package/dist/cli-error.js +2 -0
- package/dist/cli-error.js.map +1 -1
- package/dist/commands/add.d.ts.map +1 -1
- package/dist/commands/add.js +55 -4
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/capture.d.ts +9 -0
- package/dist/commands/capture.d.ts.map +1 -1
- package/dist/commands/capture.js +93 -41
- package/dist/commands/capture.js.map +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +6 -1
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +44 -3
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +1 -50
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +6 -0
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/heartbeat.js +2 -2
- package/dist/commands/heartbeat.js.map +1 -1
- package/dist/commands/proof.d.ts.map +1 -1
- package/dist/commands/proof.js +11 -5
- package/dist/commands/proof.js.map +1 -1
- package/dist/commands/provider.d.ts.map +1 -1
- package/dist/commands/provider.js +72 -0
- package/dist/commands/provider.js.map +1 -1
- package/dist/commands/ps.d.ts +7 -0
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +20 -6
- package/dist/commands/ps.js.map +1 -1
- package/dist/commands/queue.d.ts +48 -5
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +215 -45
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +57 -36
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/seat.d.ts.map +1 -1
- package/dist/commands/seat.js +167 -10
- package/dist/commands/seat.js.map +1 -1
- package/dist/commands/setup.d.ts +6 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +100 -44
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +9 -4
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +10 -0
- package/dist/commands/up.js.map +1 -1
- package/dist/commands/usage.d.ts.map +1 -1
- package/dist/commands/usage.js +10 -5
- package/dist/commands/usage.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +8 -8
- package/dist/commands/workflow.js.map +1 -1
- package/dist/config-store.d.ts +8 -1
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +57 -0
- package/dist/config-store.js.map +1 -1
- package/dist/daemon-lifecycle.d.ts +4 -0
- package/dist/daemon-lifecycle.d.ts.map +1 -1
- package/dist/daemon-lifecycle.js +16 -4
- package/dist/daemon-lifecycle.js.map +1 -1
- package/dist/lib/scope/capability-delta.d.ts.map +1 -1
- package/dist/lib/scope/capability-delta.js +8 -0
- package/dist/lib/scope/capability-delta.js.map +1 -1
- package/dist/lib/scope/mission-composition.d.ts +5 -1
- package/dist/lib/scope/mission-composition.d.ts.map +1 -1
- package/dist/lib/scope/mission-composition.js +61 -1
- package/dist/lib/scope/mission-composition.js.map +1 -1
- package/dist/lib/scope/progress-edit.js +1 -1
- package/dist/lib/scope/progress-edit.js.map +1 -1
- package/dist/lib/scope/scope-fs.d.ts +2 -1
- package/dist/lib/scope/scope-fs.d.ts.map +1 -1
- package/dist/lib/scope/scope-fs.js +7 -7
- package/dist/lib/scope/scope-fs.js.map +1 -1
- package/dist/lib/scope/slice-claims.d.ts +8 -0
- package/dist/lib/scope/slice-claims.d.ts.map +1 -0
- package/dist/lib/scope/slice-claims.js +88 -0
- package/dist/lib/scope/slice-claims.js.map +1 -0
- package/dist/queue-receipt.d.ts +41 -0
- package/dist/queue-receipt.d.ts.map +1 -0
- package/dist/queue-receipt.js +272 -0
- package/dist/queue-receipt.js.map +1 -0
- package/dist/rig-handle.d.ts +45 -0
- package/dist/rig-handle.d.ts.map +1 -0
- package/dist/rig-handle.js +51 -0
- package/dist/rig-handle.js.map +1 -0
- package/dist/system-preflight.d.ts +5 -0
- package/dist/system-preflight.d.ts.map +1 -1
- package/dist/system-preflight.js +8 -0
- package/dist/system-preflight.js.map +1 -1
- package/dist/terminal-window.d.ts.map +1 -1
- package/dist/terminal-window.js +3 -1
- package/dist/terminal-window.js.map +1 -1
- package/package.json +1 -1
- package/tui/dist/config/config-model.d.ts.map +1 -1
- package/tui/dist/config/config-model.js +3 -0
- package/tui/dist/config/config-model.js.map +1 -1
- package/tui/dist/daemon-client.d.ts +3 -0
- package/tui/dist/daemon-client.d.ts.map +1 -1
- package/tui/dist/daemon-client.js +4 -1
- package/tui/dist/daemon-client.js.map +1 -1
- package/tui/dist/detail.d.ts.map +1 -1
- package/tui/dist/detail.js +6 -1
- package/tui/dist/detail.js.map +1 -1
- package/tui/dist/hydrate.d.ts.map +1 -1
- package/tui/dist/hydrate.js +30 -21
- package/tui/dist/hydrate.js.map +1 -1
- package/tui/dist/input.d.ts.map +1 -1
- package/tui/dist/input.js +15 -4
- package/tui/dist/input.js.map +1 -1
- package/tui/dist/local-reading.js +2 -2
- package/tui/dist/local-reading.js.map +1 -1
- package/tui/dist/pulse/pulse-model.d.ts +1 -0
- package/tui/dist/pulse/pulse-model.d.ts.map +1 -1
- package/tui/dist/pulse/pulse-model.js +7 -3
- package/tui/dist/pulse/pulse-model.js.map +1 -1
- package/tui/dist/pulse/render-pulse.d.ts.map +1 -1
- package/tui/dist/pulse/render-pulse.js +3 -1
- package/tui/dist/pulse/render-pulse.js.map +1 -1
- package/tui/dist/reading.d.ts.map +1 -1
- package/tui/dist/reading.js +14 -3
- package/tui/dist/reading.js.map +1 -1
- package/tui/dist/render.d.ts.map +1 -1
- package/tui/dist/render.js +26 -11
- package/tui/dist/render.js.map +1 -1
- package/tui/dist/types.d.ts +3 -0
- package/tui/dist/types.d.ts.map +1 -1
- package/ui/dist/assets/{index-D5n8EwkG.js → index-DhmxKuZf.js} +159 -159
- package/ui/dist/index.html +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { CODEX_TURN_STATUS_PATTERN } from "./codex-pane-patterns.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { inspectStartupStagedText, startupSubmissionEvidence } from "./startup-submission-evidence.js";
|
|
3
4
|
export { inspectStartupStagedText } from "./startup-submission-evidence.js";
|
|
4
5
|
import { OutboxHandler } from "./outbox-handler.js";
|
|
6
|
+
import { claudeResumeRotation } from "./session-registry.js";
|
|
5
7
|
import { latestHookWaitsOnPerson } from "./agent-activity-store.js";
|
|
6
8
|
import { wrapPaneEnvelope, appendDeliveredSegment } from "../lib/pane-envelope.js";
|
|
7
9
|
import { SeatIdentityStore } from "./seat-identity-store.js";
|
|
@@ -36,10 +38,6 @@ const MID_WORK_PATTERNS = [
|
|
|
36
38
|
// placeholder counts as idle only through MID_WORK_PATTERNS here, and classifySendReadiness
|
|
37
39
|
// never lets a placeholder-only verdict override a display-fresh running/needs_input hook.
|
|
38
40
|
const CODEX_EMPTY_COMPOSER_PATTERN = /^›\s+Ask Codex to do anything\s*$/;
|
|
39
|
-
// Codex's live turn-status row: a bullet, a header ("Working", or the reasoning summary Codex shows in its place),
|
|
40
|
-
// then the elapsed time and "esc to interrupt" in parentheses, e.g. "• Working (1h 09m 39s • esc to interrupt)".
|
|
41
|
-
// Completed output that merely says "Working directory: …" or "Working tree is clean." never matches.
|
|
42
|
-
const CODEX_TURN_STATUS_PATTERN = /^[•◦]\s+\S.*\((?:\d+[hms]\s*)+•\s*esc to interrupt\)/;
|
|
43
41
|
const IDLE_PROMPT_PATTERNS = [
|
|
44
42
|
/^[❯›]\s*$/, // prompt char + optional whitespace + end-of-line only
|
|
45
43
|
CODEX_EMPTY_COMPOSER_PATTERN,
|
|
@@ -47,11 +45,19 @@ const IDLE_PROMPT_PATTERNS = [
|
|
|
47
45
|
const PROMPT_DRAFT_PATTERNS = [
|
|
48
46
|
/^[❯›]\s+\S/,
|
|
49
47
|
];
|
|
48
|
+
// Claude's permission-mode footers: default, accept edits, bypass, auto and plan, optionally after a
|
|
49
|
+
// vim-mode marker such as "-- INSERT --" (#808). A suffix such as "· 1 shell" can follow the mode.
|
|
50
|
+
const CLAUDE_MODE_FOOTER = /^(?:-- [A-Z]+ -- )?(?:⏵⏵ (?:accept edits|bypass permissions|auto mode) on\b|⏸ plan mode on\b|\? for shortcuts\b)/;
|
|
51
|
+
// Sibling pattern identical to CLAUDE_MODE_FOOTER except that it ends in \? for shortcuts$,
|
|
52
|
+
// keeping unframed-composer and selectionLines checks anchored on the default footer (#808).
|
|
53
|
+
const CLAUDE_ANCHORED_MODE_FOOTER = /^(?:-- [A-Z]+ -- )?(?:⏵⏵ (?:accept edits|bypass permissions|auto mode) on\b|⏸ plan mode on\b|\? for shortcuts$)/;
|
|
54
|
+
// Permission mode status footers specifically (excluding shortcuts hint that appears in Codex footers).
|
|
55
|
+
const CLAUDE_PERMISSION_MODE_FOOTER = /(?:-- [A-Z]+ -- )?(?:⏵⏵ (?:accept edits|bypass permissions|auto mode) on\b|⏸ plan mode on\b)/;
|
|
50
56
|
// Footer hints, not proof of inactivity: Claude also renders its mode bar
|
|
51
57
|
// during a turn. Current live-status evidence must take precedence below.
|
|
52
58
|
const IDLE_STATUS_BAR_PATTERNS = [
|
|
53
59
|
/gpt-\d[\d.]* .+ · Context \[/, // Codex model/context footer
|
|
54
|
-
|
|
60
|
+
CLAUDE_PERMISSION_MODE_FOOTER, // Claude Code mode footers (#808)
|
|
55
61
|
];
|
|
56
62
|
const IDLE_TERMINAL_COMMANDS = new Set(["zsh", "bash", "sh", "fish", "nu", "tmux"]);
|
|
57
63
|
// OPR.0.4.1.10 — permission / confirmation question signatures. The numbered-selector pattern
|
|
@@ -118,9 +124,6 @@ const CLAUDE_STATUS_WARNINGS = [
|
|
|
118
124
|
/^tmux focus-events off · add 'set -g focus-events on' to ~\/\.tmux\.conf and re…$/,
|
|
119
125
|
/^You've used (?:\d|[1-9]\d)% of your weekly limit · resets \d{1,2}(?::\d{2})?(?:am|pm) \(UTC\)$/,
|
|
120
126
|
];
|
|
121
|
-
// Claude's permission-mode footers: default, accept edits, bypass, auto and plan, optionally after a
|
|
122
|
-
// vim-mode marker such as "-- INSERT --" (#808). A suffix such as "· 1 shell" can follow the mode.
|
|
123
|
-
const CLAUDE_MODE_FOOTER = /^(?:-- [A-Z]+ -- )?(?:⏵⏵ (?:accept edits|bypass permissions|auto mode) on\b|⏸ plan mode on\b|\? for shortcuts\b)/;
|
|
124
127
|
// Current Claude status rows need not end in "thinking)" or show "esc to interrupt".
|
|
125
128
|
// Completed summaries such as "✻ Crunched for 2s" lack the live ellipsis/timer shape.
|
|
126
129
|
// While a hook runs, the timer follows its label: "(running PostToolUse hook · 3m 12s · …)".
|
|
@@ -148,7 +151,7 @@ function findClaudeComposer(paneContent, options = {}) {
|
|
|
148
151
|
if (indent === undefined) {
|
|
149
152
|
// The unframed Claude prompt has the same current status/task block.
|
|
150
153
|
// Keep Codex footers and a bare prompt with no visible block on their old path.
|
|
151
|
-
if (bar < 2 ||
|
|
154
|
+
if (bar < 2 || !CLAUDE_ANCHORED_MODE_FOOTER.test(lines[bar].trim()))
|
|
152
155
|
return null;
|
|
153
156
|
prompt = lines[bar - 1] ?? "";
|
|
154
157
|
indent = /^([ \t]*)❯\s*$/.exec(prompt)?.[1];
|
|
@@ -207,6 +210,78 @@ function findPromptDraftBeforeFooter(paneContent) {
|
|
|
207
210
|
return null;
|
|
208
211
|
return truncateEvidence(priorTrimmed);
|
|
209
212
|
}
|
|
213
|
+
/** A capture taken with escape sequences, as lines of cells that know whether they're faint (SGR 2).
|
|
214
|
+
* Color parameters (38/48/58 with 5;n or 2;r;g;b) are skipped, so a color value of 2 isn't faint.
|
|
215
|
+
* Other escape sequences carry no text. */
|
|
216
|
+
function styledLines(styledPane) {
|
|
217
|
+
let faint = false;
|
|
218
|
+
return styledPane.replace(/\r\n/g, "\n").split("\n").map((raw) => {
|
|
219
|
+
const cells = [];
|
|
220
|
+
let offset = 0;
|
|
221
|
+
for (const match of raw.matchAll(/\x1b\[([0-9;:?]*)[ -\/]*([@-~])|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b./g)) {
|
|
222
|
+
for (const ch of raw.slice(offset, match.index))
|
|
223
|
+
cells.push({ ch, faint });
|
|
224
|
+
offset = match.index + match[0].length;
|
|
225
|
+
if (match[2] !== "m")
|
|
226
|
+
continue;
|
|
227
|
+
const codes = match[1].split(";");
|
|
228
|
+
for (let i = 0; i < codes.length; i++) {
|
|
229
|
+
const code = codes[i];
|
|
230
|
+
if (code === "38" || code === "48" || code === "58") {
|
|
231
|
+
i += codes[i + 1] === "5" ? 2 : codes[i + 1] === "2" ? 4 : 0;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
if (code === "2")
|
|
235
|
+
faint = true;
|
|
236
|
+
else if (code === "" || code === "0" || code === "22")
|
|
237
|
+
faint = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
for (const ch of raw.slice(offset))
|
|
241
|
+
cells.push({ ch, faint });
|
|
242
|
+
return cells;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
/** The last line that starts with a prompt glyph (`❯` or `›`), or -1. */
|
|
246
|
+
function lastPromptLine(lines) {
|
|
247
|
+
for (let i = lines.length - 1; i >= 0; i--)
|
|
248
|
+
if (/^\s*[❯›]/.test(lines[i]))
|
|
249
|
+
return i;
|
|
250
|
+
return -1;
|
|
251
|
+
}
|
|
252
|
+
/** True only when the last prompt line has text and all of it is faint, as Claude renders its
|
|
253
|
+
* suggestion. An empty last prompt, or a capture without escape sequences, never proves faint. */
|
|
254
|
+
export function promptTextIsFaint(styledPane) {
|
|
255
|
+
const lines = styledLines(styledPane);
|
|
256
|
+
const last = lastPromptLine(lines.map((cells) => cells.map((cell) => cell.ch).join("")));
|
|
257
|
+
if (last < 0)
|
|
258
|
+
return false;
|
|
259
|
+
const glyph = lines[last].findIndex((cell) => cell.ch === "❯" || cell.ch === "›");
|
|
260
|
+
const text = lines[last].slice(glyph + 1).filter((cell) => !/\s/.test(cell.ch));
|
|
261
|
+
return text.length > 0 && text.every((cell) => cell.faint);
|
|
262
|
+
}
|
|
263
|
+
/** Classifies one Claude capture taken with escape sequences, as a single frame. `suggestion` is set
|
|
264
|
+
* only when that frame's own draft is its last prompt line and all of that text is faint: it's then
|
|
265
|
+
* the frame classified as an empty composer. */
|
|
266
|
+
export function classifyStyledFrame(styledPane, options = {}) {
|
|
267
|
+
const lines = styledLines(styledPane).map((cells) => cells.map((cell) => cell.ch).join(""));
|
|
268
|
+
const frame = lines.join("\n");
|
|
269
|
+
const asIs = classifyPaneActivity(frame, options);
|
|
270
|
+
const composer = draftComposerLine(lines, asIs);
|
|
271
|
+
if (composer === null || !promptTextIsFaint(styledPane))
|
|
272
|
+
return { frame, asIs, composer, suggestion: null };
|
|
273
|
+
const last = lastPromptLine(lines);
|
|
274
|
+
lines[last] = /^\s*[❯›]/.exec(lines[last])[0];
|
|
275
|
+
return { frame, asIs, composer, suggestion: classifyPaneActivity(lines.join("\n"), options) };
|
|
276
|
+
}
|
|
277
|
+
/** The full text of the line a `prompt_draft` verdict read as the draft, when that's the last prompt
|
|
278
|
+
* line; otherwise null. Evidence is compacted and truncated, so callers compare this instead. */
|
|
279
|
+
function draftComposerLine(lines, verdict) {
|
|
280
|
+
const last = lastPromptLine(lines);
|
|
281
|
+
if (verdict.reason !== "prompt_draft" || last < 0 || verdict.evidence !== truncateEvidence(lines[last]))
|
|
282
|
+
return null;
|
|
283
|
+
return lines[last].trim();
|
|
284
|
+
}
|
|
210
285
|
export function classifyPaneActivity(paneContent, options = {}) {
|
|
211
286
|
const lastNonBlank = trimPaneLines(paneContent);
|
|
212
287
|
if (lastNonBlank.length === 0) {
|
|
@@ -227,7 +302,7 @@ export function classifyPaneActivity(paneContent, options = {}) {
|
|
|
227
302
|
// A complete later empty composer with a recognized Claude bar makes the preceding question history.
|
|
228
303
|
// Keep draft handling and selectors without this dialog boundary unchanged.
|
|
229
304
|
const selectionLines = claudeComposer?.framed &&
|
|
230
|
-
|
|
305
|
+
CLAUDE_ANCHORED_MODE_FOOTER.test(claudeComposer.bar) &&
|
|
231
306
|
IDLE_PROMPT_PATTERNS.some(pattern => pattern.test(claudeComposer.text)) &&
|
|
232
307
|
oldQuestionEnd >= 0 && oldQuestionEnd < promptScanLines.length - 1
|
|
233
308
|
? promptScanLines.slice(oldQuestionEnd + 1) : promptScanLines;
|
|
@@ -281,7 +356,8 @@ export function classifyPaneActivity(paneContent, options = {}) {
|
|
|
281
356
|
CLAUDE_LIVE_STATUS_PATTERN,
|
|
282
357
|
...(options.timerlessStatusIsLive ? [CLAUDE_TIMERLESS_STATUS_PATTERN] : []),
|
|
283
358
|
]);
|
|
284
|
-
|
|
359
|
+
const isClaudeStatusBar = CLAUDE_PERMISSION_MODE_FOOTER.test(idleStatusBarLine ?? "");
|
|
360
|
+
if (idleStatusBarLine && (!isClaudeStatusBar || !midWorkEvidence)) {
|
|
285
361
|
return {
|
|
286
362
|
state: "agent_idle",
|
|
287
363
|
reason: "idle_status_bar",
|
|
@@ -440,8 +516,26 @@ export async function probeSessionActivity(input) {
|
|
|
440
516
|
try {
|
|
441
517
|
const paneContent = await tmuxAdapter.capturePaneContent(sessionName, 20);
|
|
442
518
|
const capturedAt = new Date().toISOString();
|
|
443
|
-
const
|
|
444
|
-
|
|
519
|
+
const classifyOptions = { timerlessStatusIsLive: input.timerlessStatusIsLive === true };
|
|
520
|
+
let classification = classifyPaneActivity(paneContent ?? "", classifyOptions);
|
|
521
|
+
// Claude renders its prompt suggestion faint, which the plain capture loses. When a Claude pane reads
|
|
522
|
+
// as a draft, one capture with styling is classified on its own. Newer attention or work there is
|
|
523
|
+
// kept. A faint suggestion counts only when it's the same composer text this capture read as a draft;
|
|
524
|
+
// the verdict is then the styled frame's, as an empty composer. Anything else leaves the draft standing.
|
|
525
|
+
let slot = captureSlot(paneContent, capturedAt, captureSeq);
|
|
526
|
+
if (classification.reason === "prompt_draft" && runtime === "claude-code" && paneContent) {
|
|
527
|
+
const styled = await tmuxAdapter.capturePaneContent(sessionName, 20, { escapeSequences: true }).catch(() => null);
|
|
528
|
+
const later = styled ? classifyStyledFrame(styled, classifyOptions) : null;
|
|
529
|
+
const newer = !later ? null
|
|
530
|
+
: later.asIs.state === "agent_active" || (later.asIs.state === "attention" && later.asIs.reason !== "prompt_draft") ? later.asIs
|
|
531
|
+
: later.suggestion && later.composer === draftComposerLine(paneContent.split("\n"), classification) ? later.suggestion
|
|
532
|
+
: null;
|
|
533
|
+
if (later && newer) {
|
|
534
|
+
classification = newer;
|
|
535
|
+
slot = captureSlot(later.frame, new Date().toISOString(), captureSeq);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return observeProbe(slot, {
|
|
445
539
|
state: mapPaneState(classification.state),
|
|
446
540
|
reason: classification.reason,
|
|
447
541
|
evidence: classification.evidence,
|
|
@@ -513,6 +607,98 @@ function countOccurrences(haystack, needle) {
|
|
|
513
607
|
}
|
|
514
608
|
return count;
|
|
515
609
|
}
|
|
610
|
+
/**
|
|
611
|
+
* Claude indents continuation lines in submitted prompts. Compare the same
|
|
612
|
+
* line content in the sent text and both captures, retaining line breaks and
|
|
613
|
+
* within-line spacing rather than allowing unrelated words to match.
|
|
614
|
+
*/
|
|
615
|
+
function normalizeVerificationText(text) {
|
|
616
|
+
return text.replace(/\r?\n[ \t]*/g, "\n");
|
|
617
|
+
}
|
|
618
|
+
/** Match the full envelope with whitespace collapsed, including body and recipient.
|
|
619
|
+
* Raw sends retain their normalized 40-character prefix and spacing. */
|
|
620
|
+
function verificationSnippet(text) {
|
|
621
|
+
const normalized = normalizeVerificationText(text);
|
|
622
|
+
const header = /^From: [^\n]+\nTo: [^\n]+\n(?:Sent: [^\n]+\n)?---\n/.exec(normalized);
|
|
623
|
+
return { snippet: header ? normalized.replace(/\s+/g, " ").trim() : normalized.slice(0, 40), envelope: !!header };
|
|
624
|
+
}
|
|
625
|
+
/** Count whole visible envelope blocks, not prefixes of longer messages.
|
|
626
|
+
* A new envelope, prompt or same-column output ends an indented user block.
|
|
627
|
+
* Whitespace is compared only after retaining boundaries and quote indentation. */
|
|
628
|
+
function countEnvelopeOccurrences(transcript, expected) {
|
|
629
|
+
const lines = transcript.split("\n");
|
|
630
|
+
let count = 0;
|
|
631
|
+
let block = null;
|
|
632
|
+
let blockIndent = 0;
|
|
633
|
+
let blockPrompted = false;
|
|
634
|
+
let inAgentOutput = false;
|
|
635
|
+
const finish = () => {
|
|
636
|
+
if (block?.join("\n").replace(/\s+/g, " ").trim() === expected)
|
|
637
|
+
count++;
|
|
638
|
+
block = null;
|
|
639
|
+
};
|
|
640
|
+
for (let i = 0; i < lines.length; i++) {
|
|
641
|
+
const line = lines[i];
|
|
642
|
+
const indent = /^[ \t]*/.exec(line)[0].length;
|
|
643
|
+
const prompted = /^[❯›]\s+/.test(line.slice(indent));
|
|
644
|
+
const header = line.slice(indent).replace(/^[❯›]\s+/, "");
|
|
645
|
+
const atBoundary = (!block && !inAgentOutput) || indent <= blockIndent;
|
|
646
|
+
const candidate = normalizeVerificationText([header, ...lines.slice(i + 1, i + 5)].join("\n"));
|
|
647
|
+
const envelopeHeader = /^From: [^\n]+\nTo: [^\n]+\n(?:Sent: [^\n]+\n)?---\n/.test(candidate);
|
|
648
|
+
if (atBoundary && envelopeHeader && (!inAgentOutput || prompted)) {
|
|
649
|
+
finish();
|
|
650
|
+
block = [header];
|
|
651
|
+
blockIndent = indent;
|
|
652
|
+
// Flat historical captures have no role columns; retain whole-block
|
|
653
|
+
// equality there instead of treating their own header/body as output.
|
|
654
|
+
blockPrompted = prompted && /^[ \t]*/.exec(lines[i + 1] ?? "")[0].length > indent;
|
|
655
|
+
inAgentOutput = false;
|
|
656
|
+
}
|
|
657
|
+
else if (atBoundary && blockPrompted && line.trim().length > 0) {
|
|
658
|
+
// Any nonblank boundary line is outside an indented user block. Deeper
|
|
659
|
+
// quotes stay in the body, and blank separators do not change its text.
|
|
660
|
+
finish();
|
|
661
|
+
inAgentOutput = true;
|
|
662
|
+
}
|
|
663
|
+
else if (atBoundary && /^[❯›](?:\s|$)/.test(line.slice(indent))) {
|
|
664
|
+
finish();
|
|
665
|
+
}
|
|
666
|
+
else if (block) {
|
|
667
|
+
block.push(line);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
finish();
|
|
671
|
+
return count;
|
|
672
|
+
}
|
|
673
|
+
/** The last prompt marker owns the current input region. Never count it or its
|
|
674
|
+
* continuation lines as submitted transcript, in either capture. If a capture
|
|
675
|
+
* ends at a historical prompt with no later input marker, leave it unconfirmed. */
|
|
676
|
+
function verificationTranscript(content) {
|
|
677
|
+
const lines = (content ?? "").replace(/\r\n/g, "\n").split("\n");
|
|
678
|
+
let inputAt = lastPromptLine(lines);
|
|
679
|
+
if (inputAt >= 0) {
|
|
680
|
+
let indent = /^[ \t]*/.exec(lines[inputAt])[0].length;
|
|
681
|
+
// Claude indents draft continuation lines. A quoted prompt on one of them
|
|
682
|
+
// belongs to the enclosing input, not a new transcript/composer boundary.
|
|
683
|
+
for (let i = inputAt - 1; i >= 0; i--) {
|
|
684
|
+
const prompt = /^([ \t]*)[❯›]/.exec(lines[i]);
|
|
685
|
+
if (prompt && prompt[1].length < indent) {
|
|
686
|
+
inputAt = i;
|
|
687
|
+
indent = prompt[1].length;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
// A visible framed composer starts at its upper rule, not its prompt.
|
|
692
|
+
// Keep quoted/indented rules in the message; require a matching lower frame.
|
|
693
|
+
if (inputAt > 0) {
|
|
694
|
+
const indent = /^[ \t]*/.exec(lines[inputAt])[0];
|
|
695
|
+
const frame = (line) => line.startsWith(indent) &&
|
|
696
|
+
/^─{3,}(?: .+ ─+)?$/.test(line.slice(indent.length).trimEnd());
|
|
697
|
+
if (frame(lines[inputAt - 1]) && lines.slice(inputAt + 1).some(frame))
|
|
698
|
+
inputAt--;
|
|
699
|
+
}
|
|
700
|
+
return (inputAt < 0 ? lines : lines.slice(0, inputAt)).join("\n");
|
|
701
|
+
}
|
|
516
702
|
/** Map a resolved fan-out target + its recipient list → the EnvelopeScope (ruling 03c35295). The
|
|
517
703
|
* transport knows the target shape + resolved seats, so it builds the honest scale daemon-side:
|
|
518
704
|
* DM / multi (full list) / rig- or pod-scoped broadcast (with seat count) / topology. */
|
|
@@ -718,6 +904,10 @@ export class SessionTransport {
|
|
|
718
904
|
b.tmux_session AS binding_session,
|
|
719
905
|
b.tmux_pane AS pane,
|
|
720
906
|
s.resume_token AS resume_token,
|
|
907
|
+
s.resume_provenance AS resume_provenance,
|
|
908
|
+
s.resume_rotated_from AS resume_rotated_from,
|
|
909
|
+
s.resume_rotated_process AS resume_rotated_process,
|
|
910
|
+
s.resume_launch_process AS resume_launch_process,
|
|
721
911
|
(SELECT generation_uuid FROM occupant_tenures t WHERE t.node_id = n.id ORDER BY generation_ordinal DESC LIMIT 1) AS occupant
|
|
722
912
|
FROM sessions s
|
|
723
913
|
JOIN nodes n ON s.node_id = n.id
|
|
@@ -735,6 +925,7 @@ export class SessionTransport {
|
|
|
735
925
|
pane: row?.binding_session === sessionName ? row?.pane ?? null : null,
|
|
736
926
|
occupant: row?.binding_session === sessionName ? row?.occupant ?? null : null,
|
|
737
927
|
resumeToken: row?.resume_token ?? null,
|
|
928
|
+
rotation: claudeResumeRotation(row),
|
|
738
929
|
};
|
|
739
930
|
}
|
|
740
931
|
async resolveSessions(target) {
|
|
@@ -941,6 +1132,9 @@ export class SessionTransport {
|
|
|
941
1132
|
const pref = guard.preference(target.nodeId);
|
|
942
1133
|
return pref.desired || pref.effective ? target : null;
|
|
943
1134
|
}
|
|
1135
|
+
/** Deliver text and Enter to the resolved recipient under the send guards.
|
|
1136
|
+
* With verification requested, compare rendered message counts, excluding
|
|
1137
|
+
* agent input; a confirmed render does not prove agent consumption. */
|
|
944
1138
|
async send(sessionName, text, opts) {
|
|
945
1139
|
const guard = this.tmuxAdapter.deliveryGuard;
|
|
946
1140
|
if (!guard)
|
|
@@ -973,7 +1167,44 @@ export class SessionTransport {
|
|
|
973
1167
|
return retainedResult();
|
|
974
1168
|
}
|
|
975
1169
|
}
|
|
976
|
-
|
|
1170
|
+
// #1084 — the idle wait must not occupy the seat's serial lease: a long
|
|
1171
|
+
// --wait-for-idle inside guard.operation holds up other sends and queue
|
|
1172
|
+
// creates to this seat for the whole wait. The lease-free wait, main's
|
|
1173
|
+
// quick refusals ahead of it, and the occupant binding all live in
|
|
1174
|
+
// preWaitForIdle; the lease is entered only for the idle re-check +
|
|
1175
|
+
// write. A null return means go through guard.operation exactly as on
|
|
1176
|
+
// main (guard held, target unresolvable, or a quick refusal).
|
|
1177
|
+
let sendOpts = opts;
|
|
1178
|
+
if (opts?.waitForIdleMs !== undefined && !opts.submitOnly && !opts.preWaitedIdle) {
|
|
1179
|
+
const pre = await this.preWaitForIdle(sessionName, opts, guard);
|
|
1180
|
+
if (pre === null) {
|
|
1181
|
+
// Fall through with today's path (in-lease wait / retain / throw).
|
|
1182
|
+
}
|
|
1183
|
+
else if (!pre.ok) {
|
|
1184
|
+
return pre.failure;
|
|
1185
|
+
}
|
|
1186
|
+
else {
|
|
1187
|
+
// Occupant check (review round 3): the lease starts only after the
|
|
1188
|
+
// pre-wait, so refuse here when the seat changed hands meanwhile —
|
|
1189
|
+
// exactly as main refuses with guard_target_changed. Retention must
|
|
1190
|
+
// not adopt the new occupant either, so this runs before operation.
|
|
1191
|
+
const current = guard.target(sessionName);
|
|
1192
|
+
if (JSON.stringify(pre.bound) !== JSON.stringify(current)) {
|
|
1193
|
+
return {
|
|
1194
|
+
ok: false,
|
|
1195
|
+
sessionName,
|
|
1196
|
+
sent: false,
|
|
1197
|
+
reason: "guard_target_changed",
|
|
1198
|
+
error: "Input target/occupant changed; no input written.",
|
|
1199
|
+
activity: pre.evidence.activity,
|
|
1200
|
+
waitedMs: pre.evidence.waitedMs,
|
|
1201
|
+
attempts: pre.evidence.attempts,
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
sendOpts = { ...opts, preWaitedIdle: pre.evidence, preWaitBound: pre.bound };
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return await guard.operation(sessionName, () => this.sendUnguarded(sessionName, text, sendOpts), async (target) => {
|
|
977
1208
|
if (opts?.submitOnly)
|
|
978
1209
|
return { ok: false, sessionName, sent: false, reason: "typing_guard_enabled", error: "Typing guard prevents submit-only; no Enter was sent." };
|
|
979
1210
|
this.db.transaction(() => {
|
|
@@ -1008,6 +1239,131 @@ export class SessionTransport {
|
|
|
1008
1239
|
return { ok: false, sessionName, sent: false, reason: error.code ?? "guard_unavailable", error: error.message };
|
|
1009
1240
|
}
|
|
1010
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* #1084/#1103: lease-free idle wait before the serial lease, so a long
|
|
1244
|
+
* --wait-for-idle holds up no other send or queue create to this seat.
|
|
1245
|
+
* Returns the wait evidence plus the occupant bound before the wait (both
|
|
1246
|
+
* threaded into the leased send), a failure result using main's exact
|
|
1247
|
+
* reasons, or null when the send must go through guard.operation exactly
|
|
1248
|
+
* as on main. Order matters: typing-guard decision first, then main's
|
|
1249
|
+
* quick refusals (runtime identity, wait validations, session probe),
|
|
1250
|
+
* then the wait. Only the lookups sit inside try: a late throw surfaces
|
|
1251
|
+
* instead of silently running a second full wait inside the lease.
|
|
1252
|
+
*/
|
|
1253
|
+
async preWaitForIdle(sessionName, opts, guard) {
|
|
1254
|
+
const timeoutMs = opts.waitForIdleMs;
|
|
1255
|
+
if (timeoutMs === undefined)
|
|
1256
|
+
return null;
|
|
1257
|
+
let meta;
|
|
1258
|
+
let bound;
|
|
1259
|
+
try {
|
|
1260
|
+
const preTarget = guard.maybeTarget(sessionName);
|
|
1261
|
+
if (!preTarget)
|
|
1262
|
+
return null;
|
|
1263
|
+
const prePref = guard.preference(preTarget.nodeId);
|
|
1264
|
+
if (prePref.effective || prePref.desired)
|
|
1265
|
+
return null;
|
|
1266
|
+
meta = this.getSessionMeta(sessionName);
|
|
1267
|
+
bound = guard.target(sessionName);
|
|
1268
|
+
}
|
|
1269
|
+
catch {
|
|
1270
|
+
return null;
|
|
1271
|
+
}
|
|
1272
|
+
// External CLI seats never wait: main refuses them before anything else,
|
|
1273
|
+
// and a probe or wait would only delay that refusal (or mistake it for
|
|
1274
|
+
// an absent session).
|
|
1275
|
+
if (meta.attachmentType === "external_cli") {
|
|
1276
|
+
return { ok: false, failure: {
|
|
1277
|
+
ok: false,
|
|
1278
|
+
sessionName,
|
|
1279
|
+
reason: "transport_unavailable",
|
|
1280
|
+
error: `Session '${sessionName}' is attached as an external CLI node. Inbound tmux transport is unavailable for this target.`,
|
|
1281
|
+
} };
|
|
1282
|
+
}
|
|
1283
|
+
const runtime = meta.runtime;
|
|
1284
|
+
if (runtime && runtime !== "terminal" && runtime !== "claude-code") {
|
|
1285
|
+
const unverified = await this.unverifiedShellForeground(sessionName, runtime, meta.pane, meta.resumeToken);
|
|
1286
|
+
if (unverified) {
|
|
1287
|
+
return { ok: false, failure: {
|
|
1288
|
+
ok: false,
|
|
1289
|
+
sessionName,
|
|
1290
|
+
sent: false,
|
|
1291
|
+
reason: "target_runtime_unverified",
|
|
1292
|
+
error: `Refused: '${sessionName}' reports ${unverified} as the foreground command, but OpenRig could not verify its expected ${runtime} agent in the bound pane. The agent may still be running behind a wrapper. No text was sent.`,
|
|
1293
|
+
} };
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (opts.force) {
|
|
1297
|
+
return { ok: false, failure: {
|
|
1298
|
+
ok: false,
|
|
1299
|
+
sessionName,
|
|
1300
|
+
reason: "invalid_wait_for_idle",
|
|
1301
|
+
error: "--wait-for-idle cannot be combined with force. No text was sent.",
|
|
1302
|
+
sent: false,
|
|
1303
|
+
} };
|
|
1304
|
+
}
|
|
1305
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
1306
|
+
return { ok: false, failure: {
|
|
1307
|
+
ok: false,
|
|
1308
|
+
sessionName,
|
|
1309
|
+
reason: "invalid_wait_for_idle",
|
|
1310
|
+
error: "waitForIdleMs must be a positive number. No text was sent.",
|
|
1311
|
+
sent: false,
|
|
1312
|
+
} };
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
const target = this.absenceProbeTarget(sessionName);
|
|
1316
|
+
const observedAt = this.now().toISOString();
|
|
1317
|
+
const probe = await this.tmuxAdapter.probeSession(sessionName);
|
|
1318
|
+
if (probe.state === "absent") {
|
|
1319
|
+
this.recordSessionMissingVerdict(target, observedAt);
|
|
1320
|
+
return { ok: false, failure: {
|
|
1321
|
+
ok: false,
|
|
1322
|
+
sessionName,
|
|
1323
|
+
reason: "session_missing",
|
|
1324
|
+
error: `Session '${sessionName}' not found: tmux reports no session with this name. No text was sent. Check available sessions with: rig ps --nodes`,
|
|
1325
|
+
} };
|
|
1326
|
+
}
|
|
1327
|
+
if (probe.state === "transport_unavailable") {
|
|
1328
|
+
return { ok: false, failure: {
|
|
1329
|
+
ok: false,
|
|
1330
|
+
sessionName,
|
|
1331
|
+
reason: "tmux_unavailable",
|
|
1332
|
+
error: `The tmux server could not be reached (${probe.cause}). Whether session '${sessionName}' exists was not determined. No text was sent.`,
|
|
1333
|
+
} };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
catch (err) {
|
|
1337
|
+
return { ok: false, failure: {
|
|
1338
|
+
ok: false,
|
|
1339
|
+
sessionName,
|
|
1340
|
+
reason: "tmux_unavailable",
|
|
1341
|
+
error: `The tmux session probe failed unexpectedly (${err instanceof Error ? err.message : String(err)}). Whether session '${sessionName}' exists was not determined. No text was sent.`,
|
|
1342
|
+
} };
|
|
1343
|
+
}
|
|
1344
|
+
const deadlineAt = Date.now() + timeoutMs;
|
|
1345
|
+
const preWait = await this.waitForIdle({
|
|
1346
|
+
sessionName,
|
|
1347
|
+
runtime: meta.runtime,
|
|
1348
|
+
attachmentType: meta.attachmentType,
|
|
1349
|
+
timeoutMs,
|
|
1350
|
+
binding: { sessionName, nodeId: meta.nodeId, occupant: meta.occupant, pane: meta.pane },
|
|
1351
|
+
readinessFromPaneOnly: opts.readinessFromPaneOnly === true,
|
|
1352
|
+
});
|
|
1353
|
+
if (!preWait.ok) {
|
|
1354
|
+
return { ok: false, failure: {
|
|
1355
|
+
ok: false,
|
|
1356
|
+
sessionName,
|
|
1357
|
+
reason: preWait.reason,
|
|
1358
|
+
error: preWait.error,
|
|
1359
|
+
sent: false,
|
|
1360
|
+
activity: preWait.activity,
|
|
1361
|
+
waitedMs: preWait.waitedMs,
|
|
1362
|
+
attempts: preWait.attempts,
|
|
1363
|
+
} };
|
|
1364
|
+
}
|
|
1365
|
+
return { ok: true, evidence: { activity: preWait.activity, waitedMs: preWait.waitedMs, attempts: preWait.attempts, deadlineAt }, bound };
|
|
1366
|
+
}
|
|
1011
1367
|
async sendUnguarded(sessionName, text, opts) {
|
|
1012
1368
|
let preVerifyContent = null;
|
|
1013
1369
|
const sessionMeta = this.getSessionMeta(sessionName);
|
|
@@ -1021,7 +1377,7 @@ export class SessionTransport {
|
|
|
1021
1377
|
return null;
|
|
1022
1378
|
if (bindingChanged())
|
|
1023
1379
|
return changedRecipient();
|
|
1024
|
-
const observation = await this.claudeDeliveryObservation(sessionName, sessionMeta.pane, sessionMeta.resumeToken);
|
|
1380
|
+
const observation = await this.claudeDeliveryObservation(sessionName, sessionMeta.pane, sessionMeta.resumeToken, sessionMeta.rotation);
|
|
1025
1381
|
if (bindingChanged())
|
|
1026
1382
|
return changedRecipient();
|
|
1027
1383
|
if (observation.state === "idle_shell" || observation.state === "conflict") {
|
|
@@ -1186,7 +1542,10 @@ export class SessionTransport {
|
|
|
1186
1542
|
}
|
|
1187
1543
|
return observe({ ok: true, sessionName, outcome: "rendered-unconfirmed", submitOnly: true });
|
|
1188
1544
|
}
|
|
1189
|
-
|
|
1545
|
+
// #1084: when send() already waited lease-free (preWaitedIdle), the wait
|
|
1546
|
+
// below is skipped; otherwise wait here, exactly as before
|
|
1547
|
+
// (typing-guard flip and unresolvable-target races keep today's behavior).
|
|
1548
|
+
if (waitForIdleMs !== undefined && !opts?.preWaitedIdle) {
|
|
1190
1549
|
const waitResult = await this.waitForIdle({
|
|
1191
1550
|
sessionName,
|
|
1192
1551
|
runtime,
|
|
@@ -1211,6 +1570,104 @@ export class SessionTransport {
|
|
|
1211
1570
|
};
|
|
1212
1571
|
}
|
|
1213
1572
|
}
|
|
1573
|
+
else if (opts?.preWaitedIdle && waitForIdleMs !== undefined) {
|
|
1574
|
+
// Occupant check (review round 3): the lease started only after the
|
|
1575
|
+
// pre-wait, so refuse when the seat changed hands meanwhile, exactly
|
|
1576
|
+
// as main refuses with guard_target_changed. No new refusal.
|
|
1577
|
+
const preGuard = this.tmuxAdapter.deliveryGuard;
|
|
1578
|
+
if (preGuard && opts.preWaitBound) {
|
|
1579
|
+
const current = preGuard.target(sessionName);
|
|
1580
|
+
if (JSON.stringify(opts.preWaitBound) !== JSON.stringify(current)) {
|
|
1581
|
+
return {
|
|
1582
|
+
ok: false,
|
|
1583
|
+
sessionName,
|
|
1584
|
+
sent: false,
|
|
1585
|
+
reason: "guard_target_changed",
|
|
1586
|
+
error: "Input target/occupant changed; no input written.",
|
|
1587
|
+
activity: opts.preWaitedIdle.activity,
|
|
1588
|
+
waitedMs: opts.preWaitedIdle.waitedMs,
|
|
1589
|
+
attempts: opts.preWaitedIdle.attempts,
|
|
1590
|
+
};
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
// Idle re-check (review round 1 + bot round 2): the wait-mode contract
|
|
1594
|
+
// holds at type time too — only idle types. The budget is measured
|
|
1595
|
+
// from the ORIGINAL deadline (lease-queue time counts too), so the
|
|
1596
|
+
// total wait never exceeds what the caller asked for. Below one poll
|
|
1597
|
+
// interval, waiting buys nothing: one unraced observation decides, so
|
|
1598
|
+
// an idle seat at a dead budget still sends instead of losing a race
|
|
1599
|
+
// against a zero timer.
|
|
1600
|
+
const preEvidence = opts.preWaitedIdle;
|
|
1601
|
+
const deadlineRemainingMs = preEvidence.deadlineAt - Date.now();
|
|
1602
|
+
if (deadlineRemainingMs > this.waitForIdlePollMs) {
|
|
1603
|
+
const rewait = await this.waitForIdle({
|
|
1604
|
+
sessionName,
|
|
1605
|
+
runtime,
|
|
1606
|
+
attachmentType: sessionMeta.attachmentType,
|
|
1607
|
+
timeoutMs: deadlineRemainingMs,
|
|
1608
|
+
// Message-only: the re-wait runs on the leftover budget, but the
|
|
1609
|
+
// timeout names the configured --wait-for-idle value.
|
|
1610
|
+
timeoutLabelMs: waitForIdleMs,
|
|
1611
|
+
binding: observed?.binding,
|
|
1612
|
+
readinessFromPaneOnly: opts.readinessFromPaneOnly === true,
|
|
1613
|
+
});
|
|
1614
|
+
waitEvidence = {
|
|
1615
|
+
activity: rewait.activity,
|
|
1616
|
+
waitedMs: preEvidence.waitedMs + rewait.waitedMs,
|
|
1617
|
+
attempts: preEvidence.attempts + rewait.attempts,
|
|
1618
|
+
};
|
|
1619
|
+
if (!rewait.ok) {
|
|
1620
|
+
return {
|
|
1621
|
+
ok: false,
|
|
1622
|
+
sessionName,
|
|
1623
|
+
reason: rewait.reason,
|
|
1624
|
+
error: rewait.error,
|
|
1625
|
+
sent: false,
|
|
1626
|
+
...waitEvidence,
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
else {
|
|
1631
|
+
const recheck = await this.classifySendReadiness({
|
|
1632
|
+
sessionName,
|
|
1633
|
+
runtime,
|
|
1634
|
+
attachmentType: sessionMeta.attachmentType,
|
|
1635
|
+
binding: observed?.binding,
|
|
1636
|
+
readinessFromPaneOnly: opts.readinessFromPaneOnly === true,
|
|
1637
|
+
});
|
|
1638
|
+
if (recheck.state !== "idle") {
|
|
1639
|
+
const late = recheck.state === "needs_input"
|
|
1640
|
+
? {
|
|
1641
|
+
reason: "target_needs_input",
|
|
1642
|
+
error: `Refused: '${sessionName}' is at an interactive prompt (${recheck.reason}). A message must not select or approve it. To deliberately drive the prompt: rig send ${sessionName} "<text>" --dangerously-interact --reason "<why>". No text was sent.`,
|
|
1643
|
+
}
|
|
1644
|
+
: recheck.state === "unknown"
|
|
1645
|
+
? {
|
|
1646
|
+
reason: "target_activity_unknown",
|
|
1647
|
+
error: `Target activity could not be determined (${recheck.reason}) when the ${waitForIdleMs}ms wait ended. No text was sent.`,
|
|
1648
|
+
}
|
|
1649
|
+
: {
|
|
1650
|
+
reason: "wait_for_idle_timeout",
|
|
1651
|
+
error: `Target was still busy (${recheck.reason}) when the ${waitForIdleMs}ms wait ended. No text was sent.`,
|
|
1652
|
+
};
|
|
1653
|
+
return {
|
|
1654
|
+
ok: false,
|
|
1655
|
+
sessionName,
|
|
1656
|
+
sent: false,
|
|
1657
|
+
reason: late.reason,
|
|
1658
|
+
error: late.error,
|
|
1659
|
+
activity: recheck,
|
|
1660
|
+
waitedMs: preEvidence.waitedMs,
|
|
1661
|
+
attempts: preEvidence.attempts + 1,
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
waitEvidence = {
|
|
1665
|
+
activity: recheck,
|
|
1666
|
+
waitedMs: preEvidence.waitedMs,
|
|
1667
|
+
attempts: preEvidence.attempts + 1,
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1214
1671
|
// 2. OPR.0.4.1.10 — robust prompt/permission + mid-work guard on the DEFAULT path.
|
|
1215
1672
|
// Runs the same detector previously reachable only via --wait-for-idle: fresh runtime-hook primary
|
|
1216
1673
|
// (within the send-readiness window) + hardened capture-pane fallback. This closes the rig-send
|
|
@@ -1301,7 +1758,8 @@ export class SessionTransport {
|
|
|
1301
1758
|
if (opts?.verify) {
|
|
1302
1759
|
const captureSeq = observed ? nextCaptureSeq++ : 0;
|
|
1303
1760
|
try {
|
|
1304
|
-
preVerifyContent = await this.runStage("session_transport.pre_capture", () => this.tmuxAdapter.capturePaneContent(sessionName, 30)
|
|
1761
|
+
preVerifyContent = await this.runStage("session_transport.pre_capture", () => runtime === "terminal" || !verificationSnippet(text).envelope ? this.tmuxAdapter.capturePaneContent(sessionName, 30)
|
|
1762
|
+
: this.tmuxAdapter.capturePaneContent(sessionName, 30, { joinWrappedLines: true }));
|
|
1305
1763
|
if (observed)
|
|
1306
1764
|
observed.pre = captureSlot(preVerifyContent, this.now().toISOString(), captureSeq);
|
|
1307
1765
|
}
|
|
@@ -1376,12 +1834,19 @@ export class SessionTransport {
|
|
|
1376
1834
|
await this.sleep(500);
|
|
1377
1835
|
const captureSeq = observed ? nextCaptureSeq++ : 0;
|
|
1378
1836
|
try {
|
|
1379
|
-
const content = await this.runStage("session_transport.post_capture", () => this.tmuxAdapter.capturePaneContent(sessionName, 30)
|
|
1837
|
+
const content = await this.runStage("session_transport.post_capture", () => runtime === "terminal" || !verificationSnippet(text).envelope ? this.tmuxAdapter.capturePaneContent(sessionName, 30)
|
|
1838
|
+
: this.tmuxAdapter.capturePaneContent(sessionName, 30, { joinWrappedLines: true }));
|
|
1380
1839
|
if (observed)
|
|
1381
1840
|
observed.post = captureSlot(content, this.now().toISOString(), captureSeq);
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
const
|
|
1841
|
+
// A terminal prompt is command history, not an agent composer. Preserve
|
|
1842
|
+
// its original prefix-count verification even while a command is running.
|
|
1843
|
+
const terminal = runtime === "terminal";
|
|
1844
|
+
const { snippet, envelope } = terminal ? { snippet: text.slice(0, 40), envelope: false } : verificationSnippet(text);
|
|
1845
|
+
const transcript = terminal ? (pane) => pane ?? ""
|
|
1846
|
+
: envelope ? verificationTranscript : (pane) => normalizeVerificationText(verificationTranscript(pane));
|
|
1847
|
+
const count = envelope ? countEnvelopeOccurrences : countOccurrences;
|
|
1848
|
+
const preCount = count(transcript(preVerifyContent), snippet);
|
|
1849
|
+
const postCount = count(transcript(content), snippet);
|
|
1385
1850
|
const verified = postCount > preCount;
|
|
1386
1851
|
return observe({ ok: true, sessionName, ...interaction, verified, outcome: verified ? "delivered" : "rendered-unconfirmed", ...(sendAdvisory ? { warning: sendAdvisory } : {}), ...(waitMode ? { sent: true, ...waitEvidence } : {}) });
|
|
1387
1852
|
}
|
|
@@ -1414,6 +1879,7 @@ export class SessionTransport {
|
|
|
1414
1879
|
const deadline = Date.now() + input.timeoutMs;
|
|
1415
1880
|
let attempts = 0;
|
|
1416
1881
|
let last = null;
|
|
1882
|
+
const labelMs = input.timeoutLabelMs ?? input.timeoutMs;
|
|
1417
1883
|
const expire = () => {
|
|
1418
1884
|
const count = `${attempts} observation${attempts === 1 ? "" : "s"}`;
|
|
1419
1885
|
const activity = last ?? {
|
|
@@ -1425,14 +1891,14 @@ export class SessionTransport {
|
|
|
1425
1891
|
ok: false,
|
|
1426
1892
|
reason: "target_activity_unknown",
|
|
1427
1893
|
error: last
|
|
1428
|
-
? `Target activity could not be determined (${activity.reason}) when the ${
|
|
1429
|
-
: `Target activity could not be determined: no observation completed within the ${
|
|
1894
|
+
? `Target activity could not be determined (${activity.reason}) when the ${labelMs}ms wait ended (${count}). No text was sent.`
|
|
1895
|
+
: `Target activity could not be determined: no observation completed within the ${labelMs}ms wait. No text was sent.`,
|
|
1430
1896
|
activity, waitedMs: input.timeoutMs, attempts,
|
|
1431
1897
|
}
|
|
1432
1898
|
: {
|
|
1433
1899
|
ok: false,
|
|
1434
1900
|
reason: "wait_for_idle_timeout",
|
|
1435
|
-
error: `Target was still busy (${activity.reason}) when the ${
|
|
1901
|
+
error: `Target was still busy (${activity.reason}) when the ${labelMs}ms wait ended (${count}). No text was sent.`,
|
|
1436
1902
|
activity, waitedMs: input.timeoutMs, attempts,
|
|
1437
1903
|
};
|
|
1438
1904
|
};
|
|
@@ -1496,7 +1962,7 @@ export class SessionTransport {
|
|
|
1496
1962
|
clearTimeout(timer);
|
|
1497
1963
|
}
|
|
1498
1964
|
}
|
|
1499
|
-
async claudeDeliveryObservation(sessionName, pane, resumeToken) {
|
|
1965
|
+
async claudeDeliveryObservation(sessionName, pane, resumeToken, rotation = null) {
|
|
1500
1966
|
const unknown = { state: "unknown", detail: "Claude runtime observation or older launch binding is unavailable" };
|
|
1501
1967
|
try {
|
|
1502
1968
|
const panes = await this.tmuxAdapter.listPanes(sessionName);
|
|
@@ -1508,7 +1974,7 @@ export class SessionTransport {
|
|
|
1508
1974
|
const [sessionPid, panePid] = await Promise.all([this.tmuxAdapter.getPanePid(sessionName), this.tmuxAdapter.getPanePid(pane)]);
|
|
1509
1975
|
if (sessionPid && panePid && sessionPid !== panePid)
|
|
1510
1976
|
return { state: "conflict", detail: "The session and bound pane name different processes" };
|
|
1511
|
-
const observation = await observeClaudeDelivery({ target: pane, tmux: this.tmuxAdapter, listProcesses: this.listProcesses, expectedToken: resumeToken });
|
|
1977
|
+
const observation = await observeClaudeDelivery({ target: pane, tmux: this.tmuxAdapter, listProcesses: this.listProcesses, expectedToken: resumeToken, rotation });
|
|
1512
1978
|
// Refusal already has positive evidence; a later failed read cannot erase it.
|
|
1513
1979
|
if (observation.state === "conflict" || observation.state === "idle_shell")
|
|
1514
1980
|
return observation;
|