@phnx-labs/agents-cli 1.22.46 → 1.22.49

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 (348) hide show
  1. package/CHANGELOG.md +663 -0
  2. package/LICENSE +102 -182
  3. package/README.md +96 -49
  4. package/dist/bootstrap.js +4 -2
  5. package/dist/cli/command-registry.d.ts +2 -1
  6. package/dist/cli/command-registry.js +6 -2
  7. package/dist/commands/accounts.d.ts +58 -0
  8. package/dist/commands/accounts.js +243 -25
  9. package/dist/commands/artifacts.js +7 -6
  10. package/dist/commands/attach.js +1 -1
  11. package/dist/commands/auth.js +7 -5
  12. package/dist/commands/browser-sessions-picker.js +9 -7
  13. package/dist/commands/browser.js +711 -62
  14. package/dist/commands/config.d.ts +0 -1
  15. package/dist/commands/config.js +26 -24
  16. package/dist/commands/cost.d.ts +1 -1
  17. package/dist/commands/daemon.d.ts +21 -0
  18. package/dist/commands/daemon.js +147 -12
  19. package/dist/commands/detach.d.ts +13 -0
  20. package/dist/commands/detach.js +11 -8
  21. package/dist/commands/exec.js +107 -53
  22. package/dist/commands/feed-watch.d.ts +2 -0
  23. package/dist/commands/feed-watch.js +28 -0
  24. package/dist/commands/feed.js +27 -0
  25. package/dist/commands/feedback.js +3 -19
  26. package/dist/commands/focus.d.ts +14 -0
  27. package/dist/commands/focus.js +48 -5
  28. package/dist/commands/go.d.ts +3 -0
  29. package/dist/commands/go.js +16 -3
  30. package/dist/commands/insights.d.ts +1 -1
  31. package/dist/commands/insights.js +2 -2
  32. package/dist/commands/inspect.js +2 -2
  33. package/dist/commands/lease.d.ts +1 -1
  34. package/dist/commands/lease.js +7 -6
  35. package/dist/commands/mailboxes.js +4 -3
  36. package/dist/commands/menubar.js +11 -4
  37. package/dist/commands/projects.d.ts +16 -1
  38. package/dist/commands/projects.js +58 -64
  39. package/dist/commands/resume.js +1 -1
  40. package/dist/commands/route.js +4 -4
  41. package/dist/commands/routines.js +4 -3
  42. package/dist/commands/run-broadcast.d.ts +1 -1
  43. package/dist/commands/run-broadcast.js +1 -1
  44. package/dist/commands/sessions-picker-factory.d.ts +2 -1
  45. package/dist/commands/sessions-picker-factory.js +1 -1
  46. package/dist/commands/sessions-picker.d.ts +22 -0
  47. package/dist/commands/sessions-picker.js +48 -9
  48. package/dist/commands/sessions-stop.d.ts +24 -0
  49. package/dist/commands/sessions-stop.js +66 -0
  50. package/dist/commands/sessions-trace.d.ts +82 -0
  51. package/dist/commands/sessions-trace.js +387 -0
  52. package/dist/commands/sessions.js +5 -0
  53. package/dist/commands/setup-secrets.js +23 -0
  54. package/dist/commands/share.d.ts +20 -5
  55. package/dist/commands/share.js +152 -52
  56. package/dist/commands/ssh.d.ts +15 -0
  57. package/dist/commands/ssh.js +243 -18
  58. package/dist/commands/teams.js +34 -2
  59. package/dist/commands/traces.d.ts +2 -0
  60. package/dist/commands/traces.js +184 -0
  61. package/dist/commands/view.d.ts +2 -0
  62. package/dist/commands/view.js +32 -62
  63. package/dist/commands/watchdog.js +9 -14
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +5 -0
  66. package/dist/lib/account-capabilities.d.ts +4 -0
  67. package/dist/lib/account-capabilities.js +13 -9
  68. package/dist/lib/account-registry.d.ts +2 -0
  69. package/dist/lib/account-registry.js +30 -2
  70. package/dist/lib/accounting/rotate.d.ts +9 -0
  71. package/dist/lib/accounting/rotate.js +12 -2
  72. package/dist/lib/accounting/usage.d.ts +84 -6
  73. package/dist/lib/accounting/usage.js +293 -75
  74. package/dist/lib/agent-modes.js +29 -1
  75. package/dist/lib/agent-spec/agents.d.ts +4 -3
  76. package/dist/lib/agent-spec/agents.js +25 -15
  77. package/dist/lib/analytics/mix-commands.d.ts +1 -1
  78. package/dist/lib/analytics/mix-commands.js +2 -2
  79. package/dist/lib/auth-health.d.ts +13 -3
  80. package/dist/lib/auth-health.js +50 -8
  81. package/dist/lib/browser/devices.d.ts +19 -0
  82. package/dist/lib/browser/devices.js +23 -0
  83. package/dist/lib/browser/drivers/ssh.d.ts +16 -2
  84. package/dist/lib/browser/drivers/ssh.js +36 -9
  85. package/dist/lib/browser/ipc.d.ts +1 -1
  86. package/dist/lib/browser/ipc.js +40 -9
  87. package/dist/lib/browser/profiles.d.ts +104 -65
  88. package/dist/lib/browser/profiles.js +277 -165
  89. package/dist/lib/browser/registry.d.ts +39 -0
  90. package/dist/lib/browser/registry.js +149 -0
  91. package/dist/lib/browser/remote-control.d.ts +28 -2
  92. package/dist/lib/browser/remote-control.js +35 -3
  93. package/dist/lib/browser/resolve-target.d.ts +68 -0
  94. package/dist/lib/browser/resolve-target.js +250 -0
  95. package/dist/lib/browser/runtime-state.d.ts +44 -11
  96. package/dist/lib/browser/runtime-state.js +98 -15
  97. package/dist/lib/browser/service.d.ts +65 -9
  98. package/dist/lib/browser/service.js +196 -61
  99. package/dist/lib/browser/sessions-list.d.ts +1 -3
  100. package/dist/lib/browser/sessions-list.js +3 -16
  101. package/dist/lib/browser/task-index.d.ts +70 -0
  102. package/dist/lib/browser/task-index.js +180 -0
  103. package/dist/lib/browser/types.d.ts +12 -1
  104. package/dist/lib/byok-usage.js +2 -2
  105. package/dist/lib/claude-statusline.d.ts +29 -0
  106. package/dist/lib/claude-statusline.js +139 -0
  107. package/dist/lib/cloud/host.d.ts +1 -1
  108. package/dist/lib/cloud/host.js +1 -1
  109. package/dist/lib/cloud/rush.js +1 -1
  110. package/dist/lib/codex-policy.d.ts +11 -1
  111. package/dist/lib/codex-policy.js +20 -5
  112. package/dist/lib/computer/computer-rpc.js +2 -2
  113. package/dist/lib/computer/download.d.ts +15 -15
  114. package/dist/lib/computer/download.js +35 -98
  115. package/dist/lib/computer/ssh-tunnel.d.ts +0 -3
  116. package/dist/lib/computer/ssh-tunnel.js +12 -22
  117. package/dist/lib/config-keys.d.ts +6 -11
  118. package/dist/lib/config-keys.js +18 -19
  119. package/dist/lib/crabbox/cli.d.ts +1 -1
  120. package/dist/lib/crabbox/cli.js +2 -2
  121. package/dist/lib/crabbox/lease.d.ts +9 -0
  122. package/dist/lib/crabbox/lease.js +70 -15
  123. package/dist/lib/crabbox/runtimes.js +1 -1
  124. package/dist/lib/daemon/account-state-daemon-service.d.ts +16 -0
  125. package/dist/lib/daemon/account-state-daemon-service.js +26 -0
  126. package/dist/lib/daemon/browser-ipc-service.d.ts +20 -0
  127. package/dist/lib/daemon/browser-ipc-service.js +46 -0
  128. package/dist/lib/daemon/daemon.d.ts +3 -0
  129. package/dist/lib/daemon/daemon.js +212 -444
  130. package/dist/lib/daemon/device-probe-service.d.ts +26 -0
  131. package/dist/lib/daemon/device-probe-service.js +47 -0
  132. package/dist/lib/daemon/keychain-reap-service.d.ts +17 -0
  133. package/dist/lib/daemon/keychain-reap-service.js +32 -0
  134. package/dist/lib/daemon/monitor-engine-service.d.ts +22 -0
  135. package/dist/lib/daemon/monitor-engine-service.js +29 -0
  136. package/dist/lib/daemon/runner.js +15 -2
  137. package/dist/lib/daemon/secrets-broker-service.d.ts +20 -0
  138. package/dist/lib/daemon/secrets-broker-service.js +66 -0
  139. package/dist/lib/daemon/self-heal-service.d.ts +27 -0
  140. package/dist/lib/daemon/self-heal-service.js +48 -0
  141. package/dist/lib/daemon/service.d.ts +102 -0
  142. package/dist/lib/daemon/service.js +98 -0
  143. package/dist/lib/daemon/session-index-service.d.ts +22 -0
  144. package/dist/lib/daemon/session-index-service.js +40 -0
  145. package/dist/lib/daemon/state-dir-check-service.d.ts +43 -0
  146. package/dist/lib/daemon/state-dir-check-service.js +63 -0
  147. package/dist/lib/daemon/supervisor.d.ts +87 -0
  148. package/dist/lib/daemon/supervisor.js +298 -0
  149. package/dist/lib/daemon/watchdog-service.d.ts +19 -0
  150. package/dist/lib/daemon/watchdog-service.js +40 -0
  151. package/dist/lib/daemon-health.d.ts +15 -0
  152. package/dist/lib/daemon-health.js +29 -5
  153. package/dist/lib/daemon-services.d.ts +8 -1
  154. package/dist/lib/daemon-services.js +55 -0
  155. package/dist/lib/daemon-ticks.d.ts +13 -2
  156. package/dist/lib/daemon-ticks.js +22 -20
  157. package/dist/lib/device-config.d.ts +25 -10
  158. package/dist/lib/device-config.js +90 -39
  159. package/dist/lib/devices/config-migration.d.ts +7 -0
  160. package/dist/lib/devices/config-migration.js +67 -3
  161. package/dist/lib/devices/health.d.ts +27 -2
  162. package/dist/lib/devices/health.js +39 -9
  163. package/dist/lib/devices/interactive-host.d.ts +15 -0
  164. package/dist/lib/devices/interactive-host.js +53 -0
  165. package/dist/lib/devices/registry.d.ts +61 -4
  166. package/dist/lib/devices/registry.js +94 -38
  167. package/dist/lib/devices/stats-cache.d.ts +26 -0
  168. package/dist/lib/devices/stats-cache.js +59 -7
  169. package/dist/lib/devices/worker-pick.d.ts +48 -0
  170. package/dist/lib/devices/worker-pick.js +113 -0
  171. package/dist/lib/exec.d.ts +70 -14
  172. package/dist/lib/exec.js +110 -38
  173. package/dist/lib/feed/answer.d.ts +29 -0
  174. package/dist/lib/feed/answer.js +145 -0
  175. package/dist/lib/feed/attention.d.ts +83 -0
  176. package/dist/lib/feed/attention.js +0 -0
  177. package/dist/lib/feed/feed.d.ts +109 -0
  178. package/dist/lib/feed/feed.js +154 -3
  179. package/dist/lib/feed/pr-status.d.ts +14 -0
  180. package/dist/lib/feed/pr-status.js +47 -0
  181. package/dist/lib/feed/watch.d.ts +59 -0
  182. package/dist/lib/feed/watch.js +180 -0
  183. package/dist/lib/fleet/capture.js +6 -0
  184. package/dist/lib/fleet/remote-login.js +8 -3
  185. package/dist/lib/fleet/types.d.ts +17 -0
  186. package/dist/lib/harness/adapter.d.ts +11 -0
  187. package/dist/lib/harness/adapters/claude.js +38 -24
  188. package/dist/lib/harness/adapters/codex.js +2 -4
  189. package/dist/lib/harness/adapters/cursor.js +8 -11
  190. package/dist/lib/helper-download.d.ts +108 -0
  191. package/dist/lib/helper-download.js +194 -0
  192. package/dist/lib/helper-versions.d.ts +52 -0
  193. package/dist/lib/helper-versions.js +58 -0
  194. package/dist/lib/hooks/cache.js +19 -0
  195. package/dist/lib/hooks/install.js +2 -2
  196. package/dist/lib/hooks/match.d.ts +3 -0
  197. package/dist/lib/hooks/match.js +25 -0
  198. package/dist/lib/hosts/credentials.d.ts +1 -1
  199. package/dist/lib/hosts/credentials.js +1 -1
  200. package/dist/lib/hosts/dispatch.d.ts +7 -1
  201. package/dist/lib/hosts/dispatch.js +24 -5
  202. package/dist/lib/hosts/passthrough.d.ts +8 -15
  203. package/dist/lib/hosts/passthrough.js +35 -16
  204. package/dist/lib/hosts/providers/devices.d.ts +1 -1
  205. package/dist/lib/hosts/providers/devices.js +1 -1
  206. package/dist/lib/hosts/reconnect.d.ts +179 -21
  207. package/dist/lib/hosts/reconnect.js +266 -45
  208. package/dist/lib/hosts/registry.js +7 -0
  209. package/dist/lib/installations/migrate.d.ts +4 -5
  210. package/dist/lib/installations/migrate.js +6 -7
  211. package/dist/lib/installations/shims.d.ts +1 -1
  212. package/dist/lib/installations/shims.js +23 -13
  213. package/dist/lib/installations/versions.js +25 -2
  214. package/dist/lib/linear-autoclose.d.ts +1 -1
  215. package/dist/lib/linear-autoclose.js +1 -1
  216. package/dist/lib/loop.d.ts +2 -2
  217. package/dist/lib/loop.js +1 -1
  218. package/dist/lib/menubar/download-menubar.d.ts +47 -0
  219. package/dist/lib/menubar/download-menubar.js +60 -0
  220. package/dist/lib/menubar/install-menubar.d.ts +44 -7
  221. package/dist/lib/menubar/install-menubar.js +105 -23
  222. package/dist/lib/menubar/notify-desktop.d.ts +3 -3
  223. package/dist/lib/menubar/notify-desktop.js +6 -6
  224. package/dist/lib/model-tiers.d.ts +1 -1
  225. package/dist/lib/open-url.d.ts +60 -2
  226. package/dist/lib/open-url.js +167 -12
  227. package/dist/lib/project-probe.d.ts +1 -1
  228. package/dist/lib/project-probe.js +1 -1
  229. package/dist/lib/project-pull.d.ts +1 -1
  230. package/dist/lib/project-pull.js +2 -2
  231. package/dist/lib/projects.d.ts +1 -1
  232. package/dist/lib/projects.js +1 -1
  233. package/dist/lib/refresh-coordinator.d.ts +1 -1
  234. package/dist/lib/refresh-coordinator.js +1 -1
  235. package/dist/lib/sandbox.js +6 -7
  236. package/dist/lib/secrets/download-keychain.d.ts +47 -0
  237. package/dist/lib/secrets/download-keychain.js +70 -0
  238. package/dist/lib/secrets/install-helper.d.ts +20 -1
  239. package/dist/lib/secrets/install-helper.js +44 -3
  240. package/dist/lib/session/active.d.ts +67 -0
  241. package/dist/lib/session/active.js +86 -2
  242. package/dist/lib/session/db.d.ts +11 -1
  243. package/dist/lib/session/db.js +142 -8
  244. package/dist/lib/session/discover.d.ts +23 -1
  245. package/dist/lib/session/discover.js +44 -10
  246. package/dist/lib/session/highlights.d.ts +49 -1
  247. package/dist/lib/session/highlights.js +95 -0
  248. package/dist/lib/session/host-link.d.ts +16 -2
  249. package/dist/lib/session/host-link.js +7 -1
  250. package/dist/lib/session/parse.js +33 -1
  251. package/dist/lib/session/prompt.d.ts +35 -0
  252. package/dist/lib/session/prompt.js +56 -0
  253. package/dist/lib/session/remote/remote-list.d.ts +9 -0
  254. package/dist/lib/session/remote/remote-list.js +19 -2
  255. package/dist/lib/session/remote/watch.d.ts +1 -1
  256. package/dist/lib/session/remote/watch.js +17 -3
  257. package/dist/lib/session/render.js +7 -3
  258. package/dist/lib/session/session-cache.d.ts +53 -0
  259. package/dist/lib/session/session-cache.js +93 -1
  260. package/dist/lib/session/state.js +1 -1
  261. package/dist/lib/session/tool-calls.d.ts +2 -0
  262. package/dist/lib/session/tool-calls.js +1 -1
  263. package/dist/lib/session/trajectory-compare.d.ts +47 -0
  264. package/dist/lib/session/trajectory-compare.js +116 -0
  265. package/dist/lib/session/trajectory-html.d.ts +19 -0
  266. package/dist/lib/session/trajectory-html.js +770 -0
  267. package/dist/lib/session/trajectory-lineage.d.ts +92 -0
  268. package/dist/lib/session/trajectory-lineage.js +195 -0
  269. package/dist/lib/session/trajectory-text.d.ts +37 -0
  270. package/dist/lib/session/trajectory-text.js +284 -0
  271. package/dist/lib/session/trajectory.d.ts +102 -0
  272. package/dist/lib/session/trajectory.js +384 -0
  273. package/dist/lib/session/types.d.ts +15 -0
  274. package/dist/lib/sha256-asset.d.ts +26 -0
  275. package/dist/lib/sha256-asset.js +41 -0
  276. package/dist/lib/share/backend.d.ts +95 -0
  277. package/dist/lib/share/backend.js +139 -0
  278. package/dist/lib/share/delete.d.ts +6 -2
  279. package/dist/lib/share/delete.js +8 -9
  280. package/dist/lib/share/html.d.ts +22 -0
  281. package/dist/lib/share/html.js +88 -0
  282. package/dist/lib/share/provision.d.ts +18 -4
  283. package/dist/lib/share/provision.js +38 -4
  284. package/dist/lib/share/publish.d.ts +39 -15
  285. package/dist/lib/share/publish.js +55 -26
  286. package/dist/lib/share/worker-template.js +171 -11
  287. package/dist/lib/sqlite.js +16 -3
  288. package/dist/lib/ssh-exec.d.ts +65 -0
  289. package/dist/lib/ssh-exec.js +120 -4
  290. package/dist/lib/startup/command-registry.d.ts +3 -1
  291. package/dist/lib/startup/command-registry.js +6 -3
  292. package/dist/lib/startup/dev-build.js +3 -3
  293. package/dist/lib/state.d.ts +2 -2
  294. package/dist/lib/state.js +11 -5
  295. package/dist/lib/subagents-registry.js +1 -1
  296. package/dist/lib/teams/agents.js +9 -0
  297. package/dist/lib/terminal/index.d.ts +1 -1
  298. package/dist/lib/terminal/inject.d.ts +19 -0
  299. package/dist/lib/terminal/inject.js +19 -9
  300. package/dist/lib/terminal/types.d.ts +1 -1
  301. package/dist/lib/terminal/types.js +1 -1
  302. package/dist/lib/tmux/orphan-reap.js +1 -1
  303. package/dist/lib/tmux/session.d.ts +46 -13
  304. package/dist/lib/tmux/session.js +204 -27
  305. package/dist/lib/traces/backend.d.ts +29 -0
  306. package/dist/lib/traces/backend.js +46 -0
  307. package/dist/lib/traces/classify.d.ts +52 -0
  308. package/dist/lib/traces/classify.js +72 -0
  309. package/dist/lib/traces/config.d.ts +10 -0
  310. package/dist/lib/traces/config.js +2 -0
  311. package/dist/lib/traces/provision.d.ts +19 -0
  312. package/dist/lib/traces/provision.js +30 -0
  313. package/dist/lib/traces/sync.d.ts +150 -0
  314. package/dist/lib/traces/sync.js +447 -0
  315. package/dist/lib/traces/worker-template.d.ts +4 -0
  316. package/dist/lib/traces/worker-template.js +159 -0
  317. package/dist/lib/types.d.ts +34 -19
  318. package/dist/lib/types.js +14 -0
  319. package/dist/lib/usage-backoff.d.ts +2 -1
  320. package/dist/lib/usage-backoff.js +60 -19
  321. package/dist/lib/usage-refresh.d.ts +18 -4
  322. package/dist/lib/usage-refresh.js +68 -19
  323. package/dist/lib/watchdog/log.d.ts +1 -1
  324. package/dist/lib/watchdog/log.js +1 -1
  325. package/dist/lib/watchdog/runner.d.ts +36 -45
  326. package/dist/lib/watchdog/runner.js +150 -119
  327. package/dist/lib/watchdog/service.d.ts +0 -1
  328. package/dist/lib/watchdog/service.js +0 -1
  329. package/dist/lib/watchdog/watchdog-agent.d.ts +38 -0
  330. package/dist/lib/watchdog/watchdog-agent.js +59 -0
  331. package/dist/lib/watchdog/watchdog.d.ts +11 -2
  332. package/dist/lib/watchdog/watchdog.js +37 -104
  333. package/dist/lib/workflows.d.ts +1 -1
  334. package/package.json +5 -7
  335. package/dist/commands/usage.d.ts +0 -27
  336. package/dist/commands/usage.js +0 -96
  337. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  338. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +0 -24
  339. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  340. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  341. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +0 -128
  342. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  343. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -24
  344. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  345. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +0 -123
  346. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  347. package/dist/lib/usage-fleet.d.ts +0 -32
  348. package/dist/lib/usage-fleet.js +0 -125
