@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.
Files changed (570) hide show
  1. package/README.md +2 -2
  2. package/daemon/assets/onboarding/02-self-and-competent-action.md +4 -3
  3. package/daemon/assets/plugins/openrig-core/.claude-plugin/plugin.json +1 -1
  4. package/daemon/assets/plugins/openrig-core/.codex-plugin/plugin.json +1 -1
  5. package/daemon/assets/plugins/openrig-core/hooks/scripts/activity-relay.cjs +42 -2
  6. package/daemon/assets/plugins/openrig-core/skills/openrig-skills/SKILL.md +1 -1
  7. package/daemon/assets/plugins/openrig-core/skills/openrig-user/SKILL.md +7 -1
  8. package/daemon/assets/plugins/openrig-core/skills/refocusing/scripts/trace-to-root.py +6 -1
  9. package/daemon/assets/skills/rigs/SKILL.md +7 -5
  10. package/daemon/context-packs/help/manifest.yaml +1 -1
  11. package/daemon/context-packs/onboarding-width/manifest.yaml +1 -1
  12. package/daemon/context-packs/onboarding-width/public-what-you-can-do.md +64 -14
  13. package/daemon/context-packs/reference/getting-started.md +10 -6
  14. package/daemon/context-packs/reference/manifest.yaml +1 -1
  15. package/daemon/context-packs/reference/rig-spec.md +2 -2
  16. package/daemon/context-packs/reference/whats-new.md +151 -74
  17. package/daemon/context-packs/skills/agent-operated-software/manifest.yaml +1 -1
  18. package/daemon/context-packs/skills/agent-operated-workflows/manifest.yaml +1 -1
  19. package/daemon/context-packs/skills/applying-a-permission-policy/manifest.yaml +1 -1
  20. package/daemon/context-packs/skills/claude-compaction-restore/manifest.yaml +1 -1
  21. package/daemon/context-packs/skills/core/agent-starters/manifest.yaml +1 -1
  22. package/daemon/context-packs/skills/core/agent-startup-and-context-ingestion/manifest.yaml +1 -1
  23. package/daemon/context-packs/skills/core/cross-host-rig-commands/manifest.yaml +1 -1
  24. package/daemon/context-packs/skills/core/human-in-the-loop/manifest.yaml +1 -1
  25. package/daemon/context-packs/skills/core/messaging-the-human/manifest.yaml +1 -1
  26. package/daemon/context-packs/skills/core/openrig-architect/manifest.yaml +1 -1
  27. package/daemon/context-packs/skills/core/openrig-cmux/manifest.yaml +1 -1
  28. package/daemon/context-packs/skills/core/openrig-herdr/manifest.yaml +1 -1
  29. package/daemon/context-packs/skills/core/openrig-skills/SKILL.md +1 -1
  30. package/daemon/context-packs/skills/core/openrig-skills/manifest.yaml +3 -3
  31. package/daemon/context-packs/skills/core/openrig-software-factory/manifest.yaml +1 -1
  32. package/daemon/context-packs/skills/core/openrig-upgrade/manifest.yaml +1 -1
  33. package/daemon/context-packs/skills/core/rig-bundles-and-shareable-artifacts/manifest.yaml +1 -1
  34. package/daemon/context-packs/skills/core/rig-lifecycle/manifest.yaml +1 -1
  35. package/daemon/context-packs/skills/core/session-source-fork/manifest.yaml +1 -1
  36. package/daemon/context-packs/skills/core/specification-system/manifest.yaml +1 -1
  37. package/daemon/context-packs/skills/core/topology-mutation-and-seat-management/SKILL.md +2 -2
  38. package/daemon/context-packs/skills/core/topology-mutation-and-seat-management/manifest.yaml +1 -1
  39. package/daemon/context-packs/skills/core/watchdog/manifest.yaml +1 -1
  40. package/daemon/context-packs/skills/delegating-work/manifest.yaml +1 -1
  41. package/daemon/context-packs/skills/forming-an-openrig-mental-model/manifest.yaml +1 -1
  42. package/daemon/context-packs/skills/loading-addressable-markdown/manifest.yaml +1 -1
  43. package/daemon/context-packs/skills/mission-slice-sop/manifest.yaml +1 -1
  44. package/daemon/context-packs/skills/openrig-operating-model/manifest.yaml +1 -1
  45. package/daemon/context-packs/skills/openrig-user/SKILL.md +7 -1
  46. package/daemon/context-packs/skills/openrig-user/manifest.yaml +1 -1
  47. package/daemon/context-packs/skills/orienting-to-an-inherited-seat/manifest.yaml +1 -1
  48. package/daemon/context-packs/skills/pm/backlog-capture/manifest.yaml +1 -1
  49. package/daemon/context-packs/skills/pm/context-builder/manifest.yaml +1 -1
  50. package/daemon/context-packs/skills/pm/exec-summary/manifest.yaml +1 -1
  51. package/daemon/context-packs/skills/pm/office-hours/manifest.yaml +1 -1
  52. package/daemon/context-packs/skills/pm/plan-review/manifest.yaml +1 -1
  53. package/daemon/context-packs/skills/pm/requirements-writer/manifest.yaml +1 -1
  54. package/daemon/context-packs/skills/pm/ui-mockup/manifest.yaml +1 -1
  55. package/daemon/context-packs/skills/pods/development-team/manifest.yaml +1 -1
  56. package/daemon/context-packs/skills/pods/orchestration-team/manifest.yaml +1 -1
  57. package/daemon/context-packs/skills/pods/oversight-team/manifest.yaml +1 -1
  58. package/daemon/context-packs/skills/pods/review-team/manifest.yaml +1 -1
  59. package/daemon/context-packs/skills/process/agent-browser/manifest.yaml +1 -1
  60. package/daemon/context-packs/skills/process/context-engineering/manifest.yaml +1 -1
  61. package/daemon/context-packs/skills/process/dogfood/manifest.yaml +1 -1
  62. package/daemon/context-packs/skills/process/frontend-design/manifest.yaml +1 -1
  63. package/daemon/context-packs/skills/process/systematic-debugging/manifest.yaml +1 -1
  64. package/daemon/context-packs/skills/process/test-driven-development/manifest.yaml +1 -1
  65. package/daemon/context-packs/skills/process/verification-before-completion/manifest.yaml +1 -1
  66. package/daemon/context-packs/skills/queue-handoff/manifest.yaml +1 -1
  67. package/daemon/context-packs/skills/refocusing/manifest.yaml +1 -1
  68. package/daemon/context-packs/skills/refocusing/scripts/trace-to-root.py +6 -1
  69. package/daemon/context-packs/skills/retiring-and-inheriting-a-seat/manifest.yaml +1 -1
  70. package/daemon/context-packs/skills/seat-continuity-and-handover/manifest.yaml +1 -1
  71. package/daemon/context-packs/skills/session-compaction-and-restore/manifest.yaml +1 -1
  72. package/daemon/context-packs/skills/software-for-agents/manifest.yaml +1 -1
  73. package/daemon/context-packs/world-example/manifest.yaml +1 -1
  74. package/daemon/context-packs/world-public/manifest.yaml +1 -1
  75. package/daemon/dist/adapters/claude-code-adapter.d.ts +0 -14
  76. package/daemon/dist/adapters/claude-code-adapter.d.ts.map +1 -1
  77. package/daemon/dist/adapters/claude-code-adapter.js +43 -80
  78. package/daemon/dist/adapters/claude-code-adapter.js.map +1 -1
  79. package/daemon/dist/adapters/claude-resume-launch.d.ts +4 -0
  80. package/daemon/dist/adapters/claude-resume-launch.d.ts.map +1 -0
  81. package/daemon/dist/adapters/claude-resume-launch.js +13 -0
  82. package/daemon/dist/adapters/claude-resume-launch.js.map +1 -0
  83. package/daemon/dist/adapters/claude-resume.d.ts +2 -1
  84. package/daemon/dist/adapters/claude-resume.d.ts.map +1 -1
  85. package/daemon/dist/adapters/claude-resume.js +9 -4
  86. package/daemon/dist/adapters/claude-resume.js.map +1 -1
  87. package/daemon/dist/adapters/codex-runtime-adapter.d.ts +35 -0
  88. package/daemon/dist/adapters/codex-runtime-adapter.d.ts.map +1 -1
  89. package/daemon/dist/adapters/codex-runtime-adapter.js +94 -44
  90. package/daemon/dist/adapters/codex-runtime-adapter.js.map +1 -1
  91. package/daemon/dist/adapters/pi-runner.d.ts.map +1 -1
  92. package/daemon/dist/adapters/pi-runner.js +6 -3
  93. package/daemon/dist/adapters/pi-runner.js.map +1 -1
  94. package/daemon/dist/adapters/pi-runtime-adapter.js +1 -1
  95. package/daemon/dist/adapters/pi-runtime-adapter.js.map +1 -1
  96. package/daemon/dist/adapters/tmux-server.d.ts +15 -0
  97. package/daemon/dist/adapters/tmux-server.d.ts.map +1 -0
  98. package/daemon/dist/adapters/tmux-server.js +58 -0
  99. package/daemon/dist/adapters/tmux-server.js.map +1 -0
  100. package/daemon/dist/adapters/tmux.d.ts +26 -1
  101. package/daemon/dist/adapters/tmux.d.ts.map +1 -1
  102. package/daemon/dist/adapters/tmux.js +130 -7
  103. package/daemon/dist/adapters/tmux.js.map +1 -1
  104. package/daemon/dist/build-info.js +3 -3
  105. package/daemon/dist/db/all-migrations.d.ts.map +1 -1
  106. package/daemon/dist/db/all-migrations.js +6 -0
  107. package/daemon/dist/db/all-migrations.js.map +1 -1
  108. package/daemon/dist/db/migrations/099_queue_items_recovery_tags.d.ts +3 -0
  109. package/daemon/dist/db/migrations/099_queue_items_recovery_tags.d.ts.map +1 -0
  110. package/daemon/dist/db/migrations/099_queue_items_recovery_tags.js +19 -0
  111. package/daemon/dist/db/migrations/099_queue_items_recovery_tags.js.map +1 -0
  112. package/daemon/dist/db/migrations/101_claude_resume_rotation.d.ts +3 -0
  113. package/daemon/dist/db/migrations/101_claude_resume_rotation.d.ts.map +1 -0
  114. package/daemon/dist/db/migrations/101_claude_resume_rotation.js +19 -0
  115. package/daemon/dist/db/migrations/101_claude_resume_rotation.js.map +1 -0
  116. package/daemon/dist/db/migrations/103_session_stop_intent.d.ts +4 -0
  117. package/daemon/dist/db/migrations/103_session_stop_intent.d.ts.map +1 -0
  118. package/daemon/dist/db/migrations/103_session_stop_intent.js +9 -0
  119. package/daemon/dist/db/migrations/103_session_stop_intent.js.map +1 -0
  120. package/daemon/dist/domain/active-occupant.d.ts +14 -0
  121. package/daemon/dist/domain/active-occupant.d.ts.map +1 -1
  122. package/daemon/dist/domain/active-occupant.js +17 -0
  123. package/daemon/dist/domain/active-occupant.js.map +1 -1
  124. package/daemon/dist/domain/agent-activity-store.js +3 -0
  125. package/daemon/dist/domain/agent-activity-store.js.map +1 -1
  126. package/daemon/dist/domain/bootstrap-repository.js +1 -1
  127. package/daemon/dist/domain/bootstrap-repository.js.map +1 -1
  128. package/daemon/dist/domain/chat-repository.d.ts +3 -0
  129. package/daemon/dist/domain/chat-repository.d.ts.map +1 -1
  130. package/daemon/dist/domain/chat-repository.js +15 -4
  131. package/daemon/dist/domain/chat-repository.js.map +1 -1
  132. package/daemon/dist/domain/claude-managed-launch.d.ts +1 -1
  133. package/daemon/dist/domain/claude-managed-launch.d.ts.map +1 -1
  134. package/daemon/dist/domain/claude-managed-launch.js +3 -2
  135. package/daemon/dist/domain/claude-managed-launch.js.map +1 -1
  136. package/daemon/dist/domain/cmux-layout-service.d.ts.map +1 -1
  137. package/daemon/dist/domain/cmux-layout-service.js +2 -1
  138. package/daemon/dist/domain/cmux-layout-service.js.map +1 -1
  139. package/daemon/dist/domain/codex-git-add-dirs.js +1 -1
  140. package/daemon/dist/domain/codex-git-add-dirs.js.map +1 -1
  141. package/daemon/dist/domain/codex-network-default.d.ts +3 -0
  142. package/daemon/dist/domain/codex-network-default.d.ts.map +1 -1
  143. package/daemon/dist/domain/codex-network-default.js +1 -1
  144. package/daemon/dist/domain/codex-network-default.js.map +1 -1
  145. package/daemon/dist/domain/codex-pane-patterns.d.ts +2 -0
  146. package/daemon/dist/domain/codex-pane-patterns.d.ts.map +1 -0
  147. package/daemon/dist/domain/codex-pane-patterns.js +5 -0
  148. package/daemon/dist/domain/codex-pane-patterns.js.map +1 -0
  149. package/daemon/dist/domain/context-monitor.d.ts.map +1 -1
  150. package/daemon/dist/domain/context-monitor.js +46 -22
  151. package/daemon/dist/domain/context-monitor.js.map +1 -1
  152. package/daemon/dist/domain/context-usage-store.d.ts.map +1 -1
  153. package/daemon/dist/domain/context-usage-store.js +6 -1
  154. package/daemon/dist/domain/context-usage-store.js.map +1 -1
  155. package/daemon/dist/domain/crash-cart-conductor.d.ts.map +1 -1
  156. package/daemon/dist/domain/crash-cart-conductor.js +3 -1
  157. package/daemon/dist/domain/crash-cart-conductor.js.map +1 -1
  158. package/daemon/dist/domain/cwd-resolution.d.ts +8 -0
  159. package/daemon/dist/domain/cwd-resolution.d.ts.map +1 -1
  160. package/daemon/dist/domain/cwd-resolution.js +24 -3
  161. package/daemon/dist/domain/cwd-resolution.js.map +1 -1
  162. package/daemon/dist/domain/files/file-read.d.ts.map +1 -1
  163. package/daemon/dist/domain/files/file-read.js +11 -3
  164. package/daemon/dist/domain/files/file-read.js.map +1 -1
  165. package/daemon/dist/domain/gateway/connections-projection.d.ts +1 -1
  166. package/daemon/dist/domain/gateway/slack/channel-recovery.d.ts.map +1 -1
  167. package/daemon/dist/domain/gateway/slack/channel-recovery.js +4 -3
  168. package/daemon/dist/domain/gateway/slack/channel-recovery.js.map +1 -1
  169. package/daemon/dist/domain/gateway/slack/inbound.d.ts +21 -2
  170. package/daemon/dist/domain/gateway/slack/inbound.d.ts.map +1 -1
  171. package/daemon/dist/domain/gateway/slack/inbound.js +70 -11
  172. package/daemon/dist/domain/gateway/slack/inbound.js.map +1 -1
  173. package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts +4 -0
  174. package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts.map +1 -1
  175. package/daemon/dist/domain/gateway/slack/slack-delivery.js +1 -1
  176. package/daemon/dist/domain/gateway/slack/slack-delivery.js.map +1 -1
  177. package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts.map +1 -1
  178. package/daemon/dist/domain/gateway/slack/slack-subsystem.js +7 -4
  179. package/daemon/dist/domain/gateway/slack/slack-subsystem.js.map +1 -1
  180. package/daemon/dist/domain/history-query.d.ts.map +1 -1
  181. package/daemon/dist/domain/history-query.js +8 -6
  182. package/daemon/dist/domain/history-query.js.map +1 -1
  183. package/daemon/dist/domain/human-questions.d.ts +4 -1
  184. package/daemon/dist/domain/human-questions.d.ts.map +1 -1
  185. package/daemon/dist/domain/human-questions.js.map +1 -1
  186. package/daemon/dist/domain/install-engine.js +2 -2
  187. package/daemon/dist/domain/install-engine.js.map +1 -1
  188. package/daemon/dist/domain/native-process-lineage.d.ts +57 -4
  189. package/daemon/dist/domain/native-process-lineage.d.ts.map +1 -1
  190. package/daemon/dist/domain/native-process-lineage.js +277 -42
  191. package/daemon/dist/domain/native-process-lineage.js.map +1 -1
  192. package/daemon/dist/domain/node-cmux-service.d.ts.map +1 -1
  193. package/daemon/dist/domain/node-cmux-service.js +2 -1
  194. package/daemon/dist/domain/node-cmux-service.js.map +1 -1
  195. package/daemon/dist/domain/node-inventory.d.ts.map +1 -1
  196. package/daemon/dist/domain/node-inventory.js +71 -18
  197. package/daemon/dist/domain/node-inventory.js.map +1 -1
  198. package/daemon/dist/domain/node-launcher.d.ts.map +1 -1
  199. package/daemon/dist/domain/node-launcher.js +2 -1
  200. package/daemon/dist/domain/node-launcher.js.map +1 -1
  201. package/daemon/dist/domain/pane-cutover.d.ts +27 -0
  202. package/daemon/dist/domain/pane-cutover.d.ts.map +1 -0
  203. package/daemon/dist/domain/pane-cutover.js +37 -0
  204. package/daemon/dist/domain/pane-cutover.js.map +1 -0
  205. package/daemon/dist/domain/parent-session-env.d.ts +8 -0
  206. package/daemon/dist/domain/parent-session-env.d.ts.map +1 -0
  207. package/daemon/dist/domain/parent-session-env.js +37 -0
  208. package/daemon/dist/domain/parent-session-env.js.map +1 -0
  209. package/daemon/dist/domain/policies/parked-owner-consumer.d.ts +11 -0
  210. package/daemon/dist/domain/policies/parked-owner-consumer.d.ts.map +1 -1
  211. package/daemon/dist/domain/policies/parked-owner-consumer.js +81 -11
  212. package/daemon/dist/domain/policies/parked-owner-consumer.js.map +1 -1
  213. package/daemon/dist/domain/profile-resolver.d.ts.map +1 -1
  214. package/daemon/dist/domain/profile-resolver.js +2 -3
  215. package/daemon/dist/domain/profile-resolver.js.map +1 -1
  216. package/daemon/dist/domain/progress/progress-indexer.d.ts.map +1 -1
  217. package/daemon/dist/domain/progress/progress-indexer.js +19 -0
  218. package/daemon/dist/domain/progress/progress-indexer.js.map +1 -1
  219. package/daemon/dist/domain/proof/judgments.d.ts +10 -5
  220. package/daemon/dist/domain/proof/judgments.d.ts.map +1 -1
  221. package/daemon/dist/domain/proof/judgments.js +27 -19
  222. package/daemon/dist/domain/proof/judgments.js.map +1 -1
  223. package/daemon/dist/domain/provider/codex-limit-banner.d.ts +40 -0
  224. package/daemon/dist/domain/provider/codex-limit-banner.d.ts.map +1 -0
  225. package/daemon/dist/domain/provider/codex-limit-banner.js +168 -0
  226. package/daemon/dist/domain/provider/codex-limit-banner.js.map +1 -0
  227. package/daemon/dist/domain/provider/codex-usage-reader.d.ts +31 -0
  228. package/daemon/dist/domain/provider/codex-usage-reader.d.ts.map +1 -0
  229. package/daemon/dist/domain/provider/codex-usage-reader.js +115 -0
  230. package/daemon/dist/domain/provider/codex-usage-reader.js.map +1 -0
  231. package/daemon/dist/domain/provider/codex-usage-windows.d.ts +78 -0
  232. package/daemon/dist/domain/provider/codex-usage-windows.d.ts.map +1 -0
  233. package/daemon/dist/domain/provider/codex-usage-windows.js +259 -0
  234. package/daemon/dist/domain/provider/codex-usage-windows.js.map +1 -0
  235. package/daemon/dist/domain/provider/provider-service-impl.d.ts +10 -0
  236. package/daemon/dist/domain/provider/provider-service-impl.d.ts.map +1 -1
  237. package/daemon/dist/domain/provider/provider-service-impl.js +27 -1
  238. package/daemon/dist/domain/provider/provider-service-impl.js.map +1 -1
  239. package/daemon/dist/domain/provider/provider-service.d.ts +5 -0
  240. package/daemon/dist/domain/provider/provider-service.d.ts.map +1 -1
  241. package/daemon/dist/domain/provider/provider-types.d.ts +65 -0
  242. package/daemon/dist/domain/provider/provider-types.d.ts.map +1 -1
  243. package/daemon/dist/domain/ps-projection.d.ts +5 -2
  244. package/daemon/dist/domain/ps-projection.d.ts.map +1 -1
  245. package/daemon/dist/domain/ps-projection.js +29 -26
  246. package/daemon/dist/domain/ps-projection.js.map +1 -1
  247. package/daemon/dist/domain/queue-recovery.d.ts.map +1 -1
  248. package/daemon/dist/domain/queue-recovery.js +10 -3
  249. package/daemon/dist/domain/queue-recovery.js.map +1 -1
  250. package/daemon/dist/domain/queue-repository.d.ts +68 -13
  251. package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
  252. package/daemon/dist/domain/queue-repository.js +72 -43
  253. package/daemon/dist/domain/queue-repository.js.map +1 -1
  254. package/daemon/dist/domain/queue-stuck-sweep.d.ts.map +1 -1
  255. package/daemon/dist/domain/queue-stuck-sweep.js +26 -9
  256. package/daemon/dist/domain/queue-stuck-sweep.js.map +1 -1
  257. package/daemon/dist/domain/queue-wake-ladder.d.ts +17 -0
  258. package/daemon/dist/domain/queue-wake-ladder.d.ts.map +1 -1
  259. package/daemon/dist/domain/queue-wake-ladder.js +49 -3
  260. package/daemon/dist/domain/queue-wake-ladder.js.map +1 -1
  261. package/daemon/dist/domain/restore-check-service.d.ts +8 -0
  262. package/daemon/dist/domain/restore-check-service.d.ts.map +1 -1
  263. package/daemon/dist/domain/restore-check-service.js +12 -3
  264. package/daemon/dist/domain/restore-check-service.js.map +1 -1
  265. package/daemon/dist/domain/restore-guidance.d.ts +39 -0
  266. package/daemon/dist/domain/restore-guidance.d.ts.map +1 -0
  267. package/daemon/dist/domain/restore-guidance.js +163 -0
  268. package/daemon/dist/domain/restore-guidance.js.map +1 -0
  269. package/daemon/dist/domain/restore-orchestrator.d.ts +10 -1
  270. package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
  271. package/daemon/dist/domain/restore-orchestrator.js +192 -9
  272. package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
  273. package/daemon/dist/domain/restore-plan-preview.d.ts +4 -0
  274. package/daemon/dist/domain/restore-plan-preview.d.ts.map +1 -1
  275. package/daemon/dist/domain/restore-plan-preview.js +14 -3
  276. package/daemon/dist/domain/restore-plan-preview.js.map +1 -1
  277. package/daemon/dist/domain/rigspec-preflight.d.ts.map +1 -1
  278. package/daemon/dist/domain/rigspec-preflight.js +7 -6
  279. package/daemon/dist/domain/rigspec-preflight.js.map +1 -1
  280. package/daemon/dist/domain/runtime-adapter.d.ts +3 -0
  281. package/daemon/dist/domain/runtime-adapter.d.ts.map +1 -1
  282. package/daemon/dist/domain/runtime-adapter.js.map +1 -1
  283. package/daemon/dist/domain/scope/scope-view-projection.d.ts +4 -2
  284. package/daemon/dist/domain/scope/scope-view-projection.d.ts.map +1 -1
  285. package/daemon/dist/domain/scope/scope-view-projection.js +11 -11
  286. package/daemon/dist/domain/scope/scope-view-projection.js.map +1 -1
  287. package/daemon/dist/domain/seat-attention-reconciler.d.ts +5 -2
  288. package/daemon/dist/domain/seat-attention-reconciler.d.ts.map +1 -1
  289. package/daemon/dist/domain/seat-attention-reconciler.js +18 -4
  290. package/daemon/dist/domain/seat-attention-reconciler.js.map +1 -1
  291. package/daemon/dist/domain/seat-identity-reconciler.d.ts.map +1 -1
  292. package/daemon/dist/domain/seat-identity-reconciler.js +7 -2
  293. package/daemon/dist/domain/seat-identity-reconciler.js.map +1 -1
  294. package/daemon/dist/domain/seat-lifecycle-service.d.ts +117 -2
  295. package/daemon/dist/domain/seat-lifecycle-service.d.ts.map +1 -1
  296. package/daemon/dist/domain/seat-lifecycle-service.js +335 -74
  297. package/daemon/dist/domain/seat-lifecycle-service.js.map +1 -1
  298. package/daemon/dist/domain/seat-restart-in-place.d.ts +292 -0
  299. package/daemon/dist/domain/seat-restart-in-place.d.ts.map +1 -0
  300. package/daemon/dist/domain/seat-restart-in-place.js +895 -0
  301. package/daemon/dist/domain/seat-restart-in-place.js.map +1 -0
  302. package/daemon/dist/domain/seat-status-service.d.ts +2 -1
  303. package/daemon/dist/domain/seat-status-service.d.ts.map +1 -1
  304. package/daemon/dist/domain/seat-status-service.js +1 -0
  305. package/daemon/dist/domain/seat-status-service.js.map +1 -1
  306. package/daemon/dist/domain/seat-structural-activity-service.d.ts +9 -1
  307. package/daemon/dist/domain/seat-structural-activity-service.d.ts.map +1 -1
  308. package/daemon/dist/domain/seat-structural-activity-service.js +27 -4
  309. package/daemon/dist/domain/seat-structural-activity-service.js.map +1 -1
  310. package/daemon/dist/domain/seat-switch-client-service.d.ts.map +1 -1
  311. package/daemon/dist/domain/seat-switch-client-service.js +2 -1
  312. package/daemon/dist/domain/seat-switch-client-service.js.map +1 -1
  313. package/daemon/dist/domain/session-fingerprinter.d.ts.map +1 -1
  314. package/daemon/dist/domain/session-fingerprinter.js +9 -5
  315. package/daemon/dist/domain/session-fingerprinter.js.map +1 -1
  316. package/daemon/dist/domain/session-registry.d.ts +49 -0
  317. package/daemon/dist/domain/session-registry.d.ts.map +1 -1
  318. package/daemon/dist/domain/session-registry.js +92 -5
  319. package/daemon/dist/domain/session-registry.js.map +1 -1
  320. package/daemon/dist/domain/session-transport.d.ts +46 -4
  321. package/daemon/dist/domain/session-transport.d.ts.map +1 -1
  322. package/daemon/dist/domain/session-transport.js +492 -26
  323. package/daemon/dist/domain/session-transport.js.map +1 -1
  324. package/daemon/dist/domain/startup-orchestrator.d.ts +10 -0
  325. package/daemon/dist/domain/startup-orchestrator.d.ts.map +1 -1
  326. package/daemon/dist/domain/startup-orchestrator.js +41 -4
  327. package/daemon/dist/domain/startup-orchestrator.js.map +1 -1
  328. package/daemon/dist/domain/successor-session-launcher.d.ts +3 -7
  329. package/daemon/dist/domain/successor-session-launcher.d.ts.map +1 -1
  330. package/daemon/dist/domain/successor-session-launcher.js +6 -32
  331. package/daemon/dist/domain/successor-session-launcher.js.map +1 -1
  332. package/daemon/dist/domain/terminal/terminal-service.d.ts +5 -1
  333. package/daemon/dist/domain/terminal/terminal-service.d.ts.map +1 -1
  334. package/daemon/dist/domain/terminal/terminal-service.js +30 -4
  335. package/daemon/dist/domain/terminal/terminal-service.js.map +1 -1
  336. package/daemon/dist/domain/terminal/view-composer.d.ts +6 -0
  337. package/daemon/dist/domain/terminal/view-composer.d.ts.map +1 -1
  338. package/daemon/dist/domain/terminal/view-composer.js +2 -1
  339. package/daemon/dist/domain/terminal/view-composer.js.map +1 -1
  340. package/daemon/dist/domain/transcript-store.js +1 -1
  341. package/daemon/dist/domain/transcript-store.js.map +1 -1
  342. package/daemon/dist/domain/types.d.ts +38 -0
  343. package/daemon/dist/domain/types.d.ts.map +1 -1
  344. package/daemon/dist/domain/types.js +5 -0
  345. package/daemon/dist/domain/types.js.map +1 -1
  346. package/daemon/dist/domain/usage-series.d.ts.map +1 -1
  347. package/daemon/dist/domain/usage-series.js +9 -4
  348. package/daemon/dist/domain/usage-series.js.map +1 -1
  349. package/daemon/dist/domain/user-settings/settings-store.d.ts +1 -1
  350. package/daemon/dist/domain/user-settings/settings-store.d.ts.map +1 -1
  351. package/daemon/dist/domain/user-settings/settings-store.js +44 -0
  352. package/daemon/dist/domain/user-settings/settings-store.js.map +1 -1
  353. package/daemon/dist/domain/view-projector.d.ts +4 -0
  354. package/daemon/dist/domain/view-projector.d.ts.map +1 -1
  355. package/daemon/dist/domain/view-projector.js +19 -4
  356. package/daemon/dist/domain/view-projector.js.map +1 -1
  357. package/daemon/dist/domain/wake-resolver.js +2 -2
  358. package/daemon/dist/domain/wake-resolver.js.map +1 -1
  359. package/daemon/dist/domain/watchdog-jobs-repository.d.ts.map +1 -1
  360. package/daemon/dist/domain/watchdog-jobs-repository.js +19 -3
  361. package/daemon/dist/domain/watchdog-jobs-repository.js.map +1 -1
  362. package/daemon/dist/domain/whoami-service.d.ts +4 -1
  363. package/daemon/dist/domain/whoami-service.d.ts.map +1 -1
  364. package/daemon/dist/domain/whoami-service.js +25 -19
  365. package/daemon/dist/domain/whoami-service.js.map +1 -1
  366. package/daemon/dist/domain/workflow-runtime.d.ts +1 -1
  367. package/daemon/dist/domain/workspace/project-read.d.ts +2 -0
  368. package/daemon/dist/domain/workspace/project-read.d.ts.map +1 -1
  369. package/daemon/dist/domain/workspace/project-read.js +19 -8
  370. package/daemon/dist/domain/workspace/project-read.js.map +1 -1
  371. package/daemon/dist/index.d.ts.map +1 -1
  372. package/daemon/dist/index.js +6 -0
  373. package/daemon/dist/index.js.map +1 -1
  374. package/daemon/dist/routes/activity.d.ts.map +1 -1
  375. package/daemon/dist/routes/activity.js +33 -1
  376. package/daemon/dist/routes/activity.js.map +1 -1
  377. package/daemon/dist/routes/chat.d.ts.map +1 -1
  378. package/daemon/dist/routes/chat.js +29 -4
  379. package/daemon/dist/routes/chat.js.map +1 -1
  380. package/daemon/dist/routes/projects.d.ts.map +1 -1
  381. package/daemon/dist/routes/projects.js +5 -1
  382. package/daemon/dist/routes/projects.js.map +1 -1
  383. package/daemon/dist/routes/proof.d.ts.map +1 -1
  384. package/daemon/dist/routes/proof.js +99 -10
  385. package/daemon/dist/routes/proof.js.map +1 -1
  386. package/daemon/dist/routes/provider.d.ts.map +1 -1
  387. package/daemon/dist/routes/provider.js +31 -0
  388. package/daemon/dist/routes/provider.js.map +1 -1
  389. package/daemon/dist/routes/query-limit.d.ts +19 -0
  390. package/daemon/dist/routes/query-limit.d.ts.map +1 -0
  391. package/daemon/dist/routes/query-limit.js +24 -0
  392. package/daemon/dist/routes/query-limit.js.map +1 -0
  393. package/daemon/dist/routes/queue.d.ts.map +1 -1
  394. package/daemon/dist/routes/queue.js +35 -12
  395. package/daemon/dist/routes/queue.js.map +1 -1
  396. package/daemon/dist/routes/rig-cmux.js +8 -10
  397. package/daemon/dist/routes/rig-cmux.js.map +1 -1
  398. package/daemon/dist/routes/rigs.d.ts.map +1 -1
  399. package/daemon/dist/routes/rigs.js +11 -7
  400. package/daemon/dist/routes/rigs.js.map +1 -1
  401. package/daemon/dist/routes/scopes.d.ts.map +1 -1
  402. package/daemon/dist/routes/scopes.js +5 -3
  403. package/daemon/dist/routes/scopes.js.map +1 -1
  404. package/daemon/dist/routes/seat.d.ts.map +1 -1
  405. package/daemon/dist/routes/seat.js +32 -0
  406. package/daemon/dist/routes/seat.js.map +1 -1
  407. package/daemon/dist/routes/stream.d.ts.map +1 -1
  408. package/daemon/dist/routes/stream.js +5 -1
  409. package/daemon/dist/routes/stream.js.map +1 -1
  410. package/daemon/dist/routes/up.d.ts.map +1 -1
  411. package/daemon/dist/routes/up.js +2 -1
  412. package/daemon/dist/routes/up.js.map +1 -1
  413. package/daemon/dist/routes/views.d.ts.map +1 -1
  414. package/daemon/dist/routes/views.js +7 -1
  415. package/daemon/dist/routes/views.js.map +1 -1
  416. package/daemon/dist/startup.d.ts.map +1 -1
  417. package/daemon/dist/startup.js +30 -1
  418. package/daemon/dist/startup.js.map +1 -1
  419. package/daemon/docs/reference/agent-startup-guide.md +19 -0
  420. package/daemon/docs/reference/getting-started.md +10 -6
  421. package/daemon/docs/reference/keeping-a-watch.md +156 -0
  422. package/daemon/docs/reference/rig-spec.md +2 -2
  423. package/daemon/docs/reference/sdlc-conventions.md +11 -1
  424. package/daemon/docs/reference/slack-app-setup.md +12 -4
  425. package/daemon/docs/reference/whats-new.md +151 -74
  426. package/daemon/specs/agents/development/qa/guidance/role.md +9 -0
  427. package/daemon/specs/agents/shared/skills/core/openrig-skills/SKILL.md +1 -1
  428. package/daemon/specs/agents/shared/skills/core/topology-mutation-and-seat-management/SKILL.md +2 -2
  429. package/daemon/specs/rigs/launch/factory/CULTURE.md +13 -0
  430. package/daemon/specs/rigs/launch/factory/guidance/lead-first-move.md +18 -5
  431. package/daemon/specs/rigs/launch/kernel/agents/operator/agent/guidance/role.md +20 -1
  432. package/daemon/specs/rigs/launch/starter/CULTURE.md +13 -0
  433. package/daemon/specs/rigs/launch/starter/guidance/lead-first-move.md +18 -5
  434. package/dist/build-info.js +3 -3
  435. package/dist/cli-error.d.ts.map +1 -1
  436. package/dist/cli-error.js +2 -0
  437. package/dist/cli-error.js.map +1 -1
  438. package/dist/commands/add.d.ts.map +1 -1
  439. package/dist/commands/add.js +55 -4
  440. package/dist/commands/add.js.map +1 -1
  441. package/dist/commands/capture.d.ts +9 -0
  442. package/dist/commands/capture.d.ts.map +1 -1
  443. package/dist/commands/capture.js +93 -41
  444. package/dist/commands/capture.js.map +1 -1
  445. package/dist/commands/config.d.ts.map +1 -1
  446. package/dist/commands/config.js +6 -1
  447. package/dist/commands/config.js.map +1 -1
  448. package/dist/commands/daemon.d.ts.map +1 -1
  449. package/dist/commands/daemon.js +44 -3
  450. package/dist/commands/daemon.js.map +1 -1
  451. package/dist/commands/down.d.ts.map +1 -1
  452. package/dist/commands/down.js +1 -50
  453. package/dist/commands/down.js.map +1 -1
  454. package/dist/commands/env.d.ts.map +1 -1
  455. package/dist/commands/env.js +6 -0
  456. package/dist/commands/env.js.map +1 -1
  457. package/dist/commands/heartbeat.js +2 -2
  458. package/dist/commands/heartbeat.js.map +1 -1
  459. package/dist/commands/proof.d.ts.map +1 -1
  460. package/dist/commands/proof.js +11 -5
  461. package/dist/commands/proof.js.map +1 -1
  462. package/dist/commands/provider.d.ts.map +1 -1
  463. package/dist/commands/provider.js +72 -0
  464. package/dist/commands/provider.js.map +1 -1
  465. package/dist/commands/ps.d.ts +7 -0
  466. package/dist/commands/ps.d.ts.map +1 -1
  467. package/dist/commands/ps.js +20 -6
  468. package/dist/commands/ps.js.map +1 -1
  469. package/dist/commands/queue.d.ts +48 -5
  470. package/dist/commands/queue.d.ts.map +1 -1
  471. package/dist/commands/queue.js +215 -45
  472. package/dist/commands/queue.js.map +1 -1
  473. package/dist/commands/scope.d.ts.map +1 -1
  474. package/dist/commands/scope.js +57 -36
  475. package/dist/commands/scope.js.map +1 -1
  476. package/dist/commands/seat.d.ts.map +1 -1
  477. package/dist/commands/seat.js +167 -10
  478. package/dist/commands/seat.js.map +1 -1
  479. package/dist/commands/setup.d.ts +6 -0
  480. package/dist/commands/setup.d.ts.map +1 -1
  481. package/dist/commands/setup.js +100 -44
  482. package/dist/commands/setup.js.map +1 -1
  483. package/dist/commands/start.d.ts.map +1 -1
  484. package/dist/commands/start.js +9 -4
  485. package/dist/commands/start.js.map +1 -1
  486. package/dist/commands/up.d.ts.map +1 -1
  487. package/dist/commands/up.js +10 -0
  488. package/dist/commands/up.js.map +1 -1
  489. package/dist/commands/usage.d.ts.map +1 -1
  490. package/dist/commands/usage.js +10 -5
  491. package/dist/commands/usage.js.map +1 -1
  492. package/dist/commands/workflow.d.ts.map +1 -1
  493. package/dist/commands/workflow.js +8 -8
  494. package/dist/commands/workflow.js.map +1 -1
  495. package/dist/config-store.d.ts +8 -1
  496. package/dist/config-store.d.ts.map +1 -1
  497. package/dist/config-store.js +57 -0
  498. package/dist/config-store.js.map +1 -1
  499. package/dist/daemon-lifecycle.d.ts +4 -0
  500. package/dist/daemon-lifecycle.d.ts.map +1 -1
  501. package/dist/daemon-lifecycle.js +16 -4
  502. package/dist/daemon-lifecycle.js.map +1 -1
  503. package/dist/lib/scope/capability-delta.d.ts.map +1 -1
  504. package/dist/lib/scope/capability-delta.js +8 -0
  505. package/dist/lib/scope/capability-delta.js.map +1 -1
  506. package/dist/lib/scope/mission-composition.d.ts +5 -1
  507. package/dist/lib/scope/mission-composition.d.ts.map +1 -1
  508. package/dist/lib/scope/mission-composition.js +61 -1
  509. package/dist/lib/scope/mission-composition.js.map +1 -1
  510. package/dist/lib/scope/progress-edit.js +1 -1
  511. package/dist/lib/scope/progress-edit.js.map +1 -1
  512. package/dist/lib/scope/scope-fs.d.ts +2 -1
  513. package/dist/lib/scope/scope-fs.d.ts.map +1 -1
  514. package/dist/lib/scope/scope-fs.js +7 -7
  515. package/dist/lib/scope/scope-fs.js.map +1 -1
  516. package/dist/lib/scope/slice-claims.d.ts +8 -0
  517. package/dist/lib/scope/slice-claims.d.ts.map +1 -0
  518. package/dist/lib/scope/slice-claims.js +88 -0
  519. package/dist/lib/scope/slice-claims.js.map +1 -0
  520. package/dist/queue-receipt.d.ts +41 -0
  521. package/dist/queue-receipt.d.ts.map +1 -0
  522. package/dist/queue-receipt.js +272 -0
  523. package/dist/queue-receipt.js.map +1 -0
  524. package/dist/rig-handle.d.ts +45 -0
  525. package/dist/rig-handle.d.ts.map +1 -0
  526. package/dist/rig-handle.js +51 -0
  527. package/dist/rig-handle.js.map +1 -0
  528. package/dist/system-preflight.d.ts +5 -0
  529. package/dist/system-preflight.d.ts.map +1 -1
  530. package/dist/system-preflight.js +8 -0
  531. package/dist/system-preflight.js.map +1 -1
  532. package/dist/terminal-window.d.ts.map +1 -1
  533. package/dist/terminal-window.js +3 -1
  534. package/dist/terminal-window.js.map +1 -1
  535. package/package.json +1 -1
  536. package/tui/dist/config/config-model.d.ts.map +1 -1
  537. package/tui/dist/config/config-model.js +3 -0
  538. package/tui/dist/config/config-model.js.map +1 -1
  539. package/tui/dist/daemon-client.d.ts +3 -0
  540. package/tui/dist/daemon-client.d.ts.map +1 -1
  541. package/tui/dist/daemon-client.js +4 -1
  542. package/tui/dist/daemon-client.js.map +1 -1
  543. package/tui/dist/detail.d.ts.map +1 -1
  544. package/tui/dist/detail.js +6 -1
  545. package/tui/dist/detail.js.map +1 -1
  546. package/tui/dist/hydrate.d.ts.map +1 -1
  547. package/tui/dist/hydrate.js +30 -21
  548. package/tui/dist/hydrate.js.map +1 -1
  549. package/tui/dist/input.d.ts.map +1 -1
  550. package/tui/dist/input.js +15 -4
  551. package/tui/dist/input.js.map +1 -1
  552. package/tui/dist/local-reading.js +2 -2
  553. package/tui/dist/local-reading.js.map +1 -1
  554. package/tui/dist/pulse/pulse-model.d.ts +1 -0
  555. package/tui/dist/pulse/pulse-model.d.ts.map +1 -1
  556. package/tui/dist/pulse/pulse-model.js +7 -3
  557. package/tui/dist/pulse/pulse-model.js.map +1 -1
  558. package/tui/dist/pulse/render-pulse.d.ts.map +1 -1
  559. package/tui/dist/pulse/render-pulse.js +3 -1
  560. package/tui/dist/pulse/render-pulse.js.map +1 -1
  561. package/tui/dist/reading.d.ts.map +1 -1
  562. package/tui/dist/reading.js +14 -3
  563. package/tui/dist/reading.js.map +1 -1
  564. package/tui/dist/render.d.ts.map +1 -1
  565. package/tui/dist/render.js +26 -11
  566. package/tui/dist/render.js.map +1 -1
  567. package/tui/dist/types.d.ts +3 -0
  568. package/tui/dist/types.d.ts.map +1 -1
  569. package/ui/dist/assets/{index-D5n8EwkG.js → index-DhmxKuZf.js} +159 -159
  570. 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
- /⏵⏵ accept edits/, // Claude Code edit-accept bar
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 || !/^(?:⏵⏵ (?:accept edits|bypass permissions) on\b|⏸ plan mode on\b|\? for shortcuts$)/.test(lines[bar].trim()))
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
- /^(?:⏵⏵ (?:accept edits|bypass permissions|auto mode) on\b|⏸ plan mode on\b|\? for shortcuts$)/.test(claudeComposer.bar) &&
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
- if (idleStatusBarLine && (!idleStatusBarLine.includes("⏵⏵ accept edits") || !midWorkEvidence)) {
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 classification = classifyPaneActivity(paneContent ?? "", { timerlessStatusIsLive: input.timerlessStatusIsLive === true });
444
- return observeProbe(captureSlot(paneContent, capturedAt, captureSeq), {
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
- return await guard.operation(sessionName, () => this.sendUnguarded(sessionName, text, opts), async (target) => {
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
- if (waitForIdleMs !== undefined) {
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
- const snippet = text.substring(0, Math.min(text.length, 40));
1383
- const preCount = countOccurrences(preVerifyContent ?? "", snippet);
1384
- const postCount = countOccurrences(content ?? "", snippet);
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 ${input.timeoutMs}ms wait ended (${count}). No text was sent.`
1429
- : `Target activity could not be determined: no observation completed within the ${input.timeoutMs}ms wait. No text was sent.`,
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 ${input.timeoutMs}ms wait ended (${count}). No text was sent.`,
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;