@@ -18,6 +18,14 @@ import { getCacheDir } from './state.js';
18
18
  * smuggled in as part of a remote command, and `sshExec` additionally rejects a
19
19
  * leading `-` so it can never be parsed as an ssh argv flag.
20
20
  */
21
+ /**
22
+ * ssh's own connection-layer failure code — the transport dropped or never came
23
+ * up, as opposed to the remote command choosing an exit status. Defined here,
24
+ * in the layer that owns the ssh invocation, and re-exported by
25
+ * `lib/hosts/reconnect.ts` as `SSH_CONN_FAILURE` so there is one constant for
26
+ * the concept rather than two that can drift apart.
27
+ */
28
+ export const SSH_CONN_FAILURE_CODE = 255;
21
29
  export const SSH_TARGET_RE = /^[a-zA-Z0-9._-]+(@[a-zA-Z0-9._-]+)?$/;
22
30
  export function assertValidSshTarget(host) {
23
31
  if (host.startsWith('-') || !SSH_TARGET_RE.test(host)) {
@@ -314,20 +322,128 @@ export function sshExecRawStream(target, remoteCmd, opts) {
314
322
  export function sshReachable(target, timeoutMs = 10000) {
315
323
  return sshExec(target, 'true', { timeoutMs, multiplex: true }).code === 0;
316
324
  }
325
+ /**
326
+ * The DEC private modes a full-screen remote app turns ON and, when it exits
327
+ * cleanly, turns back off itself. A remote agent TUI killed by a dropped link
328
+ * never gets to, so they stay enabled on the LOCAL terminal after ssh is gone:
329
+ * focus reporting (1004) and the mode/colour-scheme reports (996/997) then make
330
+ * the terminal emit answerback sequences at a shell that is not expecting them,
331
+ * which is the `^[[?997;1n ^[[I ^[[O` litter seen after a drop (RUSH-3125). Also
332
+ * resets bracketed paste (2004), the alternate screen (1049), mouse tracking
333
+ * (1000/1002/1003/1006), and re-shows the cursor (25) — every one of which a TUI
334
+ * leaves armed the same way.
335
+ */
336
+ export const TERMINAL_MODE_RESET = '\x1b[?1004l\x1b[?996l\x1b[?997l\x1b[?2004l\x1b[?1049l'
337
+ + '\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\x1b[?25h';
338
+ /**
339
+ * Put the local terminal back the way ssh found it.
340
+ *
341
+ * Two independent kinds of state survive a hard ssh death, and BOTH have to be
342
+ * undone or the next thing the user sees is corrupted:
343
+ *
344
+ * - **termios** (kernel side): `-tt` puts the tty in raw mode, so a killed ssh
345
+ * leaves it with echo and line-editing off. `saved` is the `stty -g` snapshot
346
+ * taken before the spawn; restoring it is exact, unlike guessing at `sane`.
347
+ * - **DEC private modes** (terminal side): see {@link TERMINAL_MODE_RESET}.
348
+ *
349
+ * Then drain whatever the terminal already answered back into the input buffer.
350
+ * Without the drain those bytes are not merely cosmetic — they are still queued
351
+ * on the tty, so the NEXT attach hands them straight to the agent as if the user
352
+ * had typed them.
353
+ *
354
+ * Best-effort by construction: this runs while recovering from a failure, so a
355
+ * missing `stty`, a non-TTY stdin, or a closed stream must never turn a
356
+ * recoverable drop into a thrown error.
357
+ */
358
+ export function restoreLocalTerminal(saved, opts) {
359
+ try {
360
+ if (saved)
361
+ spawnSync('stty', [saved], { stdio: ['inherit', 'ignore', 'ignore'] });
362
+ }
363
+ catch { /* no stty, or stdin is not a tty — nothing to restore */ }
364
+ try {
365
+ if (process.stdout.isTTY)
366
+ process.stdout.write(TERMINAL_MODE_RESET);
367
+ }
368
+ catch { /* stream closed */ }
369
+ try {
370
+ if (process.stdin.isTTY)
371
+ process.stdin.setRawMode?.(false);
372
+ }
373
+ catch { /* stdin not controllable in this context */ }
374
+ // The DRAIN is the one destructive step here, so it is opt-in per exit. See
375
+ // {@link sshStream} for why it must not run on a clean exit.
376
+ if (!opts.drainStdin)
377
+ return;
378
+ try {
379
+ // read() on a paused non-flowing stdin returns the buffered bytes and
380
+ // discards them; the loop clears a burst rather than one chunk.
381
+ if (process.stdin.isTTY) {
382
+ while (process.stdin.read() !== null) { /* discard */ }
383
+ }
384
+ }
385
+ catch { /* stdin not readable in this context */ }
386
+ }
387
+ /** `stty -g` snapshot of the local tty, or undefined when there is nothing to snapshot. */
388
+ export function saveLocalTerminal() {
389
+ if (!process.stdin.isTTY)
390
+ return undefined;
391
+ try {
392
+ const r = spawnSync('stty', ['-g'], { stdio: ['inherit', 'pipe', 'ignore'], encoding: 'utf-8' });
393
+ const out = r.status === 0 ? r.stdout?.trim() : '';
394
+ return out || undefined;
395
+ }
396
+ catch {
397
+ return undefined;
398
+ }
399
+ }
317
400
  /**
318
401
  * Foreground counterpart to `sshExec`: run `remoteCmd` on `target` with the
319
402
  * local stdio wired straight through (`stdio: 'inherit'`), so output streams as
320
403
  * it is produced and — with `tty` — keystrokes reach a remote picker. Blocks
321
404
  * until the remote command exits and returns its exit code (255 is ssh's own
322
405
  * connection-layer failure; any other non-zero is the remote command's code).
406
+ *
407
+ * On a `tty` stream the local terminal state is snapshotted before the spawn and
408
+ * restored after it. ssh restores termios itself when it exits cleanly, so this
409
+ * is for the case that matters here: the link dying under a full-screen agent
410
+ * TUI, which leaves the tty raw and the TUI's DEC modes armed. Doing it HERE
411
+ * rather than in the reconnect loop means every caller that opens an interactive
412
+ * remote stream is covered, not just the one that noticed (RUSH-3125).
413
+ *
414
+ * **The stdin drain is gated on an ABNORMAL exit, and the distinction is not
415
+ * cosmetic.** Resetting termios and the DEC modes is idempotent — on a clean
416
+ * exit ssh and the remote TUI have already done it, so re-doing it changes
417
+ * nothing. Draining stdin is destructive: it discards whatever the user has
418
+ * typed ahead. On a clean exit those bytes are legitimate type-ahead the
419
+ * resuming shell should receive, and eating them would be a new bug in every
420
+ * caller of this function (`runInteractiveOnHost`, the remote-tmux attach in
421
+ * commands/go.ts, the remote secrets browse) rather than a fix. Only an
422
+ * abnormal exit — ssh killed by a signal (`status === null`) or its own
423
+ * connection-layer failure (255) — produces the answerback storm the drain
424
+ * exists to clear, because only then did the TUI die without sending its own
425
+ * mode resets.
323
426
  */
324
427
  export function sshStream(target, remoteCmd, opts = {}) {
325
428
  assertValidSshTarget(target);
326
429
  const mux = opts.multiplex === false ? [] : controlOpts();
327
430
  const tty = opts.tty ? ['-tt'] : [];
328
431
  const args = [...sshConnectOpts(mux, opts.hostKeyOpts), ...(opts.extraSshArgs ?? []), ...tty, target, remoteCmd];
329
- const res = spawnSync('ssh', args, { stdio: 'inherit' });
330
- if (typeof res.status === 'number')
331
- return res.status;
332
- return 255; // spawn error / signal treat as a connection-layer failure
432
+ const saved = opts.tty ? saveLocalTerminal() : undefined;
433
+ let abnormal = true; // a throw before/inside the spawn is abnormal by definition
434
+ try {
435
+ const res = spawnSync('ssh', args, { stdio: 'inherit' });
436
+ const code = typeof res.status === 'number' ? res.status : 255;
437
+ // Killed by a signal (no numeric status) or ssh's own connection-layer
438
+ // failure — the two shapes in which the remote TUI never got to reset the
439
+ // terminal. Any other code is ssh exiting normally with the remote's status.
440
+ abnormal = res.status === null || code === SSH_CONN_FAILURE_CODE;
441
+ return code;
442
+ }
443
+ finally {
444
+ // `finally`, because the abnormal exits are precisely the ones that leave
445
+ // the terminal wrecked — but the drain within it is gated (see the doc).
446
+ if (opts.tty)
447
+ restoreLocalTerminal(saved, { drainStdin: abnormal });
448
+ }
333
449
  }
@@ -32,7 +32,9 @@ export declare const KNOWN_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
32
32
  * `agents fleet apply` / `agents devices apply`. `beta` nested under
33
33
  * `agents setup beta` (RUSH-2981). `org` (the Prix-coupled account layer) was
34
34
  * removed; `agents auth` returned against Phoenix ID with `auth space` as the
35
- * team surface (RUSH-2581).
35
+ * team surface (RUSH-2581). `usage` was removed as a duplicate surface of
36
+ * `agents view`, which renders per-account usage with account, version, and
37
+ * auth state beside it (RUSH-3079).
36
38
  */
37
39
  export declare const RETIRED_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
38
40
  export declare function isKnownTopLevelCommand(name: string): boolean;
@@ -5,12 +5,12 @@ const LOADED_COMMAND_NAMES = [
5
5
  'routines', 'monitors', 'projects', 'run', 'open', 'reconnect', 'fork', 'config',
6
6
  'models', 'modes', 'trash', 'restore', 'doctor',
7
7
  'route', 'harness', 'harnesses', 'secrets', 'menubar', 'sync',
8
- 'refresh-rules', 'factory', 'usage', 'insights', 'perf',
8
+ 'refresh-rules', 'factory', 'insights', 'trace', 'perf',
9
9
  'pty', 'tmux', 'watchdog', 'browser', 'computer', 'logs', 'events',
10
10
  'ssh', 'devices', 'fleet', 'repos', 'repo', 'setup', 'uninstall', 'upgrade', 'sessions',
11
11
  'teams', 'cloud', 'message', 'send', 'notify', 'feed',
12
12
  'mailboxes', 'mailbox', 'artifacts', 'webhooks',
13
- 'humans', 'daemon',
13
+ 'humans', 'daemon', 'traces',
14
14
  ];
15
15
  const INLINE_COMMAND_NAMES = [
16
16
  'perms', 'exec', 'jobs', 'cron', 'check', 'resources', 'hq', '_internal',
@@ -52,12 +52,15 @@ export const KNOWN_TOP_LEVEL_COMMANDS = new Set([
52
52
  * `agents fleet apply` / `agents devices apply`. `beta` nested under
53
53
  * `agents setup beta` (RUSH-2981). `org` (the Prix-coupled account layer) was
54
54
  * removed; `agents auth` returned against Phoenix ID with `auth space` as the
55
- * team surface (RUSH-2581).
55
+ * team surface (RUSH-2581). `usage` was removed as a duplicate surface of
56
+ * `agents view`, which renders per-account usage with account, version, and
57
+ * auth state beside it (RUSH-3079).
56
58
  */
57
59
  export const RETIRED_TOP_LEVEL_COMMANDS = new Set([
58
60
  'webhook',
59
61
  'org',
60
62
  'serve',
63
+ 'usage',
61
64
  'login',
62
65
  'logout',
63
66
  'budget',
@@ -22,12 +22,12 @@ import * as path from 'path';
22
22
  * looking for `.git`.
23
23
  *
24
24
  * That package root does not always hold `.git` itself, though — this repo is a
25
- * monorepo where the package lives at `apps/cli` and `.git` sits one level
25
+ * monorepo where the package lives at `cli` and `.git` sits one level
26
26
  * higher, at the true repo root. `.git` is therefore searched for in a SMALL,
27
27
  * bounded set of ancestors above the package root (see
28
28
  * `DEV_BUILD_GIT_ANCESTOR_DEPTH`), not just the package root itself — a source
29
- * checkout run via tsx/ts-node from `apps/cli/src/` or built to
30
- * `apps/cli/dist/` must still be recognized. The bound keeps this well short of
29
+ * checkout run via tsx/ts-node from `cli/src/` or built to
30
+ * `cli/dist/` must still be recognized. The bound keeps this well short of
31
31
  * an unrelated ancestor repo: a real Homebrew npm-global install's package root
32
32
  * sits 3 directories below `/opt/homebrew` (`lib/node_modules/@phnx-labs/…`),
33
33
  * so a depth of 2 finds this repo's `.git` while staying short of Homebrew's.
@@ -31,7 +31,7 @@ export declare function getHumansFilePath(): string;
31
31
  * file against `schema/agents-yaml.schema.json`. Exported so
32
32
  * `lib/devices/config-migration.ts` rewrites a device doc with the same header.
33
33
  */
34
- export declare const META_HEADER = "# agents-cli metadata\n# Auto-generated - do not edit manually\n# https://github.com/phnx-labs/agents-cli\n# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/apps/cli/schema/agents-yaml.schema.json\n\n";
34
+ export declare const META_HEADER = "# agents-cli metadata\n# Auto-generated - do not edit manually\n# https://github.com/phnx-labs/agents-cli\n# yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/cli/schema/agents-yaml.schema.json\n\n";
35
35
  /** Root of the system data directory (~/.agents/.system/). */
36
36
  export declare function getAgentsDir(): string;
37
37
  /** Root of the system data directory (~/.agents/.system/). */
@@ -258,7 +258,7 @@ export declare function getTeamsAgentsDir(): string;
258
258
  export declare function getTeamsRegistryPath(): string;
259
259
  /** Path to the device registry — SSH device profiles with platform/auth metadata. Durable runtime, per-machine (host list + addresses are NOT pulled by `agents repo push`). */
260
260
  export declare function getDevicesRegistryPath(): string;
261
- /** Path to the device ignore-list — tailscale node names the user dismissed, so auto-discovery never re-suggests them. Per-machine, same dir as the registry. */
261
+ /** Path to the LEGACY per-machine device ignore-list — superseded by the tracked `fleet.ignored` list in central agents.yaml (RUSH-3062); only lib/devices/config-migration.ts still reads it (to fold + remove it). */
262
262
  export declare function getDevicesIgnoredPath(): string;
263
263
  /** Path to the LEGACY device auto-launch preference file — which registered devices are eligible/preferred for the ext's auto-host selection. Superseded by the per-device doc `config:` block; only lib/devices/config-migration.ts still reads it (to fold + remove it). */
264
264
  export declare function getDevicesAutoLaunchPath(): string;
package/dist/lib/state.js CHANGED
@@ -168,7 +168,7 @@ const USER_PROMPTCUTS_FILE = path.join(USER_AGENTS_DIR, 'hooks', 'promptcuts.yam
168
168
  export const META_HEADER = `# agents-cli metadata
169
169
  # Auto-generated - do not edit manually
170
170
  # https://github.com/phnx-labs/agents-cli
171
- # yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/apps/cli/schema/agents-yaml.schema.json
171
+ # yaml-language-server: $schema=https://raw.githubusercontent.com/phnx-labs/agents-cli/main/cli/schema/agents-yaml.schema.json
172
172
 
173
173
  `;
174
174
  // ─── Root getters ─────────────────────────────────────────────────────────────
@@ -563,7 +563,9 @@ export function getTeamsAgentsDir() { return TEAMS_AGENTS_DIR; }
563
563
  /** Path to the team registry — list of named teams with timestamps. Durable runtime, per-machine. */
564
564
  export function getTeamsRegistryPath() { return path.join(HISTORY_DIR, 'teams', 'registry.json'); }
565
565
  /**
566
- * The devices dir (registry + ignore-list live here). Read at CALL time so a
566
+ * The devices dir (the registry lives here; the ignore-list moved to the
567
+ * tracked central agents.yaml as `fleet.ignored` — RUSH-3062). Read at CALL
568
+ * time so a
567
569
  * test can redirect it to a temp dir via AGENTS_DEVICES_DIR without racing the
568
570
  * module-load capture of HISTORY_DIR — mirrors the AGENTS_EVENTS_PATH /
569
571
  * AGENTS_SECRETS_AGENT_DIR test-isolation escape hatches. Never set in
@@ -575,7 +577,7 @@ function getDevicesDir() {
575
577
  }
576
578
  /** Path to the device registry — SSH device profiles with platform/auth metadata. Durable runtime, per-machine (host list + addresses are NOT pulled by `agents repo push`). */
577
579
  export function getDevicesRegistryPath() { return path.join(getDevicesDir(), 'registry.json'); }
578
- /** Path to the device ignore-list — tailscale node names the user dismissed, so auto-discovery never re-suggests them. Per-machine, same dir as the registry. */
580
+ /** Path to the LEGACY per-machine device ignore-list — superseded by the tracked `fleet.ignored` list in central agents.yaml (RUSH-3062); only lib/devices/config-migration.ts still reads it (to fold + remove it). */
579
581
  export function getDevicesIgnoredPath() { return path.join(getDevicesDir(), 'ignored.json'); }
580
582
  /** Path to the LEGACY device auto-launch preference file — which registered devices are eligible/preferred for the ext's auto-host selection. Superseded by the per-device doc `config:` block; only lib/devices/config-migration.ts still reads it (to fold + remove it). */
581
583
  export function getDevicesAutoLaunchPath() { return path.join(getDevicesDir(), 'auto-launch.json'); }
@@ -893,7 +895,6 @@ const META_KEY_SCOPE = {
893
895
  actors: 'central',
894
896
  seededPresets: 'central',
895
897
  hooks: 'central',
896
- browser: 'central',
897
898
  config: 'central',
898
899
  hosts: 'central',
899
900
  fleet: 'central',
@@ -908,7 +909,12 @@ const META_KEY_SCOPE = {
908
909
  * in central is stale and must be migrated out). A key NOT listed here — e.g. one
909
910
  * a newer CLI version added — is preserved verbatim, never dropped + synced away.
910
911
  */
911
- const KNOWN_META_KEYS = new Set(Object.keys(META_KEY_SCOPE));
912
+ const KNOWN_META_KEYS = new Set([
913
+ ...Object.keys(META_KEY_SCOPE),
914
+ // Removed browser-profile store. Kept only as a serializer tombstone so the
915
+ // first registry read can migrate it into this device's file and delete it.
916
+ 'browser',
917
+ ]);
912
918
  /**
913
919
  * Serialize the central (synced) meta to `agents.yaml` WITHOUT destroying the
914
920
  * hand-written comments in the committed file.
@@ -27,7 +27,7 @@
27
27
  *
28
28
  * The per-agent `transform`/metadata parsers are the escape hatch: they live in
29
29
  * `subagents.ts` and are referenced by the table, so the generic engine has zero
30
- * per-agent branches. See the integration tiers in `docs/subagents.md`.
30
+ * per-agent branches. See `docs/orchestration.md`.
31
31
  */
32
32
  import * as fs from 'fs';
33
33
  import * as path from 'path';
@@ -1451,6 +1451,15 @@ export class AgentManager {
1451
1451
  }
1452
1452
  this.defaultMode = resolvedDefaultMode;
1453
1453
  this.initPromise = this.doInitialize();
1454
+ // Mark the deferred rejection as observed. Construction fires init
1455
+ // fire-and-forget; every public method still surfaces a failed init at its
1456
+ // own `await this.initialize()` (the same promise rejects for each new
1457
+ // awaiter). Without this, an init that loses a race with its directory
1458
+ // being removed — measured twice as an unhandled
1459
+ // `ENOENT mkdir /tmp/agents-retention-*` that failed a fully-green suite
1460
+ // (exit 1 with 12k tests passed) and blocked release attestation — crashes
1461
+ // the process instead of failing the caller that actually cares.
1462
+ this.initPromise.catch(() => { });
1454
1463
  }
1455
1464
  async initialize() {
1456
1465
  if (!this.initPromise) {
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Public entry point. Callers typically use `openSurfaces` (a batch with a
6
6
  * layout policy) or `openSurface` (a single request), and `availableBackends` /
7
- * `detectCurrentBackend` to pick a target. See docs/terminal-engine.md.
7
+ * `detectCurrentBackend` to pick a target. See docs/interfaces.md.
8
8
  */
9
9
  export type { Backend, SplitDirection, Layout, EngineContext, LaunchSpec, LaunchRequest, LaunchResult, TerminalBackend, } from './types.js';
10
10
  export { currentContext } from './types.js';
@@ -98,12 +98,31 @@ export interface InjectOptions {
98
98
  export interface InjectResult {
99
99
  ok: boolean;
100
100
  backend: InjectBackend;
101
+ /**
102
+ * Delivery is CONFIRMED to have reached the running agent, not merely dispatched.
103
+ *
104
+ * For tmux / iterm / ghostty / pty, `ok` already means delivered: `tmux
105
+ * send-keys` errors on a missing pane, `osascript write text` errors on a bad
106
+ * session id, and a pty write errors if the sidecar can't reach the fd — so a
107
+ * successful transport IS the confirmation. For **vscodium**, `ok` only means the
108
+ * editor CLI accepted the `--open-url` hand-off; whether the swarm-ext extension
109
+ * actually parsed the verb and typed anything is unknown from the CLI side (the
110
+ * ext must ack — RUSH follow-up). So vscodium is delivered-but-UNCONFIRMED, and
111
+ * the watchdog must not book a nudge as landed on that alone.
112
+ */
113
+ confirmed: boolean;
101
114
  /** Discrete writes delivered: 2 for the Ink-safe text+Enter split, 1 when combined or enter=false. */
102
115
  writes: number;
103
116
  /** For the tmux / AppleScript backends (or any dryRun), the spec(s) that ran / would run. */
104
117
  specs?: LaunchSpec[];
105
118
  error?: string;
106
119
  }
120
+ /**
121
+ * Whether a successful transport on this backend CONFIRMS delivery to the agent.
122
+ * Everything but vscodium self-confirms (see InjectResult.confirmed); vscodium's
123
+ * `--open-url` is fire-and-forget until the swarm-ext extension acks the verb.
124
+ */
125
+ export declare function backendConfirmsDelivery(backend: InjectBackend): boolean;
107
126
  /**
108
127
  * argv for `tmux send-keys` targeting a pane by id. Starts with `tmux` (like the
109
128
  * engine's `tmuxTabArgv`) so the same transport runs it locally or over SSH. The
@@ -46,6 +46,14 @@ import { appleScriptStr } from './quote.js';
46
46
  import { runSpec } from './transport.js';
47
47
  import { itermBackend, ghosttyBackend } from './backends/index.js';
48
48
  import { currentContext } from './types.js';
49
+ /**
50
+ * Whether a successful transport on this backend CONFIRMS delivery to the agent.
51
+ * Everything but vscodium self-confirms (see InjectResult.confirmed); vscodium's
52
+ * `--open-url` is fire-and-forget until the swarm-ext extension acks the verb.
53
+ */
54
+ export function backendConfirmsDelivery(backend) {
55
+ return backend !== 'vscodium';
56
+ }
49
57
  /** Carriage return — what Enter delivers into a raw PTY/tmux byte stream. */
50
58
  const CR = '\r';
51
59
  // --- tmux -------------------------------------------------------------------
@@ -180,22 +188,23 @@ export function vscodiumInjectSpec(target, text, opts) {
180
188
  // --- pty --------------------------------------------------------------------
181
189
  async function injectPty(target, text, o) {
182
190
  if (o.host && o.host !== 'local') {
183
- return { ok: false, backend: 'pty', writes: 0, error: 'pty injection is local-only (the sidecar is not a remote surface)' };
191
+ return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: 'pty injection is local-only (the sidecar is not a remote surface)' };
184
192
  }
185
193
  // Same Ink-safe split as tmux: text write, then a separate CR write.
186
194
  const writes = o.enter && o.combined ? [text + CR] : o.enter ? [text, CR] : [text];
187
195
  if (o.dryRun)
188
- return { ok: true, backend: 'pty', writes: writes.length };
196
+ return { ok: true, confirmed: true, backend: 'pty', writes: writes.length };
189
197
  try {
190
198
  for (const input of writes) {
191
199
  const res = await ptyRequest('write', target.id, { input });
192
200
  if (!res.ok)
193
- return { ok: false, backend: 'pty', writes: 0, error: res.error ?? 'pty write failed' };
201
+ return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: res.error ?? 'pty write failed' };
194
202
  }
195
- return { ok: true, backend: 'pty', writes: writes.length };
203
+ // A successful sidecar write reached the fd — real delivery.
204
+ return { ok: true, confirmed: true, backend: 'pty', writes: writes.length };
196
205
  }
197
206
  catch (err) {
198
- return { ok: false, backend: 'pty', writes: 0, error: err instanceof Error ? err.message : String(err) };
207
+ return { ok: false, confirmed: false, backend: 'pty', writes: 0, error: err instanceof Error ? err.message : String(err) };
199
208
  }
200
209
  }
201
210
  // --- public entry -----------------------------------------------------------
@@ -225,8 +234,9 @@ export async function injectIntoTerminal(target, text, opts = {}) {
225
234
  : target.backend === 'ghostty'
226
235
  ? enter ? 2 : 1
227
236
  : enter && !combined ? 2 : 1;
237
+ const confirmed = backendConfirmsDelivery(target.backend);
228
238
  if (opts.dryRun)
229
- return { ok: true, backend: target.backend, writes, specs };
239
+ return { ok: true, confirmed, backend: target.backend, writes, specs };
230
240
  // AppleScript backends: guard on the backend's own availability, but only for a
231
241
  // LOCAL run (a remote host is assumed to have the app — its ssh leg reports failure).
232
242
  if (target.backend === 'iterm' || target.backend === 'ghostty') {
@@ -234,14 +244,14 @@ export async function injectIntoTerminal(target, text, opts = {}) {
234
244
  const backend = target.backend === 'iterm' ? itermBackend : ghosttyBackend;
235
245
  const ctx = opts.ctx ?? currentContext();
236
246
  if (!backend.isAvailable(ctx)) {
237
- return { ok: false, backend: target.backend, writes: 0, specs, error: `${backend.label} is not available here (platform ${ctx.platform})` };
247
+ return { ok: false, confirmed: false, backend: target.backend, writes: 0, specs, error: `${backend.label} is not available here (platform ${ctx.platform})` };
238
248
  }
239
249
  }
240
250
  }
241
251
  for (const spec of specs) {
242
252
  const res = await runSpec(spec, opts.host, opts.resolveHost);
243
253
  if (!res.ok)
244
- return { ok: false, backend: target.backend, writes: 0, specs, error: res.error };
254
+ return { ok: false, confirmed: false, backend: target.backend, writes: 0, specs, error: res.error };
245
255
  }
246
- return { ok: true, backend: target.backend, writes, specs };
256
+ return { ok: true, confirmed, backend: target.backend, writes, specs };
247
257
  }
@@ -5,7 +5,7 @@
5
5
  * command, in a chosen terminal backend (iTerm / Ghostty / tmux), on this
6
6
  * machine or a remote host. This is distinct from cloud providers
7
7
  * (src/lib/cloud), which dispatch autonomous headless tasks; a terminal surface
8
- * is attended and live. See docs/terminal-engine.md.
8
+ * is attended and live. See docs/interfaces.md.
9
9
  */
10
10
  /** An interactive terminal backend the engine can drive. */
11
11
  export type Backend = 'iterm' | 'ghostty' | 'tmux' | 'vscodium-agent' | 'terminal';
@@ -5,7 +5,7 @@
5
5
  * command, in a chosen terminal backend (iTerm / Ghostty / tmux), on this
6
6
  * machine or a remote host. This is distinct from cloud providers
7
7
  * (src/lib/cloud), which dispatch autonomous headless tasks; a terminal surface
8
- * is attended and live. See docs/terminal-engine.md.
8
+ * is attended and live. See docs/interfaces.md.
9
9
  */
10
10
  /** Snapshot the live process context. */
11
11
  export function currentContext() {
@@ -172,7 +172,7 @@ export const DETACHED_HELPER_RULES = [CLAUDE_BG_DAEMON];
172
172
  export function isProtectedAgentsService(args) {
173
173
  return /\b__daemon-run\b/.test(args)
174
174
  || /\bsecrets\s+_agent-run\b/.test(args)
175
- || /\bMenubarHelper\b/.test(args)
175
+ || /\bAGI Menu\b/.test(args)
176
176
  || /\bAgents CLI\.app\b/.test(args);
177
177
  }
178
178
  /** Is this pid currently running? Signal-0 probe, matching `platform/process.ts`. */
@@ -9,6 +9,22 @@
9
9
  * pure provenance — `listSessions()` always reconciles them against
10
10
  * `tmux list-sessions` and prunes stale entries on the fly.
11
11
  */
12
+ /**
13
+ * Ten times tmux's 2,000-line default, while keeping per-pane memory bounded:
14
+ * history storage scales with pane width, so a 200-column pane retains at most
15
+ * four million grid cells rather than an unbounded agent transcript.
16
+ */
17
+ export declare const AGENTS_TMUX_HISTORY_LIMIT = 20000;
18
+ /**
19
+ * Bump when the generated startup config changes, so an ALREADY-RUNNING server
20
+ * picks the new settings up. tmux reads `-f` only when it actually starts a
21
+ * server: a second `new-session -f other.conf` against a live server exits 0
22
+ * and silently applies nothing (verified against tmux 3.4). Since agents-cli
23
+ * uses one long-lived shared socket, the normal state at upgrade is a running
24
+ * server — so without this reconcile every existing machine would get none of
25
+ * these defaults, silently. Same shape as AGENT_HOOK_SCHEMA below.
26
+ */
27
+ export declare const AGENTS_TMUX_CONFIG_SCHEMA = 1;
12
28
  /** Provenance written alongside each live tmux session. */
13
29
  export interface SessionMeta {
14
30
  name: string;
@@ -62,6 +78,8 @@ export interface ListedSession {
62
78
  attached: boolean;
63
79
  meta?: SessionMeta;
64
80
  }
81
+ /** Build the atomic server-configuration + session-creation command. */
82
+ export declare function buildCreateSessionArgs(opts: CreateSessionOptions, startupConfig: string): string[];
65
83
  export declare class TmuxSessionError extends Error {
66
84
  readonly cause?: unknown | undefined;
67
85
  constructor(message: string, cause?: unknown | undefined);
@@ -213,21 +231,36 @@ export declare function setSessionHook(name: string, hook: string, command: stri
213
231
  * variant could stall the hook on a loaded CI runner, letting the dead
214
232
  * split survive until the test's wait timeout expired (flake in CI shard
215
233
  * 3: pane-guarded pane-died hook / user split exit).
234
+ * v6 — the agent-pane branch is now `session_attached`-aware: with a client
235
+ * attached it `detach-client`s (the interactive path, so runInTmux's
236
+ * attach returns and reads the exit status, EXEC-23b unchanged); with NO
237
+ * client it `kill-session`s outright. A `remain-on-exit` pane with no
238
+ * attach client to reap it otherwise lingers as a dead husk until the
239
+ * daemon's periodic `reapDeadTmuxPanes` sweep — so a run whose terminal
240
+ * was closed, or a `/exit` after the user detached, left an idle-looking
241
+ * dead session on the socket. Killing it the instant the agent exits is
242
+ * the "one close tears down every layer" contract (#5a).
216
243
  */
217
- export declare const AGENT_HOOK_SCHEMA = 5;
244
+ export declare const AGENT_HOOK_SCHEMA = 6;
218
245
  /**
219
- * The guarded `pane-died` hook. Detach the client ONLY when the agent pane dies
220
- * (so the blocking attach in runInTmux returns and the exit status can be read);
221
- * a user split's death runs the else-branch, closing just that split. The
222
- * else-branch goes through `run-shell -b -C` with an explicit `-t #{hook_pane}`
223
- * target: tmux format-expands the command at fire time and executes it inside
224
- * the server command queue in the background, so the event pane is always the
225
- * one killed without launching a second tmux client against the same socket and
226
- * without stalling the hook on a loaded runner. A bare `kill-pane` relied on
227
- * the hook context supplying a "current pane", while an external self-client
228
- * could race the server under load. Single source of truth: both the
229
- * spawn-wrap (exec.ts) and the daemon reconcile build the hook here, so the
230
- * two can never drift.
246
+ * The guarded `pane-died` hook. When the AGENT pane dies, tear the whole session
247
+ * down but HOW depends on whether a client is attached:
248
+ * - attached (`#{session_attached}`): `detach-client` so the blocking attach in
249
+ * runInTmux returns and `resolveAfterAttach` reads the exit status off the
250
+ * still-`remain-on-exit` dead pane, then kills the session (EXEC-23b, unchanged);
251
+ * - no client: `kill-session` outright. Without this the dead pane sits under
252
+ * `remain-on-exit` as an idle-looking husk until the daemon's periodic
253
+ * `reapDeadTmuxPanes` sweep the "second exit" / orphaned-idle session bug
254
+ * when a wrapped agent exits with nobody attached (a closed terminal, or a
255
+ * `/exit` after the user detached). `detach-client` on an unattached session
256
+ * is a no-op, so the old hook left the husk; `kill-session` ends it at once.
257
+ * A user split's death still runs the else-branch, closing just that split via
258
+ * `run-shell -b -C` with an explicit `-t #{hook_pane}` target: tmux format-expands
259
+ * the command at fire time and executes it inside the server command queue in the
260
+ * background, so the event pane is always the one killed without launching a second
261
+ * tmux client against the same socket and without stalling the hook on a loaded
262
+ * runner. Single source of truth: both the spawn-wrap (exec.ts) and the daemon
263
+ * reconcile build the hook here, so the two can never drift.
231
264
  */
232
265
  export declare function agentPaneDiedHook(sessionName: string, agentPane: string): string;
233
266
  /** Stamp a session's hook-schema marker to the current version. */