@songsid/agend 2.3.5-alpha.5 → 2.3.5-beta.1

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 (976) hide show
  1. package/README.md +0 -2
  2. package/README.zh-TW.md +0 -2
  3. package/THIRD_PARTY_NOTICES.md +0 -68
  4. package/dist/agent-endpoint.d.ts +1 -5
  5. package/dist/agent-endpoint.js +2 -12
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-file-read.js +1 -1
  8. package/dist/agent-file-read.js.map +1 -1
  9. package/dist/auth-api.d.ts +0 -48
  10. package/dist/auth-api.js +4 -250
  11. package/dist/auth-api.js.map +1 -1
  12. package/dist/authority/session-authorizations.d.ts +2 -0
  13. package/dist/authority/session-authorizations.js +7 -1
  14. package/dist/authority/session-authorizations.js.map +1 -1
  15. package/dist/authority/web-authority.d.ts +2 -12
  16. package/dist/authority/web-authority.js +4 -25
  17. package/dist/authority/web-authority.js.map +1 -1
  18. package/dist/backend/claude-code.d.ts +12 -15
  19. package/dist/backend/claude-code.js +133 -152
  20. package/dist/backend/claude-code.js.map +1 -1
  21. package/dist/backend/codex-footer-hints.d.ts +0 -3
  22. package/dist/backend/codex-footer-hints.js +0 -14
  23. package/dist/backend/codex-footer-hints.js.map +1 -1
  24. package/dist/backend/codex-package.d.ts +2 -5
  25. package/dist/backend/codex-package.js +5 -1
  26. package/dist/backend/codex-package.js.map +1 -1
  27. package/dist/backend/codex-quota-history.js +19 -4
  28. package/dist/backend/codex-quota-history.js.map +1 -1
  29. package/dist/backend/codex.d.ts +115 -142
  30. package/dist/backend/codex.js +1094 -1187
  31. package/dist/backend/codex.js.map +1 -1
  32. package/dist/backend/grok.d.ts +0 -2
  33. package/dist/backend/grok.js +0 -22
  34. package/dist/backend/grok.js.map +1 -1
  35. package/dist/backend/types.d.ts +2 -7
  36. package/dist/backend/types.js.map +1 -1
  37. package/dist/bundled-tmux-guard.d.ts +21 -0
  38. package/dist/bundled-tmux-guard.js +96 -0
  39. package/dist/bundled-tmux-guard.js.map +1 -0
  40. package/dist/channel/ipc-bridge.d.ts +0 -2
  41. package/dist/channel/mcp-tools.js +2 -2
  42. package/dist/channel/mcp-tools.js.map +1 -1
  43. package/dist/classic-channel-manager.d.ts +2 -0
  44. package/dist/classic-channel-manager.js +5 -1
  45. package/dist/classic-channel-manager.js.map +1 -1
  46. package/dist/cli.js +30 -127
  47. package/dist/cli.js.map +1 -1
  48. package/dist/config-validator.js +13 -74
  49. package/dist/config-validator.js.map +1 -1
  50. package/dist/config.js +3 -17
  51. package/dist/config.js.map +1 -1
  52. package/dist/daemon.d.ts +5 -72
  53. package/dist/daemon.js +275 -869
  54. package/dist/daemon.js.map +1 -1
  55. package/dist/delivery-consumed-watch.d.ts +3 -15
  56. package/dist/delivery-consumed-watch.js +1 -23
  57. package/dist/delivery-consumed-watch.js.map +1 -1
  58. package/dist/delivery-outbox.d.ts +0 -5
  59. package/dist/delivery-outbox.js +4 -32
  60. package/dist/delivery-outbox.js.map +1 -1
  61. package/dist/delivery-reconciliation.d.ts +42 -2
  62. package/dist/delivery-reconciliation.js +164 -2
  63. package/dist/delivery-reconciliation.js.map +1 -1
  64. package/dist/doctor.js +9 -20
  65. package/dist/doctor.js.map +1 -1
  66. package/dist/fleet-context.d.ts +2 -2
  67. package/dist/fleet-level-config.d.ts +1 -1
  68. package/dist/fleet-level-config.js +0 -2
  69. package/dist/fleet-level-config.js.map +1 -1
  70. package/dist/fleet-manager.d.ts +9 -498
  71. package/dist/fleet-manager.js +202 -2849
  72. package/dist/fleet-manager.js.map +1 -1
  73. package/dist/general-instance.d.ts +0 -7
  74. package/dist/general-instance.js +3 -20
  75. package/dist/general-instance.js.map +1 -1
  76. package/dist/general-knowledge/skills/concepts/SKILL.md +18 -50
  77. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +2 -5
  78. package/dist/general-knowledge/skills/fleet-health/SKILL.md +2 -2
  79. package/dist/general-knowledge/skills/session-management/SKILL.md +1 -1
  80. package/dist/general-knowledge/skills/tui-effort/SKILL.md +10 -10
  81. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +13 -23
  82. package/dist/instance-config-impact.js +4 -8
  83. package/dist/instance-config-impact.js.map +1 -1
  84. package/dist/instance-lifecycle.d.ts +3 -22
  85. package/dist/instance-lifecycle.js +176 -393
  86. package/dist/instance-lifecycle.js.map +1 -1
  87. package/dist/instructions.d.ts +0 -2
  88. package/dist/instructions.js +0 -6
  89. package/dist/instructions.js.map +1 -1
  90. package/dist/locale.js +2 -147
  91. package/dist/locale.js.map +1 -1
  92. package/dist/login-flows.d.ts +7 -8
  93. package/dist/login-flows.js +1 -24
  94. package/dist/login-flows.js.map +1 -1
  95. package/dist/ls-rows.js +4 -3
  96. package/dist/ls-rows.js.map +1 -1
  97. package/dist/muse-usage-relay.d.ts +2 -0
  98. package/dist/muse-usage-relay.js +5 -3
  99. package/dist/muse-usage-relay.js.map +1 -1
  100. package/dist/needs-you-hub.d.ts +0 -5
  101. package/dist/needs-you-hub.js +2 -18
  102. package/dist/needs-you-hub.js.map +1 -1
  103. package/dist/needs-you.d.ts +2 -45
  104. package/dist/needs-you.js +2 -26
  105. package/dist/needs-you.js.map +1 -1
  106. package/dist/office-share-page.js +3 -3
  107. package/dist/office-share-page.js.map +1 -1
  108. package/dist/office-share.d.ts +0 -11
  109. package/dist/office-share.js +0 -14
  110. package/dist/office-share.js.map +1 -1
  111. package/dist/outbound-handlers.d.ts +2 -30
  112. package/dist/outbound-handlers.js +4 -109
  113. package/dist/outbound-handlers.js.map +1 -1
  114. package/dist/outbound-schemas.d.ts +1 -71
  115. package/dist/outbound-schemas.js +14 -36
  116. package/dist/outbound-schemas.js.map +1 -1
  117. package/dist/pane-quota-history.d.ts +0 -2
  118. package/dist/pane-quota-history.js +0 -20
  119. package/dist/pane-quota-history.js.map +1 -1
  120. package/dist/pause-marker.d.ts +1 -1
  121. package/dist/pause-marker.js +1 -1
  122. package/dist/pause-marker.js.map +1 -1
  123. package/dist/public-web-gateway.js +0 -16
  124. package/dist/public-web-gateway.js.map +1 -1
  125. package/dist/public-web-link.d.ts +0 -11
  126. package/dist/public-web-link.js +0 -13
  127. package/dist/public-web-link.js.map +1 -1
  128. package/dist/quickstart-api.js +5 -9
  129. package/dist/quickstart-api.js.map +1 -1
  130. package/dist/quickstart.d.ts +0 -6
  131. package/dist/quickstart.js +6 -44
  132. package/dist/quickstart.js.map +1 -1
  133. package/dist/quota-notice-config.d.ts +22 -0
  134. package/dist/quota-notice-config.js +41 -0
  135. package/dist/quota-notice-config.js.map +1 -0
  136. package/dist/scheduler/task-board-text.js +1 -1
  137. package/dist/scheduler/task-board-text.js.map +1 -1
  138. package/dist/scheduler/task-board.d.ts +0 -4
  139. package/dist/scheduler/task-board.js +3 -9
  140. package/dist/scheduler/task-board.js.map +1 -1
  141. package/dist/service-installer.d.ts +2 -7
  142. package/dist/service-installer.js +10 -13
  143. package/dist/service-installer.js.map +1 -1
  144. package/dist/settings-api.d.ts +0 -12
  145. package/dist/settings-api.js +7 -487
  146. package/dist/settings-api.js.map +1 -1
  147. package/dist/settings-baseline.d.ts +0 -7
  148. package/dist/settings-baseline.js +1 -3
  149. package/dist/settings-baseline.js.map +1 -1
  150. package/dist/settings-change.js +7 -35
  151. package/dist/settings-change.js.map +1 -1
  152. package/dist/settings-confirmation.d.ts +1 -16
  153. package/dist/settings-confirmation.js +9 -47
  154. package/dist/settings-confirmation.js.map +1 -1
  155. package/dist/settings-effect.d.ts +0 -17
  156. package/dist/settings-effect.js +1 -155
  157. package/dist/settings-effect.js.map +1 -1
  158. package/dist/settings-http-confirmation.d.ts +0 -4
  159. package/dist/settings-http-confirmation.js +3 -43
  160. package/dist/settings-http-confirmation.js.map +1 -1
  161. package/dist/settings-instance-patch.js +1 -1
  162. package/dist/settings-instance-patch.js.map +1 -1
  163. package/dist/settings-request-capability.d.ts +0 -2
  164. package/dist/settings-request-capability.js +0 -4
  165. package/dist/settings-request-capability.js.map +1 -1
  166. package/dist/settings-transaction.d.ts +0 -4
  167. package/dist/settings-transaction.js +0 -9
  168. package/dist/settings-transaction.js.map +1 -1
  169. package/dist/setup-form.js +3 -6
  170. package/dist/setup-form.js.map +1 -1
  171. package/dist/setup-i18n.js +0 -3
  172. package/dist/setup-i18n.js.map +1 -1
  173. package/dist/setup-wizard.js +2 -6
  174. package/dist/setup-wizard.js.map +1 -1
  175. package/dist/task-board-tool.d.ts +1 -2
  176. package/dist/task-board-tool.js +6 -62
  177. package/dist/task-board-tool.js.map +1 -1
  178. package/dist/tmux-control.d.ts +1 -2
  179. package/dist/tmux-control.js +4 -4
  180. package/dist/tmux-control.js.map +1 -1
  181. package/dist/tmux-exec-helper.js +1 -32
  182. package/dist/tmux-exec-helper.js.map +1 -1
  183. package/dist/tmux-manager.d.ts +2 -3
  184. package/dist/tmux-manager.js +36 -45
  185. package/dist/tmux-manager.js.map +1 -1
  186. package/dist/tmux-read.js +3 -4
  187. package/dist/tmux-read.js.map +1 -1
  188. package/dist/tool-permissions.js +1 -2
  189. package/dist/tool-permissions.js.map +1 -1
  190. package/dist/topic-commands.js +5 -9
  191. package/dist/topic-commands.js.map +1 -1
  192. package/dist/transcript-delta-reader.d.ts +1 -1
  193. package/dist/transcript-delta-reader.js +1 -1
  194. package/dist/transcript-delta-reader.js.map +1 -1
  195. package/dist/transcript-monitor.d.ts +0 -3
  196. package/dist/transcript-monitor.js +51 -27
  197. package/dist/transcript-monitor.js.map +1 -1
  198. package/dist/transcript-sources.d.ts +0 -5
  199. package/dist/transcript-sources.js +79 -8
  200. package/dist/transcript-sources.js.map +1 -1
  201. package/dist/types.d.ts +3 -29
  202. package/dist/ui/app.html +8 -1
  203. package/dist/ui/backend-signin.js +21 -39
  204. package/dist/ui/chat-ambient.js +1 -1
  205. package/dist/ui/chat-md-strings.js +9 -3
  206. package/dist/ui/chat-render.js +670 -3
  207. package/dist/ui/chat-store.js +3 -14
  208. package/dist/ui/chat-strings.js +285 -3
  209. package/dist/ui/chat-thread.js +969 -3
  210. package/dist/ui/chat-time.js +80 -3
  211. package/dist/ui/chat-tour.js +1 -1
  212. package/dist/ui/fleet-office-pack.d.ts +0 -9
  213. package/dist/ui/fleet-office-pack.js +4 -76
  214. package/dist/ui/fleet-office-skin.js +5 -23
  215. package/dist/ui/fleet-office-store.js +0 -5
  216. package/dist/ui/fleet-office.js +6 -173
  217. package/dist/ui/fleet-task-card.js +4 -8
  218. package/dist/ui/fleet-task-common.js +2 -2
  219. package/dist/ui/fleet-tasks.js +39 -125
  220. package/dist/ui/html-attachment.js +40 -3
  221. package/dist/ui/office-share-page.js +2 -4
  222. package/dist/ui/panel-chat.js +75 -90
  223. package/dist/ui/panel-details.js +2 -3
  224. package/dist/ui/panel-fleet.js +18 -105
  225. package/dist/ui/panel-log.js +2 -2
  226. package/dist/ui/panel-needs.js +4 -162
  227. package/dist/ui/panel-notices.js +4 -3
  228. package/dist/ui/panel-settings.js +202 -281
  229. package/dist/ui/preview-pages.js +1 -1
  230. package/dist/ui/preview-panel.js +13 -21
  231. package/dist/ui/preview-versions.js +1 -1
  232. package/dist/ui/preview.js +476 -3
  233. package/dist/ui/search-dialog.js +3 -4
  234. package/dist/ui/settings-appearance.js +23 -122
  235. package/dist/ui/settings-catalog.js +10 -37
  236. package/dist/ui/settings-confirm.js +1 -3
  237. package/dist/ui/settings-dialogs.js +20 -48
  238. package/dist/ui/settings-guests.js +14 -27
  239. package/dist/ui/settings-model.js +1 -10
  240. package/dist/ui/settings-quota-notice.js +100 -0
  241. package/dist/ui/settings-registry.js +5 -15
  242. package/dist/ui/settings-strings.js +32 -105
  243. package/dist/ui/settings-web-authority.js +13 -18
  244. package/dist/ui/shared/app-i18n.js +2 -2
  245. package/dist/ui/shared/app-route.js +3 -12
  246. package/dist/ui/shared/app-session.js +2 -6
  247. package/dist/ui/shared/app-shell.js +26 -87
  248. package/dist/ui/shared/app-store.js +2 -3
  249. package/dist/ui/shared/app-stream.js +1 -3
  250. package/dist/ui/shared/app.css +5 -1368
  251. package/dist/ui/shared/app.js +5 -17
  252. package/dist/ui/shared/header-tools.js +9 -9
  253. package/dist/ui/shared/instance-nav.js +7 -34
  254. package/dist/ui/shared/instance-switch.js +3 -4
  255. package/dist/ui/shared/loading.css +37 -0
  256. package/dist/ui/shared/signin.js +6 -211
  257. package/dist/ui/shared/tokens.css +0 -1
  258. package/dist/ui/shared/ui-dialog.js +2 -2
  259. package/dist/ui/shared/ui-menu.js +10 -30
  260. package/dist/ui/shared/ui-states.js +14 -3
  261. package/dist/ui/shared/vendor/vendor.json +2 -19
  262. package/dist/ui/shared/view-strings.js +0 -8
  263. package/dist/ui/signin.html +5 -23
  264. package/dist/unattended-install.js +1 -2
  265. package/dist/unattended-install.js.map +1 -1
  266. package/dist/usage/providers.d.ts +5 -0
  267. package/dist/usage/providers.js +6 -2
  268. package/dist/usage/providers.js.map +1 -1
  269. package/dist/usage/quota-notices.d.ts +38 -0
  270. package/dist/usage/quota-notices.js +160 -0
  271. package/dist/usage/quota-notices.js.map +1 -0
  272. package/dist/usage/usage-api.d.ts +1 -0
  273. package/dist/usage/usage-api.js +16 -0
  274. package/dist/usage/usage-api.js.map +1 -1
  275. package/dist/view-api.js +7 -5
  276. package/dist/view-api.js.map +1 -1
  277. package/dist/web-api.d.ts +2 -80
  278. package/dist/web-api.js +10 -513
  279. package/dist/web-api.js.map +1 -1
  280. package/dist/web-backend-login.d.ts +1 -23
  281. package/dist/web-backend-login.js +17 -11
  282. package/dist/web-backend-login.js.map +1 -1
  283. package/dist/web-chat-history.d.ts +2 -19
  284. package/dist/web-chat-history.js +3 -72
  285. package/dist/web-chat-history.js.map +1 -1
  286. package/dist/web-office-skins.d.ts +0 -9
  287. package/dist/web-office-skins.js +1 -16
  288. package/dist/web-office-skins.js.map +1 -1
  289. package/dist/web-only-fleet.d.ts +0 -20
  290. package/dist/web-only-fleet.js +0 -21
  291. package/dist/web-only-fleet.js.map +1 -1
  292. package/dist/web-session.d.ts +0 -42
  293. package/dist/web-session.js +0 -9
  294. package/dist/web-session.js.map +1 -1
  295. package/dist/web-settings-patch.d.ts +1 -3
  296. package/dist/web-settings-patch.js +1 -4
  297. package/dist/web-settings-patch.js.map +1 -1
  298. package/dist/web-shell-routes.d.ts +1 -10
  299. package/dist/web-shell-routes.js +2 -14
  300. package/dist/web-shell-routes.js.map +1 -1
  301. package/dist/web-terminal.d.ts +30 -4
  302. package/dist/web-terminal.js +6 -9
  303. package/dist/web-terminal.js.map +1 -1
  304. package/dist/yaml-shared-nodes.js +3 -8
  305. package/dist/yaml-shared-nodes.js.map +1 -1
  306. package/launcher/agend-install.cjs +1 -8
  307. package/launcher/postinstall.cjs +2 -27
  308. package/package.json +3 -10
  309. package/templates/launchd.plist.ejs +1 -3
  310. package/dist/accounts-view.d.ts +0 -97
  311. package/dist/accounts-view.js +0 -116
  312. package/dist/accounts-view.js.map +0 -1
  313. package/dist/agent-runtime-api.d.ts +0 -4
  314. package/dist/agent-runtime-api.js +0 -59
  315. package/dist/agent-runtime-api.js.map +0 -1
  316. package/dist/agent-runtime-change.d.ts +0 -76
  317. package/dist/agent-runtime-change.js +0 -114
  318. package/dist/agent-runtime-change.js.map +0 -1
  319. package/dist/agent-workspace.d.ts +0 -93
  320. package/dist/agent-workspace.js +0 -353
  321. package/dist/agent-workspace.js.map +0 -1
  322. package/dist/backend/grok-model-cache.d.ts +0 -7
  323. package/dist/backend/grok-model-cache.js +0 -65
  324. package/dist/backend/grok-model-cache.js.map +0 -1
  325. package/dist/backend/native-pane.d.ts +0 -11
  326. package/dist/backend/native-pane.js +0 -59
  327. package/dist/backend/native-pane.js.map +0 -1
  328. package/dist/build-provenance-cli.d.ts +0 -3
  329. package/dist/build-provenance-cli.js +0 -11
  330. package/dist/build-provenance-cli.js.map +0 -1
  331. package/dist/build-provenance.d.ts +0 -20
  332. package/dist/build-provenance.js +0 -67
  333. package/dist/build-provenance.js.map +0 -1
  334. package/dist/build-provenance.json +0 -1
  335. package/dist/claude-auth-status.d.ts +0 -48
  336. package/dist/claude-auth-status.js +0 -149
  337. package/dist/claude-auth-status.js.map +0 -1
  338. package/dist/fleet-windows-runtime.d.ts +0 -98
  339. package/dist/fleet-windows-runtime.js +0 -467
  340. package/dist/fleet-windows-runtime.js.map +0 -1
  341. package/dist/general-binding.d.ts +0 -85
  342. package/dist/general-binding.js +0 -298
  343. package/dist/general-binding.js.map +0 -1
  344. package/dist/general-host-owner.d.ts +0 -8
  345. package/dist/general-host-owner.js +0 -26
  346. package/dist/general-host-owner.js.map +0 -1
  347. package/dist/general-role.d.ts +0 -5
  348. package/dist/general-role.js +0 -27
  349. package/dist/general-role.js.map +0 -1
  350. package/dist/hub/admission.d.ts +0 -28
  351. package/dist/hub/admission.js +0 -62
  352. package/dist/hub/admission.js.map +0 -1
  353. package/dist/hub/attachments/limits.d.ts +0 -20
  354. package/dist/hub/attachments/limits.js +0 -48
  355. package/dist/hub/attachments/limits.js.map +0 -1
  356. package/dist/hub/attachments/store.d.ts +0 -116
  357. package/dist/hub/attachments/store.js +0 -246
  358. package/dist/hub/attachments/store.js.map +0 -1
  359. package/dist/hub/audit/index.d.ts +0 -62
  360. package/dist/hub/audit/index.js +0 -344
  361. package/dist/hub/audit/index.js.map +0 -1
  362. package/dist/hub/browser-certificate.d.ts +0 -38
  363. package/dist/hub/browser-certificate.js +0 -247
  364. package/dist/hub/browser-certificate.js.map +0 -1
  365. package/dist/hub/buttons-model.d.ts +0 -9
  366. package/dist/hub/buttons-model.js +0 -42
  367. package/dist/hub/buttons-model.js.map +0 -1
  368. package/dist/hub/buttons.d.ts +0 -54
  369. package/dist/hub/buttons.js +0 -346
  370. package/dist/hub/buttons.js.map +0 -1
  371. package/dist/hub/catalog.d.ts +0 -74
  372. package/dist/hub/catalog.js +0 -195
  373. package/dist/hub/catalog.js.map +0 -1
  374. package/dist/hub/client.d.ts +0 -69
  375. package/dist/hub/client.js +0 -300
  376. package/dist/hub/client.js.map +0 -1
  377. package/dist/hub/compose/member.d.ts +0 -139
  378. package/dist/hub/compose/member.js +0 -450
  379. package/dist/hub/compose/member.js.map +0 -1
  380. package/dist/hub/compose/server.d.ts +0 -77
  381. package/dist/hub/compose/server.js +0 -349
  382. package/dist/hub/compose/server.js.map +0 -1
  383. package/dist/hub/compose/views.d.ts +0 -12
  384. package/dist/hub/compose/views.js +0 -4
  385. package/dist/hub/compose/views.js.map +0 -1
  386. package/dist/hub/db.d.ts +0 -56
  387. package/dist/hub/db.js +0 -188
  388. package/dist/hub/db.js.map +0 -1
  389. package/dist/hub/directory/config.d.ts +0 -39
  390. package/dist/hub/directory/config.js +0 -108
  391. package/dist/hub/directory/config.js.map +0 -1
  392. package/dist/hub/directory/directory.d.ts +0 -18
  393. package/dist/hub/directory/directory.js +0 -320
  394. package/dist/hub/directory/directory.js.map +0 -1
  395. package/dist/hub/directory/index.d.ts +0 -7
  396. package/dist/hub/directory/index.js +0 -4
  397. package/dist/hub/directory/index.js.map +0 -1
  398. package/dist/hub/directory/profiles.d.ts +0 -41
  399. package/dist/hub/directory/profiles.js +0 -90
  400. package/dist/hub/directory/profiles.js.map +0 -1
  401. package/dist/hub/directory/types.d.ts +0 -71
  402. package/dist/hub/directory/types.js +0 -14
  403. package/dist/hub/directory/types.js.map +0 -1
  404. package/dist/hub/execution/cli-provider.d.ts +0 -23
  405. package/dist/hub/execution/cli-provider.js +0 -341
  406. package/dist/hub/execution/cli-provider.js.map +0 -1
  407. package/dist/hub/execution/index.d.ts +0 -13
  408. package/dist/hub/execution/index.js +0 -8
  409. package/dist/hub/execution/index.js.map +0 -1
  410. package/dist/hub/execution/model-broker.d.ts +0 -24
  411. package/dist/hub/execution/model-broker.js +0 -208
  412. package/dist/hub/execution/model-broker.js.map +0 -1
  413. package/dist/hub/execution/provider-setup.d.ts +0 -11
  414. package/dist/hub/execution/provider-setup.js +0 -21
  415. package/dist/hub/execution/provider-setup.js.map +0 -1
  416. package/dist/hub/execution/sandbox-runner.d.ts +0 -1
  417. package/dist/hub/execution/sandbox-runner.js +0 -221
  418. package/dist/hub/execution/sandbox-runner.js.map +0 -1
  419. package/dist/hub/execution/service.d.ts +0 -136
  420. package/dist/hub/execution/service.js +0 -459
  421. package/dist/hub/execution/service.js.map +0 -1
  422. package/dist/hub/execution/store.d.ts +0 -61
  423. package/dist/hub/execution/store.js +0 -182
  424. package/dist/hub/execution/store.js.map +0 -1
  425. package/dist/hub/execution/views.d.ts +0 -20
  426. package/dist/hub/execution/views.js +0 -48
  427. package/dist/hub/execution/views.js.map +0 -1
  428. package/dist/hub/http/compose.d.ts +0 -20
  429. package/dist/hub/http/compose.js +0 -518
  430. package/dist/hub/http/compose.js.map +0 -1
  431. package/dist/hub/http/core.d.ts +0 -49
  432. package/dist/hub/http/core.js +0 -124
  433. package/dist/hub/http/core.js.map +0 -1
  434. package/dist/hub/http/deps.d.ts +0 -61
  435. package/dist/hub/http/deps.js +0 -2
  436. package/dist/hub/http/deps.js.map +0 -1
  437. package/dist/hub/http/schema.d.ts +0 -14
  438. package/dist/hub/http/schema.js +0 -80
  439. package/dist/hub/http/schema.js.map +0 -1
  440. package/dist/hub/identity.d.ts +0 -32
  441. package/dist/hub/identity.js +0 -80
  442. package/dist/hub/identity.js.map +0 -1
  443. package/dist/hub/init/browser-certificate.d.ts +0 -9
  444. package/dist/hub/init/browser-certificate.js +0 -78
  445. package/dist/hub/init/browser-certificate.js.map +0 -1
  446. package/dist/hub/init/browser-tls.d.ts +0 -18
  447. package/dist/hub/init/browser-tls.js +0 -62
  448. package/dist/hub/init/browser-tls.js.map +0 -1
  449. package/dist/hub/init/cli.d.ts +0 -3
  450. package/dist/hub/init/cli.js +0 -83
  451. package/dist/hub/init/cli.js.map +0 -1
  452. package/dist/hub/init/directory-certificate.d.ts +0 -24
  453. package/dist/hub/init/directory-certificate.js +0 -222
  454. package/dist/hub/init/directory-certificate.js.map +0 -1
  455. package/dist/hub/init/existing-fleet.d.ts +0 -6
  456. package/dist/hub/init/existing-fleet.js +0 -58
  457. package/dist/hub/init/existing-fleet.js.map +0 -1
  458. package/dist/hub/init/handoff.d.ts +0 -8
  459. package/dist/hub/init/handoff.js +0 -36
  460. package/dist/hub/init/handoff.js.map +0 -1
  461. package/dist/hub/init/identity.d.ts +0 -5
  462. package/dist/hub/init/identity.js +0 -24
  463. package/dist/hub/init/identity.js.map +0 -1
  464. package/dist/hub/init/initializer.d.ts +0 -24
  465. package/dist/hub/init/initializer.js +0 -385
  466. package/dist/hub/init/initializer.js.map +0 -1
  467. package/dist/hub/init/inventory.d.ts +0 -17
  468. package/dist/hub/init/inventory.js +0 -33
  469. package/dist/hub/init/inventory.js.map +0 -1
  470. package/dist/hub/init/layout.d.ts +0 -32
  471. package/dist/hub/init/layout.js +0 -53
  472. package/dist/hub/init/layout.js.map +0 -1
  473. package/dist/hub/init/network.d.ts +0 -18
  474. package/dist/hub/init/network.js +0 -104
  475. package/dist/hub/init/network.js.map +0 -1
  476. package/dist/hub/init/plan-digest.d.ts +0 -5
  477. package/dist/hub/init/plan-digest.js +0 -47
  478. package/dist/hub/init/plan-digest.js.map +0 -1
  479. package/dist/hub/init/prompt.d.ts +0 -26
  480. package/dist/hub/init/prompt.js +0 -110
  481. package/dist/hub/init/prompt.js.map +0 -1
  482. package/dist/hub/init/secret-file.d.ts +0 -1
  483. package/dist/hub/init/secret-file.js +0 -59
  484. package/dist/hub/init/secret-file.js.map +0 -1
  485. package/dist/hub/init/text.d.ts +0 -127
  486. package/dist/hub/init/text.js +0 -247
  487. package/dist/hub/init/text.js.map +0 -1
  488. package/dist/hub/init/types.d.ts +0 -114
  489. package/dist/hub/init/types.js +0 -9
  490. package/dist/hub/init/types.js.map +0 -1
  491. package/dist/hub/init/wizard.d.ts +0 -34
  492. package/dist/hub/init/wizard.js +0 -554
  493. package/dist/hub/init/wizard.js.map +0 -1
  494. package/dist/hub/initialization.d.ts +0 -23
  495. package/dist/hub/initialization.js +0 -28
  496. package/dist/hub/initialization.js.map +0 -1
  497. package/dist/hub/member/agent-mode.d.ts +0 -6
  498. package/dist/hub/member/agent-mode.js +0 -23
  499. package/dist/hub/member/agent-mode.js.map +0 -1
  500. package/dist/hub/member/attachments.d.ts +0 -29
  501. package/dist/hub/member/attachments.js +0 -107
  502. package/dist/hub/member/attachments.js.map +0 -1
  503. package/dist/hub/member/exposure-api.d.ts +0 -87
  504. package/dist/hub/member/exposure-api.js +0 -64
  505. package/dist/hub/member/exposure-api.js.map +0 -1
  506. package/dist/hub/member/exposures.d.ts +0 -123
  507. package/dist/hub/member/exposures.js +0 -282
  508. package/dist/hub/member/exposures.js.map +0 -1
  509. package/dist/hub/member/pairing-api.d.ts +0 -85
  510. package/dist/hub/member/pairing-api.js +0 -385
  511. package/dist/hub/member/pairing-api.js.map +0 -1
  512. package/dist/hub/member/quota.d.ts +0 -20
  513. package/dist/hub/member/quota.js +0 -98
  514. package/dist/hub/member/quota.js.map +0 -1
  515. package/dist/hub/member-journal.d.ts +0 -104
  516. package/dist/hub/member-journal.js +0 -336
  517. package/dist/hub/member-journal.js.map +0 -1
  518. package/dist/hub/member-reconciliation.d.ts +0 -14
  519. package/dist/hub/member-reconciliation.js +0 -97
  520. package/dist/hub/member-reconciliation.js.map +0 -1
  521. package/dist/hub/mount.d.ts +0 -39
  522. package/dist/hub/mount.js +0 -123
  523. package/dist/hub/mount.js.map +0 -1
  524. package/dist/hub/pairing-client.d.ts +0 -17
  525. package/dist/hub/pairing-client.js +0 -108
  526. package/dist/hub/pairing-client.js.map +0 -1
  527. package/dist/hub/pairing.d.ts +0 -65
  528. package/dist/hub/pairing.js +0 -211
  529. package/dist/hub/pairing.js.map +0 -1
  530. package/dist/hub/policy.d.ts +0 -63
  531. package/dist/hub/policy.js +0 -147
  532. package/dist/hub/policy.js.map +0 -1
  533. package/dist/hub/preview.d.ts +0 -32
  534. package/dist/hub/preview.js +0 -64
  535. package/dist/hub/preview.js.map +0 -1
  536. package/dist/hub/private-files.d.ts +0 -5
  537. package/dist/hub/private-files.js +0 -44
  538. package/dist/hub/private-files.js.map +0 -1
  539. package/dist/hub/profile.d.ts +0 -59
  540. package/dist/hub/profile.js +0 -73
  541. package/dist/hub/profile.js.map +0 -1
  542. package/dist/hub/protocol.d.ts +0 -779
  543. package/dist/hub/protocol.js +0 -93
  544. package/dist/hub/protocol.js.map +0 -1
  545. package/dist/hub/quota.d.ts +0 -24
  546. package/dist/hub/quota.js +0 -112
  547. package/dist/hub/quota.js.map +0 -1
  548. package/dist/hub/representative.d.ts +0 -141
  549. package/dist/hub/representative.js +0 -528
  550. package/dist/hub/representative.js.map +0 -1
  551. package/dist/hub/retention.d.ts +0 -17
  552. package/dist/hub/retention.js +0 -45
  553. package/dist/hub/retention.js.map +0 -1
  554. package/dist/hub/rooms.d.ts +0 -120
  555. package/dist/hub/rooms.js +0 -535
  556. package/dist/hub/rooms.js.map +0 -1
  557. package/dist/hub/runtime.d.ts +0 -106
  558. package/dist/hub/runtime.js +0 -298
  559. package/dist/hub/runtime.js.map +0 -1
  560. package/dist/hub/server.d.ts +0 -56
  561. package/dist/hub/server.js +0 -202
  562. package/dist/hub/server.js.map +0 -1
  563. package/dist/hub/service-start.d.ts +0 -17
  564. package/dist/hub/service-start.js +0 -109
  565. package/dist/hub/service-start.js.map +0 -1
  566. package/dist/hub/sessions.d.ts +0 -53
  567. package/dist/hub/sessions.js +0 -196
  568. package/dist/hub/sessions.js.map +0 -1
  569. package/dist/hub/socket.d.ts +0 -24
  570. package/dist/hub/socket.js +0 -79
  571. package/dist/hub/socket.js.map +0 -1
  572. package/dist/hub/store.d.ts +0 -77
  573. package/dist/hub/store.js +0 -88
  574. package/dist/hub/store.js.map +0 -1
  575. package/dist/hub/tls.d.ts +0 -31
  576. package/dist/hub/tls.js +0 -135
  577. package/dist/hub/tls.js.map +0 -1
  578. package/dist/hub/types.d.ts +0 -401
  579. package/dist/hub/types.js +0 -2
  580. package/dist/hub/types.js.map +0 -1
  581. package/dist/hub/validation.d.ts +0 -33
  582. package/dist/hub/validation.js +0 -136
  583. package/dist/hub/validation.js.map +0 -1
  584. package/dist/hub/version-floor.d.ts +0 -10
  585. package/dist/hub/version-floor.js +0 -45
  586. package/dist/hub/version-floor.js.map +0 -1
  587. package/dist/hub-browser-certificate-policy.d.ts +0 -31
  588. package/dist/hub-browser-certificate-policy.js +0 -25
  589. package/dist/hub-browser-certificate-policy.js.map +0 -1
  590. package/dist/hub-membership-settings.d.ts +0 -6
  591. package/dist/hub-membership-settings.js +0 -21
  592. package/dist/hub-membership-settings.js.map +0 -1
  593. package/dist/hub-mode.d.ts +0 -15
  594. package/dist/hub-mode.js +0 -17
  595. package/dist/hub-mode.js.map +0 -1
  596. package/dist/hub-pairing-summary.d.ts +0 -48
  597. package/dist/hub-pairing-summary.js +0 -36
  598. package/dist/hub-pairing-summary.js.map +0 -1
  599. package/dist/hub-report-target.d.ts +0 -32
  600. package/dist/hub-report-target.js +0 -33
  601. package/dist/hub-report-target.js.map +0 -1
  602. package/dist/hub-retention-policy.d.ts +0 -14
  603. package/dist/hub-retention-policy.js +0 -15
  604. package/dist/hub-retention-policy.js.map +0 -1
  605. package/dist/launchd-service-domain.d.ts +0 -5
  606. package/dist/launchd-service-domain.js +0 -21
  607. package/dist/launchd-service-domain.js.map +0 -1
  608. package/dist/local-pane-endpoint.d.ts +0 -46
  609. package/dist/local-pane-endpoint.js +0 -189
  610. package/dist/local-pane-endpoint.js.map +0 -1
  611. package/dist/local-pane-launcher.d.ts +0 -2
  612. package/dist/local-pane-launcher.js +0 -38
  613. package/dist/local-pane-launcher.js.map +0 -1
  614. package/dist/local-pane-stage.ps1 +0 -101
  615. package/dist/model-catalog-defaults.d.ts +0 -14
  616. package/dist/model-catalog-defaults.js +0 -20
  617. package/dist/model-catalog-defaults.js.map +0 -1
  618. package/dist/native-agent-admission.d.ts +0 -7
  619. package/dist/native-agent-admission.js +0 -26
  620. package/dist/native-agent-admission.js.map +0 -1
  621. package/dist/native-daemon-client.d.ts +0 -13
  622. package/dist/native-daemon-client.js +0 -28
  623. package/dist/native-daemon-client.js.map +0 -1
  624. package/dist/native-instance-api.d.ts +0 -13
  625. package/dist/native-instance-api.js +0 -217
  626. package/dist/native-instance-api.js.map +0 -1
  627. package/dist/native-instance-input.d.ts +0 -87
  628. package/dist/native-instance-input.js +0 -59
  629. package/dist/native-instance-input.js.map +0 -1
  630. package/dist/native-instance-registry.d.ts +0 -38
  631. package/dist/native-instance-registry.js +0 -129
  632. package/dist/native-instance-registry.js.map +0 -1
  633. package/dist/native-mcp.d.ts +0 -43
  634. package/dist/native-mcp.js +0 -291
  635. package/dist/native-mcp.js.map +0 -1
  636. package/dist/native-model-catalog.d.ts +0 -5
  637. package/dist/native-model-catalog.js +0 -76
  638. package/dist/native-model-catalog.js.map +0 -1
  639. package/dist/native-pane-crash-budget.d.ts +0 -20
  640. package/dist/native-pane-crash-budget.js +0 -60
  641. package/dist/native-pane-crash-budget.js.map +0 -1
  642. package/dist/native-pane-create.d.ts +0 -26
  643. package/dist/native-pane-create.js +0 -53
  644. package/dist/native-pane-create.js.map +0 -1
  645. package/dist/native-pane-lifecycle.d.ts +0 -73
  646. package/dist/native-pane-lifecycle.js +0 -239
  647. package/dist/native-pane-lifecycle.js.map +0 -1
  648. package/dist/native-pane-manager.d.ts +0 -59
  649. package/dist/native-pane-manager.js +0 -592
  650. package/dist/native-pane-manager.js.map +0 -1
  651. package/dist/native-pane-output-log.d.ts +0 -15
  652. package/dist/native-pane-output-log.js +0 -108
  653. package/dist/native-pane-output-log.js.map +0 -1
  654. package/dist/native-pane-reconciliation.d.ts +0 -4
  655. package/dist/native-pane-reconciliation.js +0 -40
  656. package/dist/native-pane-reconciliation.js.map +0 -1
  657. package/dist/native-pane-recovery.d.ts +0 -12
  658. package/dist/native-pane-recovery.js +0 -10
  659. package/dist/native-pane-recovery.js.map +0 -1
  660. package/dist/native-pane-tools.d.ts +0 -2
  661. package/dist/native-pane-tools.js +0 -14
  662. package/dist/native-pane-tools.js.map +0 -1
  663. package/dist/office-talk.d.ts +0 -26
  664. package/dist/office-talk.js +0 -50
  665. package/dist/office-talk.js.map +0 -1
  666. package/dist/pane-port.d.ts +0 -118
  667. package/dist/pane-port.js +0 -2
  668. package/dist/pane-port.js.map +0 -1
  669. package/dist/runtime-api.d.ts +0 -10
  670. package/dist/runtime-api.js +0 -189
  671. package/dist/runtime-api.js.map +0 -1
  672. package/dist/runtime-assets/@xterm/addon-serialize/LICENSE +0 -21
  673. package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.js +0 -2
  674. package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.mjs +0 -19
  675. package/dist/runtime-assets/@xterm/addon-serialize/package.json +0 -29
  676. package/dist/runtime-assets/@xterm/headless/LICENSE +0 -21
  677. package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.js +0 -2
  678. package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.mjs +0 -46
  679. package/dist/runtime-assets/@xterm/headless/package.json +0 -29
  680. package/dist/runtime-assets/node-pty/CONPTY-LICENSE +0 -21
  681. package/dist/runtime-assets/node-pty/CONPTY-NOTICE.md +0 -503
  682. package/dist/runtime-assets/node-pty/LICENSE +0 -69
  683. package/dist/runtime-assets/node-pty/lib/conpty_console_list_agent.js +0 -16
  684. package/dist/runtime-assets/node-pty/lib/eventEmitter2.js +0 -47
  685. package/dist/runtime-assets/node-pty/lib/index.js +0 -52
  686. package/dist/runtime-assets/node-pty/lib/interfaces.js +0 -7
  687. package/dist/runtime-assets/node-pty/lib/shared/conout.js +0 -11
  688. package/dist/runtime-assets/node-pty/lib/terminal.js +0 -190
  689. package/dist/runtime-assets/node-pty/lib/types.js +0 -7
  690. package/dist/runtime-assets/node-pty/lib/unixTerminal.js +0 -346
  691. package/dist/runtime-assets/node-pty/lib/utils.js +0 -39
  692. package/dist/runtime-assets/node-pty/lib/windowsConoutConnection.js +0 -125
  693. package/dist/runtime-assets/node-pty/lib/windowsPtyAgent.js +0 -320
  694. package/dist/runtime-assets/node-pty/lib/windowsTerminal.js +0 -199
  695. package/dist/runtime-assets/node-pty/lib/worker/conoutSocketWorker.js +0 -22
  696. package/dist/runtime-assets/node-pty/package.json +0 -64
  697. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  698. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  699. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
  700. package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  701. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  702. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  703. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty.node +0 -0
  704. package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
  705. package/dist/runtime-build-info.d.ts +0 -9
  706. package/dist/runtime-build-info.js +0 -59
  707. package/dist/runtime-build-info.js.map +0 -1
  708. package/dist/runtime-bundle.d.ts +0 -22
  709. package/dist/runtime-bundle.js +0 -158
  710. package/dist/runtime-bundle.js.map +0 -1
  711. package/dist/runtime-cli.d.ts +0 -14
  712. package/dist/runtime-cli.js +0 -271
  713. package/dist/runtime-cli.js.map +0 -1
  714. package/dist/runtime-client-store.d.ts +0 -75
  715. package/dist/runtime-client-store.js +0 -372
  716. package/dist/runtime-client-store.js.map +0 -1
  717. package/dist/runtime-client.d.ts +0 -174
  718. package/dist/runtime-client.js +0 -1010
  719. package/dist/runtime-client.js.map +0 -1
  720. package/dist/runtime-connection.d.ts +0 -22
  721. package/dist/runtime-connection.js +0 -93
  722. package/dist/runtime-connection.js.map +0 -1
  723. package/dist/runtime-coordinator.d.ts +0 -98
  724. package/dist/runtime-coordinator.js +0 -1089
  725. package/dist/runtime-coordinator.js.map +0 -1
  726. package/dist/runtime-credential-profiles.d.ts +0 -78
  727. package/dist/runtime-credential-profiles.js +0 -280
  728. package/dist/runtime-credential-profiles.js.map +0 -1
  729. package/dist/runtime-general-protocol.d.ts +0 -22
  730. package/dist/runtime-general-protocol.js +0 -57
  731. package/dist/runtime-general-protocol.js.map +0 -1
  732. package/dist/runtime-general-workspace.d.ts +0 -17
  733. package/dist/runtime-general-workspace.js +0 -213
  734. package/dist/runtime-general-workspace.js.map +0 -1
  735. package/dist/runtime-install.d.ts +0 -50
  736. package/dist/runtime-install.js +0 -186
  737. package/dist/runtime-install.js.map +0 -1
  738. package/dist/runtime-pane-assets.d.ts +0 -17
  739. package/dist/runtime-pane-assets.js +0 -23
  740. package/dist/runtime-pane-assets.js.map +0 -1
  741. package/dist/runtime-pane-boot.d.ts +0 -9
  742. package/dist/runtime-pane-boot.js +0 -47
  743. package/dist/runtime-pane-boot.js.map +0 -1
  744. package/dist/runtime-pane-codex-index.d.ts +0 -10
  745. package/dist/runtime-pane-codex-index.js +0 -151
  746. package/dist/runtime-pane-codex-index.js.map +0 -1
  747. package/dist/runtime-pane-config.d.ts +0 -16
  748. package/dist/runtime-pane-config.js +0 -54
  749. package/dist/runtime-pane-config.js.map +0 -1
  750. package/dist/runtime-pane-host.d.ts +0 -63
  751. package/dist/runtime-pane-host.js +0 -467
  752. package/dist/runtime-pane-host.js.map +0 -1
  753. package/dist/runtime-pane-job-owner.d.ts +0 -1
  754. package/dist/runtime-pane-job-owner.js +0 -29
  755. package/dist/runtime-pane-job-owner.js.map +0 -1
  756. package/dist/runtime-pane-job.ps1 +0 -116
  757. package/dist/runtime-pane-link.d.ts +0 -47
  758. package/dist/runtime-pane-link.js +0 -337
  759. package/dist/runtime-pane-link.js.map +0 -1
  760. package/dist/runtime-pane-mcp-protocol.d.ts +0 -32
  761. package/dist/runtime-pane-mcp-protocol.js +0 -39
  762. package/dist/runtime-pane-mcp-protocol.js.map +0 -1
  763. package/dist/runtime-pane-mcp-relay.d.ts +0 -18
  764. package/dist/runtime-pane-mcp-relay.js +0 -80
  765. package/dist/runtime-pane-mcp-relay.js.map +0 -1
  766. package/dist/runtime-pane-mcp.d.ts +0 -55
  767. package/dist/runtime-pane-mcp.js +0 -153
  768. package/dist/runtime-pane-mcp.js.map +0 -1
  769. package/dist/runtime-pane-native.d.ts +0 -75
  770. package/dist/runtime-pane-native.js +0 -114
  771. package/dist/runtime-pane-native.js.map +0 -1
  772. package/dist/runtime-pane-output.d.ts +0 -19
  773. package/dist/runtime-pane-output.js +0 -53
  774. package/dist/runtime-pane-output.js.map +0 -1
  775. package/dist/runtime-pane-port.d.ts +0 -144
  776. package/dist/runtime-pane-port.js +0 -574
  777. package/dist/runtime-pane-port.js.map +0 -1
  778. package/dist/runtime-pane-private.d.ts +0 -14
  779. package/dist/runtime-pane-private.js +0 -51
  780. package/dist/runtime-pane-private.js.map +0 -1
  781. package/dist/runtime-pane-private.ps1 +0 -14
  782. package/dist/runtime-pane-process.d.ts +0 -4
  783. package/dist/runtime-pane-process.js +0 -117
  784. package/dist/runtime-pane-process.js.map +0 -1
  785. package/dist/runtime-pane-protocol.d.ts +0 -182
  786. package/dist/runtime-pane-protocol.js +0 -217
  787. package/dist/runtime-pane-protocol.js.map +0 -1
  788. package/dist/runtime-pane-quota-history.d.ts +0 -6
  789. package/dist/runtime-pane-quota-history.js +0 -44
  790. package/dist/runtime-pane-quota-history.js.map +0 -1
  791. package/dist/runtime-pane-screen.d.ts +0 -19
  792. package/dist/runtime-pane-screen.js +0 -76
  793. package/dist/runtime-pane-screen.js.map +0 -1
  794. package/dist/runtime-pane-service.d.ts +0 -16
  795. package/dist/runtime-pane-service.js +0 -256
  796. package/dist/runtime-pane-service.js.map +0 -1
  797. package/dist/runtime-pane-stdio-protocol.d.ts +0 -54
  798. package/dist/runtime-pane-stdio-protocol.js +0 -94
  799. package/dist/runtime-pane-stdio-protocol.js.map +0 -1
  800. package/dist/runtime-pane-stdio.d.ts +0 -3
  801. package/dist/runtime-pane-stdio.js +0 -123
  802. package/dist/runtime-pane-stdio.js.map +0 -1
  803. package/dist/runtime-pane-store.d.ts +0 -33
  804. package/dist/runtime-pane-store.js +0 -164
  805. package/dist/runtime-pane-store.js.map +0 -1
  806. package/dist/runtime-pane-transcript-protocol.d.ts +0 -43
  807. package/dist/runtime-pane-transcript-protocol.js +0 -58
  808. package/dist/runtime-pane-transcript-protocol.js.map +0 -1
  809. package/dist/runtime-pane-transcript.d.ts +0 -50
  810. package/dist/runtime-pane-transcript.js +0 -666
  811. package/dist/runtime-pane-transcript.js.map +0 -1
  812. package/dist/runtime-pane-worker.d.ts +0 -1
  813. package/dist/runtime-pane-worker.js +0 -167
  814. package/dist/runtime-pane-worker.js.map +0 -1
  815. package/dist/runtime-protocol.d.ts +0 -177
  816. package/dist/runtime-protocol.js +0 -49
  817. package/dist/runtime-protocol.js.map +0 -1
  818. package/dist/runtime-switches.d.ts +0 -79
  819. package/dist/runtime-switches.js +0 -90
  820. package/dist/runtime-switches.js.map +0 -1
  821. package/dist/runtime-transcript-delta-reader.d.ts +0 -23
  822. package/dist/runtime-transcript-delta-reader.js +0 -74
  823. package/dist/runtime-transcript-delta-reader.js.map +0 -1
  824. package/dist/runtime-transcript-source.d.ts +0 -36
  825. package/dist/runtime-transcript-source.js +0 -189
  826. package/dist/runtime-transcript-source.js.map +0 -1
  827. package/dist/runtime-update-activity.d.ts +0 -21
  828. package/dist/runtime-update-activity.js +0 -51
  829. package/dist/runtime-update-activity.js.map +0 -1
  830. package/dist/runtime-update-artifact.d.ts +0 -31
  831. package/dist/runtime-update-artifact.js +0 -252
  832. package/dist/runtime-update-artifact.js.map +0 -1
  833. package/dist/runtime-update-install.d.ts +0 -31
  834. package/dist/runtime-update-install.js +0 -205
  835. package/dist/runtime-update-install.js.map +0 -1
  836. package/dist/runtime-update-journal.d.ts +0 -44
  837. package/dist/runtime-update-journal.js +0 -121
  838. package/dist/runtime-update-journal.js.map +0 -1
  839. package/dist/runtime-update-lock.d.ts +0 -48
  840. package/dist/runtime-update-lock.js +0 -217
  841. package/dist/runtime-update-lock.js.map +0 -1
  842. package/dist/runtime-update-owners.d.ts +0 -29
  843. package/dist/runtime-update-owners.js +0 -240
  844. package/dist/runtime-update-owners.js.map +0 -1
  845. package/dist/runtime-update-protocol.d.ts +0 -15
  846. package/dist/runtime-update-protocol.js +0 -13
  847. package/dist/runtime-update-protocol.js.map +0 -1
  848. package/dist/runtime-windows-bridge.d.ts +0 -42
  849. package/dist/runtime-windows-bridge.js +0 -167
  850. package/dist/runtime-windows-bridge.js.map +0 -1
  851. package/dist/service-drain.d.ts +0 -19
  852. package/dist/service-drain.js +0 -64
  853. package/dist/service-drain.js.map +0 -1
  854. package/dist/settings-route-lifetime.d.ts +0 -14
  855. package/dist/settings-route-lifetime.js +0 -48
  856. package/dist/settings-route-lifetime.js.map +0 -1
  857. package/dist/tmux-binary.d.ts +0 -59
  858. package/dist/tmux-binary.js +0 -200
  859. package/dist/tmux-binary.js.map +0 -1
  860. package/dist/tmux-pane-host.d.ts +0 -26
  861. package/dist/tmux-pane-host.js +0 -25
  862. package/dist/tmux-pane-host.js.map +0 -1
  863. package/dist/transcript-needles.d.ts +0 -42
  864. package/dist/transcript-needles.js +0 -166
  865. package/dist/transcript-needles.js.map +0 -1
  866. package/dist/transcript-records.d.ts +0 -21
  867. package/dist/transcript-records.js +0 -141
  868. package/dist/transcript-records.js.map +0 -1
  869. package/dist/ui/account-identity.js +0 -27
  870. package/dist/ui/accounts-view.js +0 -214
  871. package/dist/ui/agent-proposals.js +0 -90
  872. package/dist/ui/agent-runtime-settings.js +0 -68
  873. package/dist/ui/agent-runtime.js +0 -124
  874. package/dist/ui/cli-picker.js +0 -42
  875. package/dist/ui/credential-profile-select.js +0 -54
  876. package/dist/ui/fleet-office-dialogue.d.ts +0 -30
  877. package/dist/ui/fleet-office-dialogue.js +0 -382
  878. package/dist/ui/fleet-task-menu.js +0 -188
  879. package/dist/ui/fleet-task-stats.js +0 -124
  880. package/dist/ui/fleet-task-view.js +0 -340
  881. package/dist/ui/general-location.js +0 -71
  882. package/dist/ui/hub-agent-copy.js +0 -23
  883. package/dist/ui/hub-button-actions.js +0 -65
  884. package/dist/ui/hub-chat-store.js +0 -158
  885. package/dist/ui/hub-chat-strings.js +0 -77
  886. package/dist/ui/hub-client.js +0 -329
  887. package/dist/ui/hub-owner-strings.js +0 -51
  888. package/dist/ui/hub-provider-guidance.js +0 -31
  889. package/dist/ui/hub-quota.js +0 -85
  890. package/dist/ui/hub-strings.js +0 -162
  891. package/dist/ui/hub-switcher.js +0 -27
  892. package/dist/ui/native-instance-settings.js +0 -44
  893. package/dist/ui/native-pane-recovery.js +0 -45
  894. package/dist/ui/panel-hub.js +0 -619
  895. package/dist/ui/panel-runtimes.js +0 -425
  896. package/dist/ui/panel-windows-agent.js +0 -86
  897. package/dist/ui/panel-windows-cli.js +0 -189
  898. package/dist/ui/provider-marks.js +0 -83
  899. package/dist/ui/runtime-catalog.js +0 -55
  900. package/dist/ui/runtime-commands.d.ts +0 -4
  901. package/dist/ui/runtime-commands.js +0 -59
  902. package/dist/ui/settings-access-strings.js +0 -42
  903. package/dist/ui/settings-agent-filters.js +0 -94
  904. package/dist/ui/settings-build-version.js +0 -35
  905. package/dist/ui/settings-devices.js +0 -253
  906. package/dist/ui/settings-hub-browser-certificate.js +0 -74
  907. package/dist/ui/settings-hub-membership.js +0 -72
  908. package/dist/ui/settings-hub-retention.js +0 -52
  909. package/dist/ui/settings-hub.js +0 -196
  910. package/dist/ui/settings-layout.js +0 -242
  911. package/dist/ui/settings-outside.js +0 -242
  912. package/dist/ui/settings-shared-strings.js +0 -143
  913. package/dist/ui/settings-shared.js +0 -345
  914. package/dist/ui/settings-shortcuts.js +0 -65
  915. package/dist/ui/shared/accounts.css +0 -15
  916. package/dist/ui/shared/chat-composer.js +0 -68
  917. package/dist/ui/shared/chat-md-strings.js +0 -9
  918. package/dist/ui/shared/chat-render.js +0 -682
  919. package/dist/ui/shared/chat-strings.js +0 -333
  920. package/dist/ui/shared/chat-thread.js +0 -969
  921. package/dist/ui/shared/chat-time.js +0 -80
  922. package/dist/ui/shared/html-attachment.js +0 -42
  923. package/dist/ui/shared/hub-chat.css +0 -85
  924. package/dist/ui/shared/hub-owner.css +0 -33
  925. package/dist/ui/shared/hub-quota.css +0 -8
  926. package/dist/ui/shared/hub.css +0 -71
  927. package/dist/ui/shared/machine-name.js +0 -13
  928. package/dist/ui/shared/page-head.js +0 -8
  929. package/dist/ui/shared/preview.js +0 -476
  930. package/dist/ui/shared/qrcode.module.js +0 -2237
  931. package/dist/ui/shared/runtime-install.css +0 -26
  932. package/dist/ui/shared/settings-width.js +0 -31
  933. package/dist/ui/shared/side-rail.js +0 -15
  934. package/dist/ui/shared/task-board.css +0 -95
  935. package/dist/ui/shared/vendor/qrcode-generator.LICENSE +0 -27
  936. package/dist/ui/shared/workspace.css +0 -20
  937. package/dist/ui/windows-agents-nav.js +0 -2
  938. package/dist/ui/windows-cli-catalog.js +0 -108
  939. package/dist/ui/workspace-panel.js +0 -189
  940. package/dist/ui/workspace-strings.js +0 -35
  941. package/dist/web-device-login.d.ts +0 -153
  942. package/dist/web-device-login.js +0 -278
  943. package/dist/web-device-login.js.map +0 -1
  944. package/dist/web-remote-access.d.ts +0 -152
  945. package/dist/web-remote-access.js +0 -368
  946. package/dist/web-remote-access.js.map +0 -1
  947. package/dist/windows-agents.d.ts +0 -127
  948. package/dist/windows-agents.js +0 -491
  949. package/dist/windows-agents.js.map +0 -1
  950. package/dist/windows-cli-api.d.ts +0 -34
  951. package/dist/windows-cli-api.js +0 -185
  952. package/dist/windows-cli-api.js.map +0 -1
  953. package/dist/windows-cli-providers.d.ts +0 -106
  954. package/dist/windows-cli-providers.js +0 -424
  955. package/dist/windows-cli-providers.js.map +0 -1
  956. package/dist/windows-cli-worker.d.ts +0 -83
  957. package/dist/windows-cli-worker.js +0 -570
  958. package/dist/windows-cli-worker.js.map +0 -1
  959. package/dist/windows-cli.d.ts +0 -126
  960. package/dist/windows-cli.js +0 -416
  961. package/dist/windows-cli.js.map +0 -1
  962. package/dist/windows-native-mcp.d.ts +0 -19
  963. package/dist/windows-native-mcp.js +0 -143
  964. package/dist/windows-native-mcp.js.map +0 -1
  965. package/dist/windows-pane-mcp.d.ts +0 -26
  966. package/dist/windows-pane-mcp.js +0 -187
  967. package/dist/windows-pane-mcp.js.map +0 -1
  968. package/launcher/launchd-session-type.cjs +0 -14
  969. package/launcher/tmux-bundle.cjs +0 -247
  970. package/templates/runtime-assets/conpty-LICENSE +0 -21
  971. package/templates/runtime-assets/conpty-NOTICE.md +0 -503
  972. package/templates/runtime-assets/local-pane-stage.ps1 +0 -101
  973. package/templates/runtime-assets/runtime-pane-job.ps1 +0 -116
  974. package/templates/runtime-assets/runtime-pane-private.ps1 +0 -14
  975. package/templates/runtime-assets/xterm-LICENSE +0 -21
  976. package/templates/runtime-install/install.ps1 +0 -270
@@ -1,10 +1,6 @@
1
- import { readInstalledBuild } from "./build-provenance.js";
2
- import { buildNativeFleetInstructions } from "./instructions.js";
3
- import { GENERAL_ROLE_INSTRUCTIONS } from "./general-role.js";
4
- import { NativePaneRecoveryError } from "./native-pane-recovery.js";
5
- import { nativePaneToolAvailable, nativePaneToolRefusal } from "./native-pane-tools.js";
6
- import { windowsAgentsFor } from "./windows-agents.js";
7
- import { AgentRuntimeChanges } from "./agent-runtime-change.js";
1
+ import { QuotaNotices } from "./usage/quota-notices.js";
2
+ import { quotaNoticeRule } from "./quota-notice-config.js";
3
+ import { onUsageSnapshot } from "./usage/usage-api.js";
8
4
  import { supportsVerifiedKiroMigration, getCachedKiroCliCompatibility, kiroV1DbPath, kiroV2SessionsDir } from "./backend/kiro.js";
9
5
  import { listKiroV1Sessions, readKiroClassicLinks } from "./backend/kiro-identity.js";
10
6
  import { readKiroV1SavedModel } from "./backend/kiro-retired-model.js";
@@ -17,17 +13,10 @@ import { WorkingRecoveryCoordinator, WORKING_RECOVERY_KIND, buildWorkingRecovery
17
13
  import { PUBLIC_TUNNEL_TOKEN_ENV, PUBLIC_TUNNEL_TOKEN_HINT, normalizePublicTunnelToken } from "./public-tunnel-config.js";
18
14
  import { humanOwnerObjectId, webHumanOrigin } from "./human-delivery-receipts.js";
19
15
  import { SettingsBaselines } from "./settings-baseline.js";
20
- import { hubMembershipSettings } from "./hub-membership-settings.js";
21
- import { prepareSettingsEffect } from "./settings-effect.js";
22
- import { settingsDisplay } from "./settings-change.js";
23
- import { SettingsConfirmationError, SettingsConfirmationStore, settingsSourceLabel } from "./settings-confirmation.js";
16
+ import { SettingsConfirmationStore, settingsSourceLabel } from "./settings-confirmation.js";
24
17
  import { SettingsHttpConfirmation } from "./settings-http-confirmation.js";
25
18
  import { SettingsControlServer } from "./settings-control.js";
26
- import { SettingsRouteLifetime } from "./settings-route-lifetime.js";
27
- import { GeneralManagedFileError } from "./runtime-general-protocol.js";
28
- import { GeneralBindings, generalBindingRevision, parseGeneralSwitchInput, switchGeneralLocation } from "./general-binding.js";
29
- import { confirmGeneralHostExit } from "./general-host-owner.js";
30
- import { settingsFingerprint, SettingsExecution, settingsRevision, noteSettingsWrite, settingsFileResource, assertSettingsLease, trySettingsLease, waitSettingsLease, settingsUndo, undoSettingsPaths } from "./settings-transaction.js";
19
+ import { SettingsExecution, settingsRevision, noteSettingsWrite, settingsFileResource, assertSettingsLease, trySettingsLease, waitSettingsLease, settingsUndo, undoSettingsPaths } from "./settings-transaction.js";
31
20
  import { performance } from "node:perf_hooks";
32
21
  import { withSpawnHoldReply } from "./spawn-hold-reply.js";
33
22
  import { gatewayRequestContext } from "./web-request-context.js";
@@ -47,8 +36,6 @@ import { InstallStream } from "./install-stream.js";
47
36
  import { namedTestLane, runNamedLinkTest } from "./tunnel/named-link-test.js";
48
37
  import { daemonTopicMode, isWebOnlyFleetConfig } from "./web-only-fleet.js";
49
38
  import { OFFICE_SHARE_TUNNEL_DIR, OfficeShareService } from "./office-share-service.js";
50
- import { OfficeTalk } from "./office-talk.js";
51
- import { pairingSummary } from "./hub-pairing-summary.js";
52
39
  import { ALL_OFFICES, WHOLE_FLEET } from "./office-share.js";
53
40
  import { TunnelPurposeLane } from "./tunnel/purpose-lane.js";
54
41
  import { ManagedTunnel } from "./tunnel/manager.js";
@@ -59,7 +46,7 @@ import { ProfileControlServer } from "./profile-control.js";
59
46
  import { existsSync, readFileSync, mkdirSync, writeFileSync, unlinkSync, rmSync, readdirSync, renameSync, copyFileSync, chmodSync, statSync, realpathSync, lstatSync, closeSync } from "node:fs";
60
47
  import { atomicWriteFileSync } from "./atomic-write.js";
61
48
  import { createHash, randomBytes, randomUUID } from "node:crypto";
62
- import { freemem, totalmem, homedir, hostname } from "node:os";
49
+ import { freemem, totalmem, homedir } from "node:os";
63
50
  import { effectiveCpuCount } from "./cpu-quota.js";
64
51
  import { createServer } from "node:http";
65
52
  import { Worker } from "node:worker_threads";
@@ -85,11 +72,6 @@ import { tmuxExecHelper } from "./tmux-exec-helper.js";
85
72
  import { kiroLogGate } from "./kiro-log-gate.js";
86
73
  const __filename = fileURLToPath(import.meta.url);
87
74
  const __dirname = dirname(__filename);
88
- // Describe this running program, not files an updater may replace later. No reads on dashboard polls.
89
- const RUNNING_BUILD = readInstalledBuild();
90
- if (RUNNING_BUILD.source)
91
- Object.freeze(RUNNING_BUILD.source);
92
- Object.freeze(RUNNING_BUILD);
93
75
  /** Fallback access policy for a channel with no `access:` block — open (no gate). */
94
76
  const DEFAULT_OPEN_ACCESS = { mode: "open", allowed_users: [], max_pending_codes: 0, code_expiry_minutes: 0 };
95
77
  import { STATUS_EMOJI_CONFIG_KEYS, STATUS_EMOJI_KEYS, STATUS_EMOJI_SUGGESTIONS, TELEGRAM_REACTION_EMOJIS, builtinStatusEmojis, customEmojiValue, emojiImageUrl, normalizeEmoji, previewStatusEmojis, reactionForm, reactionMatchKey, resolveStatusEmojis, statusAvoidList, statusEmojiProblem, statusMatchKey, statusMatchKeys, textForm, } from "./status-emojis.js";
@@ -107,8 +89,7 @@ import { startEventLoopWatch } from "./event-loop-watch.js";
107
89
  import { TmuxManager } from "./tmux-manager.js";
108
90
  import { AccessManager } from "./channel/access-manager.js";
109
91
  import { IpcClient } from "./channel/ipc-bridge.js";
110
- import { NativeDaemonClient } from "./native-daemon-client.js";
111
- import { usesChannelAddress, TOOLS } from "./channel/mcp-tools.js";
92
+ import { usesChannelAddress } from "./channel/mcp-tools.js";
112
93
  import { ParkedNotices, PARKED_NOTICE_LEDGER_FILE } from "./parked-notices.js";
113
94
  import { formatFleetTime } from "./tz-utils.js";
114
95
  import { createAdapter } from "./channel/factory.js";
@@ -119,7 +100,6 @@ import { createBackend } from "./backend/factory.js";
119
100
  import { readEffortMetadata } from "./backend/effort-metadata.js";
120
101
  import { codexEffort, flagEffort } from "./backend/launch-effort.js";
121
102
  import { codexMetadataHomes, readCodexModelEffortLevels, resolveCodexLaunchModel } from "./backend/codex-metadata.js";
122
- import { readGrokModelCache } from "./backend/grok-model-cache.js";
123
103
  import { CLI_ENV_TTL_MS, isModelCompatible, noteResolvedBinary, resolveBinary, SYSINFO_BACKEND_IDS, UnsupportedCliError, ResumeBlockedError } from "./backend/types.js";
124
104
  import { createLogger, rotateLogIfNeeded } from "./logger.js";
125
105
  import { processAttachments } from "./channel/attachment-handler.js";
@@ -140,7 +120,6 @@ import { InstanceLifecycle, SupersededStartError, BACKEND_INSTALLATION_INFO, che
140
120
  import { TopicArchiver } from "./topic-archiver.js";
141
121
  import { StatuslineWatcher } from "./statusline-watcher.js";
142
122
  import { applyInstanceConfigPatch, outboundHandlers } from "./outbound-handlers.js";
143
- import { HUB_REPORT_TARGET } from "./hub-report-target.js";
144
123
  import { backendArgs, backendStep, joinBackendArgs, modelNameCarried, planDisplay } from "./backend-command.js";
145
124
  import { DeliveryOutbox, DEFAULT_REPLY_OBLIGATION_TTL_MS, isSilentScheduleDelivery } from "./delivery-outbox.js";
146
125
  // The target Daemon can legitimately wait 30 minutes for a busy pane before
@@ -209,24 +188,11 @@ export function applyTaskListCap(tasks, filterAssignee, filterStatus) {
209
188
  return tasks;
210
189
  }
211
190
  import { handleWebRequest, webPeerOf, broadcastSseEvent, broadcastToGuests, SSE_HEARTBEAT_MS } from "./web-api.js";
212
- import { windowsCliEnabled, closeWindowsBridge, inventoryProbed, LOCAL_WINDOWS_CLI_OFF } from "./windows-cli.js";
213
- import { nativeCliVersionState } from "./runtime-pane-native.js";
214
- import { localWindowsCliEnabled, remoteRuntimeEnabled, setConfiguredRuntimeSwitches, maxAgentsPerMachine } from "./runtime-switches.js";
215
- import { RuntimeInstallService } from "./runtime-install.js";
216
- import { RUNTIME_CREDENTIAL_CAPABILITY } from "./runtime-credential-profiles.js";
217
- import { RuntimeCoordinator } from "./runtime-coordinator.js";
218
- import { RuntimeWindowsBridge } from "./runtime-windows-bridge.js";
219
- import { FleetWindowsRuntime } from "./fleet-windows-runtime.js";
220
- import { NativeMcpSessions } from "./native-mcp.js";
221
- import { nativeProviderMcpSupported } from "./windows-cli-providers.js";
222
- import { isWindowsInstance, isNativePaneInstance, isNativeExecInstance, WINDOWS_CONFIG_REFUSED, nativeCredentialProfile } from "./native-instance-registry.js";
223
- import { createNativeWindowsInstance, updateNativeWindowsInstance } from "./native-instance-api.js";
224
- import { NativeInstanceConfigEdit, parseNativeInstanceCreate } from "./native-instance-input.js";
225
191
  import { LogService } from "./log-service.js";
226
192
  import { viewHistory } from "./view-history.js";
227
193
  import { WebChatHistory, WEB_CHAT_TEXT_MAX, isWebMessageId, newWebMessageId, webChatFrom } from "./web-chat-history.js";
228
194
  import { WEB_ECHO_COPIES_MAX, WEB_MIRROR_TIMEOUT_MS, webReactionEmoji, webReactionProblem } from "./web-reactions.js";
229
- import { WebChatDiskStore, isSafeInstanceDirName } from "./web-chat-store.js";
195
+ import { WebChatDiskStore } from "./web-chat-store.js";
230
196
  import { SeenUsers, resolveMentions } from "./web-mentions.js";
231
197
  import { OPERATOR, WebPrefsStore } from "./web-prefs.js";
232
198
  import { agendVersion } from "./agent-whats-new.js";
@@ -257,7 +223,7 @@ import { discordGeneralChannelId } from "./discord-general.js";
257
223
  import { parseEnvText } from "./env-file.js";
258
224
  import { isDisallowedIntentsError, isRejectedTokenError } from "./discord-permissions.js";
259
225
  import { filterUsageProviders, formatDiscordUsageActivity, getUsageSnapshot, handleUsageRequest, isUsagePath, setCapacityLookup, usageProviderIdForBackend } from "./usage/usage-api.js";
260
- import { LOGIN_FLOWS, checkAuthStatus, checkAuthIdentity, authIdentitySupported, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
226
+ import { LOGIN_FLOWS, checkAuthStatus, LOGIN_BACKEND_ALIASES } from "./login-flows.js";
261
227
  import { LoginSession } from "./login-manager.js";
262
228
  import { runOnLoginShell } from "./login-shell.js";
263
229
  import { tightenInstanceDirs } from "./private-dir.js";
@@ -324,10 +290,8 @@ import { mayUseTool, resolveToolSet, scheduleOpRefusal, toolForIpcType, toolRefu
324
290
  import { authorizeSession, decideWebGate, loadOrCreateWebToken, readSessionCookies, readWebToken, sessionCookieUpgrade } from "./web-auth.js";
325
291
  import { bypassesWebGate, handleAuthRequest, serveGuestRefusedPage, serveSigninPage } from "./auth-api.js";
326
292
  import { isWebPageNavigation } from "./web-shell-routes.js";
327
- import { guestSessionPolicy, localSessionPolicy, sessionVia, tokenEpoch, WebSessionStore } from "./web-session.js";
293
+ import { guestSessionPolicy, localSessionPolicy, tokenEpoch, WebSessionStore } from "./web-session.js";
328
294
  import { WebNotices, WEB_NOTICES_FILE } from "./web-notices.js";
329
- import { DeviceLoginRequests } from "./web-device-login.js";
330
- import { isOutsideLinkOwner, OUTSIDE_LINK_OWNER, OUTSIDE_LINK_RETRY_MS, OUTSIDE_OWN_CLOSES, outsideDuration, outsideTimer, TOTP_STEP_SECONDS, WEB_REMOTE_ACCESS_FILE, WebRemoteAccess } from "./web-remote-access.js";
331
295
  import { authorityNow, evaluateWebAuthority, HOST_OPERATOR, VERIFIED_SESSION_HOURS, webAuthorityPolicy } from "./authority/web-authority.js";
332
296
  import { AuthorityStateStore } from "./authority/authority-state.js";
333
297
  import { SessionAuthorizations } from "./authority/session-authorizations.js";
@@ -349,10 +313,9 @@ import { PROVIDER_SECRET_SPECS, PUBLIC_TUNNEL_SECRET_SPEC, providerSecretSpec, p
349
313
  const SELF_RESTART_DEADLINE_MS = 300_000;
350
314
  import { APPLY_FLEET_TARGET, ApplyJobStore, viewOf, } from "./apply-job.js";
351
315
  import { kiroHome } from "./backend/kiro-kas-store.js";
352
- import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileEnv, credentialProfileLogin, credentialProfileLoginCommand, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile, listConfiguredProfiles } from "./backend/credential-profile.js";
316
+ import { CHAT_PROFILE_LOGIN_BACKENDS, credentialHomeSpec, credentialProfileHome, credentialProfileLogin, credentialProfileLoginCommand, credentialProfileStoreHome, credentialSwitchCarriesConversation, credentialSwitchStartsFresh, instanceCredentialProfile, listConfiguredProfiles } from "./backend/credential-profile.js";
353
317
  import { carryClaudeConversation } from "./backend/claude-carry.js";
354
- import { classicProfile, mergeBackendOptions, profileKey, profileName, profileOf, readClassicBindings } from "./classic-bindings.js";
355
- import { ACCOUNT_CLIS, PROFILE_CLIS, buildAccountsView, profilesOnDisk, weeklyUsage } from "./accounts-view.js";
318
+ import { mergeBackendOptions, profileKey, profileName, profileOf } from "./classic-bindings.js";
356
319
  import { classifyInstanceChange, CLASSIC_HOT_CONFIG_KEYS, HOT_INSTANCE_CONFIG_KEYS, hotConfigUpdate, } from "./instance-config-impact.js";
357
320
  import { cleanupWatchVerdict, formatRestartProgressCompletion, RestartProgress, } from "./restart-progress.js";
358
321
  import { FleetStartup } from "./fleet-startup.js";
@@ -805,1403 +768,7 @@ export class FleetManager {
805
768
  /** Live view of lifecycle.daemons — used throughout; not deprecated. */
806
769
  get daemons() { return this.lifecycle.daemons; }
807
770
  fleetConfig = null;
808
- fleetWindowsRuntime = null;
809
- nativeMcpStore = null;
810
- paneManager = null;
811
- localPaneRelay = null;
812
- openingLocalPane = null;
813
- runtimeHub = null;
814
- runtimeInstall;
815
- /** One service for the public installer routes and the administrator API: the hash the dashboard shows is the hash of what is sent. */
816
- runtimeInstaller() { return this.runtimeInstall ??= new RuntimeInstallService(); }
817
- runtimeExecutor = null;
818
- runtimeStatusScheduled = false;
819
- reconciledRemoteResults = new Set();
820
- /** Machine credentials are separate from dashboard sessions and per-turn agent capabilities. */
821
- runtimeCoordinator() {
822
- if (!remoteRuntimeEnabled())
823
- throw new Error("Remote runtimes are not enabled");
824
- return this.runtimeHub ??= new RuntimeCoordinator(this.dataDir, {
825
- handleMcp: (machineId, runId, body) => this.runtimeWindowsExecutor().mcp(machineId, runId, body),
826
- handlePaneMcp: async (machineId, body, current) => (await this.nativePanes()).handle(machineId, body, current),
827
- warn: (fields, message) => this.logger.warn(fields, message),
828
- changed: event => {
829
- if (event?.type === "machine" && this.runtimeHub?.machine(event.machineId)?.status !== "online") {
830
- for (const [name, config] of Object.entries(this.fleetConfig?.instances ?? {}))
831
- if (config.machine_id === event.machineId) {
832
- this.nativeMcpStore?.closeInstance(name);
833
- if (config.general_topic) {
834
- this.cancelPendingDeliveries(name);
835
- this.holdGeneralOldDeliveries(name);
836
- }
837
- }
838
- this.needsYou?.poke();
839
- }
840
- if (this.runtimeStatusScheduled || this.shuttingDown)
841
- return;
842
- this.runtimeStatusScheduled = true;
843
- queueMicrotask(() => {
844
- this.runtimeStatusScheduled = false;
845
- if (this.shuttingDown)
846
- return;
847
- try {
848
- this.fleetWindowsRuntime?.importHistory();
849
- for (const snapshot of this.runtimeHub?.tasks() ?? [])
850
- this.reconcileRemoteDelivery(snapshot.machineId, snapshot.task);
851
- this.emitSseEvent("status", this.getUiStatus());
852
- this.scheduleDeliveryOutboxPump();
853
- }
854
- catch (error) {
855
- this.logger.warn({ err: error }, "Runtime status reconciliation failed");
856
- }
857
- });
858
- },
859
- });
860
- }
861
- runtimeWindowsExecutor() {
862
- return this.runtimeExecutor ??= new RuntimeWindowsBridge(this, () => remoteRuntimeEnabled() && !this.shuttingDown ? this.runtimeCoordinator() : null);
863
- }
864
- /** Per-turn capabilities for native MCP, separate from browser and Linux-agent credentials. */
865
- nativeMcpSessions() {
866
- return this.nativeMcpStore ??= new NativeMcpSessions(this);
867
- }
868
- /** Retire only existing native capabilities; ordinary lifecycle changes do not instantiate the Windows path. */
869
- invalidateNativeMcp(name) { this.nativeMcpStore?.closeInstance(name); }
870
- /** The same instance registry and delivery entry point, with a native Windows execution adapter. */
871
- nativeInstanceRuntime() {
872
- return this.fleetWindowsRuntime ??= new FleetWindowsRuntime({
873
- owner: this, fleet: () => this.fleetConfig, save: () => this.saveFleetConfig(), stopping: () => this.shuttingDown, history: this.webChatHistory,
874
- openMcp: (instance, requestId, sandbox, stillCurrent) => this.healthServerListening
875
- ? this.nativeMcpSessions().grant({ instance, requestId, sandbox, stillCurrent, endpoint: `http://127.0.0.1:${this.fleetConfig?.health_port ?? 19280}/agent` }) : null,
876
- warn: (message, error) => this.logger.warn({ err: error }, message),
877
- removed: name => this.nativeInstanceRemoved(name),
878
- paneCurrent: name => this.daemons.get(name)?.nativePaneCurrent?.() === true,
879
- machineAvailable: id => remoteRuntimeEnabled() && this.runtimeCoordinator().machine(id)?.status === "online",
880
- machineGeneration: id => this.runtimeCoordinator().machine(id)?.bootId ?? null,
881
- paneAvailable: id => this.runtimeCoordinator().machine(id)?.paneProtocol === 1,
882
- validateMachine: (id, cli, sandbox, credentialProfile) => {
883
- const machine = this.runtimeCoordinator().machine(id);
884
- const provider = machine?.inventory?.clis.find(row => row.id === cli && row.runnable && row.supports.resume);
885
- if (!machine || machine.status !== "online" || machine.platform !== "win32" || !provider)
886
- throw new Error("Choose an online runtime and an available CLI");
887
- if (sandbox === "read-only" && !provider.readOnlySupport)
888
- throw new Error("This runtime CLI cannot enforce read-only execution");
889
- if (credentialProfile !== undefined) {
890
- // Fail closed at selection time, not at the first message: an old Runtime would ignore the name and run another account.
891
- if (!machine.runtimeInfo?.capabilities.includes(RUNTIME_CREDENTIAL_CAPABILITY))
892
- throw new Error("This computer's Runtime is too old to choose a Claude account. Update the Runtime on that computer, then try again.");
893
- const row = machine.inventory?.credentialProfiles?.profiles.find(profile => profile.name === credentialProfile);
894
- if (!row)
895
- throw new Error(`This computer has not reported a credential profile named "${credentialProfile}". On that computer run: agend-runtime login claude-code ${credentialProfile}, then choose Detect again in that computer's CLI diagnostics (Settings → Runtime).`);
896
- if (row.loggedIn === false)
897
- throw new Error(`Credential profile "${credentialProfile}" is not signed in on that computer. Sign in there: agend-runtime login claude-code ${credentialProfile}`);
898
- }
899
- },
900
- emit: (event, data) => {
901
- this.emitSseEvent(event, event === "status" ? this.getUiStatus() : data);
902
- if (event === "activity")
903
- this.scheduleDeliveryOutboxPump();
904
- },
905
- }, { bridge: this.runtimeWindowsExecutor() });
906
- }
907
- agentRuntimeChangeService;
908
- agentRuntimeChanges() {
909
- if (this.agentRuntimeChangeService)
910
- return this.agentRuntimeChangeService;
911
- const windowPins = new Map();
912
- return this.agentRuntimeChangeService ??= new AgentRuntimeChanges({
913
- config: name => Object.hasOwn(this.fleetConfig?.instances ?? {}, name) ? this.fleetConfig.instances[name] : undefined,
914
- defaults: () => this.fleetConfig?.defaults,
915
- generation: () => [this.managerBootId, this.settingsGeneration],
916
- epoch: name => this.lifecycle.epochOf(name),
917
- backendState: (name, backend) => !isWindowsInstance(this.fleetConfig?.instances[name]) ? this.backendAvailabilityFor(name).find(row => row.backend === backend)?.state : undefined,
918
- transition: (name, run) => this.lifecycle.runTransition(name, async (transition) => {
919
- const config = this.fleetConfig?.instances[name];
920
- if (!isWindowsInstance(config)) {
921
- const path = join(this.getInstanceDir(name), "window-id");
922
- let pin;
923
- if (existsSync(path)) {
924
- if (statSync(path).size > 128)
925
- throw Error("Invalid saved CLI window identity");
926
- pin = readFileSync(path, "utf8").trim();
927
- if (!/^@\d+$/.test(pin))
928
- throw Error("Invalid saved CLI window identity");
929
- }
930
- else if (this.daemons.has(name))
931
- throw Error("The current CLI window identity is unavailable");
932
- windowPins.set(name, pin);
933
- }
934
- try {
935
- return await run(async () => {
936
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
937
- this.lifecycle.invalidate(name);
938
- this.nativeInstanceRuntime().setPaused(name, true);
939
- }
940
- else
941
- await this.stopInstance(name, transition);
942
- });
943
- }
944
- finally {
945
- windowPins.delete(name);
946
- }
947
- }),
948
- idle: name => {
949
- if (this.shuttingDown || this.lifecycle.hasWorkLease(name) || this.deliveryOutbox?.countForTarget(name, ["queued", "retry_wait", "delivering", "submission_started", "reconciliation_pending", "uncertain"]))
950
- return false;
951
- const config = this.fleetConfig?.instances[name];
952
- if (!config)
953
- return false;
954
- if (isWindowsInstance(config)) {
955
- try {
956
- const agent = this.nativeInstanceRuntime().get(name);
957
- if (!agent || agent.blocked || agent.activeId && !isNativePaneInstance(config))
958
- return false;
959
- }
960
- catch {
961
- return false;
962
- }
963
- }
964
- if (!this.daemons.has(name) && !isWindowsInstance(config) && existsSync(join(this.getInstanceDir(name), "window-id")))
965
- return false;
966
- return !this.daemons.has(name) || this.getInstanceExecutionState(name) === "idle";
967
- },
968
- prepare: async (name, before, target, current) => {
969
- if (!current())
970
- throw Error("Runtime change authority expired");
971
- if (isWindowsInstance(before))
972
- this.nativeInstanceRuntime().importHistory(name);
973
- if (target.runtime === "windows")
974
- return this.nativeInstanceRuntime().prepareRuntimeChange(name, before, target, current);
975
- const sameAccount = !isWindowsInstance(before) && target.backend === (before.backend || this.fleetConfig?.defaults?.backend || "claude-code");
976
- const availability = (sameAccount ? this.backendAvailabilityFor(name) : this.backendAvailabilityRows()).find(row => row.backend === target.backend);
977
- if (availability && ["not_installed", "not_signed_in", "too_old"].includes(availability.state))
978
- throw Error("Choose an available CLI on the Fleet host");
979
- const supplied = target.directory.trim(), cwd = supplied || join(this.dataDir, "workspaces", name);
980
- if (!isAbsolute(cwd) || /^[A-Za-z]:[\\/]/.test(cwd) || /[\x00-\x1f]/.test(cwd))
981
- throw Error("Enter an absolute Linux working directory or leave it empty");
982
- if (!current())
983
- throw Error("Runtime change authority expired");
984
- if (supplied) {
985
- if (!statSync(cwd).isDirectory())
986
- throw Error("The Linux working directory is not a directory");
987
- }
988
- else
989
- mkdirSync(cwd, { recursive: true, mode: 0o700 });
990
- const config = { ...before, runtime: "wsl", working_directory: cwd, backend: target.backend, model: target.model };
991
- if (before.backend !== target.backend)
992
- config.model_failover = [];
993
- for (const key of ["machine_id", "native_agent_id", "native_adapter", "native_sandbox"])
994
- delete config[key];
995
- if (!sameAccount)
996
- delete config.backend_options;
997
- return { config, discard: () => { } };
998
- },
999
- closed: async (name, before) => {
1000
- if (this.daemons.has(name))
1001
- return false;
1002
- if (isWindowsInstance(before))
1003
- return !this.nativeInstanceRuntime().get(name)?.activeId;
1004
- // An unreadable tmux state is not evidence that the old process exited.
1005
- const session = getTmuxSession();
1006
- if (!await TmuxManager.sessionExistsStrict(session))
1007
- return true;
1008
- const pin = windowPins.get(name);
1009
- return !(await TmuxManager.listWindowsStrict(session)).some(window => window.name === name || pin && window.id === pin);
1010
- },
1011
- fresh: (name, requestId) => {
1012
- const dir = this.getInstanceDir(name), moved = [];
1013
- const restore = () => { for (const [source, saved] of moved.reverse())
1014
- if (!existsSync(source) && existsSync(saved))
1015
- renameSync(saved, source); };
1016
- try {
1017
- for (const file of ["session-id", "session-id.backend"]) {
1018
- const source = join(dir, file), saved = join(dir, `${file}.runtime-${requestId}`);
1019
- if (existsSync(source)) {
1020
- if (existsSync(saved))
1021
- throw Error("Session backup already exists");
1022
- renameSync(source, saved);
1023
- moved.push([source, saved]);
1024
- }
1025
- }
1026
- return restore;
1027
- }
1028
- catch (error) {
1029
- restore();
1030
- throw error;
1031
- }
1032
- },
1033
- save: (name, before, next) => {
1034
- if (this.fleetConfig?.instances[name] !== before)
1035
- throw Error("Agent settings changed before saving Runtime");
1036
- // Mark the replacement stopped before committing its config. A marker failure must not leave a saved
1037
- // Windows config referring to a profile the preparation rollback would then remove.
1038
- writePausedMarker(this.getInstanceDir(name), Date.now(), "operator");
1039
- const fleet = this.fleetConfig;
1040
- fleet.instances[name] = next;
1041
- try {
1042
- this.saveFleetConfig();
1043
- }
1044
- catch (error) {
1045
- fleet.instances[name] = before;
1046
- throw error;
1047
- }
1048
- // A move deliberately leaves the replacement stopped; its next explicit Start opens the fresh CLI.
1049
- },
1050
- changed: name => { this.instanceStateCache.delete(name); this.instanceProcessStatus.delete(name); this.emitSseEvent("status", this.getUiStatus()); },
1051
- });
1052
- }
1053
- /** Explicit dashboard opt-in, with the same General confirmation and ordinary topic/Daemon routing. */
1054
- async createNativePaneInstance(profile, channelId, authorized, execution) {
1055
- const { createNativePane } = await import("./native-pane-create.js");
1056
- return createNativePane({ fleet: () => this.fleetConfig, runtime: this.nativeInstanceRuntime(),
1057
- topic: target => {
1058
- const world = this.worlds.get(target.id), adapter = world?.adapter;
1059
- if (!world || !adapter?.createTopic || !adapter.deleteTopic)
1060
- throw Error("The approved connection cannot create and compensate a topic");
1061
- const same = () => {
1062
- const channels = this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : []);
1063
- const matches = channels.filter(row => (row.id ?? row.type) === target.id);
1064
- return !this.shuttingDown && this.worlds.get(target.id) === world && world.adapter === adapter && matches.length === 1 && matches[0].type === target.type && matches[0].group_id === target.group_id;
1065
- };
1066
- return { current: same, create: name => adapter.createTopic(name), remove: id => adapter.deleteTopic(id) };
1067
- },
1068
- epoch: name => this.lifecycle.epochOf(name),
1069
- start: async (name, config, current) => {
1070
- if (!current())
1071
- throw Error("Native creation expired before start");
1072
- await this.startInstance(name, config, true);
1073
- if (!current())
1074
- throw Error("Native creation expired during start");
1075
- await this.connectIpcToInstance(name);
1076
- if (!current())
1077
- throw Error("Native creation expired during channel connection");
1078
- },
1079
- close: (name, config, epoch) => this.lifecycle.runTransition(name, async (transition) => {
1080
- if (this.fleetConfig?.instances[name] !== config || this.lifecycle.epochOf(name) !== epoch)
1081
- return false;
1082
- await this.stopInstance(name, transition);
1083
- return this.fleetConfig?.instances[name] === config && this.lifecycle.epochOf(name) === epoch + 1;
1084
- }),
1085
- routes: () => { if (this.fleetConfig)
1086
- this.routing.rebuild(this.fleetConfig); },
1087
- warn: error => this.logger.warn({ err: error }, "Native creation cleanup is held; retained identity and topic require attention"), }, profile, channelId, () => !this.shuttingDown && authorized(), execution);
1088
- }
1089
- /** Admin-only settings path: validate first, then close the exact old launch before changing cold fields. */
1090
- async updateNativePaneConfig(name, patch, authorized, transaction) {
1091
- return this.lifecycle.runTransition(name, async (transition) => {
1092
- const before = this.fleetConfig?.instances[name];
1093
- if (!isNativePaneInstance(before))
1094
- throw Error("Persistent Windows instance not found");
1095
- const runtime = this.nativeInstanceRuntime(), generation = this.explicitStopGeneration.get(name) ?? 0;
1096
- let expectedGeneration = generation;
1097
- const current = () => authorized() && !this.shuttingDown && this.fleetConfig?.instances[name] === before
1098
- && (this.explicitStopGeneration.get(name) ?? 0) === expectedGeneration
1099
- && (before.machine_id ? remoteRuntimeEnabled() : localWindowsCliEnabled());
1100
- if (!current())
1101
- throw Error("Native settings authority expired");
1102
- runtime.previewUpdate(name, patch);
1103
- const cold = ["working_directory", "model", "systemPrompt", "native_sandbox"].some(key => Object.hasOwn(patch, key) && (patch[key] ?? "") !== (before[key] ?? ""));
1104
- const running = this.daemons.get(name);
1105
- if (cold) {
1106
- if (running && !running.nativePaneHasExited() && (!await running.nativePauseReady({ current, accepts: () => true, revalidate: async () => current() }) || !running.nativePauseStillReady()))
1107
- throw Error("Wait for this Windows agent to be idle before changing its launch settings");
1108
- if (!current())
1109
- throw Error("Native settings authority expired before closing its old pane");
1110
- await this.stopInstance(name, transition);
1111
- expectedGeneration = generation + 1;
1112
- if (!current())
1113
- throw Error("Native settings authority expired after closing its old pane");
1114
- if (runtime.get(name)?.activeId)
1115
- throw Error("Native pane exit is unconfirmed; its settings were not changed");
1116
- }
1117
- const result = runtime.update(name, patch, current, transaction), next = this.fleetConfig.instances[name];
1118
- if (cold && running) {
1119
- if (!authorized() || this.shuttingDown || this.fleetConfig?.instances[name] !== next || (this.explicitStopGeneration.get(name) ?? 0) !== expectedGeneration)
1120
- throw Error("Native settings saved; restart authority expired");
1121
- await this.startInstance(name, next, true, "fleet-topic", false, transition);
1122
- }
1123
- return result;
1124
- });
1125
- }
1126
- /** The local machine's inventory as last used to open its pane relay (status only; never a launch decision). */
1127
- localPaneInventory = null;
1128
- nativeCliStatus(cli, machineId, daemon) {
1129
- if (cli !== "codex" && cli !== "claude-code")
1130
- return {};
1131
- // The version the running pane actually launched; only without one, the machine's last inventory.
1132
- let running;
1133
- try {
1134
- running = daemon?.usesNativePane?.() ? daemon.nativePaneCliVersion() : undefined;
1135
- }
1136
- catch {
1137
- running = undefined;
1138
- }
1139
- const inventory = machineId ? this.runtimeHub?.machine(machineId)?.inventory : this.localPaneInventory;
1140
- const version = running ?? inventory?.clis?.find(item => item.id === cli && item.runnable)?.version;
1141
- if (version === undefined || version === null)
1142
- return {};
1143
- const result = nativeCliVersionState(cli, version);
1144
- return { ...(result.version ? { native_cli_version: result.version } : {}), native_cli_verification: result.state };
1145
- }
1146
- async localNativePaneEndpoint() {
1147
- const admit = () => { if (this.shuttingDown || this.localPaneDrain || !localWindowsCliEnabled())
1148
- throw Error("Local Windows runtime is switched off or draining"); };
1149
- admit();
1150
- if (this.localPaneDrain)
1151
- throw Error("Local Windows runtime cleanup is pending");
1152
- if (this.localPaneRelay?.online())
1153
- return this.localPaneRelay;
1154
- if (this.openingLocalPane)
1155
- return this.openingLocalPane;
1156
- const opening = (async () => {
1157
- const [{ LocalPaneEndpoint }, { launchLocalPaneRelay, localPaneScope }, inventory] = await Promise.all([
1158
- import("./local-pane-endpoint.js"), import("./local-pane-launcher.js"), this.runtimeWindowsExecutor().probe({ force: false }),
1159
- ]);
1160
- admit();
1161
- this.localPaneRelay?.stop();
1162
- this.localPaneInventory = inventory;
1163
- const relay = await LocalPaneEndpoint.open({ scope: localPaneScope(this.dataDir), admit, maxAgents: maxAgentsPerMachine,
1164
- diagnostic: text => this.logger.warn({ stderr: text }, "Local Windows pane relay wrote to stderr"),
1165
- version: provider => inventory.clis.find(row => row.id === provider && row.runnable)?.version ?? null,
1166
- launch: () => launchLocalPaneRelay(this.dataDir, admit),
1167
- mcp: async (body, current) => (await this.nativePanes()).handle("local", body, current),
1168
- });
1169
- try {
1170
- admit();
1171
- this.localPaneRelay = relay;
1172
- return relay;
1173
- }
1174
- catch (error) {
1175
- relay.stop();
1176
- throw error;
1177
- }
1178
- })();
1179
- this.openingLocalPane = opening;
1180
- try {
1181
- return await opening;
1182
- }
1183
- finally {
1184
- if (this.openingLocalPane === opening)
1185
- this.openingLocalPane = null;
1186
- }
1187
- }
1188
- /** Persistent ownership is independent of the exec worker's per-turn capabilities. */
1189
- async nativePanes() {
1190
- if (this.paneManager)
1191
- return this.paneManager;
1192
- const { NativePaneManager } = await import("./native-pane-manager.js");
1193
- if (this.paneManager)
1194
- return this.paneManager;
1195
- const store = windowsAgentsFor(this, this.runtimeWindowsExecutor());
1196
- const allowed = (name, tool) => nativePaneToolAvailable(tool) && mayUseTool(resolveToolSet(this.fleetConfig?.instances[name], name), tool)
1197
- && !dashboardRefusalIn(this.fleetConfig, name, tool);
1198
- return this.paneManager = new NativePaneManager({
1199
- home: this.dataDir, store, config: name => this.fleetConfig?.instances[name],
1200
- freshNotice: async (name, provider) => {
1201
- const message = t(provider === "codex" ? "native_pane.codex_fresh_started" : "native_pane.fresh_started");
1202
- this.eventLog?.insert(name, "native_pane_fresh_started", {});
1203
- this.logger.info({ name }, message);
1204
- await this.notifyInstanceTopic(name, message);
1205
- },
1206
- admit: (name, config) => {
1207
- const now = this.fleetConfig?.instances[name];
1208
- if (this.shuttingDown || !isNativePaneInstance(now) || now?.native_agent_id !== config.native_agent_id
1209
- || now?.machine_id !== config.machine_id || now?.working_directory !== config.working_directory || now?.backend !== config.backend
1210
- || now?.model !== config.model || now?.native_sandbox !== config.native_sandbox || now?.systemPrompt !== config.systemPrompt || now?.workflow !== config.workflow
1211
- || now?.general_topic !== config.general_topic || now?.native_general_binding !== config.native_general_binding
1212
- || nativeCredentialProfile(now) !== nativeCredentialProfile(config))
1213
- throw Error("Native pane configuration or lifetime changed");
1214
- // Fail closed: a Runtime that does not report the capability would start the terminal on the computer's own account.
1215
- if (nativeCredentialProfile(config) && !this.runtimeCoordinator().machine(config.machine_id)?.runtimeInfo?.capabilities.includes(RUNTIME_CREDENTIAL_CAPABILITY))
1216
- throw Error("This computer's Runtime is too old to choose a Claude account. Update the Runtime on that computer, then try again.");
1217
- if (config.machine_id ? this.remotePaneDrain || !remoteRuntimeEnabled() : this.localPaneDrain || !localWindowsCliEnabled())
1218
- throw Error("Native runtime is switched off");
1219
- },
1220
- endpoint: async (agent, cleanup = false) => {
1221
- if (!agent.machineId) {
1222
- if (!cleanup)
1223
- return this.localNativePaneEndpoint();
1224
- if (!this.localPaneRelay)
1225
- throw Error("Local cleanup transport is unavailable; pane remains reserved");
1226
- return this.localPaneRelay;
1227
- }
1228
- const hub = cleanup ? this.runtimeHub : this.runtimeCoordinator(), machineId = agent.machineId;
1229
- if (!hub)
1230
- throw Error("Remote cleanup transport is unavailable; pane remains reserved");
1231
- const draining = () => !!this.remotePaneDrain;
1232
- return { machineId, online: () => !this.shuttingDown && (draining() || remoteRuntimeEnabled()) && this.runtimeHub === hub && hub.machine(machineId)?.status === "online",
1233
- call: request => {
1234
- if (this.runtimeHub !== hub || !(draining() || remoteRuntimeEnabled()) || draining() && request.action !== "snapshot" && request.action !== "close")
1235
- return Promise.reject(Error("Remote pane transport only permits owned cleanup"));
1236
- return hub.callPane(machineId, request, maxAgentsPerMachine());
1237
- }, subscribe: listener => hub.subscribe(listener),
1238
- epoch: () => hub.paneTransportEpoch(machineId), version: provider => hub.machine(machineId)?.inventory?.clis.find(row => row.id === provider && row.runnable)?.version ?? null };
1239
- },
1240
- instructions: (name, config) => buildNativeFleetInstructions({ instanceName: name, workingDirectory: config.working_directory,
1241
- displayName: config.display_name, description: config.description, customPrompt: config.systemPrompt,
1242
- workflow: config.workflow, taskBoard: !dashboardRefusalIn(this.fleetConfig, name, "task"),
1243
- runtimeIdentity: { kind: "fleet-topic", backend: config.backend ?? "codex", model: config.model || "default" } }, config.general_topic === true),
1244
- menu: name => TOOLS.filter(tool => allowed(name, tool.name)).map(tool => ({ ...tool })),
1245
- allowed: (name, tool, args) => allowed(name, tool) && !nativePaneToolRefusal(tool, args), published: (name, daemon) => this.daemons.get(name) === daemon,
1246
- invoke: async (daemon, input, current, name) => {
1247
- if (input.tool === "list_models") {
1248
- const { ListModelsArgs } = await import("./outbound-schemas.js");
1249
- const parsed = ListModelsArgs.safeParse(input.args);
1250
- if (!parsed.success || !current())
1251
- return { result: null, error: "Invalid native model request or expired owner" };
1252
- const target = parsed.data.instance_name ?? name;
1253
- if (!isWindowsInstance(this.fleetConfig?.instances[target]))
1254
- return { result: null, error: "Native panes can only read Windows runtime model inventories" };
1255
- return { result: await this.listModelCatalog({ instanceName: target }) };
1256
- }
1257
- const ops = { list_instances: "list", describe_instance: "describe", task: "task", send_to_instance: "send",
1258
- delegate_task: "delegate", request_information: "ask", report_result: "report", delivery_status: "delivery-status" };
1259
- const { nativeAgentAdmissionContext } = await import("./native-agent-admission.js");
1260
- const { dispatchAgentOperation } = await import("./agent-endpoint.js");
1261
- return daemon.handlePaneToolCall(input, { current, allowed: tool => allowed(name, tool),
1262
- dispatch: (tool, args) => ops[tool] ? dispatchAgentOperation(nativeAgentAdmissionContext(this, {
1263
- instance: name, current: () => current() && allowed(name, tool), taskAllowed: () => current() && allowed(name, "task"),
1264
- }), name, ops[tool], args, input.operationId) : undefined });
1265
- },
1266
- });
1267
- }
1268
- /** Prepare only bundled role files; this neither switches General nor starts a provider process. */
1269
- async prepareWindowsGeneralWorkspace(name, machineId, cli, bindingId, authorized) {
1270
- const config = this.fleetConfig?.instances[name], hub = this.runtimeCoordinator(), epoch = hub.paneTransportEpoch(machineId);
1271
- if (!config?.general_topic || !epoch)
1272
- throw Error("General or its paired runtime is unavailable");
1273
- const current = () => authorized() && !this.shuttingDown && remoteRuntimeEnabled() && this.runtimeHub === hub
1274
- && this.fleetConfig?.instances[name] === config && hub.paneTransportEpoch(machineId) === epoch;
1275
- const [{ loadGeneralKnowledge }, { validateGeneralWorkspaceRequest, validateGeneralWorkspaceReceipt, generalWorkspaceUpgrade }, { assertRunnableNativePaneVersion }] = await Promise.all([
1276
- import("./runtime-general-workspace.js"), import("./runtime-general-protocol.js"), import("./runtime-pane-native.js"),
1277
- ]);
1278
- if (!current())
1279
- throw Error("General workspace authority expired");
1280
- assertRunnableNativePaneVersion(cli, hub.machine(machineId)?.inventory?.clis.find(row => row.id === cli && row.runnable)?.version ?? null);
1281
- const request = validateGeneralWorkspaceRequest({ action: "general-workspace", bindingId, instance: name, cli, packHash: loadGeneralKnowledge(cli).hash });
1282
- const inventory = await hub.callPane(machineId, { action: "list" }, maxAgentsPerMachine());
1283
- if (!current())
1284
- throw Error("General workspace authority expired");
1285
- if (inventory?.capabilities?.generalWorkspace !== true)
1286
- throw new NativePaneRecoveryError({ kind: "host-upgrade" });
1287
- const result = await hub.callPane(machineId, request, maxAgentsPerMachine());
1288
- if (!current())
1289
- throw Error("General workspace authority expired");
1290
- if (generalWorkspaceUpgrade(result, request))
1291
- throw new NativePaneRecoveryError({ kind: "host-upgrade" });
1292
- return validateGeneralWorkspaceReceipt(result, request);
1293
- }
1294
- /** S3c (S-55): the member's exposure API, attached by S5's member composition (null: no Hub member here). */
1295
- hubMemberExposureApi = null;
1296
- hubMemberExposures() { return this.hubMemberExposureApi; }
1297
- attachHubMemberExposures(api) { this.hubMemberExposureApi = api; }
1298
- /** S3c PR B: the member's pairing API, attached by S5's member composition (null: no Hub member here). */
1299
- hubMemberPairingApi = null;
1300
- hubMemberPairing() { return this.hubMemberPairingApi; }
1301
- attachHubMemberPairing(api) { this.hubMemberPairingApi = api; }
1302
- generalLocationFlights = new Map();
1303
- generalAttentionEpisodes = new Map();
1304
- generalBindings() { return new GeneralBindings(this.dataDir); }
1305
- generalLocations() {
1306
- const store = this.generalBindings();
1307
- return { generals: Object.entries(this.fleetConfig?.instances ?? {}).filter(([, cfg]) => cfg.general_topic).map(([name, cfg]) => {
1308
- const record = store.read(name), machine = cfg.machine_id ? this.runtimeHub?.machine(cfg.machine_id) : null;
1309
- return { name, displayName: cfg.display_name ?? name, location: isWindowsInstance(cfg) ? "windows" : "host",
1310
- machineId: cfg.machine_id ?? null, cli: cfg.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code", model: cfg.model ?? "",
1311
- revision: generalBindingRevision(record, cfg), canSwitchBack: !!record && record.active !== "host",
1312
- offline: isWindowsInstance(cfg) && (!remoteRuntimeEnabled() || machine?.status !== "online" || !this.runtimeHub?.paneTransportEpoch(cfg.machine_id)),
1313
- retryTarget: record?.pending?.phase === "selected" ? (record.pending.to === "host" ? { location: "host" } : (() => { const row = record.windows.find(item => item.id === record.pending.to); return { location: "windows", machineId: row.machineId, cli: row.cli, model: row.config.model ?? "" }; })()) : null,
1314
- hold: record?.hold ?? (record?.pending ? record.pending.phase === "selected" ? "start" : "exit" : null), switching: this.generalLocationFlights.has(name) };
1315
- }), machines: (this.runtimeHub?.listMachines() ?? []).filter(row => row.status !== "pending" && row.status !== "revoked").map(row => ({
1316
- id: row.id, name: row.name, online: row.status === "online" && !!this.runtimeHub?.paneTransportEpoch(row.id),
1317
- clis: row.inventory?.clis.filter(cli => cli.runnable && (cli.id === "claude-code" || cli.id === "codex")).map(cli => ({ id: cli.id, version: cli.version })) ?? [],
1318
- })) };
1319
- }
1320
- /** Pure preflight for the confirmation diff and stale-tab refusal; never prepares files or starts a CLI. */
1321
- previewGeneralLocation(raw) {
1322
- const input = parseGeneralSwitchInput(raw), cfg = this.fleetConfig?.instances[input.name];
1323
- if (!cfg?.general_topic || this.shuttingDown)
1324
- throw Error("General is unavailable");
1325
- const record = this.generalBindings().read(input.name);
1326
- if (input.revision !== generalBindingRevision(record, cfg))
1327
- throw Error("General location changed; read its settings again");
1328
- const connections = cfg.channel_id ? [cfg.channel_id] : [];
1329
- const channels = this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : []);
1330
- if (channels.length && (connections.length !== 1 || channels.filter(row => (row.id ?? row.type) === connections[0]).length !== 1))
1331
- throw Error("General's connection cannot be resolved");
1332
- if (input.target.location === "host") {
1333
- if (!record)
1334
- throw Error("General's saved host location is unavailable");
1335
- return { input, connections, summary: [t("general_location.confirm_host"), t("general_location.history")] };
1336
- }
1337
- const active = record?.windows.find(row => row.id === record.active);
1338
- if (!record?.pending && record?.hold !== "start" && active?.machineId === input.target.machineId && active.cli === input.target.cli)
1339
- throw Error("General already runs at this location");
1340
- const machine = this.runtimeHub?.machine(input.target.machineId);
1341
- if (!remoteRuntimeEnabled() || machine?.status !== "online" || !this.runtimeHub?.paneTransportEpoch(machine.id))
1342
- throw Error(t("general_location.offline"));
1343
- const provider = input.target.cli;
1344
- if (!machine.inventory?.clis.some(cli => cli.id === provider && cli.runnable))
1345
- throw Error("The chosen Windows CLI is unavailable");
1346
- return { input, connections, summary: [t("general_location.confirm_windows", settingsDisplay(machine.name)), t("general_location.history")] };
1347
- }
1348
- /** All entry points, including automatic native recovery, must respect an interrupted binding switch. */
1349
- assertGeneralBindingStart(name, config) {
1350
- if (!config.general_topic)
1351
- return;
1352
- try {
1353
- this.generalBindings().assertStart(name, config);
1354
- }
1355
- catch (err) {
1356
- this.logger.warn({ name, err }, "General location start held by its saved binding");
1357
- this.needsYou?.poke();
1358
- throw new UnsupportedCliError(t("general_location.exit_unconfirmed"));
1359
- }
1360
- }
1361
- /** Only the Settings replay's confirmed admin can enter; the whole switch owns one lifecycle transition. */
1362
- async switchGeneralLocation(raw, authorized, execution) {
1363
- const { input } = this.previewGeneralLocation(raw), name = input.name;
1364
- return this.lifecycle.runTransition(name, async (transition) => {
1365
- execution.assert();
1366
- if (!authorized() || this.generalLocationFlights.has(name))
1367
- throw Error("General location authority expired or a switch is already running");
1368
- const token = Symbol(name), generation = this.settingsGeneration;
1369
- const destinationEpoch = input.target.location === "windows" ? this.runtimeHub?.paneTransportEpoch(input.target.machineId) : null;
1370
- let expected = this.fleetConfig.instances[name], stopGeneration = this.explicitStopGeneration.get(name) ?? 0, epoch = this.lifecycle.epochOf(name);
1371
- const hostSnapshot = !isWindowsInstance(expected) ? structuredClone(this.daemons.get(name)?.getConfigSnapshot() ?? expected) : undefined;
1372
- const current = () => !this.shuttingDown && this.settingsGeneration === generation && authorized()
1373
- && this.generalLocationFlights.get(name) === token && this.fleetConfig?.instances[name] === expected
1374
- && (input.target.location !== "windows" || remoteRuntimeEnabled() && !!destinationEpoch && this.runtimeHub?.paneTransportEpoch(input.target.machineId) === destinationEpoch)
1375
- && (this.explicitStopGeneration.get(name) ?? 0) === stopGeneration && this.lifecycle.epochOf(name) === epoch;
1376
- const check = () => { if (!current())
1377
- throw Error("General location switch was superseded"); };
1378
- this.generalLocationFlights.set(name, token);
1379
- // Old ephemeral waiters carry this epoch; they cannot wake into the selected conversation.
1380
- this.cancelPendingDeliveries(name);
1381
- let lease = null;
1382
- try {
1383
- lease = await waitSettingsLease([settingsFileResource(this.getSettingsConfigPath()), `general:${this.dataDir}:${name}`], () => current() && execution.current(), performance.now() + execution.remainingMs, execution.owner);
1384
- const store = this.generalBindings();
1385
- const result = await switchGeneralLocation({ store, current: () => expected, assert: check,
1386
- prepare: async (target, retained) => {
1387
- if (target.location !== "windows")
1388
- return null;
1389
- const old = retained.windows.find(row => row.machineId === target.machineId && row.cli === target.cli);
1390
- const id = old?.id ?? randomUUID(), agentId = old?.agentId ?? randomUUID();
1391
- const publication = await this.prepareWindowsGeneralWorkspace(name, target.machineId, target.cli, id, () => current() && execution.current());
1392
- check();
1393
- execution.assert();
1394
- const config = { ...structuredClone(expected), runtime: "windows", machine_id: target.machineId,
1395
- native_adapter: "pane", native_agent_id: agentId, native_general_binding: id, native_sandbox: "workspace-write",
1396
- backend: target.cli, model: target.model, working_directory: publication.cwd, systemPrompt: "", agent_mode: "cli" };
1397
- // Host credentials, shell hooks and provider-specific options never become Windows launch configuration.
1398
- for (const key of ["backend_options", "credential_profile", "pre_task_command"])
1399
- delete config[key];
1400
- windowsAgentsFor(this, this.runtimeWindowsExecutor()).ensureGeneralBinding({ id: agentId, instance: name, machineId: target.machineId,
1401
- cli: target.cli, cwd: publication.cwd, name: (expected.display_name ?? name).slice(0, 64), model: target.model, instructions: "", description: expected.description ?? "" }, () => current() && execution.current());
1402
- return { id, agentId, machineId: target.machineId, cli: target.cli, config };
1403
- },
1404
- idle: async () => {
1405
- check();
1406
- const daemon = this.daemons.get(name);
1407
- return !daemon || daemon.getInstanceStateSnapshot().state === "idle" && daemon.getWorkingRecoveryState().idle
1408
- && daemon.getInteractionSnapshot().phase === "clear" && !this.lifecycle.hasWorkLease(name)
1409
- && Date.now() - (this.lastDeliveryAt.get(name) ?? 0) >= 2000;
1410
- },
1411
- hostSnapshot: () => hostSnapshot,
1412
- capture: async () => {
1413
- if (isWindowsInstance(expected))
1414
- return undefined;
1415
- const daemon = this.daemons.get(name);
1416
- if (!daemon)
1417
- throw Error(t("general_location.exit_unconfirmed"));
1418
- const owner = await daemon.generalHostOwner();
1419
- check();
1420
- execution.assert();
1421
- return owner;
1422
- },
1423
- stop: async (owner) => {
1424
- check();
1425
- const stopping = this.stopInstance(name, transition);
1426
- stopGeneration++;
1427
- epoch++;
1428
- await stopping;
1429
- check();
1430
- if (isWindowsInstance(expected)) {
1431
- const profile = windowsAgentsFor(this, this.runtimeWindowsExecutor()).get(expected.native_agent_id);
1432
- if (profile.activeId || profile.paneLaunchId)
1433
- throw Error(t("general_location.exit_unconfirmed"));
1434
- execution.mutate(() => this.holdGeneralOldDeliveries(name));
1435
- return undefined;
1436
- }
1437
- if (!owner || !await confirmGeneralHostExit(owner, getTmuxSession()))
1438
- throw Error(t("general_location.exit_unconfirmed"));
1439
- check();
1440
- execution.mutate(() => this.holdGeneralOldDeliveries(name));
1441
- return owner.pin;
1442
- },
1443
- save: next => {
1444
- check();
1445
- const previous = expected;
1446
- try {
1447
- this.fleetConfig.instances[name] = next;
1448
- this.saveFleetConfig([{ path: ["instances", name], value: next }]);
1449
- expected = next;
1450
- this.routing.rebuild(this.fleetConfig);
1451
- }
1452
- catch (err) {
1453
- this.fleetConfig.instances[name] = previous;
1454
- throw err;
1455
- }
1456
- },
1457
- start: async () => {
1458
- check();
1459
- if (!execution.completed)
1460
- throw Error("General's selected binding has no committed approval");
1461
- // The committed receipt permits only this selected startup. Live authority still fences every awaited spawn boundary.
1462
- const startup = new SettingsExecution({ current: () => current() && execution.completed && execution.remainingMs > 0,
1463
- snapshot: () => this.fleetConfig });
1464
- try {
1465
- await this.startInstance(name, expected, true, "fleet-topic", false, transition, startup);
1466
- }
1467
- finally {
1468
- startup.close();
1469
- }
1470
- check();
1471
- const daemon = this.daemons.get(name);
1472
- if (!daemon || daemon.getProcessStatus() !== "running" || isNativePaneInstance(expected) && !daemon.nativePaneCurrent())
1473
- throw Error(t("general_location.start_failed"));
1474
- await this.connectIpcToInstance(name);
1475
- check();
1476
- },
1477
- notify: (kind, err) => {
1478
- const message = t(kind === "exit" ? "general_location.exit_unconfirmed" : kind === "start" ? "general_location.start_failed" : "general_location.complete", isWindowsInstance(expected) ? this.runtimeHub?.machine(expected.machine_id)?.name ?? t("general_location.windows") : t("general_location.host"));
1479
- this.eventLog?.insert(name, "general_location", { phase: kind, message });
1480
- if (err)
1481
- this.logger.warn({ name, phase: kind, err }, message);
1482
- else
1483
- this.logger.info({ name, phase: kind }, message);
1484
- this.needsYou?.poke();
1485
- this.emitSseEvent("status", this.getUiStatus());
1486
- this.notifyInstanceTopic(name, message);
1487
- },
1488
- }, input, execution);
1489
- return { ...result, message: result.started ? t("general_location.complete", isWindowsInstance(expected)
1490
- ? this.runtimeHub?.machine(expected.machine_id)?.name ?? t("general_location.windows") : t("general_location.host")) : t("general_location.start_failed") };
1491
- }
1492
- finally {
1493
- lease?.release();
1494
- if (this.generalLocationFlights.get(name) === token)
1495
- this.generalLocationFlights.delete(name);
1496
- this.needsYou?.poke();
1497
- }
1498
- });
1499
- }
1500
- /** An offline PC or a confirmed binding boundary holds old waiting input; it never becomes another conversation's queue. */
1501
- holdGeneralOldDeliveries(name) {
1502
- const outbox = this.deliveryOutbox;
1503
- if (!outbox)
1504
- return;
1505
- for (const row of outbox.listPending().filter(row => row.targetInstance === name)) {
1506
- if (row.state !== "submission_started")
1507
- outbox.failBeforeBegin(row.deliveryId, t("general_location.input_not_replayed"));
1508
- else if (row.targetDaemonBootId) {
1509
- outbox.markTargetReconciliationPending(name, row.targetDaemonBootId);
1510
- if (!outbox.reconcileAttempt(row.deliveryId, row.targetDaemonBootId, row.attemptNo, "uncertain", t("general_location.input_not_replayed")))
1511
- throw Error("General's old delivery could not be held");
1512
- }
1513
- else
1514
- throw Error("General's old delivery has no confirmed owner");
1515
- }
1516
- this.needsYou?.poke();
1517
- }
1518
- generalLocationDeliveryHold(name) {
1519
- const config = this.fleetConfig?.instances[name];
1520
- if (!config?.general_topic)
1521
- return null;
1522
- if (this.generalLocationFlights?.has(name))
1523
- return t("general_location.exit_unconfirmed");
1524
- let record;
1525
- try {
1526
- record = this.generalBindings().read(name);
1527
- }
1528
- catch {
1529
- return t("general_location.start_failed");
1530
- }
1531
- if (record?.pending || record?.hold)
1532
- return t(record.pending?.phase === "selected" || record.hold === "start" ? "general_location.start_failed" : "general_location.exit_unconfirmed");
1533
- if (isWindowsInstance(config) && config.machine_id && (!remoteRuntimeEnabled() || this.runtimeHub?.machine(config.machine_id)?.status !== "online" || !this.runtimeHub?.paneTransportEpoch(config.machine_id)))
1534
- return t("general_location.offline");
1535
- return null;
1536
- }
1537
- generalLocationAttention(name) {
1538
- const cfg = this.fleetConfig?.instances[name];
1539
- if (!cfg?.general_topic)
1540
- return null;
1541
- const detail = this.generalLocationDeliveryHold(name);
1542
- if (!detail) {
1543
- this.generalAttentionEpisodes.delete(name);
1544
- return null;
1545
- }
1546
- let record = null;
1547
- try {
1548
- record = this.generalBindings().read(name);
1549
- }
1550
- catch { /* a corrupt journal is a visible hold too */ }
1551
- const offline = !record?.pending && !record?.hold && isWindowsInstance(cfg) && detail === t("general_location.offline");
1552
- const key = record?.pending?.id ?? `${record?.revision ?? "unknown"}:${offline ? "offline" : "hold"}`;
1553
- let episode = this.generalAttentionEpisodes.get(name);
1554
- if (!episode || episode.key !== key) {
1555
- episode = { key, since: record?.pending?.at ?? Date.now() };
1556
- this.generalAttentionEpisodes.set(name, episode);
1557
- }
1558
- return { ...episode, detail, offline };
1559
- }
1560
- async nativePaneFor(name, config, assertCurrent) {
1561
- try {
1562
- return await (await this.nativePanes()).prepare(name, config, assertCurrent);
1563
- }
1564
- catch (err) {
1565
- if (config.general_topic && err instanceof GeneralManagedFileError) {
1566
- assertCurrent();
1567
- const store = this.generalBindings(), record = store.read(name);
1568
- if (record && record.active === config.native_general_binding) {
1569
- record.hold = "start";
1570
- store.write(record);
1571
- }
1572
- this.logger.warn({ name, managedFile: err.file }, t("general_location.start_failed"));
1573
- this.needsYou?.poke();
1574
- throw new UnsupportedCliError(t("general_location.start_failed"));
1575
- }
1576
- throw err;
1577
- }
1578
- }
1579
- async recoverNativePane(name, launchId, authorized) {
1580
- const config = this.fleetConfig?.instances[name];
1581
- if (!isNativePaneInstance(config))
1582
- throw Error("Persistent Windows pane is not configured");
1583
- const generation = this.explicitStopGeneration.get(name) ?? 0;
1584
- await this.lifecycle.runTransition(name, async (transition) => {
1585
- const current = () => { if (!authorized() || this.shuttingDown || this.fleetConfig?.instances[name] !== config || (this.explicitStopGeneration.get(name) ?? 0) !== generation)
1586
- throw Error(t("native_pane.authority_expired")); };
1587
- current();
1588
- if (this.daemons.get(name)?.nativePaneCurrent())
1589
- throw Error(t("native_pane.still_running"));
1590
- this.cancelStartupRetry(name);
1591
- this.lifecycle.cancelNativeRecovery(name);
1592
- await (await this.nativePanes()).recover(name, config, launchId, true, current);
1593
- current();
1594
- // The host supplied a positive exit before ordinary lifecycle cleanup. No signal or input is required.
1595
- await this.lifecycle.stop(name, transition);
1596
- current();
1597
- this.instanceProcessStatus.set(name, "stopped");
1598
- this.eventLog?.insert(name, "native_pane_recovered", { launchId, confirmedStopped: true });
1599
- this.logger.info({ name, launchId }, "Windows saved pane recovered by an administrator; waiting for Start");
1600
- this.emitSseEvent("status", this.getUiStatus());
1601
- });
1602
- }
1603
- holdNativePaneRecovery(name, error) {
1604
- if (!(error instanceof NativePaneRecoveryError))
1605
- return false;
1606
- this.instanceProcessStatus.set(name, "stopped");
1607
- this.logger.warn({ name, recovery: error.recovery }, error.message);
1608
- this.eventLog?.insert(name, "native_pane_recovery_needed", { ...error.recovery });
1609
- this.needsYou?.poke();
1610
- this.emitSseEvent("status", this.getUiStatus());
1611
- return true;
1612
- }
1613
- async startFreshNativePane(name, launchId, sessionId, authorized) {
1614
- const config = this.fleetConfig?.instances[name];
1615
- if (!isNativePaneInstance(config))
1616
- throw Error("Persistent Windows pane is not configured");
1617
- const generation = this.explicitStopGeneration.get(name) ?? 0;
1618
- await this.lifecycle.runTransition(name, async (transition) => {
1619
- const current = () => { if (!authorized() || this.shuttingDown || this.fleetConfig?.instances[name] !== config || (this.explicitStopGeneration.get(name) ?? 0) !== generation)
1620
- throw Error(t("native_pane.authority_expired")); };
1621
- current();
1622
- const held = windowsAgentsFor(this, this.runtimeWindowsExecutor()).get(config.native_agent_id).paneRecovery;
1623
- if (held?.kind !== "session-unconfirmed" || held.launchId !== launchId || (held.sessionId ?? null) !== sessionId)
1624
- throw Error(t("native_pane.changed"));
1625
- if (this.daemons.get(name)?.nativePaneCurrent())
1626
- throw Error(t("native_pane.still_running"));
1627
- this.cancelStartupRetry(name);
1628
- this.lifecycle.cancelNativeRecovery(name);
1629
- await this.lifecycle.stop(name, transition);
1630
- current();
1631
- try {
1632
- await (await this.nativePanes()).startFresh(name, config, launchId, sessionId, current);
1633
- current();
1634
- }
1635
- catch (error) {
1636
- current();
1637
- this.needsYou?.poke();
1638
- this.emitSseEvent("status", this.getUiStatus());
1639
- throw error;
1640
- }
1641
- this.eventLog?.insert(name, "native_pane_new_conversation", { launchId, sessionId });
1642
- await this.startInstance(name, config, true, "fleet-topic", true, transition);
1643
- current();
1644
- this.needsYou?.poke();
1645
- this.emitSseEvent("status", this.getUiStatus());
1646
- });
1647
- }
1648
- nativePanePinnedSession(name) {
1649
- const config = this.fleetConfig?.instances[name];
1650
- if (!isNativePaneInstance(config) || !config.native_agent_id)
1651
- return null;
1652
- const agent = windowsAgentsFor(this, this.runtimeWindowsExecutor()).get(config.native_agent_id);
1653
- return agent.fleet_instance === name && agent.machineId === config.machine_id ? agent.threadId ?? null : null;
1654
- }
1655
- onNativePaneRecoverySettled(name) {
1656
- if (this.daemons.get(name)?.nativePaneCurrent())
1657
- this.instanceProcessStatus.delete(name);
1658
- this.onAttentionChanged(name);
1659
- this.scheduleDeliveryOutboxPump();
1660
- }
1661
- setNativePanePaused(name, paused) {
1662
- const id = this.fleetConfig?.instances[name]?.native_agent_id;
1663
- if (!id)
1664
- throw Error("Native profile is missing");
1665
- windowsAgentsFor(this, this.runtimeWindowsExecutor()).setPaused(id, paused);
1666
- }
1667
- async closeRetainedNativePane(name) {
1668
- const config = this.fleetConfig?.instances[name];
1669
- if (!config)
1670
- throw Error("Native instance configuration is missing");
1671
- await (await this.nativePanes()).closeRetained(name, config, () => { if (this.shuttingDown)
1672
- throw Error("Fleet is stopping"); });
1673
- }
1674
- async reconcileNativePaneBeforeStart(name, daemon) {
1675
- const outbox = this.deliveryOutbox;
1676
- if (!outbox)
1677
- return true;
1678
- const epoch = this.lifecycle.epochOf(name);
1679
- return daemon.reconcileNativeDeliveries(outbox, () => !this.shuttingDown && this.lifecycle.epochOf(name) === epoch);
1680
- }
1681
- /** M-2255: the Windows runtimes' switches the fleet's own state reflects (null until the start-up has applied them). */
1682
- runtimeEffects = null;
1683
- runtimeEffectsScheduled = false;
1684
- localPaneDrain = null;
1685
- remotePaneDrain = null;
1686
- /** Revoke admissions synchronously; keep the captured transport only long enough for exact owned closes. */
1687
- drainNativeRuntime(remote) {
1688
- const existing = remote ? this.remotePaneDrain : this.localPaneDrain;
1689
- if (existing)
1690
- return existing;
1691
- const hub = this.runtimeHub, relay = this.localPaneRelay;
1692
- if (!remote)
1693
- relay?.beginCleanup();
1694
- const targets = Object.entries(this.fleetConfig?.instances ?? {}).filter(([, config]) => isWindowsInstance(config) && !!config.machine_id === remote);
1695
- for (const [name, config] of targets) {
1696
- this.nativeMcpStore?.closeInstance(name);
1697
- if (isNativePaneInstance(config)) {
1698
- this.lifecycle.cancelNativeRecovery(name);
1699
- this.lifecycle.invalidate(name);
1700
- this.daemons.get(name)?.fenceDeliveryWritesForStop();
1701
- }
1702
- }
1703
- const run = Promise.resolve().then(async () => {
1704
- const results = await Promise.allSettled(targets.filter(([, config]) => isNativePaneInstance(config)).map(([name]) => this.stopInstance(name)));
1705
- results.forEach((result, index) => { if (result.status === "rejected")
1706
- this.logger.warn({ err: result.reason, remote, index }, "Windows pane cleanup unconfirmed; its reservation is retained"); });
1707
- try {
1708
- if (remote)
1709
- await hub?.shutdownPaneHosts();
1710
- else
1711
- await relay?.shutdownHost();
1712
- }
1713
- catch (error) {
1714
- this.logger.warn({ err: error, remote }, "Windows pane host shutdown unconfirmed; owned state is retained");
1715
- }
1716
- }).finally(() => {
1717
- if (remote) {
1718
- hub?.close();
1719
- if (this.runtimeHub === hub)
1720
- this.runtimeHub = null;
1721
- }
1722
- else {
1723
- relay?.stop();
1724
- if (this.localPaneRelay === relay)
1725
- this.localPaneRelay = null;
1726
- closeWindowsBridge(this);
1727
- }
1728
- });
1729
- if (remote)
1730
- this.remotePaneDrain = run;
1731
- else
1732
- this.localPaneDrain = run;
1733
- void run.finally(() => {
1734
- if (remote) {
1735
- if (this.remotePaneDrain === run)
1736
- this.remotePaneDrain = null;
1737
- }
1738
- else if (this.localPaneDrain === run)
1739
- this.localPaneDrain = null;
1740
- if (!this.shuttingDown && (remote ? remoteRuntimeEnabled() : localWindowsCliEnabled())) {
1741
- this.initializeNativeInstances();
1742
- this.initializeNativeInstances(true);
1743
- }
1744
- try {
1745
- this.emitSseEvent("status", this.getUiStatus());
1746
- }
1747
- catch { }
1748
- }).catch(error => this.logger.warn({ err: error }, "Windows runtime cleanup failed; retained ownership requires reconciliation"));
1749
- return run;
1750
- }
1751
- /**
1752
- * M-2255: Settings → Runtime's switches (fleet.yaml `runtimes`, or their environment override) in force — set each time
1753
- * the configuration in force changes (a load, a save, a reload's rollback), next to the web authority's. Every gate reads
1754
- * them at once; what the fleet itself holds (paired computers' connections, this PC's Windows process, agents' tool
1755
- * sessions) follows just after, outside the save that changed them (applyRuntimeSwitches): no restart is needed.
1756
- */
1757
- /** S-51: the Hub switches follow the configuration in force (load, save, reload rollback), like the runtime ones. */
1758
- observeHubSwitches() {
1759
- this.observeHubAgentIncarnations();
1760
- if (!this.hubEffectsReady || this.hubEffectsScheduled || this.shuttingDown)
1761
- return;
1762
- this.hubEffectsScheduled = true;
1763
- queueMicrotask(() => { this.hubEffectsScheduled = false; void this.applyHubSwitches(); });
1764
- }
1765
- /** Reads the raw configuration at run time (the newest wins); off and never started: returns before any import. */
1766
- async applyHubSwitches() {
1767
- if (this.shuttingDown || !this.hubEffectsReady)
1768
- return;
1769
- const raw = this.rawFleetConfig;
1770
- const on = raw?.hub?.enabled === true || raw?.hub_member?.enabled === true;
1771
- if (!on && !this.hubRuntime && !this.hubRuntimeInit)
1772
- return;
1773
- try {
1774
- const runtime = this.hubRuntime ?? (await this.hubRuntimeCreated());
1775
- if (this.shuttingDown || !this.hubEffectsReady)
1776
- return; // Stop owns it now (fenced in stopAll, closed in doStopAll)
1777
- await this.observeHubMemberPairing(raw?.hub_member?.enabled === true);
1778
- const current = this.rawFleetConfig;
1779
- await runtime.apply({ hub: current?.hub, hub_member: current?.hub_member });
1780
- }
1781
- catch (err) {
1782
- this.logger.warn({ err: err?.message ?? String(err) }, "Hub switches could not be applied");
1783
- }
1784
- }
1785
- /** Single flight: one import, one runtime, assigned once; every caller (and Stop) gets that same runtime. */
1786
- hubRuntimeCreated() {
1787
- this.hubRuntimeInit ??= (async () => {
1788
- const { HubRuntime } = await import("./hub/runtime.js");
1789
- const runtime = new HubRuntime({
1790
- home: this.dataDir,
1791
- log: (level, message, meta) => this.logger[level](meta ?? {}, message),
1792
- onHoldsChanged: holds => { this.hubHolds = holds; this.needsYou?.poke(); },
1793
- // S-51 PR 2b: the fleet's side of a Hub membership (pairing binding, local agents, attachment points).
1794
- member: {
1795
- // S3c's currentBinding: a fresh, fail-closed read of the pairing state (null unless consistent and connectable).
1796
- boundHub: () => this.hubMemberBinding?.(this.dataDir)?.hubId ?? null,
1797
- agents: () => this.hubMemberAgents(),
1798
- // P1 representative mode (docs/design/hub-representative-agent.md): ordinary delivery + the report_result hook.
1799
- representative: {
1800
- quota: agent => this.hubAgentQuota(agent),
1801
- deliver: input => this.deliverHubQuestion(input),
1802
- agentToken: agent => this.hubAgentToken(agent),
1803
- // attachments-v1: a question's files go into the agent's inbox; an answer's come from its own paths.
1804
- files: { stateDir: this.dataDir,
1805
- inboxDir: agent => isSafeInstanceDirName(agent) ? join(this.dataDir, "workspaces", agent, "inbox") : null,
1806
- scratchDir: agent => isSafeInstanceDirName(agent) ? join(this.dataDir, "workspaces", agent, "hub-files") : null },
1807
- attachReports: (handler, failed) => { this.hubReports = handler; this.hubDeliveryFailed = failed; },
1808
- },
1809
- attachExposures: api => this.attachHubMemberExposures(api),
1810
- attachAttention: read => this.attachHubExecutionAttention(read),
1811
- pokeAttention: () => this.needsYou?.poke(),
1812
- linkState: state => { this.hubMemberLink = state; },
1813
- },
1814
- });
1815
- // Stop began during the import: it holds this promise and closes the runtime; do not hand it out again.
1816
- if (this.shuttingDown || !this.hubEffectsReady)
1817
- runtime.fence();
1818
- else
1819
- this.hubRuntime = runtime;
1820
- return runtime;
1821
- })();
1822
- return this.hubRuntimeInit;
1823
- }
1824
- /**
1825
- * S-51 PR 2b: while the member switch is on, the local owner's "Connect to a Hub" backend (S3c's pairing API) is
1826
- * attached, paired or not; switched off, it is detached. A binding change re-composes the member side.
1827
- */
1828
- async observeHubMemberPairing(on) {
1829
- if (!on) {
1830
- this.attachHubMemberPairing(null);
1831
- return;
1832
- }
1833
- if (this.hubMemberPairingApi)
1834
- return;
1835
- const { createHubMemberPairingApi, currentBinding } = await import("./hub/member/pairing-api.js");
1836
- if (this.shuttingDown || !this.hubEffectsReady || this.hubMemberPairingApi)
1837
- return;
1838
- this.hubMemberBinding = currentBinding;
1839
- this.attachHubMemberPairing(createHubMemberPairingApi({
1840
- home: this.dataDir,
1841
- enabled: () => this.rawFleetConfig?.hub_member?.enabled === true,
1842
- link: () => (this.hubMemberLink === "online" ? "online" : "offline"),
1843
- hold: () => (this.hubMemberLink === "revoked" ? "revoked" : null),
1844
- // Disconnect: stop the member client now (the runtime re-composes from the saved binding, fencing first).
1845
- fence: () => { void this.hubRuntime?.refresh(); },
1846
- // A claim saved, a disconnect tombstone or a cancel: re-compose the member side for the new binding.
1847
- bindingChanged: () => { void this.hubRuntime?.refresh(); },
1848
- // Disconnect withdraws every published exposure locally (each saved before this returns).
1849
- withdrawAll: () => this.hubMemberWithdrawAll(),
1850
- }));
1851
- }
1852
- /**
1853
- * Disconnect: every published exposure is withdrawn and saved before the disconnect reports success. Through the
1854
- * running member's API, or — held or not composed — directly in the member's own store; a failure here makes the
1855
- * disconnect report "unsaved" (S3c), so consent never outlives the disconnect.
1856
- */
1857
- async hubMemberWithdrawAll() {
1858
- const api = this.hubMemberExposureApi;
1859
- if (api) {
1860
- for (const view of api.list()) {
1861
- if (view.exposureId && view.state === "published")
1862
- await api.withdraw(view.exposureId, view.revision);
1863
- }
1864
- return;
1865
- }
1866
- const hubId = this.hubMemberPairingApi?.boundHub()?.hubId ?? null;
1867
- if (!hubId)
1868
- return;
1869
- const { withdrawMemberExposuresOffline } = await import("./hub/compose/member.js");
1870
- await withdrawMemberExposuresOffline(this.dataDir, hubId);
1871
- }
1872
- /**
1873
- * The Hub exposure picker's agents (docs/design/hub-representative-agent.md): every configured instance normal
1874
- * delivery addresses, General included, shown by its display name. The dedicated Hub daemon is not a fleet instance.
1875
- */
1876
- hubMemberAgents() {
1877
- return Object.keys(this.fleetConfig?.instances ?? {}).map(name => ({ name, display: this.shownInstanceName(name) }));
1878
- }
1879
- /** Same cached usage source as the owner's rail, scoped to the representative's CLI and credential profile. */
1880
- async hubAgentQuota(agent) {
1881
- const binding = () => {
1882
- const config = this.fleetConfig?.instances[agent];
1883
- if (!config || isWindowsInstance(config))
1884
- return null; // runner usage never belongs to a Linux login
1885
- const backend = this.backendNameForInstance(agent), provider = usageProviderIdForBackend(backend);
1886
- if (!provider)
1887
- return null;
1888
- const profile = this.credentialProfileOf(agent, backend);
1889
- return profile ? `${provider}:${profile}` : provider;
1890
- };
1891
- const id = binding();
1892
- const { quotaBand } = await import("./hub/member/quota.js");
1893
- const paused = () => this.daemons.get(agent)?.quotaPaused?.() === true
1894
- || this.lifecycle.isPaused(agent) && readPauseReason(this.getInstanceDir(agent)) === "quota";
1895
- if (!id)
1896
- return { band: "unknown" };
1897
- try {
1898
- const snapshot = await getUsageSnapshot(false, new Set([id]));
1899
- if (binding() !== id)
1900
- return { band: "unknown" };
1901
- return quotaBand(snapshot.providers.find(p => p.id === id), paused());
1902
- }
1903
- catch {
1904
- return quotaBand(undefined, paused());
1905
- }
1906
- }
1907
- /**
1908
- * A published representative's lifecycle token (#3145, #3147 review): a DURABLE incarnation id per agent name, rotated
1909
- * whenever its backend, folder or runtime changes and dropped when the instance is removed. A replacement (remove and
1910
- * re-add, even with the same name and config, across restarts) or any retarget — including A→B→A — gets a new id, so
1911
- * earlier consent never matches again without an explicit republish. Stopping or pausing changes nothing.
1912
- */
1913
- hubAgentToken(agent) {
1914
- const instances = this.fleetConfig?.instances ?? {};
1915
- if (!Object.prototype.hasOwnProperty.call(instances, agent))
1916
- return null;
1917
- if (this.hubIncarnationsBroken)
1918
- return null;
1919
- let id;
1920
- try {
1921
- this.settleHubIncarnations();
1922
- id = this.hubIncarnationsBroken ? null : this.hubAgentIncarnation(agent, instances[agent], true);
1923
- }
1924
- catch (err) {
1925
- this.logger.warn({ err: err?.message, agent }, "Hub agent incarnation could not be saved; the agent is not a deliverable representative");
1926
- return null;
1927
- }
1928
- return id ? createHash("sha256").update(JSON.stringify(["agend-hub-agent-v2", agent, id])).digest("hex") : null;
1929
- }
1930
- hubIncarnationsPath() { return join(this.dataDir, "hub-member", "agent-incarnations.json"); }
1931
- /**
1932
- * M-2451 (#3147 residual): written beside hub-member/, in the data directory itself, when an invalidation could not be
1933
- * made durable in hub-member/. A later process that finds it invalidates every incarnation before handing out any
1934
- * token, so a restart never revives consent the earlier process had to withdraw (A→B→A, or a removal, while hub-member/
1935
- * was unwritable).
1936
- */
1937
- hubIncarnationsMarkerPath() { return join(this.dataDir, "hub-member-invalidate.pending"); }
1938
- /** Whether this process has settled the incarnations on disk (the marker handled, the file proven writable). */
1939
- hubIncarnationsSettled = false;
1940
- /**
1941
- * Once per process, before the first token: an invalidation an earlier process could not persist (the marker) clears
1942
- * every incarnation; and an existing file must be rewritable here, else this process is broken (no token at all) —
1943
- * an incarnation this process could not rotate must never be handed out. Fails closed: any doubt marks it broken.
1944
- */
1945
- settleHubIncarnations() {
1946
- if (this.hubIncarnationsSettled || this.hubIncarnationsBroken || !this.dataDir)
1947
- return;
1948
- const path = this.hubIncarnationsPath(), marker = this.hubIncarnationsMarkerPath();
1949
- if (existsSync(marker)) {
1950
- try {
1951
- unlinkSync(path);
1952
- }
1953
- catch (err) {
1954
- if (err?.code !== "ENOENT") {
1955
- this.hubIncarnationsBroken = true;
1956
- this.logger.warn({ err: err?.message }, "An earlier Hub consent invalidation is still pending and cannot be applied; no Hub representative token is handed out");
1957
- return;
1958
- }
1959
- }
1960
- // The clear is durable; a marker that cannot be removed only clears again next time (over-invalidation, never revival).
1961
- try {
1962
- unlinkSync(marker);
1963
- }
1964
- catch { /* left: harmless */ }
1965
- this.logger.warn("An earlier Hub consent invalidation was applied: every Hub representative needs a republish");
1966
- }
1967
- if (existsSync(path))
1968
- this.writeHubIncarnations(this.readHubIncarnations()); // throws (broken) when hub-member/ is not writable
1969
- this.hubIncarnationsSettled = true;
1970
- } /**
1971
- * #3147 r2: set when an invalidation could not be made durable in any way (neither the atomic write nor removing the
1972
- * file worked). From then on this process hands out no representative token at all, and the transitions the fleet
1973
- * controls (a config save, a removal) are refused instead of applied.
1974
- */
1975
- hubIncarnationsBroken = false;
1976
- readHubIncarnations() {
1977
- try {
1978
- const value = JSON.parse(readFileSync(this.hubIncarnationsPath(), "utf8"));
1979
- return value && typeof value === "object" && !Array.isArray(value) ? Object.assign(Object.create(null), value) : Object.create(null);
1980
- }
1981
- catch {
1982
- return Object.create(null);
1983
- }
1984
- }
1985
- /**
1986
- * Persist the incarnation map: "written", or — when the atomic write fails — "cleared": the whole file is removed, which
1987
- * durably invalidates EVERY earlier incarnation (all consent then needs a republish; never a revival). Throws (and
1988
- * marks the process broken) only when neither is possible.
1989
- */
1990
- writeHubIncarnations(map) {
1991
- const path = this.hubIncarnationsPath(), tmp = `${path}.${process.pid}.tmp`;
1992
- try {
1993
- mkdirSync(join(this.dataDir, "hub-member"), { recursive: true, mode: 0o700 });
1994
- writeFileSync(tmp, JSON.stringify(map), { mode: 0o600 });
1995
- renameSync(tmp, path);
1996
- return "written";
1997
- }
1998
- catch (err) {
1999
- try {
2000
- rmSync(tmp, { recursive: false, force: true });
2001
- }
2002
- catch { /* a directory planted there: left alone */ }
2003
- try {
2004
- unlinkSync(path);
2005
- }
2006
- catch (gone) {
2007
- if (gone?.code !== "ENOENT") {
2008
- this.hubIncarnationsBroken = true;
2009
- // M-2451: record the withdrawal outside hub-member/, so a restart does not revive what this process withdrew.
2010
- try {
2011
- writeFileSync(this.hubIncarnationsMarkerPath(), "", { mode: 0o600 });
2012
- }
2013
- catch { /* the data directory too: nothing durable is left */ }
2014
- throw err;
2015
- }
2016
- }
2017
- this.logger.warn({ err: err?.message }, "Hub agent incarnations could not be saved; every earlier Hub consent is invalidated (republish needed)");
2018
- return "cleared";
2019
- }
2020
- }
2021
- /** The agent's durable incarnation id (create: make one if none; else null). A changed fingerprint rotates it. */
2022
- hubAgentIncarnation(agent, cfg, create) {
2023
- const fp = createHash("sha256").update(JSON.stringify([cfg.backend ?? "claude-code", cfg.working_directory, cfg.runtime ?? "", cfg.machine_id ?? ""])).digest("hex");
2024
- const map = this.readHubIncarnations(), entry = Object.prototype.hasOwnProperty.call(map, agent) ? map[agent] : undefined;
2025
- if (entry && entry.fp === fp && typeof entry.id === "string")
2026
- return entry.id;
2027
- if (!entry && !create)
2028
- return null;
2029
- map[agent] = { id: randomUUID(), fp };
2030
- // A rotation that could only clear the file still invalidated the old id durably; the new one is made next time.
2031
- return this.writeHubIncarnations(map) === "written" ? map[agent].id : null;
2032
- }
2033
- /**
2034
- * Every configuration in force: rotate a recorded agent's incarnation when its backend/folder/runtime changed; drop
2035
- * removed ones. strict (a config save the fleet is about to write): a failure to make that durable throws, so the save
2036
- * is refused instead of applied. Otherwise (a load or reload of a hand-edited file) a failure is logged — the
2037
- * invalidation then is the cleared file, or, if nothing could be written, this process hands out no token at all.
2038
- */
2039
- observeHubAgentIncarnations(strict = false) {
2040
- // Only against a loaded configuration: never forget every agent because none is loaded yet.
2041
- if (!this.dataDir || !this.fleetConfig?.instances)
2042
- return;
2043
- try {
2044
- this.settleHubIncarnations();
2045
- if (this.hubIncarnationsBroken)
2046
- throw new Error("Hub agent incarnations cannot be written here");
2047
- if (!existsSync(this.hubIncarnationsPath()))
2048
- return;
2049
- const instances = this.fleetConfig.instances;
2050
- for (const agent of Object.keys(this.readHubIncarnations())) {
2051
- // Gone from the configuration in force (removed by hand in fleet.yaml, too): forgotten, never revived by a re-add.
2052
- if (Object.prototype.hasOwnProperty.call(instances, agent))
2053
- this.hubAgentIncarnation(agent, instances[agent], false);
2054
- else
2055
- this.forgetHubAgentIncarnation(agent, true);
2056
- }
2057
- }
2058
- catch (err) {
2059
- this.logger?.warn({ err: err?.message }, "Hub agent incarnations could not be checked");
2060
- if (strict)
2061
- throw new Error(`Hub consent for a changed agent could not be invalidated durably; the change is not saved (${err?.message})`);
2062
- }
2063
- }
2064
- /** A removed instance's incarnation goes with it (a later instance of the same name is a new one). strict: throw if not durable. */
2065
- forgetHubAgentIncarnation(agent, strict = false) {
2066
- try {
2067
- if (!this.dataDir || !existsSync(this.hubIncarnationsPath()))
2068
- return;
2069
- const map = this.readHubIncarnations();
2070
- if (!Object.prototype.hasOwnProperty.call(map, agent))
2071
- return;
2072
- delete map[agent];
2073
- this.writeHubIncarnations(map);
2074
- }
2075
- catch (err) {
2076
- this.logger.warn({ err: err?.message, agent }, "Hub agent incarnation could not be dropped");
2077
- if (strict)
2078
- throw new Error(`Hub consent for ${agent} could not be invalidated durably; refusing the change (${err?.message})`);
2079
- }
2080
- }
2081
- /**
2082
- * A Hub question to its representative agent: ordinary cross-instance delivery (idle gate, wake, retries), from the
2083
- * reserved Hub sender with the Hub-issued correlation id. beforeInput runs immediately before the IPC hand-off.
2084
- */
2085
- async deliverHubQuestion(input) {
2086
- const { HUB_REPORT_TARGET } = await import("./hub/representative.js");
2087
- if (this.shuttingDown || !this.fleetConfig?.instances[input.agent])
2088
- return false;
2089
- // Windows agents run through their own runtime, which has no pre-input hook: not a P1 representative.
2090
- if (isNativeExecInstance(this.fleetConfig.instances[input.agent]))
2091
- return false;
2092
- const ipcMeta = {
2093
- chat_id: "",
2094
- message_id: `hubmsg-${input.correlationId.slice(4)}`,
2095
- user: `instance:${HUB_REPORT_TARGET}`,
2096
- user_id: `instance:${HUB_REPORT_TARGET}`,
2097
- ts: new Date().toISOString(),
2098
- thread_id: "",
2099
- from_instance: HUB_REPORT_TARGET,
2100
- from_display: "Hub",
2101
- correlation_id: input.correlationId,
2102
- request_kind: "query",
2103
- requires_reply: "true",
2104
- task_summary: "A question from the Hub",
2105
- };
2106
- const result = await this.deliverToInstance(input.agent, { type: "fleet_inbound", targetSession: input.agent, content: input.text, meta: ipcMeta }, { isCrossInstance: true, stillCurrent: input.stillCurrent, beforeInput: input.beforeInput });
2107
- return result !== false;
2108
- }
2109
- /** S-51: why the Hub (or this fleet's Hub membership) is not running, if it is held. Empty while off or running. */
2110
- getHubHolds() {
2111
- return this.hubHolds;
2112
- }
2113
- hubBrowserCertificate() { return this.hubRuntime?.browserCertificateSettings() ?? null; }
2114
- hubRetention() { return this.hubRuntime?.retentionSettings() ?? null; }
2115
- observeRuntimeSwitches() {
2116
- setConfiguredRuntimeSwitches(this.fleetConfig?.runtimes);
2117
- if (!this.runtimeEffects || this.runtimeEffectsScheduled)
2118
- return;
2119
- this.runtimeEffectsScheduled = true;
2120
- queueMicrotask(() => { this.runtimeEffectsScheduled = false; this.applyRuntimeSwitches(); });
2121
- }
2122
- /**
2123
- * Turned off: what it allowed ends now — the paired computers' connections (their agents show as offline) or this PC's
2124
- * Windows process (a turn in progress is cancelled, as when the fleet stops), and those agents' tool sessions.
2125
- * Turned on: the Windows agents are registered and their chat history read, as at start-up.
2126
- */
2127
- applyRuntimeSwitches() {
2128
- const was = this.runtimeEffects;
2129
- if (!was || this.shuttingDown)
2130
- return;
2131
- const now = { windows_cli: localWindowsCliEnabled(), remote_runtime: remoteRuntimeEnabled() };
2132
- if (was.windows_cli === now.windows_cli && was.remote_runtime === now.remote_runtime)
2133
- return;
2134
- this.runtimeEffects = now;
2135
- try {
2136
- if (was.remote_runtime && !now.remote_runtime) {
2137
- void this.drainNativeRuntime(true);
2138
- }
2139
- if (was.windows_cli && !now.windows_cli) {
2140
- void this.drainNativeRuntime(false);
2141
- }
2142
- if (!was.windows_cli && !was.remote_runtime && !this.localPaneDrain && !this.remotePaneDrain) {
2143
- this.initializeNativeInstances();
2144
- this.initializeNativeInstances(true);
2145
- }
2146
- this.logger.info({ windows_cli: now.windows_cli, remote_runtime: now.remote_runtime }, "Windows runtime switches applied");
2147
- }
2148
- catch (error) {
2149
- this.logger.warn({ err: error }, "Windows runtime switches could not be applied in full");
2150
- }
2151
- try {
2152
- this.emitSseEvent("status", this.getUiStatus());
2153
- }
2154
- catch { /* the next status frame carries it */ }
2155
- }
2156
- /** An unavailable opt-in runtime must not prevent the Linux fleet from starting or restoring its own history. */
2157
- initializeNativeInstances(history = false) {
2158
- if (!windowsCliEnabled())
2159
- return;
2160
- try {
2161
- const runtime = this.nativeInstanceRuntime();
2162
- if (history)
2163
- runtime.importHistory();
2164
- else
2165
- runtime.register();
2166
- }
2167
- catch (error) {
2168
- this.logger.warn({ err: error }, "Windows runtime state could not be loaded; affected native instances remain unavailable");
2169
- }
2170
- }
2171
- /** Native CLIs are headless: the manager owns the durable delivery generation instead of a tmux daemon. */
2172
- deliveryBootId(name) {
2173
- const config = this.fleetConfig?.instances[name];
2174
- if (isNativeExecInstance(config)) {
2175
- if (!this.nativeInstanceRuntime().available(name))
2176
- return null;
2177
- const machine = config.machine_id ? this.runtimeCoordinator().machine(config.machine_id) : null;
2178
- return machine ? `runtime:${machine.id}:${machine.bootId ?? machine.generation}` : this.managerBootId;
2179
- }
2180
- return this.daemons.get(name)?.bootId ?? null;
2181
- }
2182
771
  rawFleetConfig = {};
2183
- /**
2184
- * S-51 (#3095, experimental, off by default): the Hub runtime. src/hub/runtime.js is imported only once a Hub switch
2185
- * (hub.enabled / hub_member.enabled) is on; while both are off nothing Hub-related loads, opens or listens.
2186
- */
2187
- hubRuntime = null;
2188
- /** S3c's currentBinding reader, loaded with the pairing API (only while the member switch is on). */
2189
- hubMemberBinding = null;
2190
- /** The member link as the "Connect to a Hub" view shows it (S5's client lifecycle). */
2191
- hubMemberLink = "offline";
2192
- /** OutboundContext: the Hub representative's answer hook (null: no member service running, or not a Hub answer). */
2193
- hubReport(input) {
2194
- return this.hubReports?.(input) ?? null;
2195
- }
2196
- /** The member side's pane-failure hook for a Hub-issued correlation id (else null). */
2197
- hubDeliveryFailed = null;
2198
- /** The member side's report_result hook for HUB_REPORT_TARGET while a representative service runs (else null). */
2199
- hubReports = null;
2200
- /** The one in-flight creation (single flight): overlapping first observations share it, Stop waits for it. */
2201
- hubRuntimeInit = null;
2202
- hubEffectsReady = false;
2203
- hubEffectsScheduled = false;
2204
- hubHolds = [];
2205
772
  rawFleetDocument = null;
2206
773
  /** #2208: the paths a saveFleetConfig() wrote, for its check that nothing else changed. */
2207
774
  fleetDocumentWrites = null;
@@ -2380,7 +947,6 @@ export class FleetManager {
2380
947
  kiroMigrationRuns = new Map();
2381
948
  /** #1386 "Needs you": the list, its live chat messages and Acknowledge. Started with the fleet (finishStartup). */
2382
949
  needsYou = null;
2383
- hubExecutionAttention = null;
2384
950
  missedOfflineStore = null;
2385
951
  configHistory = null;
2386
952
  configHistoryTimer = null;
@@ -2695,8 +1261,6 @@ export class FleetManager {
2695
1261
  /** #2855: a trusted guest's searches, per account. */
2696
1262
  guestSearches = new OfficeWriteLimiter(undefined, { perWindow: GUEST_SEARCHES.perWindow, windowMs: GUEST_SEARCHES.windowMs });
2697
1263
  officeSharesInstance;
2698
- /** #2861: when each instance was last talked to and answered (no words), for share links' dialogue box. */
2699
- officeTalk = new OfficeTalk();
2700
1264
  /**
2701
1265
  * #2636: Office share links — the live Office view for people outside the fleet, read-only, on a quick tunnel of
2702
1266
  * their own (a manager and lease of its own: never the fleet's one tunnel lane nor the public link's preview tunnel).
@@ -2713,7 +1277,6 @@ export class FleetManager {
2713
1277
  hidden: name => crossInstanceVisibility(this.fleetConfig, name) === "hidden",
2714
1278
  skins: this.officeSkins,
2715
1279
  levels: () => this.officeLevelsSnapshot(),
2716
- talk: scope => this.officeTalk.recent(scope),
2717
1280
  },
2718
1281
  manager: new ManagedTunnel({ dataDir: join(this.dataDir, OFFICE_SHARE_TUNNEL_DIR), log: () => this.logger.warn("Office share tunnel cleanup requires attention") }),
2719
1282
  ensure: signal => ensureCloudflared({ dataDir: this.dataDir, pinnedOnly: true, signal }),
@@ -3009,8 +1572,6 @@ export class FleetManager {
3009
1572
  * authorizing revoked links and cookies until the fleet restarted.
3010
1573
  */
3011
1574
  settingsGeneration = 0;
3012
- /** #2942: the settings baseline the agent-requested Windows changes are checked against (set with the gate). */
3013
- settingsBaselineReader = null;
3014
1575
  settingsRuntimeRevision = 0;
3015
1576
  settingsConfirmation = null;
3016
1577
  settingsControl = null;
@@ -3067,29 +1628,8 @@ export class FleetManager {
3067
1628
  return this.settingsConfirmation;
3068
1629
  const generation = this.settingsGeneration;
3069
1630
  const current = () => !this.shuttingDown && this.settingsGeneration === generation;
3070
- const baselines = this.settingsBaselineReader = new SettingsBaselines({ dataDir: this.dataDir, configPath: () => this.configPath,
1631
+ const baselines = new SettingsBaselines({ dataDir: this.dataDir, configPath: () => this.configPath,
3071
1632
  config: () => this.fleetConfig, current: () => [this.settingsGeneration, this.settingsRuntimeRevision, this.shuttingDown],
3072
- generalLocation: input => this.previewGeneralLocation(input),
3073
- hubMembership: raw => hubMembershipSettings(raw),
3074
- // S3c (S-55): a Hub exposure publish's confirmation summary, while a member composition is attached.
3075
- hubBrowserCertificate: (input, binding) => {
3076
- const control = this.hubBrowserCertificate();
3077
- if (!control)
3078
- throw Error("unavailable");
3079
- return control.preview(input, binding);
3080
- },
3081
- hubExposure: (input) => {
3082
- const api = this.hubMemberExposureApi;
3083
- if (!api)
3084
- throw Object.assign(new Error("hub_member_unavailable"), { code: "hub_member_unavailable" });
3085
- return api.preview(input);
3086
- },
3087
- hubPairing: (action, input) => {
3088
- const api = this.hubMemberPairingApi;
3089
- if (!api)
3090
- throw Object.assign(new Error("hub_member_unavailable"), { code: "hub_member_unavailable" });
3091
- return pairingSummary(action, input, api.view(), api.boundHub());
3092
- },
3093
1633
  proof: (kind, target, id, binding, key) => {
3094
1634
  const map = kind === "provider" ? this.providerSecretChallenges : kind === "binding" ? this.connectionBindingChallenges : this.connectionSecretChallenges;
3095
1635
  const proof = map.get(id);
@@ -3110,17 +1650,6 @@ export class FleetManager {
3110
1650
  const store = new SettingsConfirmationStore({
3111
1651
  audit: (event, fields) => this.logger.info({ event: `settings_confirmation.${event}`, ...fields }, "Settings confirmation audit"),
3112
1652
  notify: view => view.state === "pending" ? this.promptSettingsChange(store, view) : Promise.resolve(),
3113
- // alpha.2 (M-2300): an agent's request came, was decided or ran out — the open pages read the list again (a ping
3114
- // with nothing in it: only a web-only fleet's verified owner may read it, GET /ui/agent-proposals).
3115
- changed: view => {
3116
- if (view.source !== "agent_request")
3117
- return;
3118
- this.agentProposalsRev++;
3119
- broadcastSseEvent(this.sseClients, "agent_proposals", {}, err => this.logger.debug({ err }, "agent_proposals event failed"));
3120
- // …and a status frame with the new revision: a tab from before the upgrade (its stream knows no `agent_proposals`)
3121
- // applies status frames, and a polling page reads the same frame at its next /ui/poll.
3122
- this.emitSseEvent("status", this.getUiStatus());
3123
- },
3124
1653
  });
3125
1654
  const gate = new SettingsHttpConfirmation(store, {
3126
1655
  principal: req => {
@@ -3134,7 +1663,6 @@ export class FleetManager {
3134
1663
  && sessions.isCurrent(record, tokenEpoch(this.webToken)) && (!exposure || exposure.isCurrent()) };
3135
1664
  }, baseline: () => baselines.read(), snapshot: () => baselines.snapshot(),
3136
1665
  selfConfirm: (req, id) => this.settingsSelfConfirmation(store, req, id),
3137
- directAgentCreation: true,
3138
1666
  job: async (id) => {
3139
1667
  const settlement = this.settingsJobSettlements.get(id);
3140
1668
  if (!settlement)
@@ -3169,10 +1697,8 @@ export class FleetManager {
3169
1697
  const auth = authorizeSession(req, this.webToken, this.webSessions, { touch: false });
3170
1698
  if (auth.kind !== "ok")
3171
1699
  return null;
3172
- // #2453 (§0.2): never over the public link — not even a named-link session that is web-verified there. #3008: except
3173
- // this computer's admin signed in from outside (the authenticator's code, over the link connections from outside
3174
- // opened): the web-only fleet's owner, judged below by the same authority as here (hostOperator).
3175
- if (gatewayRequestContext(req) && auth.session.remote !== "totp")
1700
+ // #2453 (§0.2): never over the public link — not even a named-link session that is web-verified there.
1701
+ if (gatewayRequestContext(req))
3176
1702
  return null;
3177
1703
  // …and never a change that turns the named-link option on: that one needs a General's card or the host.
3178
1704
  if (!store.selfConfirmable(id))
@@ -3205,223 +1731,30 @@ export class FleetManager {
3205
1731
  if (own.length !== 1 || own[0].type !== chat.platform || !chat.admin(actor.user_id))
3206
1732
  return null;
3207
1733
  const affected = [...store.affectedConnections(id)]; // read now: a finished change no longer has them
3208
- const destinations = this.settingsNoticeGenerals(affected, chat.platform);
3209
- if (!destinations.length)
1734
+ if (!this.settingsNoticeGenerals(affected, chat.platform).length)
3210
1735
  return null;
3211
1736
  const name = this.webPerson({ adapterId: actor.adapter_id, userId: actor.user_id }).name;
3212
1737
  return {
3213
1738
  label: t("settings_confirm.web_verified_label", name),
3214
1739
  // admin() asks chat.current() too; a General a card could go to must still be there (else the host CLI's).
3215
- current: () => !!verified() && chat.admin(actor.user_id) && destinations.some(route => route.current()),
3216
- followStop: async (name, begin) => { const route = destinations.find(item => item.current()); if (!route)
3217
- throw Error("Settings route expired"); await route.followStop(name, begin); },
1740
+ current: () => !!verified() && chat.admin(actor.user_id) && this.settingsNoticeGenerals(affected, chat.platform).length > 0,
3218
1741
  done: view => this.settingsWebConfirmedNotice(view, affected, name, chat.platform),
3219
1742
  };
3220
1743
  }
3221
- /**
3222
- * alpha.2 (M-2300; #2973's agent requests, junsion's §6): on a fleet with no chat connection there is no General to
3223
- * post an agent's Windows request in — it waited for `agend settings confirm` on the host. Its owner (decision
3224
- * 28c2d3a3: the `agend web --code` session on this computer, web-verified) now sees and decides it from the web's
3225
- * Needs you. The authority is asked from the request each time, and again at execution by the store (`current`):
3226
- * still that owner now (this computer, not revoked, signed out or run out), still a fleet with no chat connection,
3227
- * never over the public link. Anyone else — a guest, a LAN or public-link session, a chat-connected fleet's admin —
3228
- * gets nothing (a chat fleet keeps General's card as its way).
3229
- */
3230
- /**
3231
- * #2997 review (Prism P2): how many times an agent's request came or was decided — in a web-only fleet's status frame,
3232
- * so a page that never hears the stream's ping (one polling /ui/poll, one reconnecting, a tab from before the upgrade)
3233
- * reads the list again when it moves. A count, nothing more.
3234
- */
3235
- agentProposalsRev = 0;
3236
- agentProposalOwner(req) {
3237
- // #3008: this computer's admin here, or signed in from outside (over the link connections from outside opened) —
3238
- // asked of the LIVE session each time (#2997 review): a sign-out, an expiry, a revocation or the switch going off
3239
- // while the request is applied fences it.
3240
- return this.liveOwner(req, { hostOnly: false });
3241
- }
3242
- /**
3243
- * Refs #3061 (P2a): the new-device approval's one authority — the same one that decides an agent's request: this web-only
3244
- * fleet's owner, asked of the LIVE session each time (a sign-out, an expiry, a revocation, the authority switch or
3245
- * "Only this computer can authorize" turning a session into a non-owner is seen at once). A LAN session that signed in
3246
- * with a one-time code, a session a device approval made, a guest, a chat fleet's admin and the CLI's header token are
3247
- * none of them this: they are told so, and learn nothing about any request.
3248
- */
3249
- deviceApprovalOwner(req) {
3250
- return this.liveOwner(req, { hostOnly: false });
3251
- }
3252
- /**
3253
- * Refs #3061 (auth-api): device-code sign-in is offered — a web-only fleet with web authorization on. With
3254
- * `web.verified_authority.enabled: false` no one is an owner (liveOwner → null), so no one could approve: the three
3255
- * `/auth/device-login*` routes answer 404 and the sign-in page shows no button. "Only this computer can authorize"
3256
- * does not close it: this computer's own session can still approve.
3257
- */
3258
- deviceLoginAvailable() {
3259
- return this.isWebOnlyFleet() && this.webAuthorityState().enabled;
3260
- }
3261
- /** Refs #3061 (P2b): `web.login_qr` (default off), read per page served — the sign-in page then draws the approve link's QR code. */
3262
- loginQrEnabled() {
3263
- return this.fleetConfig?.web?.login_qr === true;
3264
- }
3265
- /**
3266
- * GET /ui/devices: is this a web-only fleet, may THIS session approve a new device and end another (is it the verified
3267
- * owner), and — to an admin session, never to a guest or the CLI's header token — the signed-in devices (Refs #3061, P2b).
3268
- * `authority`: web authorization is on. With it off nobody is an owner (liveOwner → null), so nobody may add a device or
3269
- * sign another out; the page says why and points to `agend web-token rotate` on this computer instead of "use the AgEnD
3270
- * computer", which would not help.
3271
- */
3272
- webDevicesStatus(req) {
3273
- const webOnly = this.isWebOnlyFleet();
3274
- const owner = webOnly && !!this.deviceApprovalOwner(req);
3275
- return { webOnly, owner, authority: webOnly && this.webAuthorityState().enabled, devices: webOnly ? this.webDeviceRows(req) : [] };
3276
- }
3277
- /**
3278
- * Refs #3061 (P2b): the device list — this fleet's admin sessions, newest activity first (a guest's sessions are the
3279
- * Guests page's, and a read-only tier has none): how each was approved, whether it carries owner authority, and which
3280
- * one is the reader's own. The same people may read this as may read `GET /auth/sessions` (any admin session).
3281
- *
3282
- * `ownerVerified` is the §3 verdict for the session as it would be used from where it signs in (a host session from
3283
- * this computer, a session from outside over the link): one that signed in with a one-time code or a device approval is
3284
- * not an owner until the authenticator vouches for it. Display only — the checks that act on it run again at each use.
3285
- */
3286
- webDeviceRows(req) {
3287
- const auth = authorizeSession(req, this.webToken, this.webSessions, { touch: false });
3288
- if (auth.kind !== "ok" || auth.session.tier !== "admin" || !this.webSessions)
3289
- return [];
3290
- const authorizations = this.sessionAuthorizationStore();
3291
- return this.webSessions.list(auth.session.idHash, r => r.tier === "admin").map(summary => {
3292
- const record = this.webSessions.findByHandle(summary.handle);
3293
- const gateway = summary.surface === "gateway";
3294
- const peer = gateway ? authorizations?.get(summary.handle)?.peer ?? "quick_link" : record?.host === true ? "loopback" : "lan";
3295
- return { handle: summary.handle, label: summary.label, via: summary.via, current: summary.current, surface: summary.surface,
3296
- created: summary.created, lastSeen: summary.lastSeen, expiresAt: Math.min(summary.absoluteExpiry, summary.idleExpiry),
3297
- ownerVerified: !!record && !this.webAuthority({ session: record, gateway, peer }).refusal };
3298
- });
3299
- }
3300
- /**
3301
- * Refs #3061 (P2b): may this request's session end ANOTHER session — one signed in elsewhere, or all of them (auth-api
3302
- * `DELETE /auth/sessions…`, which an admin session could always do). On a web-only fleet that is the verified owner's:
3303
- * a session a code or a device approval made is signed in but not an owner, and could otherwise sign out the owner's
3304
- * own. A fleet with a chat connection is as it was (its admins are the chat's). Ending one's OWN session is never this.
3305
- */
3306
- mayEndOtherSessions(req) {
3307
- return !this.isWebOnlyFleet() || !!this.deviceApprovalOwner(req);
3308
- }
3309
- /**
3310
- * Refs #3061 (P2b): POST /ui/devices/signout {handle} — the owner ends another signed-in device (the settings list's
3311
- * button). Only the verified owner, asked of the LIVE session each time (a non-owner learns nothing about any handle:
3312
- * 403 before anything is looked up); only an admin session of this fleet (a guest's is the Guests page's; an unknown
3313
- * handle and a guest's are the same 404); never the owner's own session here (Sign out is /auth/logout). The device's
3314
- * authorization goes with it, so a copy of its cookie is nobody's.
3315
- */
3316
- webDeviceSignOut(req, handle) {
3317
- if (!this.isWebOnlyFleet() || !this.webSessions)
3318
- return { status: 404, body: { error: "not_found" } };
3319
- const owner = this.deviceApprovalOwner(req);
3320
- if (!owner)
3321
- return { status: 403, body: { error: "owner_required" } };
3322
- if (handle === owner.handle)
3323
- return { status: 400, body: { error: "this_device" } };
3324
- const target = this.webSessions.findByHandle(handle);
3325
- if (!target || target.tier !== "admin")
3326
- return { status: 404, body: { error: "not_found" } };
3327
- const { found, durable } = this.webSessions.revokeByHandle(handle, r => r.tier === "admin");
3328
- if (!found)
3329
- return { status: 404, body: { error: "not_found" } };
3330
- this.sessionAuthorizationStore()?.prune(a => a.handle !== handle);
3331
- this.logger.info({ by: owner.handle.slice(0, 8), handle, label: target.label, via: sessionVia(target), durable }, "Device signed out by the owner");
3332
- if (!durable)
3333
- return { status: 500, body: { ok: false, durable: false, error: "not_durable" } };
3334
- return { status: 200, body: { ok: true } };
3335
- }
3336
- /**
3337
- * Refs #3061 (P2a): look a new device's code up (for the confirm screen), approve it, or deny it — the verified owner's,
3338
- * on a web-only fleet. The answer is the request as it now stands. Approving only marks the request approved: the new
3339
- * browser makes its own session when it next asks (auth-api POST /auth/device-login/poll), so no credential passes from
3340
- * here to there — not in the answer, not through the fleet. A wrong code counts against one fleet-wide breaker; while
3341
- * it is open every code (a right one too) waits.
3342
- */
3343
- webDeviceLoginAct(req, op, code) {
3344
- const owner = this.deviceApprovalOwner(req);
3345
- if (!owner)
3346
- return { status: 403, body: { error: "owner_required" } };
3347
- const logins = this.webDeviceLogins, token = this.webToken;
3348
- if (!logins || !token)
3349
- return { status: 404, body: { error: "not_found" } };
3350
- const epoch = tokenEpoch(token);
3351
- const shown = (v) => ({ code: v.code, label: v.label, peer: v.peer, address: v.address, expiresAt: v.expiresAt, state: v.state });
3352
- if (op === "lookup") {
3353
- const r = logins.lookup(code, epoch);
3354
- if (r.kind === "ok")
3355
- return { status: 200, body: { request: shown(r.view) } };
3356
- return r.kind === "paused" ? { status: 429, body: { error: "paused", retryAfterMs: r.retryAfterMs } } : { status: 404, body: { error: "not_found" } };
3357
- }
3358
- // (The owner above was asked of the live session just now, after the body was read; nothing is awaited since.)
3359
- const r = logins.decide(code, op === "approve" ? "approved" : "denied", owner.handle, epoch);
3360
- if (r.kind === "ok") {
3361
- this.logger.info({ approvedBy: owner.handle.slice(0, 8), label: r.view.label, peer: r.view.peer, decision: r.view.state }, op === "approve" ? "New device sign-in approved" : "New device sign-in denied");
3362
- return { status: 200, body: { request: shown(r.view) } };
3363
- }
3364
- if (r.kind === "decided")
3365
- return { status: 409, body: { error: "already_decided", state: r.state } };
3366
- return r.kind === "paused" ? { status: 429, body: { error: "paused", retryAfterMs: r.retryAfterMs } } : { status: 404, body: { error: "not_found" } };
3367
- }
3368
- /** alpha.2: the agents' requests waiting for this web-only fleet's owner, or null when the reader is not that owner. */
3369
- webAgentProposals(req) {
3370
- if (!this.agentProposalOwner(req))
3371
- return null;
3372
- return this.settingsGate().store.listBySource("agent_request");
3373
- }
3374
- /**
3375
- * alpha.2: the owner's Approve or Reject of an agent's request. The store claims it before its first await (a second
3376
- * click, from here or the host, finds it no longer pending) and asks the owner, the request (its fleet generation and
3377
- * Windows switch) and the target machine again before and while it runs the same creation code the web uses. The
3378
- * answer is always the request as it now stands: applied (with the new agent's name), rejected, expired, stale or
3379
- * failed (with its reason), or already decided.
3380
- */
3381
- async webDecideAgentProposal(req, id, decision) {
3382
- const owner = this.agentProposalOwner(req);
3383
- if (!owner)
3384
- return { status: 403, body: { error: "owner_required", message: t("settings_confirm.web_owner_required") } };
3385
- const store = this.settingsGate().store, before = store.inspect(id);
3386
- if (!before || before.source !== "agent_request")
3387
- return { status: 404, body: { error: "not_found", message: t("settings_confirm.no_longer_pending") } };
3388
- try {
3389
- const view = await store.decide(id, decision, { ...owner, via: "web" });
3390
- this.logger.info({ id, decision, state: view.state }, "Agent request decided from the web by this computer's admin");
3391
- return { status: 200, body: { view, agent: agentNameOf(view) } };
3392
- }
3393
- catch (err) {
3394
- const code = err instanceof SettingsConfirmationError ? err.code : "decide_failed";
3395
- const status = err instanceof SettingsConfirmationError ? err.status : 500;
3396
- // Already decided (a double click, the host's command), run out or withdrawn: say what it is now.
3397
- return { status, body: { error: code, message: t(code === "admin_required" ? "settings_confirm.web_owner_required" : "settings_confirm.no_longer_pending"), view: store.inspect(id) } };
3398
- }
3399
- }
3400
- /** A confirmation belongs to the server route/lifetime, not the availability of General's model. */
3401
- settingsGeneralRoute(name, platform, excluded) {
3402
- const config = this.fleetConfig?.instances[name];
3403
- if (!config?.general_topic || this.shuttingDown || this.ipcStoppingInstances.has(name))
3404
- return null;
3405
- const adapterId = this.getInstanceAdapterId(name), adapter = this.getAdapterForInstance(name);
3406
- if (!adapterId || !adapter || excluded.has(adapterId) || adapter.type !== platform)
3407
- return null;
3408
- const chatId = this.getGroupIdForInstance(name), topic = String(config.topic_id ?? "");
3409
- if (!chatId || !topic)
3410
- return null;
3411
- const route = () => !this.shuttingDown && this.fleetConfig?.instances[name]?.general_topic === true
3412
- && this.adapterState.get(adapterId)?.status === "connected"
3413
- && this.getInstanceAdapterId(name) === adapterId && this.getAdapterForInstance(name) === adapter
3414
- && this.getGroupIdForInstance(name) === chatId && String(this.fleetConfig?.instances[name]?.topic_id ?? "") === topic;
3415
- const lifetime = new SettingsRouteLifetime(name, route, () => this.lifecycle.epochOf(name), () => this.ipcStoppingInstances.has(name));
3416
- return lifetime.current() ? { adapterId, adapter, chatId, topic, current: lifetime.current, followStop: lifetime.followStop.bind(lifetime) } : null;
3417
- }
3418
- /** Connected, same-platform, unaffected server routes can post cards even while the model is offline. */
1744
+ /** #2170: the Generals a chat card could be posted in (promptSettingsChange's rule): connected, on the platform, unaffected. */
3419
1745
  settingsNoticeGenerals(affected, platform) {
3420
- const excluded = new Set(affected);
3421
- return Object.keys(this.fleetConfig?.instances ?? {}).flatMap(name => {
3422
- const route = this.settingsGeneralRoute(name, platform, excluded);
3423
- return route ? [route] : [];
3424
- });
1746
+ const excluded = new Set(affected), found = [];
1747
+ for (const [name, config] of Object.entries(this.fleetConfig?.instances ?? {})) {
1748
+ if (!config.general_topic || !this.daemons.get(name))
1749
+ continue;
1750
+ const adapterId = this.getInstanceAdapterId(name), adapter = this.getAdapterForInstance(name);
1751
+ if (!adapterId || !adapter || excluded.has(adapterId) || adapter.type !== platform || this.adapterState.get(adapterId)?.status !== "connected")
1752
+ continue;
1753
+ const chatId = this.getGroupIdForInstance(name), topic = String(config.topic_id ?? "");
1754
+ if (chatId && topic)
1755
+ found.push({ adapterId, adapter, chatId, topic });
1756
+ }
1757
+ return found;
3425
1758
  }
3426
1759
  /**
3427
1760
  * #2170: General's notice of a change confirmed from the web — the first General a chat card could have been posted
@@ -3480,119 +1813,6 @@ export class FleetManager {
3480
1813
  return { platform, current, admin: userId => current() && !!userId
3481
1814
  && identities.every(owner => this.isFleetAdmin(userId, owner.id)) };
3482
1815
  }
3483
- /**
3484
- * #2942: a Windows create or settings change an AGENT asked for. Nothing is created or written here: it becomes a
3485
- * pending settings change whose General card a fleet admin must confirm (promptSettingsChange — the same admin rule
3486
- * as every card). Its apply runs the same creation/patch code the web uses, inside the store's SettingsExecution, with
3487
- * the confirming admin, the Windows switch and the target machine asked again at that moment.
3488
- */
3489
- async proposeAgentWindowsChange(input) {
3490
- const gate = this.settingsGate(), baselines = this.settingsBaselineReader;
3491
- if (!baselines || !this.fleetConfig)
3492
- throw new Error("Fleet configuration is unavailable");
3493
- const generation = this.settingsGeneration, machineId = input.machineId;
3494
- const switchOn = () => machineId ? remoteRuntimeEnabled() : localWindowsCliEnabled();
3495
- const online = () => !machineId || (remoteRuntimeEnabled() && this.runtimeCoordinator().machine(machineId)?.status === "online");
3496
- if (!switchOn())
3497
- throw new Error(machineId ? "Remote runtime is not enabled" : LOCAL_WINDOWS_CLI_OFF);
3498
- const baseline = await baselines.read(), snapshot = settingsFingerprint(baselines.snapshot());
3499
- const { view } = gate.store.propose({ session: `agent:${input.requester}`, key: `agent-windows:${input.key}`,
3500
- requestFingerprint: input.fingerprint, fingerprint: input.fingerprint, section: "channel", requestedBy: input.requester,
3501
- source: "agent_request", summary: input.summary, bytes: input.bytes, authority: input.authority, affectedConnections: [],
3502
- current: () => !this.shuttingDown && this.settingsGeneration === generation && switchOn(),
3503
- snapshot: () => baselines.snapshot(),
3504
- unchanged: async () => (await baselines.read()).fingerprint === baseline.fingerprint && settingsFingerprint(baselines.snapshot()) === snapshot,
3505
- // The store's own fence (this confirming admin, the session, the state) plus the switch and machine now.
3506
- apply: async (actorCurrent, execution) => {
3507
- execution.assert();
3508
- // alpha.2 polish: a paired machine whose CLI inventory has not arrived yet (just paired) is asked for its own
3509
- // report first (it runs on that PC), so the creation validates against what is installed there — never a refusal
3510
- // that only a manual Refresh would undo. Its answer or failure changes nothing else; the fence is asked again.
3511
- if (machineId && online() && !inventoryProbed(this.runtimeCoordinator().machine(machineId)?.inventory)) {
3512
- // `force`: the runner's cached answer on a fresh profile is the placeholder itself (#3005 rig recheck).
3513
- try {
3514
- await this.runtimeCoordinator().call(machineId, "probe", { force: true });
3515
- }
3516
- catch { /* validation says what is missing */ }
3517
- execution.assert();
3518
- }
3519
- return input.apply(() => actorCurrent() && online(), execution);
3520
- } });
3521
- return view;
3522
- }
3523
- /** #2942: create_instance with runtime "windows" from an agent: validated like the web, then held for General. */
3524
- async requestWindowsCreate(requester, rawArgs) {
3525
- const cfg = this.fleetConfig;
3526
- if (!cfg)
3527
- throw new Error("Fleet configuration is unavailable");
3528
- let parsed;
3529
- try {
3530
- parsed = parseNativeInstanceCreate(rawArgs);
3531
- }
3532
- catch {
3533
- throw new Error("Invalid Windows agent request: check the directory, sandbox, backend and model");
3534
- }
3535
- // The web's own projection: the same validation, topic target and diff the admin's dashboard confirmation shows.
3536
- let diff;
3537
- try {
3538
- diff = prepareSettingsEffect("POST", "/ui/instances", rawArgs, { config: cfg, classic: {} }).diff;
3539
- }
3540
- catch {
3541
- throw new Error("Invalid Windows agent request: its topic connection is not one this fleet can use");
3542
- }
3543
- if (!diff)
3544
- throw new Error("Invalid Windows agent request");
3545
- return this.proposeAgentWindowsChange({ requester, key: diff.fingerprint, fingerprint: diff.fingerprint, summary: diff.summary,
3546
- machineId: parsed.input.machineId, bytes: JSON.stringify(rawArgs).length,
3547
- // The General card is the confirmation for an agent's request, whatever the requested connection (a topic or none).
3548
- authority: { connections: diff.authority?.connections ?? [], primaryGeneral: true, unknown: false },
3549
- apply: (allowed, execution) => createNativeWindowsInstance(this, parsed, allowed, execution) });
3550
- }
3551
- /** #2942: update_instance_config on a Windows agent: only the fields the web editor allows, held for General. */
3552
- async requestWindowsConfig(requester, name, patch) {
3553
- const inst = this.fleetConfig?.instances[name];
3554
- if (!isWindowsInstance(inst))
3555
- throw new Error("Windows agent not found");
3556
- let edit;
3557
- try {
3558
- edit = NativeInstanceConfigEdit.parse(patch);
3559
- }
3560
- catch {
3561
- throw new Error("Invalid Windows agent settings: display_name, description and model only");
3562
- }
3563
- const before = { display_name: inst.display_name ?? "", description: inst.description ?? "", model: inst.model ?? "" };
3564
- const changed = Object.keys(edit).filter(key => edit[key] !== before[key]);
3565
- if (!changed.length)
3566
- throw new Error("Nothing to change: these settings already have these values");
3567
- const summary = changed.map(key => `${name} ${key}: ${settingsDisplay(before[key])} → ${settingsDisplay(edit[key])}`);
3568
- const machineId = typeof inst.machine_id === "string" ? inst.machine_id : undefined;
3569
- // #2973 review (Prism P1): a persistent pane is bound to a chat connection; changing it (a model change restarts it)
3570
- // needs that connection's admins as well as the primary General's, exactly as its creation did. An instance whose
3571
- // binding names no single configured connection is refused (fail closed), never confirmed by the General alone.
3572
- const connections = [];
3573
- if (isNativePaneInstance(inst) || inst.channel_id !== undefined) {
3574
- const bound = inst.channel_id, channels = this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : []);
3575
- if (typeof bound !== "string" || channels.filter(ch => (ch.id ?? ch.type) === bound).length !== 1)
3576
- throw new Error("This Windows agent's chat connection cannot be resolved; change its settings in the web dashboard");
3577
- connections.push(bound);
3578
- }
3579
- return this.proposeAgentWindowsChange({ requester, key: `${name}:${settingsFingerprint(edit)}`, fingerprint: settingsFingerprint([name, edit]),
3580
- summary, machineId, bytes: JSON.stringify(edit).length, authority: { connections, primaryGeneral: true, unknown: false },
3581
- // #2973 review (Prism P2): the update may wait for its lifecycle turn; if the agent is rebound meanwhile (B → C), the
3582
- // approval for B must not stop or change it on C. Pin the approved target and require the execution's snapshot to be
3583
- // unchanged before every pre-commit effect; the post-commit restart keeps its own (refreshed) checks.
3584
- apply: (allowed, execution) => {
3585
- const pinned = { channel: inst.channel_id, machine: inst.machine_id, adapter: inst.native_adapter, runtime: inst.runtime };
3586
- const sameTarget = () => {
3587
- const now = this.fleetConfig?.instances[name];
3588
- return !!now && now.channel_id === pinned.channel && now.machine_id === pinned.machine && now.native_adapter === pinned.adapter && now.runtime === pinned.runtime;
3589
- };
3590
- // Before the commit: the exact target and an unchanged snapshot. After runtime.update completes the transaction,
3591
- // current() is false by design; the committed operation's restart receipt then needs the same actor, target and
3592
- // switch checks plus `completed` — a completed execution can never authorize another write (mutate refuses).
3593
- return updateNativeWindowsInstance(this, name, edit, () => allowed() && sameTarget() && (execution.completed || execution.current()), execution);
3594
- } });
3595
- }
3596
1816
  async promptSettingsChange(store, view) {
3597
1817
  const generation = this.settingsGeneration;
3598
1818
  const authority = this.settingsChatAuthority(store, view.id);
@@ -3601,15 +1821,25 @@ export class FleetManager {
3601
1821
  return;
3602
1822
  }
3603
1823
  const excluded = new Set(store.affectedConnections(view.id));
3604
- for (const name of Object.keys(this.fleetConfig?.instances ?? {})) {
3605
- const route = this.settingsGeneralRoute(name, authority.platform, excluded);
3606
- if (!route)
1824
+ for (const [name, config] of Object.entries(this.fleetConfig?.instances ?? {})) {
1825
+ if (!config.general_topic)
1826
+ continue;
1827
+ const adapterId = this.getInstanceAdapterId(name), adapter = this.getAdapterForInstance(name);
1828
+ const general = this.daemons.get(name);
1829
+ if (!adapterId || !adapter || !general || excluded.has(adapterId) || adapter.type !== authority.platform)
1830
+ continue;
1831
+ const chatId = this.getGroupIdForInstance(name), topic = String(config.topic_id ?? "");
1832
+ if (!chatId || !topic)
3607
1833
  continue;
3608
- const { adapterId, adapter, chatId, topic } = route;
3609
1834
  let retired = false, attached = false;
3610
1835
  const postingDeadline = performance.now() + Math.min(5000, view.remaining_ms);
3611
- const current = () => !retired && (attached || performance.now() < postingDeadline) && this.settingsGeneration === generation
3612
- && route.current() && authority.current();
1836
+ const current = () => !retired && (attached || performance.now() < postingDeadline) && !this.shuttingDown && this.settingsGeneration === generation
1837
+ && this.fleetConfig?.instances[name]?.general_topic === true
1838
+ && this.daemons.get(name) === general && !this.ipcStoppingInstances.has(name)
1839
+ && this.adapterState.get(adapterId)?.status === "connected"
1840
+ && this.getInstanceAdapterId(name) === adapterId && this.getAdapterForInstance(name) === adapter
1841
+ && this.getGroupIdForInstance(name) === chatId && String(this.fleetConfig?.instances[name]?.topic_id ?? "") === topic
1842
+ && authority.current();
3613
1843
  if (!current())
3614
1844
  continue;
3615
1845
  try {
@@ -3619,7 +1849,7 @@ export class FleetManager {
3619
1849
  t("settings_confirm.requester", view.requested_by), ...view.summary].join("\n"),
3620
1850
  choices: [{ action: "confirm", label: t("settings_confirm.confirm_button") }, { action: "reject", label: t("settings_confirm.reject_button") }],
3621
1851
  expiredText: t("settings_confirm.no_longer_pending"), timeoutMs: view.remaining_ms,
3622
- extra: { pendingChangeId: view.id, confirmationCurrent: current, confirmationAdmin: userId => current() && authority.admin(userId), confirmationStop: route.followStop },
1852
+ extra: { pendingChangeId: view.id, confirmationCurrent: current, confirmationAdmin: userId => current() && authority.admin(userId) },
3623
1853
  });
3624
1854
  void posted.then(nonce => {
3625
1855
  if (!retired)
@@ -3887,7 +2117,6 @@ export class FleetManager {
3887
2117
  try {
3888
2118
  const view = await this.settingsGate().store.decide(entry.pendingChangeId, action, {
3889
2119
  label: t("settings_confirm.admin_label", data.userId ?? ""),
3890
- followStop: entry.confirmationStop,
3891
2120
  current: () => entry.confirmationCurrent?.() === true && this.getAdapterForInstance(entry.instanceName) === entry.adapter
3892
2121
  && !!data.userId && entry.confirmationAdmin?.(data.userId) === true,
3893
2122
  });
@@ -3922,8 +2151,6 @@ export class FleetManager {
3922
2151
  sseHeartbeatMs = SSE_HEARTBEAT_MS;
3923
2152
  /** The dashboard's login codes. Memory only: a code that outlives the process is a code nobody can prove was not copied. */
3924
2153
  webLoginCodes = null;
3925
- /** Refs #3061 (P2a): device-code sign-in requests (in memory; web-device-login.ts). Read by auth-api as `webDeviceLogins`. */
3926
- webDeviceLogins = null;
3927
2154
  /**
3928
2155
  * #1920 step 4: guests' codes, apart from the admins' — one per account, so no guest's `/web` withdraws another's or
3929
2156
  * an admin's code (#2093 review), at most 32 at once. Local sign-in only for now (audience "local").
@@ -4281,15 +2508,12 @@ export class FleetManager {
4281
2508
  }
4282
2509
  /** Synchronous SQLite admission; the caller may acknowledge only after this returns. */
4283
2510
  admitDurableDelivery(input) {
4284
- const bindingHold = this.generalLocationDeliveryHold(input.targetInstance);
4285
- if (bindingHold)
4286
- throw Error(bindingHold);
4287
2511
  const outbox = this.deliveryOutbox;
4288
2512
  if (!outbox)
4289
2513
  throw new Error("Durable delivery store is unavailable");
4290
- const currentSourceBootId = this.getDaemonBootId(input.sourceInstance);
4291
- const sourceDaemonBootId = input.sourceDaemonBootId ?? currentSourceBootId;
4292
- if (!currentSourceBootId || !sourceDaemonBootId || currentSourceBootId !== sourceDaemonBootId) {
2514
+ const source = this.daemons.get(input.sourceInstance);
2515
+ const sourceDaemonBootId = input.sourceDaemonBootId ?? source?.bootId;
2516
+ if (!source || !sourceDaemonBootId || source.bootId !== sourceDaemonBootId) {
4293
2517
  throw new Error("Source daemon generation is no longer current; outcome is unknown");
4294
2518
  }
4295
2519
  const sourceKey = `mcp:${input.sourceInstance}:${input.operationId}:${input.targetInstance}:${input.targetSession ?? ""}:${input.kind}`;
@@ -4323,7 +2547,7 @@ export class FleetManager {
4323
2547
  const human = (item) => {
4324
2548
  if (!item.authority || !state)
4325
2549
  return item;
4326
- const authority = this.receiptAuthorityNow(item.authority, state);
2550
+ const authority = authorityNow(item.authority, state);
4327
2551
  return { ...item, authority, ...(authority.downgraded ? { source_instance: "human:web:unknown" } : {}) };
4328
2552
  };
4329
2553
  // #1641: a sender that sees only `queued` cannot tell "next in line" from "the target is stuck": say which.
@@ -4352,11 +2576,6 @@ export class FleetManager {
4352
2576
  return this.daemons.get(target)?.notAcceptingReason?.() ?? null;
4353
2577
  }
4354
2578
  getDaemonBootId(instanceName) {
4355
- if (this.generalLocationDeliveryHold(instanceName))
4356
- return undefined;
4357
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName])) {
4358
- return !this.shuttingDown && windowsCliEnabled() && this.nativeMcpStore?.hasActive(instanceName) ? this.managerBootId : undefined;
4359
- }
4360
2579
  return this.daemons.get(instanceName)?.bootId;
4361
2580
  }
4362
2581
  markDurableResponseDelivered(sourceInstance, operationId) {
@@ -4365,22 +2584,8 @@ export class FleetManager {
4365
2584
  /** Called after a replacement Daemon object is installed in lifecycle.daemons. */
4366
2585
  onDaemonReady(name, daemonBootId) {
4367
2586
  const daemon = this.daemons.get(name);
4368
- if (daemon?.bootId === daemonBootId) {
2587
+ if (daemon?.bootId === daemonBootId)
4369
2588
  this.listenForWorkingState(name, daemon);
4370
- const cfg = this.fleetConfig?.instances[name];
4371
- if (cfg?.general_topic && !this.generalLocationFlights.has(name) && daemon.getProcessStatus() === "running"
4372
- && (!isNativePaneInstance(cfg) || daemon.nativePaneCurrent())) {
4373
- try {
4374
- if (this.generalBindings().completeSelected(name, cfg)) {
4375
- this.needsYou?.poke();
4376
- this.logger.info({ name }, "General location's selected start receipt settled after recovery");
4377
- }
4378
- }
4379
- catch (err) {
4380
- this.logger.warn({ name, err }, "General location start receipt remains held");
4381
- }
4382
- }
4383
- }
4384
2589
  // #3066 §1d: a Codex launch records the credential profile it runs under, so its usage is told by account.
4385
2590
  if (daemon?.bootId === daemonBootId && this.backendNameOf(name) === "codex") {
4386
2591
  void recordProfileLaunch(join(this.getInstanceDir(name), PROFILE_TIMELINE_FILE), this.credentialProfileOf(name, "codex") ?? "default");
@@ -4528,15 +2733,6 @@ export class FleetManager {
4528
2733
  this.deliveryPumpRunning = true;
4529
2734
  this.deliveryPumpScheduled = false;
4530
2735
  try {
4531
- // Native workers have no resident daemon to wake. Pending durable work resumes their logical instance;
4532
- // the Windows bridge still owns the single physical execution slot.
4533
- for (const target of new Set(outbox.listPending().map(row => row.targetInstance))) {
4534
- if (isNativeExecInstance(this.fleetConfig?.instances[target])) {
4535
- const runtime = this.nativeInstanceRuntime(), agent = runtime.get(target);
4536
- if (agent?.paused && !agent.blocked)
4537
- runtime.setPaused(target, false);
4538
- }
4539
- }
4540
2736
  outbox.expireStale(undefined, undefined, target => {
4541
2737
  const failure = this.wakeCoordinator?.wakeFailure(target);
4542
2738
  return failure ? `target could not be woken (${failure})` : undefined;
@@ -4549,7 +2745,7 @@ export class FleetManager {
4549
2745
  // Phase 2c: a lane a queue worker owns is never claimed here.
4550
2746
  target => this.queueWorkers.has(target) || this.isInstanceRestarting(target) || this.wakeCoordinator?.blocksClaim(target)
4551
2747
  ? null
4552
- : this.deliveryBootId(target), this.activeDurableTargets, row => this.recoveryDeliveryEligible(row));
2748
+ : this.daemons.get(target)?.bootId ?? null, this.activeDurableTargets, row => this.recoveryDeliveryEligible(row));
4553
2749
  if (!claimed)
4554
2750
  break;
4555
2751
  this.activeDurableTargets.add(claimed.targetInstance);
@@ -4610,7 +2806,7 @@ export class FleetManager {
4610
2806
  wanted: () => this.deliveryWorkerMode(target) === "on",
4611
2807
  available: () => !this.shuttingDown && this.deliveryOutbox?.isOpen === true,
4612
2808
  blocked: () => this.isInstanceRestarting(target) || this.wakeCoordinator?.blocksClaim(target) === true,
4613
- daemonBootId: () => this.deliveryBootId(target),
2809
+ daemonBootId: () => this.daemons.get(target)?.bootId ?? null,
4614
2810
  claim: bootId => this.deliveryOutbox?.claimNext(this.managerBootId, candidate => candidate === target ? bootId : null, new Set(), row => this.recoveryDeliveryEligible(row)),
4615
2811
  tryAcquireBudget: () => {
4616
2812
  if (this.activeDurableTargets.size >= 8)
@@ -4641,10 +2837,6 @@ export class FleetManager {
4641
2837
  const outbox = this.deliveryOutbox;
4642
2838
  if (!outbox)
4643
2839
  return;
4644
- if (isNativeExecInstance(this.fleetConfig?.instances[claimed.targetInstance])) {
4645
- await this.dispatchNativeDurableDelivery(claimed);
4646
- return;
4647
- }
4648
2840
  const target = claimed.targetInstance;
4649
2841
  const attempt = claimed.attemptNo;
4650
2842
  const payload = { ...claimed.payload };
@@ -4726,85 +2918,6 @@ export class FleetManager {
4726
2918
  }
4727
2919
  await laneReleased;
4728
2920
  }
4729
- /** The ordinary durable lane, with provider completion proof instead of terminal/IPC submission proof. */
4730
- async dispatchNativeDurableDelivery(claimed) {
4731
- const outbox = this.deliveryOutbox;
4732
- if (!outbox)
4733
- return;
4734
- const { deliveryId, targetDaemonBootId, attemptNo, targetInstance: target } = claimed;
4735
- let begun = false;
4736
- try {
4737
- if (["raw_paste", "steer"].includes(claimed.kind)) {
4738
- if (outbox.begin(deliveryId, targetDaemonBootId, attemptNo) === "begun")
4739
- outbox.complete(deliveryId, targetDaemonBootId, attemptNo, "failed", "Windows runtime does not support terminal input or steering");
4740
- return;
4741
- }
4742
- const meta = claimed.payload.meta && typeof claimed.payload.meta === "object" ? claimed.payload.meta : {};
4743
- const task = await this.nativeInstanceRuntime().execute(target, { ...claimed.payload, requestId: deliveryId, meta: { ...meta, from_instance: claimed.sourceInstance, correlation_id: claimed.correlationId } }, {
4744
- stillCurrent: () => !this.shuttingDown,
4745
- beforeStart: () => { if (outbox.begin(deliveryId, targetDaemonBootId, attemptNo) !== "begun")
4746
- throw new Error("Windows delivery attempt is no longer current"); begun = true; },
4747
- });
4748
- const outcome = task.outcomeConfirmed === false ? "uncertain" : task.status === "completed" ? "delivered" : "failed";
4749
- outbox.complete(deliveryId, targetDaemonBootId, attemptNo, outcome, task.error || task.status);
4750
- // Return a requested peer report when the provider did not explicitly report it through scoped MCP.
4751
- if (meta.requires_reply === "true" && claimed.correlationId && !task.reportedCorrelationIds?.includes(claimed.correlationId) && this.fleetConfig?.instances[claimed.sourceInstance]) {
4752
- const text = (task.status === "completed" ? task.output : `Windows task ${task.status}: ${task.error || task.output}`).slice(0, 8000);
4753
- outbox.admit({ sourceKey: `native-result:${deliveryId}`, operationId: `native-result:${deliveryId}`, sourceInstance: target, sourceDaemonBootId: this.managerBootId,
4754
- targetInstance: claimed.sourceInstance, kind: "fleet_inbound", correlationId: claimed.correlationId,
4755
- payload: { type: "fleet_inbound", content: text || "Windows task completed without text output", meta: { from_instance: target, correlation_id: claimed.correlationId, requires_reply: "false", source: "native_runtime" } } });
4756
- this.scheduleDeliveryOutboxPump();
4757
- }
4758
- }
4759
- catch (error) {
4760
- const reason = error instanceof Error ? error.message : String(error);
4761
- if (begun)
4762
- outbox.complete(deliveryId, targetDaemonBootId, attemptNo, "uncertain", reason);
4763
- else
4764
- outbox.retryBeforeBegin(deliveryId, targetDaemonBootId, attemptNo, reason, 5000);
4765
- this.logger.warn({ target, deliveryId, err: reason }, "Windows durable delivery did not complete");
4766
- }
4767
- }
4768
- /** A runtime's persisted completion can outlive this manager. Resolve its original delivery fence, never send
4769
- * the prompt again, and reconstruct a missing peer report using the same durable operation ID. */
4770
- reconcileRemoteDelivery(machineId, task) {
4771
- const outbox = this.deliveryOutbox;
4772
- if (!outbox?.isOpen || !task.finishedAt || task.outcomeConfirmed !== true || !["completed", "failed", "cancelled"].includes(task.status))
4773
- return;
4774
- const key = `${task.id}:${task.finishedAt}:${task.status}`;
4775
- if (this.reconciledRemoteResults.has(key))
4776
- return;
4777
- const row = outbox.get(task.id);
4778
- if (!row || !isNativeExecInstance(this.fleetConfig?.instances[row.targetInstance]) || this.fleetConfig?.instances[row.targetInstance]?.machine_id !== machineId
4779
- || !row.targetDaemonBootId || this.activeDurableTargets.has(row.targetInstance))
4780
- return;
4781
- if (row.state === "submission_started" || row.state === "reconciliation_pending") {
4782
- if (!outbox.complete(row.deliveryId, row.targetDaemonBootId, row.attemptNo, task.status === "completed" ? "delivered" : "failed", task.error || "Runtime completion recovered after reconnect"))
4783
- return;
4784
- }
4785
- else if (row.state === "uncertain" && task.status === "completed") {
4786
- if (outbox.markConsumed(row.deliveryId, row.targetDaemonBootId, row.attemptNo, "turn", "Paired runtime journal confirmed this exact execution completed") === "ignored")
4787
- return;
4788
- }
4789
- else if (row.state !== "delivered" && row.state !== "failed")
4790
- return;
4791
- const meta = row.payload.meta && typeof row.payload.meta === "object" ? row.payload.meta : {};
4792
- if (meta.requires_reply !== "true" || !row.correlationId || !this.fleetConfig?.instances[row.sourceInstance]) {
4793
- this.reconciledRemoteResults.add(key);
4794
- return;
4795
- }
4796
- const reports = outbox.queryStatusForInstance(row.targetInstance, { correlationId: row.correlationId, limit: 100 }).items;
4797
- if (reports.some(report => report.source_instance === row.targetInstance && report.target_instance === row.sourceInstance
4798
- && report.kind === "report" && !["failed", "cancelled"].includes(report.state))) {
4799
- this.reconciledRemoteResults.add(key);
4800
- return;
4801
- }
4802
- const text = (task.status === "completed" ? task.output : `Windows task ${task.status}: ${task.error || task.output}`).slice(0, 8000);
4803
- outbox.admit({ sourceKey: `native-result:${row.deliveryId}`, operationId: `native-result:${row.deliveryId}`, sourceInstance: row.targetInstance, sourceDaemonBootId: this.managerBootId,
4804
- targetInstance: row.sourceInstance, kind: "fleet_inbound", correlationId: row.correlationId,
4805
- payload: { type: "fleet_inbound", content: text || "Windows task completed without text output", meta: { from_instance: row.targetInstance, correlation_id: row.correlationId, requires_reply: "false", source: "native_runtime" } } });
4806
- this.reconciledRemoteResults.add(key);
4807
- }
4808
2921
  async waitForDurableLaneRelease(claimed) {
4809
2922
  const outbox = this.deliveryOutbox;
4810
2923
  if (!outbox)
@@ -5261,8 +3374,6 @@ export class FleetManager {
5261
3374
  this.warnAboutRemovedLoginMode(loaded);
5262
3375
  this.enforceGuestSwitch();
5263
3376
  this.observeWebAuthority(); // #2453 review: a reload's web-authority switches are seen at once
5264
- this.observeRuntimeSwitches(); // M-2255: so are the Windows runtimes' switches
5265
- this.observeHubSwitches(); // S-51: and the Hub's (no-op until the fleet has started)
5266
3377
  // Child tool/guidance readers must follow this fleet's actual config, including --config.
5267
3378
  // Scope the path to its owning data directory so another inherited home's override cannot enable CW.
5268
3379
  process.env.AGEND_FLEET_DATA_DIR = resolvePath(this.dataDir);
@@ -5316,7 +3427,6 @@ export class FleetManager {
5316
3427
  * guild channel.
5317
3428
  */
5318
3429
  reregisterClassicChannels() {
5319
- this.nativeMcpStore?.observePolicies();
5320
3430
  this.agentCredentialsCache?.observePolicies();
5321
3431
  if (!this.classicChannels)
5322
3432
  return;
@@ -5370,9 +3480,13 @@ export class FleetManager {
5370
3480
  }
5371
3481
  /** AgEnD package version (for the Settings "current version" / What's New). */
5372
3482
  get currentVersion() {
5373
- return RUNNING_BUILD.version;
3483
+ try {
3484
+ return JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version ?? "unknown";
3485
+ }
3486
+ catch {
3487
+ return "unknown";
3488
+ }
5374
3489
  }
5375
- get currentBuild() { return RUNNING_BUILD; }
5376
3490
  /**
5377
3491
  * Resolve a slash-command target in a channel. Classic channels are looked up
5378
3492
  * per-bot (same-channel multi-bot); a fleet-topic instance is found via the
@@ -5689,8 +3803,6 @@ export class FleetManager {
5689
3803
  }
5690
3804
  /** Get the adapter bound to an instance, falling back to primary adapter */
5691
3805
  getAdapterForInstance(name) {
5692
- if (isNativeExecInstance(this.fleetConfig?.instances[name]))
5693
- return null;
5694
3806
  const worldId = this.getInstanceAdapterId(name);
5695
3807
  if (worldId)
5696
3808
  return this.worlds.get(worldId)?.adapter ?? this.adapter;
@@ -5753,8 +3865,6 @@ export class FleetManager {
5753
3865
  }
5754
3866
  /** Get the group_id for an instance's bound adapter */
5755
3867
  getGroupIdForInstance(name) {
5756
- if (isNativeExecInstance(this.fleetConfig?.instances[name]))
5757
- return "";
5758
3868
  const adapterId = this.getInstanceAdapterId(name);
5759
3869
  const world = adapterId ? this.worlds.get(adapterId) : undefined;
5760
3870
  return world?.groupId ?? String(this.getChannelConfig(adapterId)?.group_id ?? "");
@@ -5791,8 +3901,6 @@ export class FleetManager {
5791
3901
  */
5792
3902
  getInstanceAdapterId(name) {
5793
3903
  const cfg = this.fleetConfig?.instances[name];
5794
- if (isNativeExecInstance(cfg))
5795
- return undefined;
5796
3904
  if (cfg)
5797
3905
  return cfg.channel_id ?? this.getPrimaryAdapterId();
5798
3906
  if (this.classicChannels?.getChannelIdByInstance(name) !== undefined) {
@@ -5820,32 +3928,9 @@ export class FleetManager {
5820
3928
  this.instanceWorldBinding.set(name, adapterId);
5821
3929
  }
5822
3930
  getInstanceStatus(name) {
5823
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
5824
- try {
5825
- return this.nativeInstanceRuntime().status(name);
5826
- }
5827
- catch {
5828
- return "crashed";
5829
- }
5830
- }
5831
3931
  if (this.lifecycle.isPaused(name))
5832
3932
  return "paused";
5833
3933
  const daemon = this.lifecycle.daemons.get(name);
5834
- if (isNativePaneInstance(this.fleetConfig?.instances[name])) {
5835
- const config = this.fleetConfig.instances[name];
5836
- if (config.machine_id ? !remoteRuntimeEnabled() : !localWindowsCliEnabled())
5837
- return "stopped";
5838
- if (daemon) {
5839
- const native = daemon, status = native.getProcessStatus?.() ?? "crashed";
5840
- return status === "running" && native.nativePaneCurrent?.() !== true ? "crashed" : status;
5841
- }
5842
- try {
5843
- return this.nativeInstanceRuntime().get(name)?.activeId ? "crashed" : "stopped";
5844
- }
5845
- catch {
5846
- return "crashed";
5847
- }
5848
- }
5849
3934
  const processStatus = this.instanceProcessStatus.get(name);
5850
3935
  // IPC can be disconnected during a respawn, so the event which announces
5851
3936
  // the new live pane may be missed. The in-process daemon is authoritative
@@ -5879,14 +3964,6 @@ export class FleetManager {
5879
3964
  }
5880
3965
  }
5881
3966
  getInstanceExecutionState(name) {
5882
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
5883
- try {
5884
- return this.nativeInstanceRuntime().execution(name);
5885
- }
5886
- catch {
5887
- return null;
5888
- }
5889
- }
5890
3967
  if (this.lifecycle.isPaused(name))
5891
3968
  return null;
5892
3969
  // Process status wins over a stale pane snapshot. A dead remain-on-exit pane
@@ -5897,8 +3974,6 @@ export class FleetManager {
5897
3974
  }
5898
3975
  /** Pure read of the single daemon-owned observation; no probe/capture or second tracker. */
5899
3976
  getInstanceInteraction(name) {
5900
- if (isNativeExecInstance(this.fleetConfig?.instances[name]))
5901
- return null;
5902
3977
  if (this.lifecycle.isPaused(name) || this.instanceProcessStatus.has(name))
5903
3978
  return null;
5904
3979
  const daemon = this.lifecycle.daemons.get(name);
@@ -5986,8 +4061,6 @@ export class FleetManager {
5986
4061
  bindings.push([name, backend, profile]);
5987
4062
  };
5988
4063
  for (const [name, config] of Object.entries(this.fleetConfig?.instances ?? {})) {
5989
- if (isWindowsInstance(config))
5990
- continue; // Windows sign-ins and usage do not belong to the Linux account.
5991
4064
  // loadFleetConfig() has already merged the fleet default into each row.
5992
4065
  const backend = config.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code";
5993
4066
  add(name, backend, instanceCredentialProfile(config, this.fleetConfig?.defaults, backend));
@@ -6318,7 +4391,7 @@ export class FleetManager {
6318
4391
  finish(false);
6319
4392
  });
6320
4393
  }
6321
- async deliverWithIdleGate(instanceName, payload, timeoutMs, deliveryEpoch, noInlineWake = false, stillCurrent, onStartRetryHold, beforeInput) {
4394
+ async deliverWithIdleGate(instanceName, payload, timeoutMs, deliveryEpoch, noInlineWake = false, stillCurrent, onStartRetryHold) {
6322
4395
  if (this.shuttingDown || !this.deliveryCurrent(instanceName, deliveryEpoch, stillCurrent))
6323
4396
  return false;
6324
4397
  await this.holdDeliveryForStorm(instanceName, deliveryEpoch);
@@ -6357,8 +4430,7 @@ export class FleetManager {
6357
4430
  const log = idleGateTimeoutLog(instanceName, timeoutMs, this.instanceStateCache.get(instanceName), Date.now());
6358
4431
  this.logger[log.level](log.fields, log.message);
6359
4432
  }
6360
- // The Hub representative's begin lease + input intent run inside the final connected hand-off (sendWhenConnected).
6361
- const sent = await this.sendWhenConnected(instanceName, payload, deliveryEpoch, stillCurrent, onStartRetryHold, beforeInput);
4433
+ const sent = await this.sendWhenConnected(instanceName, payload, deliveryEpoch, stillCurrent, onStartRetryHold);
6362
4434
  if (!sent)
6363
4435
  return false;
6364
4436
  if (this.deliveryCurrent(instanceName, deliveryEpoch, stillCurrent)) {
@@ -6391,25 +4463,21 @@ export class FleetManager {
6391
4463
  * `onStartRetryHold` (#2484, a person's message): an instance in its own start-retry backoff is waited for until that
6392
4464
  * retry brings it up, however long the backoff, and fails only once the retries give up.
6393
4465
  */
6394
- async sendWhenConnected(instanceName, payload, deliveryEpoch = this.getDeliveryEpoch(instanceName), stillCurrent, onStartRetryHold, beforeInput) {
4466
+ async sendWhenConnected(instanceName, payload, deliveryEpoch = this.getDeliveryEpoch(instanceName), stillCurrent, onStartRetryHold) {
6395
4467
  if (!this.deliveryCurrent(instanceName, deliveryEpoch, stillCurrent))
6396
4468
  return false;
6397
4469
  const queued = this.ipcWaitTails.get(instanceName);
6398
4470
  if (!queued) {
6399
4471
  const ipc = this.instanceIpcClients.get(instanceName);
6400
- if (ipc?.connected) {
6401
- if (beforeInput)
6402
- return this.handOffAfterBeforeInput(instanceName, payload, deliveryEpoch, stillCurrent, beforeInput);
6403
- if (ipc.send(this.bindHumanReceiptPayload(instanceName, payload)))
6404
- return true;
6405
- }
4472
+ if (ipc?.connected && ipc.send(this.bindHumanReceiptPayload(instanceName, payload)))
4473
+ return true;
6406
4474
  }
6407
4475
  const attempt = (queued ?? Promise.resolve())
6408
4476
  .catch(() => { })
6409
4477
  .then(() => {
6410
4478
  if (!this.deliveryCurrent(instanceName, deliveryEpoch, stillCurrent))
6411
4479
  return false;
6412
- return this.sendAfterIpcReturns(instanceName, payload, deliveryEpoch, stillCurrent, onStartRetryHold, beforeInput);
4480
+ return this.sendAfterIpcReturns(instanceName, payload, deliveryEpoch, stillCurrent, onStartRetryHold);
6413
4481
  });
6414
4482
  // The chain stores a settled-either-way promise so one failed delivery cannot
6415
4483
  // wedge every later one, and so `queued` above is safe to await unguarded.
@@ -6426,21 +4494,8 @@ export class FleetManager {
6426
4494
  }
6427
4495
  }
6428
4496
  }
6429
- /**
6430
- * The Hub representative's hand-off (#3145): only once a connected client exists, after every reconnect/order wait,
6431
- * the begin lease + input intent (beforeInput), then the fence and the LIVE client are checked again and the payload
6432
- * goes in one attempt. After the intent is spent nothing here retries: false is the caller's uncertain outcome.
6433
- */
6434
- async handOffAfterBeforeInput(instanceName, payload, deliveryEpoch, stillCurrent, beforeInput) {
6435
- if (!(await beforeInput()))
6436
- return false;
6437
- if (this.shuttingDown || !this.deliveryCurrent(instanceName, deliveryEpoch, stillCurrent))
6438
- return false;
6439
- const ipc = this.instanceIpcClients.get(instanceName);
6440
- return !!ipc?.connected && ipc.send(this.bindHumanReceiptPayload(instanceName, payload));
6441
- }
6442
4497
  /** Poll for the instance's IPC to come back, then send. Throws if it does not. */
6443
- async sendAfterIpcReturns(instanceName, payload, deliveryEpoch = this.getDeliveryEpoch(instanceName), stillCurrent, onStartRetryHold, beforeInput) {
4498
+ async sendAfterIpcReturns(instanceName, payload, deliveryEpoch = this.getDeliveryEpoch(instanceName), stillCurrent, onStartRetryHold) {
6444
4499
  let deadline = Date.now() + IPC_RECONNECT_GRACE_MS;
6445
4500
  let warned = false;
6446
4501
  let held = false;
@@ -6450,8 +4505,6 @@ export class FleetManager {
6450
4505
  // Re-read every round: a reconnect replaces the IpcClient object entirely,
6451
4506
  // so a cached reference would stay dead forever.
6452
4507
  const ipc = this.instanceIpcClients.get(instanceName);
6453
- if (ipc?.connected && beforeInput)
6454
- return this.handOffAfterBeforeInput(instanceName, payload, deliveryEpoch, stillCurrent, beforeInput);
6455
4508
  if (ipc?.connected && ipc.send(this.bindHumanReceiptPayload(instanceName, payload)))
6456
4509
  return true;
6457
4510
  // #2484: while the instance waits for (or runs) its own start retry, the grace runs from now — so the message is
@@ -6621,8 +4674,6 @@ export class FleetManager {
6621
4674
  * id is no platform message, and the echo is the fleet's own post. The web send calls this on the echo's lane.
6622
4675
  */
6623
4676
  startWebChatProgress(instanceName) {
6624
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName]))
6625
- return Promise.resolve();
6626
4677
  if (this.shuttingDown)
6627
4678
  return Promise.resolve();
6628
4679
  return this.sendCancelButton(instanceName);
@@ -6888,14 +4939,6 @@ export class FleetManager {
6888
4939
  }
6889
4940
  /** Single delivery facade: wake paused CLIs and serialize non-user work behind idle. */
6890
4941
  async deliverToInstance(instanceName, payload, options = {}) {
6891
- if (this.generalLocationDeliveryHold(instanceName))
6892
- return false;
6893
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName])) {
6894
- if (options.beforeInput)
6895
- return false; // no pre-input hook on the Windows runtime
6896
- return this.nativeInstanceRuntime().deliver(instanceName, payload, { stillCurrent: options.stillCurrent,
6897
- ...(options.authorityCurrent ? { authorityCurrent: options.authorityCurrent } : {}) });
6898
- }
6899
4942
  const deliveryEpoch = this.getDeliveryEpoch(instanceName);
6900
4943
  const deliveryPayload = { ...payload, delivery_epoch: deliveryEpoch };
6901
4944
  const meta = payload.meta && typeof payload.meta === "object"
@@ -6917,7 +4960,7 @@ export class FleetManager {
6917
4960
  return false;
6918
4961
  this.enforceWarmCap(instanceName); // woke one → evict a different LRU idle if over cap
6919
4962
  }
6920
- const sent = await this.sendWhenConnected(instanceName, deliveryPayload, deliveryEpoch, options.stillCurrent, options.onStartRetryHold, options.beforeInput);
4963
+ const sent = await this.sendWhenConnected(instanceName, deliveryPayload, deliveryEpoch, options.stillCurrent, options.onStartRetryHold);
6921
4964
  if (!sent)
6922
4965
  return false;
6923
4966
  // A cross-instance item arriving before the daemon observes this turn as
@@ -6928,7 +4971,7 @@ export class FleetManager {
6928
4971
  return true;
6929
4972
  }
6930
4973
  const previous = this.idleGatedDeliveryTails.get(instanceName) ?? Promise.resolve();
6931
- const delivery = previous.catch(() => { }).then(() => this.deliverWithIdleGate(instanceName, deliveryPayload, options.idleTimeoutMs ?? 60_000, deliveryEpoch, options.noInlineWake === true, options.stillCurrent, options.onStartRetryHold, options.beforeInput));
4974
+ const delivery = previous.catch(() => { }).then(() => this.deliverWithIdleGate(instanceName, deliveryPayload, options.idleTimeoutMs ?? 60_000, deliveryEpoch, options.noInlineWake === true, options.stillCurrent, options.onStartRetryHold));
6932
4975
  this.idleGatedDeliveryTails.set(instanceName, delivery);
6933
4976
  try {
6934
4977
  return await delivery;
@@ -7453,10 +5496,6 @@ export class FleetManager {
7453
5496
  * slot admission and joins any wake already in flight for the target.
7454
5497
  */
7455
5498
  async explicitWake(name, timeoutMs = 30_000, retryStoppedSession = false) {
7456
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
7457
- this.nativeInstanceRuntime().setPaused(name, false);
7458
- return;
7459
- }
7460
5499
  if (this.sessionOomStopped(name)) {
7461
5500
  if (!retryStoppedSession)
7462
5501
  throw new SessionOomStoppedError(false, readSessionOomStop(this.getInstanceDir(name)));
@@ -7481,10 +5520,6 @@ export class FleetManager {
7481
5520
  .sort((a, b) => (readLastInboundAt(this.getInstanceDir(a)) ?? 0) - (readLastInboundAt(this.getInstanceDir(b)) ?? 0))[0];
7482
5521
  }
7483
5522
  async changeInstancePauseState(name, action) {
7484
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
7485
- this.nativeInstanceRuntime().setPaused(name, action === "pause");
7486
- return action === "pause" ? "paused" : "awake";
7487
- }
7488
5523
  if (action === "wake") {
7489
5524
  await this.explicitWake(name, 30_000, true);
7490
5525
  this.enforceWarmCap(name); // manual wake still respects the fleet warm cap
@@ -7511,6 +5546,7 @@ export class FleetManager {
7511
5546
  }
7512
5547
  classicBehaviorUpdate(instanceName) {
7513
5548
  return {
5549
+ quota_notice: this.classicChannels?.getQuotaNoticeByInstance?.(instanceName) ?? null,
7514
5550
  reply_triggers: this.classicChannels?.getReplyTriggersByInstance(instanceName, this.fleetConfig?.defaults?.reply_triggers) ?? DEFAULT_REPLY_TRIGGERS,
7515
5551
  tool_progress: this.classicChannels?.getToolProgressByInstance(instanceName, this.fleetConfig?.defaults?.tool_progress) ?? "off",
7516
5552
  reply_completion_guard: this.classicChannels?.getReplyCompletionGuardByInstance(instanceName, this.fleetConfig?.defaults?.reply_completion_guard) ?? true,
@@ -7662,14 +5698,6 @@ export class FleetManager {
7662
5698
  resumePaused = false,
7663
5699
  /** Phase 2a: the caller's transition, for a start made from inside a restart or wake. */
7664
5700
  transition, execution, kiroMigration) {
7665
- if (isNativeExecInstance(config)) {
7666
- execution?.assert();
7667
- if (!windowsCliEnabled())
7668
- throw new Error("Windows runtime is disabled");
7669
- if (resumePaused)
7670
- this.nativeInstanceRuntime().setPaused(name, false);
7671
- return;
7672
- }
7673
5701
  const migration = this.kiroMigrationRuns?.get(name);
7674
5702
  if (migration && migration.token !== kiroMigration)
7675
5703
  throw new Error(t("kiro_migrate.in_progress"));
@@ -7720,29 +5748,27 @@ export class FleetManager {
7720
5748
  if (resumePaused)
7721
5749
  this.lifecycle.markActivitySeedNow(name);
7722
5750
  const backend = config.backend ?? this.fleetConfig?.defaults?.backend ?? "claude-code";
7723
- // #1490 row 67 (main): off-loop resolve before skill/instruction publication
5751
+ // #1490 row 67: off-loop resolve before skill/instruction publication
7724
5752
  // constructs a backend (resolveKnowledgeWorkDir). Capture stop/lifecycle
7725
5753
  // ownership first: a Stop during this wait finishes and bumps the epoch,
7726
5754
  // and the inner lifecycle check would otherwise capture the new epoch and
7727
5755
  // accept the stale start. Lifecycle start looks up again before its own
7728
- // createBackend; the probe cache makes that cheap. (alpha: a native Windows
7729
- // agent's CLI is on that PC — nothing to resolve on this one.)
5756
+ // createBackend; the probe cache makes that cheap.
7730
5757
  const epoch = this.lifecycle.epochOf(name);
7731
5758
  const stopGen = this.explicitStopGeneration.get(name) ?? 0;
7732
- const windows = isWindowsInstance(config);
7733
- const resolved = windows ? null : await resolveBackendBinary(backend);
5759
+ const resolved = await resolveBackendBinary(backend);
7734
5760
  execution?.assert();
7735
5761
  if (this.lifecycle.epochOf(name) !== epoch
7736
5762
  || (this.explicitStopGeneration.get(name) ?? 0) !== stopGen) {
7737
5763
  throw new SupersededStartError(name);
7738
5764
  }
7739
5765
  const binary = BACKEND_INSTALLATION_INFO[backend]?.binary;
7740
- if (binary && !windows)
5766
+ if (binary)
7741
5767
  noteResolvedBinary(binary, resolved ?? binary);
7742
- if (!windows && config.general_topic) {
5768
+ if (config.general_topic) {
7743
5769
  this.ensureGeneralInstructions(config.working_directory, backend, name);
7744
5770
  }
7745
- else if (!isWindowsInstance(config) && kind === "fleet-topic") {
5771
+ else if (kind === "fleet-topic") {
7746
5772
  // Workers get only role-eligible on-demand skills. Classic instances are
7747
5773
  // deliberately excluded: their workspace and conversation lifecycle are
7748
5774
  // managed independently from fleet-topic workers.
@@ -7757,7 +5783,7 @@ export class FleetManager {
7757
5783
  this.logger.warn({ err, name, backend }, "Failed to sync worker skills — continuing startup");
7758
5784
  }
7759
5785
  }
7760
- else if (!isWindowsInstance(config) && kind === "classic") {
5786
+ else if (kind === "classic") {
7761
5787
  // #2163: a Classic bot gets only the skills marked `classic` (its own look, the dashboard) — never the fleet's
7762
5788
  // coordination playbooks (#557 kept those out of end-user bots) — and only into a workspace AgEnD manages: the
7763
5789
  // room's own folder under <AGEND_HOME>/workspaces/. Anywhere else, nothing is written; `agend-agent skill <name>`
@@ -7774,7 +5800,7 @@ export class FleetManager {
7774
5800
  }
7775
5801
  }
7776
5802
  const receipt = execution ? this.classicSettingsOwners.get(execution) : undefined;
7777
- const identity = { kind, backend, model: isWindowsInstance(config) ? config.model || "default" : this.resolveInstanceModel(name).display };
5803
+ const identity = { kind, backend, model: this.resolveInstanceModel(name).display };
7778
5804
  if (receipt) {
7779
5805
  await this.lifecycle.start(name, config, topicMode, identity, transition, execution, daemon => {
7780
5806
  // Only this transition's actual publication can acquire its replacement.
@@ -7844,7 +5870,7 @@ export class FleetManager {
7844
5870
  // them before grouping/staggering: startInstance() retains its own guard as
7845
5871
  // a final backstop, but putting a no-op entry in this queue still consumes a
7846
5872
  // full stagger slot for every distinct working directory.
7847
- const runnableEntries = entries.filter(([name, config]) => !isNativeExecInstance(config) && !this.lifecycle.isPaused(name));
5873
+ const runnableEntries = entries.filter(([name]) => !this.lifecycle.isPaused(name));
7848
5874
  // ClassicBot rooms queue with the fleet instances, at the same pressure pacing (#2642 follow-up): they used to start
7849
5875
  // only after the whole fleet, the chat connections, topics and icons. A name the fleet also configures is the fleet's.
7850
5876
  const fleetNames = new Set(entries.map(([name]) => name));
@@ -7910,8 +5936,6 @@ export class FleetManager {
7910
5936
  return false;
7911
5937
  if (err instanceof SessionOomStoppedError)
7912
5938
  await this.notifySessionOomStop(name);
7913
- if (this.holdNativePaneRecovery(name, err))
7914
- return false;
7915
5939
  if (this.stopOnUnsupportedCli(name, err, what))
7916
5940
  return false;
7917
5941
  this.logger.error({ err, name }, `Failed to start ${what}`);
@@ -8053,8 +6077,6 @@ export class FleetManager {
8053
6077
  if (err instanceof SupersededStartError)
8054
6078
  return;
8055
6079
  // An earlier attempt could not tell (unknown probe); this one could.
8056
- if (this.holdNativePaneRecovery(name, err))
8057
- return;
8058
6080
  if (this.stopOnUnsupportedCli(name, err, "instance"))
8059
6081
  return;
8060
6082
  this.logger.error({ err, name, attempt: attempt + 1 }, "Automatic startup retry failed");
@@ -8316,7 +6338,7 @@ export class FleetManager {
8316
6338
  return count;
8317
6339
  }
8318
6340
  configuredStartupInstanceNames(fleet, includeClassic) {
8319
- const names = new Set(Object.entries(fleet.instances).filter(([, config]) => !isNativeExecInstance(config)).map(([name]) => name));
6341
+ const names = new Set(Object.keys(fleet.instances));
8320
6342
  if (includeClassic) {
8321
6343
  for (const channel of this.classicChannels?.getAll() ?? [])
8322
6344
  names.add(channel.instanceName);
@@ -8367,10 +6389,6 @@ export class FleetManager {
8367
6389
  return poster.postCompletion({ resolveAdapter: () => this.readyProgressAdapter(adapterId), chatId, threadId }, text);
8368
6390
  }
8369
6391
  async stopInstance(name, transition) {
8370
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
8371
- this.nativeInstanceRuntime().setPaused(name, true);
8372
- return { backgroundWorkEnded: false };
8373
- }
8374
6392
  this.explicitStopGeneration.set(name, (this.explicitStopGeneration.get(name) ?? 0) + 1);
8375
6393
  this.cancelStartupRetry(name);
8376
6394
  this.failoverActive.delete(name);
@@ -8410,16 +6428,6 @@ export class FleetManager {
8410
6428
  async restartSingleInstance(name, opts) {
8411
6429
  if (opts?.ownerCurrent && !opts.ownerCurrent())
8412
6430
  throw new RestartSupersededError(this.shownInstanceName(name));
8413
- if (isNativeExecInstance(this.fleetConfig?.instances[name])) {
8414
- const runtime = this.nativeInstanceRuntime();
8415
- if (runtime.get(name)?.blocked)
8416
- throw new Error("Verify the disconnected Windows process stopped and recover this agent before restarting");
8417
- runtime.cancel(name);
8418
- runtime.setPaused(name, false);
8419
- return;
8420
- }
8421
- if (opts?.explicit && isNativePaneInstance(this.fleetConfig?.instances[name]))
8422
- this.lifecycle.resetNativeCrashBudget(name);
8423
6431
  const migration = this.kiroMigrationRuns?.get(name);
8424
6432
  if (migration && migration.token !== opts?.kiroMigration)
8425
6433
  throw new Error(t("kiro_migrate.in_progress"));
@@ -8483,7 +6491,7 @@ export class FleetManager {
8483
6491
  return run;
8484
6492
  }
8485
6493
  isInstanceRestarting(name) {
8486
- return this.restartsInFlight.has(name) || this.kiroMigrationRuns?.has(name) === true || this.lifecycle?.nativeRecoveryPending?.(name) === true;
6494
+ return this.restartsInFlight.has(name) || this.kiroMigrationRuns?.has(name) === true;
8487
6495
  }
8488
6496
  /** InstanceLifecycleContext: an explicit wake joins the restart in flight. */
8489
6497
  restartInFlight(name) {
@@ -8716,12 +6724,6 @@ export class FleetManager {
8716
6724
  },
8717
6725
  });
8718
6726
  }
8719
- if (!this.webDeviceLogins) {
8720
- this.webDeviceLogins = new DeviceLoginRequests({
8721
- onEvent: event => { if (event === "breaker-open")
8722
- this.logger.warn("Device sign-in approval paused: too many wrong codes"); },
8723
- });
8724
- }
8725
6727
  if (!this.webGuestLoginCodes) {
8726
6728
  this.webGuestLoginCodes = new GuestLoginCodes({
8727
6729
  onEvent: event => {
@@ -8784,9 +6786,6 @@ export class FleetManager {
8784
6786
  log: () => this.logger.warn("Managed tunnel cleanup requires attention") }));
8785
6787
  }
8786
6788
  publicOwnerCurrent(owner) {
8787
- // #3008: the outside link is this computer's admin's — while "Allow connections from outside" is on, web-only.
8788
- if (isOutsideLinkOwner(owner))
8789
- return !this.shuttingDown && this.outsideOn();
8790
6789
  if (this.shuttingDown || !this.hasFleetAdmins(owner.adapterId) || !this.isFleetAdmin(owner.userId, owner.adapterId))
8791
6790
  return false;
8792
6791
  const configured = (this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : [])).find(ch => (ch.id ?? ch.type) === owner.adapterId);
@@ -8876,11 +6875,7 @@ export class FleetManager {
8876
6875
  }
8877
6876
  getPublicWebLink() {
8878
6877
  this.initializeWebSessions();
8879
- return this.publicWebLink ??= new PublicWebLink(this.publicWebLinkDeps());
8880
- }
8881
- /** The public link's wiring to this fleet (a test builds one with the tunnel itself stubbed: `ensure`, `provider`). */
8882
- publicWebLinkDeps() {
8883
- return {
6878
+ return this.publicWebLink ??= new PublicWebLink({
8884
6879
  dataDir: this.dataDir, web: () => this.fleetConfig?.web,
8885
6880
  permitted: owner => this.publicOwnerCurrent(owner),
8886
6881
  reserve: id => this.getTunnelLane().reserve("dashboard", id),
@@ -8913,9 +6908,7 @@ export class FleetManager {
8913
6908
  if (world && world.adapter === owner.binding)
8914
6909
  void world.adapter.sendText(owner.chatId, t("dashboard.public_cleanup"), { threadId: owner.threadId }).catch(() => { });
8915
6910
  },
8916
- onClose: (owner, reason) => { if (isOutsideLinkOwner(owner))
8917
- this.outsideLinkClosed(reason); },
8918
- };
6911
+ });
8919
6912
  }
8920
6913
  publicLinkAutoOpen = null;
8921
6914
  /**
@@ -9336,7 +7329,6 @@ export class FleetManager {
9336
7329
  void this.publicWebLink?.close("dashboard revoke");
9337
7330
  this.forgetPublicLinkOwner();
9338
7331
  this.webLoginCodes.revoke();
9339
- this.webDeviceLogins?.revokeAll(); // …and every new device still waiting to be approved (#3061)
9340
7332
  this.webGuestLoginCodes.revokeAll();
9341
7333
  this.guestRevokes++; // and every guest /web still on its way
9342
7334
  // #1570: no browser may ask the sign-in page for a code any more.
@@ -10275,9 +8267,6 @@ export class FleetManager {
10275
8267
  const fleet = this.loadConfig(configPath, { startup: true });
10276
8268
  this.slimFleetConfigAtStartup();
10277
8269
  this.logConfigWarningsAtStartup();
10278
- this.initializeNativeInstances();
10279
- // M-2255: from here on a change to the Windows runtimes' switches applies live (observeRuntimeSwitches).
10280
- this.runtimeEffects = { windows_cli: localWindowsCliEnabled(), remote_runtime: remoteRuntimeEnabled() };
10281
8270
  setLocale(detectLocale(fleet)); // user-facing text language (fleet.yaml defaults.locale / timezone)
10282
8271
  // #1737 review: the marker this boot adopts — the startup clear may only ever clear this one.
10283
8272
  const bootUpdateMarker = updateMarkerIdentity(this.dataDir);
@@ -10300,9 +8289,20 @@ export class FleetManager {
10300
8289
  // #2910: a web-only fleet's agents run in topic mode too (their cross-instance tools work; the dashboard is their place).
10301
8290
  const topicMode = daemonTopicMode(fleet);
10302
8291
  // Set tmux socket isolation for custom AGEND_HOME
10303
- // M-2457: the bundled tmux's socket while it is on; getTmuxSocketName() as before while off.
10304
- const { tmuxSocket } = await import("./tmux-binary.js");
10305
- TmuxManager.setSocketName(tmuxSocket());
8292
+ const { getTmuxSocketName: getSocket, isDefaultAgendHome } = await import("./paths.js");
8293
+ TmuxManager.setSocketName(getSocket());
8294
+ // M-2457 §7: after a raw downgrade from 2.4, its own tmux server may still run the agents — start none beside it.
8295
+ // main / the 2.3 line ONLY: on the 2.4 line (release/2.3.5-alpha) AgEnD's own server IS the one on `agend` — a
8296
+ // main → alpha sync must keep alpha's side of this block, never both.
8297
+ {
8298
+ const { newerTmuxServer, realGuardDeps, guardMessage } = await import("./bundled-tmux-guard.js");
8299
+ const hit = await newerTmuxServer(realGuardDeps(getAgendHome(), isDefaultAgendHome(), getSocket()));
8300
+ if (hit) {
8301
+ const message = guardMessage(hit);
8302
+ this.logger.error({ socket: hit.socket, confirmed: hit.confirmed }, message);
8303
+ throw new Error(message);
8304
+ }
8305
+ }
10306
8306
  await TmuxManager.ensureSession(getTmuxSession());
10307
8307
  this.tmuxSessionEnsured = true;
10308
8308
  // #2072: what this host can run — probed in the background, published for every picker.
@@ -10310,7 +8310,7 @@ export class FleetManager {
10310
8310
  // Pre-flight (advisory, fire-and-forget): warm the per-backend auth cache
10311
8311
  // before instances spawn, so a CLI that comes up on its sign-in screen gets
10312
8312
  // an instant 🔑 verdict instead of decaying into crash/MCP-died noise.
10313
- this.lifecycle.primeAuthVerification(Object.values(fleet.instances).filter(config => !isWindowsInstance(config)).map(config => {
8313
+ this.lifecycle.primeAuthVerification(Object.values(fleet.instances).map(config => {
10314
8314
  const backend = config.backend ?? fleet.defaults?.backend ?? "claude-code";
10315
8315
  // #1699 follow-up: each instance's own login — a profile is probed with its own environment.
10316
8316
  return { backend, profile: credentialHomeSpec(backend) ? instanceCredentialProfile(config, fleet.defaults, backend) : null };
@@ -10372,7 +8372,6 @@ export class FleetManager {
10372
8372
  // #1565: the web chat as it was before this process — the configured instances' only (fleet.yaml and ClassicBot
10373
8373
  // rooms), before the dashboard serves anything; from now on every change is written back (off the loop).
10374
8374
  this.restoreWebChat(fleet);
10375
- this.initializeNativeInstances(true);
10376
8375
  this.startSearchIndex(fleet);
10377
8376
  // Parse-only source monitoring; it does not apply hand-edited Fleet settings.
10378
8377
  this.configHistoryTimer = setInterval(() => this.configHistory?.check(), 1_000);
@@ -10555,7 +8554,7 @@ export class FleetManager {
10555
8554
  }
10556
8555
  }
10557
8556
  // Phase 1: Start general instances first and wait for them
10558
- const allEntries = Object.entries(fleet.instances).filter(([, config]) => !isNativeExecInstance(config));
8557
+ const allEntries = Object.entries(fleet.instances);
10559
8558
  const generals = allEntries.filter(([_, cfg]) => cfg.general_topic);
10560
8559
  const others = allEntries.filter(([_, cfg]) => !cfg.general_topic);
10561
8560
  const startupProgress = new RestartProgress(this.runnableStartupCount(fleet, topicMode), pendingUpdateProgress?.startedAt ?? startupStartedAt, this.logger, { mode: pendingProgressOperation === "full-restart" ? "reload" : pendingUpdateProgress ? "update" : "restart" });
@@ -10567,9 +8566,6 @@ export class FleetManager {
10567
8566
  await this.startHealthServer(fleet.health_port ?? 19280);
10568
8567
  if (this.shuttingDown)
10569
8568
  return;
10570
- // S-51: from here on the Hub switches apply (an enabled Hub mounts now, beside the fleet; it never blocks startup).
10571
- this.hubEffectsReady = true;
10572
- this.observeHubSwitches();
10573
8569
  if (generals.length > 0) {
10574
8570
  for (const [name, cfg] of generals) {
10575
8571
  try {
@@ -10800,10 +8796,6 @@ export class FleetManager {
10800
8796
  // #named-link-ttl: a named link with auto_open comes back now — after the web listener and the chat connections.
10801
8797
  this.startPublicLinkAutoOpen();
10802
8798
  }
10803
- // #3008: connections from outside were on when the fleet stopped: the link opens again (its address is new for a
10804
- // quick link — Settings shows it on this computer).
10805
- if (this.outsideOn())
10806
- this.openOutsideLink([0, ...OUTSIDE_LINK_RETRY_MS]);
10807
8799
  // #1266: buttons that ended while AgEnD was down (expired, or chosen before a restart) are shown as ended now.
10808
8800
  // #1823: and polls: a deadline that passed meanwhile closes now (results once); an open one's countdown is redrawn.
10809
8801
  void this.replyButtons()?.sweep().catch(err => this.logger.warn({ err }, "Reply-button sweep failed"));
@@ -10856,7 +8848,43 @@ export class FleetManager {
10856
8848
  * Discord bot and while some account has a running agent, read every 30 min — the same cached read (its 5-minute TTL
10857
8849
  * still bounds the vendor calls), recorded like any other.
10858
8850
  */
8851
+ quotaNotices = null;
8852
+ stopQuotaObserver = null;
8853
+ quotaNoticePolicy() {
8854
+ const defaults = this.fleetConfig?.defaults.quota_notice;
8855
+ const wanted = defaults?.target ?? "general";
8856
+ const instances = this.fleetConfig?.instances ?? {};
8857
+ const target = wanted === "general"
8858
+ ? Object.entries(instances).find(([name, c]) => c.general_topic === true || name === "general")?.[0]
8859
+ : Object.hasOwn(instances, wanted) ? wanted : undefined;
8860
+ const webOnly = !!this.webOnlyPromptPlace();
8861
+ const config = target ? instances[target] : undefined;
8862
+ const adapterId = target ? this.getInstanceAdapterId(target) : undefined;
8863
+ const groupId = target ? this.getChannelConfig(adapterId)?.group_id : undefined;
8864
+ const ready = !!target && (webOnly || (config?.topic_id != null && !!groupId && !!(this.getAdapterForInstance(target) ?? this.adapter)));
8865
+ return {
8866
+ target: this.shuttingDown ? "" : ready ? target : !target && wanted === "general" && webOnly ? "/notices" : "",
8867
+ destinationBinding: JSON.stringify([webOnly, adapterId, groupId, config?.topic_id]),
8868
+ locale: this.fleetConfig?.defaults.locale ?? getLocale(),
8869
+ subscriptions: this.shuttingDown ? [] : this.activeBackendBindings().flatMap(([name, backend, profile]) => {
8870
+ const provider = usageProviderIdForBackend(backend);
8871
+ if (!provider)
8872
+ return [];
8873
+ const providerId = profile ? `${provider}:${profile}` : provider;
8874
+ return [{ account: `${backend}:host:${profile ?? ""}`, providerId,
8875
+ rule: quotaNoticeRule(defaults, backend, instances[name]?.quota_notice ?? this.classicChannels?.getQuotaNoticeByInstance?.(name)) }];
8876
+ }),
8877
+ };
8878
+ }
10859
8879
  startQuotaBackgroundReads() {
8880
+ if (!this.quotaNotices) {
8881
+ this.quotaNotices = new QuotaNotices({
8882
+ file: join(this.dataDir, "quota-notices.json"), policy: () => this.quotaNoticePolicy(),
8883
+ send: (target, text) => target === "/notices" ? this.recordWebNotice("fleet", text) : this.notifyInstanceTopic(target, text),
8884
+ warn: err => this.logger.warn({ err }, "Optional quota reminders held: could not retain dispatch state"),
8885
+ });
8886
+ this.stopQuotaObserver = onUsageSnapshot(payload => { void this.quotaNotices?.observe(payload); });
8887
+ }
10860
8888
  if (this.quotaReadTimer)
10861
8889
  clearInterval(this.quotaReadTimer);
10862
8890
  this.quotaReadTimer = setInterval(() => { void this.backgroundQuotaRead(); }, FleetManager.QUOTA_BACKGROUND_READ_MS);
@@ -11359,8 +9387,8 @@ export class FleetManager {
11359
9387
  * that should be running is running
11360
9388
  * - `degraded` — reachable, but something the operator should look at (an
11361
9389
  * adapter retrying, an instance crashed or stopped)
11362
- * - `down` — no configured adapter is connected, or a dedicated Hub's
11363
- * server mount is unavailable
9390
+ * - `down` — the fleet cannot do its job: no adapter is connected, so no
9391
+ * message can arrive or be answered
11364
9392
  *
11365
9393
  * Deliberately does NOT gate the systemd watchdog — see the comment at the
11366
9394
  * WATCHDOG timer for why.
@@ -11393,20 +9421,10 @@ export class FleetManager {
11393
9421
  if (effectiveStatus === "connected")
11394
9422
  connected++;
11395
9423
  }
11396
- const problems = [], warnings = [];
11397
- const rawHub = this.rawFleetConfig?.hub;
11398
- const hub = rawHub?.enabled === true ? { server: this.shuttingDown ? "stopped"
11399
- : this.hubRuntime?.serverHealth?.(rawHub) ?? "unavailable" } : undefined;
11400
- const hubOnly = !!hub && names.length === 0 && this.adapterState.size === 0;
11401
- if (hub && hub.server !== "ready")
11402
- problems.push(`Hub server is ${hub.server}`);
9424
+ const problems = [];
11403
9425
  const hostMemory = this.memoryPressure.snapshot();
11404
- if (hostMemory.level === "critical" || hostMemory.level === "elevated") {
11405
- const pressure = `host memory pressure is ${hostMemory.level}`;
11406
- if (hubOnly)
11407
- warnings.push(pressure);
11408
- else if (!this.memoryPressure.advisoryOnly())
11409
- problems.push(pressure);
9426
+ if (!this.memoryPressure.advisoryOnly() && (hostMemory.level === "critical" || hostMemory.level === "elevated")) {
9427
+ problems.push(`host memory pressure is ${hostMemory.level}`);
11410
9428
  }
11411
9429
  if (this.adapterState.size > 0 && connected === 0)
11412
9430
  problems.push("no channel adapter is connected");
@@ -11418,8 +9436,9 @@ export class FleetManager {
11418
9436
  }
11419
9437
  if (!this.startupComplete)
11420
9438
  problems.push("startup has not completed");
11421
- // Preserve adapter scoring. A dedicated Hub also cannot serve its workload before its server mount is ready.
11422
- const status = (this.adapterState.size > 0 && connected === 0) || (hubOnly && hub?.server !== "ready")
9439
+ // "down" is reserved for "cannot receive or answer a message at all". A fleet
9440
+ // with adapters configured but none connected is exactly that.
9441
+ const status = this.adapterState.size > 0 && connected === 0
11423
9442
  ? "down"
11424
9443
  : problems.length > 0 ? "degraded" : "ok";
11425
9444
  return {
@@ -11431,7 +9450,7 @@ export class FleetManager {
11431
9450
  memory: readFleetMemory(),
11432
9451
  hostMemory: this.memoryTop && (hostMemory.level === "elevated" || hostMemory.level === "critical")
11433
9452
  ? { ...hostMemory, topProcesses: this.memoryTop.processes, topProcessesAt: this.memoryTop.at } : hostMemory,
11434
- problems, warnings, ...(hub ? { hub } : {}),
9453
+ problems,
11435
9454
  };
11436
9455
  }
11437
9456
  /** Start the primary adapter (backward-compatible, sets this.adapter) */
@@ -11758,14 +9777,10 @@ export class FleetManager {
11758
9777
  }
11759
9778
  }
11760
9779
  }
11761
- const daemon = this.daemons.get(name);
11762
- const native = daemon?.usesNativePane?.();
11763
9780
  const sockPath = join(this.getInstanceDir(name), "channel.sock");
11764
- if (!native && !existsSync(sockPath))
9781
+ if (!existsSync(sockPath))
11765
9782
  return;
11766
- const ipc = native
11767
- ? new NativeDaemonClient(daemon, () => this.daemons.get(name) === daemon && (!this.instanceIpcClients.has(name) || this.instanceIpcClients.get(name) === ipc))
11768
- : new IpcClient(sockPath);
9783
+ const ipc = new IpcClient(sockPath);
11769
9784
  try {
11770
9785
  await ipc.connect();
11771
9786
  this.instanceIpcClients.set(name, ipc);
@@ -11828,9 +9843,6 @@ export class FleetManager {
11828
9843
  this.eventLog?.insert(targetInstance, "cross_instance_delivery_failed", {
11829
9844
  from: senderSession, correlation_id: correlationId, error,
11830
9845
  });
11831
- // A Hub question the pane never took: the representative holds it uncertain (never resent).
11832
- if (senderSession === HUB_REPORT_TARGET && !senderInstance)
11833
- this.hubDeliveryFailed?.(correlationId, targetInstance);
11834
9846
  if (senderInstance) {
11835
9847
  this.notifyInstanceTopic(senderInstance, t("cross_instance.delivery_failed_notice", targetInstance, displayError, correlationId));
11836
9848
  void this.deliverToInstance(senderInstance, {
@@ -11884,7 +9896,7 @@ export class FleetManager {
11884
9896
  // connected, so seed the cache instead of waiting for another transition.
11885
9897
  ipc.send({ type: "query_instance_state", requestId: `fleet-state-${Date.now()}` });
11886
9898
  this.logger.debug({ name }, "Connected to instance IPC");
11887
- if (!native && !this.statuslineWatcher.has(name)) {
9899
+ if (!this.statuslineWatcher.has(name)) {
11888
9900
  this.statuslineWatcher.watch(name);
11889
9901
  }
11890
9902
  // Auto-reconnect on disconnect (unless intentionally stopping)
@@ -11949,12 +9961,13 @@ export class FleetManager {
11949
9961
  try {
11950
9962
  const { execFile } = await import("./child-process.js");
11951
9963
  const { promisify } = await import("node:util");
11952
- const { tmuxArgs, tmuxBinary, tmuxEnvOption } = await import("./tmux-binary.js");
9964
+ const { getTmuxSocketName } = await import("./paths.js");
11953
9965
  // Honour socket isolation: without -L this queried the user's default
11954
9966
  // tmux server instead of the fleet's, so under a custom AGEND_HOME the
11955
9967
  // check was meaningless (it reported every pane dead).
11956
- const args = tmuxArgs(["list-panes", "-t", windowId]);
11957
- await promisify(execFile)(tmuxBinary(), args, { timeout: 5_000, ...tmuxEnvOption() });
9968
+ const socket = getTmuxSocketName();
9969
+ const args = socket ? ["-L", socket, "list-panes", "-t", windowId] : ["list-panes", "-t", windowId];
9970
+ await promisify(execFile)("tmux", args, { timeout: 5_000 });
11958
9971
  }
11959
9972
  catch {
11960
9973
  // Pane dead — respawn
@@ -12958,12 +10971,6 @@ export class FleetManager {
12958
10971
  if (msg.adapterId)
12959
10972
  this.bindInstanceAdapter(generalInstance, msg.adapterId, true);
12960
10973
  const inboundAdapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
12961
- const locationHold = this.generalLocationDeliveryHold(generalInstance);
12962
- if (locationHold) {
12963
- await inboundAdapter.sendText(msg.chatId, locationHold, { threadId: msg.threadId || undefined });
12964
- this.needsYou?.poke();
12965
- return;
12966
- }
12967
10974
  // React immediately — before any other API calls — through the status
12968
10975
  // path, which tracks Telegram slot ownership and Discord status adds.
12969
10976
  if (msg.chatId && msg.messageId) {
@@ -13051,13 +11058,6 @@ export class FleetManager {
13051
11058
  if (instanceConfig?.general_topic && await this.topicCommands.handleGeneralCommand(msg, instanceName)) {
13052
11059
  return;
13053
11060
  }
13054
- const locationHold = this.generalLocationDeliveryHold(instanceName);
13055
- if (locationHold) {
13056
- const adapter = this.worlds.get(msg.adapterId ?? "")?.adapter ?? this.adapter;
13057
- await adapter.sendText(msg.chatId, locationHold, { threadId: msg.threadId || undefined });
13058
- this.needsYou?.poke();
13059
- return;
13060
- }
13061
11061
  // Bind instance to the adapter that delivered this message
13062
11062
  if (msg.adapterId)
13063
11063
  this.bindInstanceAdapter(instanceName, msg.adapterId, true);
@@ -14666,7 +12666,7 @@ export class FleetManager {
14666
12666
  const cfg = this.fleetConfig?.instances?.[n];
14667
12667
  if (!cfg || cfg.topic_id == null || cfg.topic_id === "")
14668
12668
  return null;
14669
- return this.destinationFor(this.topicAdapterIdOf(n), cfg.topic_id);
12669
+ return this.destinationFor(this.getInstanceAdapterId(n), cfg.topic_id);
14670
12670
  };
14671
12671
  const generalDestinations = () => {
14672
12672
  const out = new Set();
@@ -14685,21 +12685,9 @@ export class FleetManager {
14685
12685
  };
14686
12686
  return { instances: this.fleetConfig?.instances, destinationOf, generalDestinations };
14687
12687
  }
14688
- /**
14689
- * The bot an agent's configured topic is on (#3105 review, Prism P1): a fleet.yaml agent's `channel_id` (else the
14690
- * primary connection) whatever its runtime — a per-turn Windows agent has no execution adapter
14691
- * (getInstanceAdapterId), yet its topic is still on that bot, and must be counted as an owner and deleted through it.
14692
- * Topic planning and ownership only; execution routing is getInstanceAdapterId's and unchanged.
14693
- */
14694
- topicAdapterIdOf(name) {
14695
- const cfg = this.fleetConfig?.instances?.[name];
14696
- if (cfg)
14697
- return cfg.channel_id ?? this.getPrimaryAdapterId();
14698
- return this.getInstanceAdapterId(name);
14699
- }
14700
12688
  /** What deleting `name`'s topic would act on: its own bot only — a bot that is not there is never substituted. */
14701
12689
  planInstanceTopicDelete(name) {
14702
- const adapterId = this.topicAdapterIdOf(name);
12690
+ const adapterId = this.getInstanceAdapterId(name);
14703
12691
  return planInstanceTopicDelete({ name, adapterId, adapter: this.instanceTopicAdapter(adapterId), ...this.topicOwnership() });
14704
12692
  }
14705
12693
  getForumTopicDeleter(adapterId) {
@@ -14979,8 +12967,6 @@ export class FleetManager {
14979
12967
  this.observeWebAuthority(); // #2453 review: so is a web-authority switch (off is always seen)
14980
12968
  if (!this.fleetConfig || !this.configPath)
14981
12969
  return;
14982
- // #3147 r2: a changed or removed Hub representative's consent is invalidated durably BEFORE the change is written.
14983
- this.observeHubAgentIncarnations(true);
14984
12970
  if (!this.savedFleetConfigSnapshot)
14985
12971
  this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
14986
12972
  normalizeNoticeDelivery(this.fleetConfig);
@@ -15103,12 +13089,9 @@ export class FleetManager {
15103
13089
  Object.assign(current, resolved);
15104
13090
  }
15105
13091
  this.savedFleetConfigSnapshot = structuredClone(this.fleetConfig);
15106
- this.nativeMcpStore?.observePolicies();
15107
13092
  this.agentCredentialsCache?.observePolicies();
15108
13093
  this.refreshSavedDashboardPermissions();
15109
13094
  this.logger.info({ path: this.configPath, strippedDefaults: redundantPaths.length }, "Saved fleet config (lossless patch)");
15110
- this.observeRuntimeSwitches(); // M-2255: a saved Windows runtime switch applies now
15111
- this.observeHubSwitches(); // S-51: so does a saved Hub switch
15112
13095
  }
15113
13096
  /** A permission tightening is effective when its save returns, independently of the later apply job or IPC. */
15114
13097
  refreshSavedDashboardPermissions() {
@@ -15342,43 +13325,10 @@ export class FleetManager {
15342
13325
  }
15343
13326
  getSettingsConfigPath() { return this.configPath; }
15344
13327
  async removeInstance(name, authorization, execution) {
15345
- if (isWindowsInstance(this.fleetConfig?.instances[name])) {
15346
- execution?.assert();
15347
- assertExplicitInstanceRemoval(authorization);
15348
- if (isNativePaneInstance(this.fleetConfig?.instances[name])) {
15349
- await this.stopInstance(name);
15350
- execution?.assert();
15351
- }
15352
- this.nativeInstanceRuntime().remove(name, () => !this.shuttingDown && (!execution || execution.current()), execution);
15353
- return;
15354
- }
15355
13328
  if (execution)
15356
13329
  return this.lifecycle.runTransition(name, transition => this.removeInstanceOwned(name, authorization, execution, transition));
15357
13330
  return this.removeInstanceOwned(name, authorization);
15358
13331
  }
15359
- /** Shared instance metadata goes with a native identity; its Windows workspace and provider sessions stay intact. */
15360
- nativeInstanceRemoved(name) {
15361
- const clean = (what, run) => { try {
15362
- run();
15363
- }
15364
- catch (error) {
15365
- this.logger.warn({ name, err: error }, `Native instance ${what} cleanup failed`);
15366
- } };
15367
- clean("delivery", () => { this.cancelPendingDeliveries(name); });
15368
- forgetInstanceContext(name);
15369
- clean("schedules", () => { for (const schedule of this.scheduler?.list(name) ?? [])
15370
- this.scheduler.delete(schedule.id); });
15371
- this.lastActivity.delete(name);
15372
- this.lastInboundUser.delete(name);
15373
- this.rateLimitWarnedAt.delete(name);
15374
- this.lastInboundMsg.delete(name);
15375
- clean("history", () => this.webChatHistory.forget(name));
15376
- clean("search", () => this.searchIndex?.removeInstance(name));
15377
- clean("files", () => this.webFiles.forget(name));
15378
- clean("compact", () => this.compactWatchService?.forget(name));
15379
- clean("preferences", () => this.webPrefs.forgetInstance(name));
15380
- clean("appearance", () => { this.themeStore.forget(name); this.webAppearance.forget(name); this.appearanceTools.forget(name); this.mods.forgetInstance(name); this.officeActsInstance?.forget(name); this.officePackToolsInstance?.forget(name); });
15381
- }
15382
13332
  async removeInstanceOwned(name, authorization, execution, transition) {
15383
13333
  execution?.assert();
15384
13334
  assertExplicitInstanceRemoval(authorization);
@@ -15418,7 +13368,6 @@ export class FleetManager {
15418
13368
  const instanceDirToClean = this.getInstanceDir(name);
15419
13369
  // #1774: replies sent from here on are a later instance's (a replacement registered while this one goes); the ones
15420
13370
  // sent before wait for the outcome before they are shown.
15421
- this.forgetHubAgentIncarnation(name, true); // #3147: a re-added instance never inherits Hub consent; not durable → refused
15422
13371
  const removedBelow = this.webChatGeneration(name) + 1, removedDaemon = this.daemons.get(name);
15423
13372
  this.webChatGenerations.set(name, removedBelow);
15424
13373
  let settleRemoval;
@@ -15505,8 +13454,6 @@ export class FleetManager {
15505
13454
  execution?.complete();
15506
13455
  }
15507
13456
  startStatuslineWatcher(name) {
15508
- if (isWindowsInstance(this.fleetConfig?.instances[name]))
15509
- return;
15510
13457
  if (this.lifecycle.isPaused(name))
15511
13458
  return;
15512
13459
  this.statuslineWatcher.watch(name);
@@ -16955,10 +14902,6 @@ export class FleetManager {
16955
14902
  * would have to make this async and change every caller.
16956
14903
  */
16957
14904
  notifyInstanceTopic(instanceName, text, extraOpts) {
16958
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName])) {
16959
- this.emitSseEvent("message", { instance: instanceName, role: "status", sender: "AgEnD", text, ts: new Date().toISOString() });
16960
- return true;
16961
- }
16962
14905
  // #2910 D2: a web-only fleet has no topics: what the agent's topic would have been told is a Notice about it.
16963
14906
  if (this.webOnlyPromptPlace())
16964
14907
  return this.recordWebNotice("instance", text, instanceName);
@@ -17403,11 +15346,6 @@ export class FleetManager {
17403
15346
  needsYouItems() {
17404
15347
  return this.needsYou?.webItems() ?? [];
17405
15348
  }
17406
- /** Enabled member composition supplies only a local attention reader, never an approval/execution callback. */
17407
- attachHubExecutionAttention(read) {
17408
- this.hubExecutionAttention = read;
17409
- this.needsYou?.poke();
17410
- }
17411
15349
  /**
17412
15350
  * #1389: the org chart's structure for the web — the instances the dashboard lists (getUiStatus's names), General,
17413
15351
  * fleet.yaml's teams, each instance's description and thread link. Read on demand; the live state rides the stream.
@@ -17444,8 +15382,6 @@ export class FleetManager {
17444
15382
  * (also for a backend that has no profiles, where the option is ignored).
17445
15383
  */
17446
15384
  credentialProfileOf(name, backend) {
17447
- if (isWindowsInstance(this.fleetConfig?.instances[name]))
17448
- return null;
17449
15385
  if (!credentialHomeSpec(backend))
17450
15386
  return null;
17451
15387
  const cfg = this.fleetConfig?.instances?.[name];
@@ -17468,7 +15404,7 @@ export class FleetManager {
17468
15404
  }
17469
15405
  /** The instances the cache analysis reads: fleet.yaml's, then the ClassicBot rooms (their workspace under the AgEnD home, their own backend). */
17470
15406
  cacheInstances() {
17471
- const fleet = Object.entries(this.fleetConfig?.instances ?? {}).filter(([, cfg]) => !isWindowsInstance(cfg)).map(([name, cfg]) => ({
15407
+ const fleet = Object.entries(this.fleetConfig?.instances ?? {}).map(([name, cfg]) => ({
17472
15408
  name, backend: this.backendNameOf(name), workingDirectory: cfg.working_directory, ledgerPath: join(this.getInstanceDir(name), "cache-ledger.json"),
17473
15409
  claudeConfigDir: this.claudeConfigDirOf(name), profile: this.usageProfileOf(name, this.backendNameOf(name)),
17474
15410
  ...(this.backendNameOf(name) === "codex" ? { profileTimelinePath: join(this.getInstanceDir(name), PROFILE_TIMELINE_FILE) } : {}),
@@ -17652,20 +15588,6 @@ export class FleetManager {
17652
15588
  crashedAt: crashed ? (this.needsCrashedAt.get(name) ?? null) : null,
17653
15589
  signIn: pauseReason === "auth" ? this.signInFor(name) : null,
17654
15590
  usageHold: this.daemons.get(name)?.usageHold?.() ?? null,
17655
- nativeDeliveryHold: this.daemons.get(name)?.nativeDeliveryHold?.() ?? null,
17656
- generalLocation: this.generalLocationAttention(name),
17657
- paneRecovery: (() => {
17658
- const config = this.fleetConfig?.instances[name];
17659
- if (!isNativePaneInstance(config) || !config.native_agent_id)
17660
- return null;
17661
- try {
17662
- const profile = windowsAgentsFor(this, this.runtimeWindowsExecutor()).get(config.native_agent_id);
17663
- return profile.paneRecovery ? { key: `${profile.paneRecovery.kind}:${profile.paneRecovery.launchId ?? "-"}`, detail: new NativePaneRecoveryError(profile.paneRecovery).message, since: profile.paneRecoveryAt ?? profile.createdAt } : null;
17664
- }
17665
- catch {
17666
- return null;
17667
- }
17668
- })(),
17669
15591
  });
17670
15592
  }
17671
15593
  return out;
@@ -17721,8 +15643,6 @@ export class FleetManager {
17721
15643
  const target = this.fleetNoticeTarget(world);
17722
15644
  return target ? { chatId: target.chatId, ...(target.opts.threadId !== undefined ? { threadId: String(target.opts.threadId) } : {}) } : null;
17723
15645
  },
17724
- // The name the web page shows for the instance (its View alias, else its own name), never the raw id alone.
17725
- instanceName: instance => resolveInstanceIdentity({ cfg: this.fleetConfig?.instances[instance], profile: profileIdentities(this.dataDir).get(instance) }).display_name || instance,
17726
15646
  instanceTopic: instance => {
17727
15647
  const topic = this.fleetConfig?.instances[instance]?.topic_id;
17728
15648
  if (topic != null)
@@ -17745,12 +15665,6 @@ export class FleetManager {
17745
15665
  acknowledgeMissed: (id, by) => this.missedOfflineStore?.acknowledge(id, by) ?? false,
17746
15666
  tasks: () => this.needsYouTaskItems(),
17747
15667
  backendSignIns: () => this.needsYouBackendSignIns(),
17748
- hubExecution: () => this.hubExecutionAttention?.() ?? [],
17749
- hubHolds: () => this.hubHolds.map(h => {
17750
- // A provider hold carries a bounded setup reason; its guidance (S4b's copy) when present, else the code's text.
17751
- const guidance = h.reason ? t(`hub.provider.${h.reason}`) : "";
17752
- return { side: h.side, code: h.code, since: h.since, detail: guidance && guidance !== `hub.provider.${h.reason}` ? guidance : t(`hub.hold.${h.code}`) };
17753
- }),
17754
15668
  configIssues: () => (this.configHistory?.currentIssues() ?? []).map(issue => ({
17755
15669
  ...issue, detail: configIssueText(issue, t), owner: this.worlds.keys().next().value,
17756
15670
  })),
@@ -18277,7 +16191,7 @@ export class FleetManager {
18277
16191
  isAdmin: (userId, adapterId) => this.adminGate(userId, adapterId) === "ok",
18278
16192
  authorization: input.session ? this.sessionAuthorizationStore()?.get(input.session.handle) ?? null : null,
18279
16193
  policy: { ...webAuthorityPolicy(this.fleetConfig?.web), enabled: state.enabled, loopbackOnly: state.loopbackOnly, namedLink: state.namedLink, quickLink: state.quickLink }, epoch: state.epoch, now: Date.now(),
18280
- hostOperator: this.isWebOnlyFleet(), outside: this.outsideLink(),
16194
+ hostOperator: this.isWebOnlyFleet(),
18281
16195
  });
18282
16196
  }
18283
16197
  sessionAuthorizations = null;
@@ -18337,269 +16251,6 @@ export class FleetManager {
18337
16251
  const { durable } = store.authorize(session.handle, HOST_OPERATOR, now, state.epoch, now + policy.maxSessionMs, peer);
18338
16252
  this.logger.info({ handle: session.handle, durable }, "Host web session authorized (this computer's admin)");
18339
16253
  }
18340
- // ── #3008: connections from outside (a web-only fleet; decision 28c2d3a3 rule 3, the user's simplified form) ──
18341
- webRemoteAccessState = null;
18342
- /** The authenticator, the outside switch and the wrong-code limiter (web-remote-access.json), read on first use. */
18343
- webRemoteAccess() {
18344
- if (!this.dataDir)
18345
- return null;
18346
- this.webRemoteAccessState ??= new WebRemoteAccess(join(this.dataDir, WEB_REMOTE_ACCESS_FILE), this.logger);
18347
- return this.webRemoteAccessState;
18348
- }
18349
- /** "Allow connections from outside" is on (an app set up) and this is a web-only fleet. */
18350
- outsideOn() {
18351
- return this.isWebOnlyFleet() && (this.webRemoteAccess()?.outside ?? false);
18352
- }
18353
- /** The link connections from outside opened, while it is open and still theirs: its exposure. Else null. */
18354
- outsideLink() {
18355
- if (!this.outsideOn())
18356
- return null;
18357
- const open = this.publicWebLink?.openSignin();
18358
- return open && isOutsideLinkOwner(open.owner) ? { exposureId: open.exposureId } : null;
18359
- }
18360
- /**
18361
- * The request's live session, asked again each time `current()` is called (its own cookie authenticated anew, the same
18362
- * handle): this web-only fleet's owner — this computer's admin, on this computer (`hostOnly`), or also signed in from
18363
- * outside — and web-verified now. Null when it is not (a guest, a LAN session, a chat fleet's admin, nobody).
18364
- */
18365
- liveOwner(req, opts) {
18366
- if (!this.isWebOnlyFleet())
18367
- return null;
18368
- if (opts.hostOnly && gatewayRequestContext(req))
18369
- return null;
18370
- const auth = authorizeSession(req, this.webToken, this.webSessions, { touch: false });
18371
- if (auth.kind !== "ok")
18372
- return null;
18373
- const handle = auth.session.handle, peer = webPeerOf(req), gateway = !!gatewayRequestContext(req);
18374
- const owner = () => {
18375
- if (!this.isWebOnlyFleet())
18376
- return false;
18377
- const live = authorizeSession(req, this.webToken, this.webSessions, { touch: false });
18378
- if (live.kind !== "ok" || live.session.handle !== handle)
18379
- return false;
18380
- if (opts.hostOnly && (live.session.host !== true || peer !== "loopback"))
18381
- return false;
18382
- const now = this.webAuthority({ session: live.session, gateway, peer });
18383
- const a = now.authority.kind === "web-verified" && now.authority.session?.handle === handle ? now.authority.actor : null;
18384
- return !!a && a.adapter_id === HOST_OPERATOR.adapterId && a.user_id === HOST_OPERATOR.userId;
18385
- };
18386
- return owner() ? { label: t("settings_confirm.host_operator_label"), handle, current: owner } : null;
18387
- }
18388
- /** Every session signed in from outside ends (and the authorizations kept for them). */
18389
- endOutsideSessions() {
18390
- const ended = new Set(this.webSessions?.list(undefined, r => r.remote === "totp").map(r => r.handle) ?? []);
18391
- if (!ended.size)
18392
- return;
18393
- this.sessionAuthorizationStore()?.prune(a => !ended.has(a.handle));
18394
- const result = this.webSessions?.revokeAll(r => r.remote === "totp");
18395
- if (result && !result.durable)
18396
- this.logger.warn("Outside sessions ended, but that could not be saved");
18397
- this.logger.info({ count: ended.size }, "Sessions signed in from outside ended");
18398
- }
18399
- /**
18400
- * #1939 §10 / #3008: a stored receipt's authority as it reads now — the web authority state, and for this computer's
18401
- * admin from outside the switch and the last revocation of remote authority (Prism P2 2).
18402
- */
18403
- receiptAuthorityNow(stored, state = this.webAuthorityState()) {
18404
- return authorityNow(stored, { ...state, outside: this.outsideOn(), outsideHandles: this.webRemoteAccess()?.admitted ?? new Set() });
18405
- }
18406
- /** #3008 (the sign-in page): this exposure is the outside link, open now. */
18407
- isOutsideLink(exposureId) {
18408
- return this.outsideLink()?.exposureId === exposureId;
18409
- }
18410
- /** What Settings shows: set up or not, the switch, a wait in force, the link — its address only to this computer. */
18411
- webOutsideStatus(req) {
18412
- const store = this.webRemoteAccess(), status = store?.status();
18413
- const here = !!this.liveOwner(req, { hostOnly: true });
18414
- const open = this.outsideOn() ? this.publicWebLink?.openSignin() : null;
18415
- const mine = !!open && isOutsideLinkOwner(open.owner);
18416
- const linkStatus = this.outsideOn() ? this.getPublicWebStatus() : null;
18417
- return { webOnly: this.isWebOnlyFleet(), here, enrolled: !!status?.enrolled, enrolledAt: status?.enrolledAt ?? null,
18418
- outside: this.outsideOn(), pausedForMs: status?.pausedForMs ?? 0,
18419
- link: linkStatus && linkStatus.state !== "closed" ? { ...linkStatus, ...(here && mine ? { url: open.url } : {}), quick: !publicLinkNamedHostname(this.fleetConfig?.web) } : null };
18420
- }
18421
- /** Set up (or set up again): a new secret, shown once to this computer's admin. Null: not them. */
18422
- webOutsideBeginEnrollment(req) {
18423
- const owner = this.liveOwner(req, { hostOnly: true }), store = this.webRemoteAccess();
18424
- if (!owner || !store)
18425
- return null;
18426
- return store.beginEnrollment(owner.handle, hostname() || "fleet");
18427
- }
18428
- /** The new app's first code: on "ok" it replaces the old one, whose sessions from outside end. */
18429
- webOutsideConfirmEnrollment(req, code) {
18430
- const owner = this.liveOwner(req, { hostOnly: true }), store = this.webRemoteAccess();
18431
- if (!owner || !store || !owner.current())
18432
- return "not_here";
18433
- const had = store.enrolled;
18434
- const r = store.confirmEnrollment(owner.handle, code);
18435
- if (r === "ok") {
18436
- this.logger.info({ again: had }, "Authenticator app set up for connections from outside");
18437
- if (had)
18438
- this.endOutsideSessions();
18439
- }
18440
- return r;
18441
- }
18442
- /** Forget the app: the switch goes off, the link closes, every session from outside ends. */
18443
- webOutsideRemove(req) {
18444
- const owner = this.liveOwner(req, { hostOnly: true }), store = this.webRemoteAccess();
18445
- if (!owner || !store || !owner.current())
18446
- return "not_here";
18447
- // #3008 review (Prism P2 1): shut at once whatever the disk says; say so when it could not be saved (retried).
18448
- const saved = store.remove();
18449
- this.closeOutsideLink("outside removed");
18450
- this.logger.info({ saved }, "Authenticator app removed; connections from outside off");
18451
- return saved ? "ok" : "unsaved";
18452
- }
18453
- /** The switch. On: needs an app set up; the link opens. Off: it closes and every session from outside ends. */
18454
- webOutsideSwitch(req, on) {
18455
- const owner = this.liveOwner(req, { hostOnly: true }), store = this.webRemoteAccess();
18456
- if (!owner || !store || !owner.current())
18457
- return "not_here";
18458
- if (on && !publicLinkSettings(this.fleetConfig?.web).allowed)
18459
- return "not_allowed";
18460
- const r = store.setOutside(on);
18461
- if (r === "not_enrolled")
18462
- return r;
18463
- this.logger.info({ on, saved: r === "ok" }, "Connections from outside switched");
18464
- // #3008 review (Prism P2 1): Off shuts at once even when it could not be saved (the store retries); On opens only
18465
- // once saved — a switch the disk does not hold would not survive a restart.
18466
- if (!on)
18467
- this.closeOutsideLink("outside off");
18468
- else if (r === "ok")
18469
- this.openOutsideLink([0, ...OUTSIDE_LINK_RETRY_MS]);
18470
- return r;
18471
- }
18472
- outsideAttempt = null;
18473
- outsideOpening = false;
18474
- /**
18475
- * Open the outside link: an attempt after each delay in turn until one opens (#3008 B: twice more at most — the quick
18476
- * link's address service limits how often it may be asked). Every attempt asks again: the switch still on, the fleet
18477
- * running. A final failure: one Notice; the switch stays on, so the next start tries again.
18478
- */
18479
- openOutsideLink(delays) {
18480
- this.outsideAttempt?.cancel();
18481
- this.outsideAttempt = null;
18482
- const attempt = (n) => {
18483
- this.outsideAttempt = null;
18484
- if (this.shuttingDown || !this.outsideOn() || this.outsideOpening)
18485
- return;
18486
- this.outsideOpening = true;
18487
- // alpha CI 4abe58cd: an On right after an Off finds the old link still closing (its tunnel stopping): wait for that
18488
- // close, then open — never "busy" and a 30 s retry. The switch is asked again after the wait by openUnattended
18489
- // itself (permitted → publicOwnerCurrent: the fleet running, the switch on): an Off meanwhile opens nothing.
18490
- const link = this.getPublicWebLink();
18491
- void link.whenClosed().then(() => link.openUnattended(OUTSIDE_LINK_OWNER))
18492
- .catch(() => "failed").then(outcome => {
18493
- this.outsideOpening = false;
18494
- if (outcome === "opened") {
18495
- this.logger.info({ attempt: n + 1 }, "Outside link opened");
18496
- return;
18497
- }
18498
- if (outcome === "busy" && this.outsideLink())
18499
- return; // already open (ours)
18500
- if (this.shuttingDown || !this.outsideOn())
18501
- return;
18502
- if (n + 1 < delays.length) {
18503
- this.outsideAttempt = outsideTimer(() => attempt(n + 1), delays[n + 1]);
18504
- return;
18505
- }
18506
- this.logger.warn({ attempts: n + 1 }, "Outside link could not be opened; giving up until the next start or switch");
18507
- this.recordWebNotice("fleet", t("outside.notice_ended", t("outside.reason_failed")));
18508
- });
18509
- };
18510
- this.outsideAttempt = outsideTimer(() => attempt(0), delays[0] ?? 0);
18511
- }
18512
- closeOutsideLink(reason) {
18513
- this.outsideAttempt?.cancel();
18514
- this.outsideAttempt = null;
18515
- const open = this.publicWebLink?.openSignin();
18516
- if (this.publicWebLink && (!open || isOutsideLinkOwner(open.owner)))
18517
- void this.publicWebLink.close(reason);
18518
- this.endOutsideSessions();
18519
- }
18520
- /** PublicWebLink.onClose for the outside link: what closed it decides what happens next (#3008 B). */
18521
- outsideLinkClosed(reason) {
18522
- if (this.shuttingDown || OUTSIDE_OWN_CLOSES.has(reason) || !this.outsideOn())
18523
- return;
18524
- // #3008 review (Prism P2 3): expiry first — its lifetime counts from the opening request, so it can run out while the
18525
- // opening is still under way: the switch goes off then too, and that attempt's end (outsideOn false) retries nothing.
18526
- if (reason === "expired") {
18527
- // Its lifetime ran out: the switch goes off (only this computer turns it on again — a session from outside can't).
18528
- this.outsideAttempt?.cancel();
18529
- this.outsideAttempt = null;
18530
- if (this.webRemoteAccess()?.setOutside(false) === "unsaved")
18531
- this.logger.warn("Outside link expired; the switch's Off could not be saved yet (retried)");
18532
- this.endOutsideSessions();
18533
- this.logger.info("Outside link expired; connections from outside off");
18534
- this.recordWebNotice("fleet", t("outside.notice_ended", t("outside.reason_expired")));
18535
- return;
18536
- }
18537
- if (this.outsideOpening)
18538
- return; // that attempt's own failure: its retry
18539
- // Dropped (the tunnel exited, the gateway failed, the configuration changed): reopened up to twice.
18540
- this.logger.info({ reason }, "Outside link closed; reopening");
18541
- this.openOutsideLink(OUTSIDE_LINK_RETRY_MS);
18542
- }
18543
- /**
18544
- * #3008 (auth-api POST /auth/totp): a sign-in through the outside link with the authenticator's code. "ok": the caller
18545
- * makes the session (SessionRecord.remote) and then calls authorizeOutsideSession. Each wrong code counts; a wait that
18546
- * starts is a Notice; a code that matches a step just outside the window logs how far this clock seems to be off.
18547
- */
18548
- outsideSignIn(exposureId, code) {
18549
- const link = this.outsideLink(), store = this.webRemoteAccess();
18550
- if (!link || !store || !exposureId || link.exposureId !== exposureId)
18551
- return { kind: "closed" };
18552
- const v = store.verify(code);
18553
- if (v.kind === "ok")
18554
- return { kind: "ok" };
18555
- if (v.kind === "paused")
18556
- return v;
18557
- if (v.kind === "not_enrolled")
18558
- return { kind: "closed" };
18559
- if (v.kind === "unavailable")
18560
- return v;
18561
- if (v.skewSteps !== null)
18562
- this.logger.warn({ offsetSeconds: v.skewSteps * TOTP_STEP_SECONDS }, "A wrong authenticator code matched a nearby time step: this computer's clock may be off");
18563
- if (v.paused) {
18564
- this.logger.warn({ waitMs: v.retryAfterMs }, "Sign-in from outside paused after wrong codes");
18565
- this.recordWebNotice("fleet", t("outside.notice_paused", outsideDuration(v.retryAfterMs), v.wrong));
18566
- return { kind: "paused", retryAfterMs: v.retryAfterMs };
18567
- }
18568
- return { kind: "wrong" };
18569
- }
18570
- /**
18571
- * #3008: a session just made by outsideSignIn's "ok" is this computer's admin for its authorization's life. False: it
18572
- * could not be admitted (its handle not saved, #3015 review r3) — the caller ends it.
18573
- */
18574
- authorizeOutsideSession(session, peer, label) {
18575
- const store = this.sessionAuthorizationStore(), link = this.outsideLink();
18576
- if (!store || session.remote !== "totp" || session.owner || !link || session.exposureId !== link.exposureId)
18577
- return false;
18578
- if (!this.webRemoteAccess()?.admit(session.handle)) {
18579
- this.logger.warn({ handle: session.handle }, "Session from outside not admitted: it could not be saved");
18580
- return false;
18581
- }
18582
- const state = this.webAuthorityState(), now = Date.now(), policy = webAuthorityPolicy(this.fleetConfig?.web);
18583
- const check = evaluateWebAuthority({
18584
- session, gateway: true, peer, tokenEpoch: this.webToken ? tokenEpoch(this.webToken) : null,
18585
- isAdmin: () => false,
18586
- authorization: { handle: session.handle, owner: HOST_OPERATOR, authorizedAt: now, until: now + policy.maxSessionMs, epoch: state.epoch },
18587
- policy: { ...policy, enabled: state.enabled, loopbackOnly: state.loopbackOnly, namedLink: state.namedLink, quickLink: state.quickLink }, epoch: state.epoch, now,
18588
- hostOperator: true, outside: link,
18589
- });
18590
- if (check.refusal) {
18591
- this.logger.info({ handle: session.handle, refusal: check.refusal }, "Session from outside not authorized");
18592
- return false;
18593
- }
18594
- {
18595
- const live = new Set(this.webSessions?.list(undefined, () => true).map(r => r.handle) ?? []);
18596
- store.prune(a => a.epoch === state.epoch && now <= Math.min(a.until, a.authorizedAt + policy.maxSessionMs) && live.has(a.handle));
18597
- const { durable } = store.authorize(session.handle, HOST_OPERATOR, now, state.epoch, now + policy.maxSessionMs, peer);
18598
- this.logger.info({ handle: session.handle, durable }, "Session from outside authorized (this computer's admin)");
18599
- }
18600
- this.recordWebNotice("fleet", t("outside.notice_signed_in", label));
18601
- return true;
18602
- }
18603
16254
  authorityStateStore = null;
18604
16255
  /**
18605
16256
  * #1939 §10: the switch, "Only this computer can authorize" and the revocation epoch in force for the configuration as
@@ -18620,10 +16271,6 @@ export class FleetManager {
18620
16271
  return true;
18621
16272
  if (session.surface === "local")
18622
16273
  return false;
18623
- // #3008 review (Prism P2 4): signed in from outside with the authenticator's code — its opt-in is the outside
18624
- // switch, not these options (the authority judges it so): it keeps its authorization.
18625
- if (session.remote === "totp")
18626
- return false;
18627
16274
  const peer = a.peer ?? "named_link";
18628
16275
  return (!!state.namedLinkClosed && peer === "named_link") || (!!state.quickLinkClosed && peer === "quick_link");
18629
16276
  };
@@ -18667,7 +16314,7 @@ export class FleetManager {
18667
16314
  // Over a link: the temporary one (§0.3), else the named one (a record from before §0.3 has no peer).
18668
16315
  ...(sessions.get(a.handle).surface === "gateway" ? (a.peer === "quick_link" ? { quick: true } : { named: true }) : {}) })) : [];
18669
16316
  const host = this.fleetConfig?.web?.public_link?.hostname;
18670
- return { state, configured, hours: VERIFIED_SESSION_HOURS, lanHosts, authorized, namedHost: typeof host === "string" && host ? host : null, webOnly: this.isWebOnlyFleet() };
16317
+ return { state, configured, hours: VERIFIED_SESSION_HOURS, lanHosts, authorized, namedHost: typeof host === "string" && host ? host : null };
18671
16318
  }
18672
16319
  authorizationRequests = null;
18673
16320
  /**
@@ -18775,7 +16422,7 @@ export class FleetManager {
18775
16422
  authorization: s?.owner ? { handle: s.handle, owner: s.owner, authorizedAt: now, until: now + policy.maxSessionMs, epoch: state.epoch } : null,
18776
16423
  policy: { ...policy, enabled: state.enabled, loopbackOnly: state.loopbackOnly, namedLink: state.namedLink, quickLink: state.quickLink }, epoch: state.epoch, now,
18777
16424
  // #2910 D1: a host operator's session is never asked about (no account to ask): only a sign-in authorizes it.
18778
- hostOperator: this.isWebOnlyFleet(), outside: this.outsideLink(),
16425
+ hostOperator: this.isWebOnlyFleet(),
18779
16426
  });
18780
16427
  return r.refusal;
18781
16428
  }
@@ -19641,8 +17288,6 @@ export class FleetManager {
19641
17288
  publication.retirePending = true;
19642
17289
  }
19643
17290
  async sendCancelButton(instanceName, correlationId, preserveProgress = false) {
19644
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName]))
19645
- return;
19646
17291
  // Post first, retire after (see the tail of this method). Retiring up front
19647
17292
  // meant that from the delete until the new message came back — a chat API
19648
17293
  // round trip, and every reply goes through here — the instance had NO live
@@ -20192,14 +17837,6 @@ export class FleetManager {
20192
17837
  }
20193
17838
  /** Interrupt an instance's current generation (cancel button / /cancel). */
20194
17839
  cancelInstance(instanceName) {
20195
- if (isNativeExecInstance(this.fleetConfig?.instances[instanceName])) {
20196
- this.cancelPendingDeliveries(instanceName);
20197
- const cancelled = this.nativeInstanceRuntime().cancel(instanceName);
20198
- this.lastInboundMsg.delete(instanceName);
20199
- for (const message of this.webChatHistory.cancelPending(instanceName))
20200
- this.emitSseEvent("delivery", { instance: instanceName, messageId: message.messageId, delivery: message.delivery });
20201
- return cancelled !== null;
20202
- }
20203
17840
  const daemon = this.daemons.get(instanceName);
20204
17841
  if (!daemon)
20205
17842
  return false;
@@ -20305,8 +17942,6 @@ export class FleetManager {
20305
17942
  }
20306
17943
  catch { /* safe metadata only */ }
20307
17944
  broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed"));
20308
- if (event.phase === "done")
20309
- this.forgetSignInIdentities(); // a sign-in may be a different account now
20310
17945
  this.needsYou?.poke();
20311
17946
  if (!this.browserSignInOwnerCurrent(owner))
20312
17947
  return;
@@ -20345,10 +17980,7 @@ export class FleetManager {
20345
17980
  cancel: () => this.cancelLoginSession(),
20346
17981
  notice: ({ shown, label, handle }) => this.notifyFleetError(t("login.web_dashboard_notice", shown, label, handle.slice(0, 8))),
20347
17982
  // No details in the event: each page reads its own view (GET /ui/backend-login), as its session may see it.
20348
- // The accounts are forgotten when ANY sign-in ends, even one a later one replaced (it may be a different account now), and not at every step of a
20349
- // running one: `changed` only tells the pages to read again.
20350
- changed: () => { broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed")); },
20351
- loginEnded: () => this.forgetSignInIdentities(),
17983
+ changed: () => broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed")),
20352
17984
  });
20353
17985
  return this.webBackendLoginsInstance;
20354
17986
  }
@@ -20362,135 +17994,6 @@ export class FleetManager {
20362
17994
  * #2233: Settings → Agents' sign-in list — every backend's default login as the availability snapshot has it, then
20363
17995
  * each credential profile an agent uses (its own login state, read offline).
20364
17996
  */
20365
- /** Which account each sign-in is, as the host's own `claude auth status` said (masked; see checkAuthIdentity). Keys are `backend\0profile` in a Map: a name is data, never a property. */
20366
- signInIdentities = new Map();
20367
- /** Reads in flight, by the same key: a second request for a sign-in that is already being read joins it instead of starting another process. */
20368
- signInIdentityReads = new Map();
20369
- /** Raised each time the accounts are forgotten: a read that began before is never allowed to write what it found. */
20370
- signInIdentityGeneration = 0;
20371
- signInIdentitySlots = 0;
20372
- signInIdentityWaiters = [];
20373
- signInIdentity(backend, profile) { return this.signInIdentities.get(`${backend}\0${profile ?? ""}`)?.identity ?? undefined; }
20374
- /** A sign-in finished (or changed): forget what was read and let no read that was already running put its answer back. */
20375
- forgetSignInIdentities() { this.signInIdentityGeneration++; this.signInIdentities.clear(); this.signInIdentityReads.clear(); }
20376
- /** At most three `claude auth status` at once across the whole host, however many requests ask. */
20377
- async withSignInIdentitySlot(task) {
20378
- while (this.signInIdentitySlots >= 3)
20379
- await new Promise(resolve => this.signInIdentityWaiters.push(resolve));
20380
- this.signInIdentitySlots++;
20381
- try {
20382
- return await task();
20383
- }
20384
- finally {
20385
- this.signInIdentitySlots--;
20386
- this.signInIdentityWaiters.shift()?.();
20387
- }
20388
- }
20389
- /** One `claude auth status` for a sign-in (the default one, or a credential profile's folder). */
20390
- async readSignInIdentity(target) {
20391
- let env;
20392
- if (target.profile)
20393
- env = credentialProfileEnv(this.dataDir, target.backend, target.profile) ?? undefined;
20394
- return checkAuthIdentity(target.backend, env);
20395
- }
20396
- /**
20397
- * Start reading, in the background, the account of each Claude Code sign-in that is signed in (the default and every profile an agent uses) and
20398
- * not read in the last five minutes. It never waits and never blocks a request: the list shows what is already known, and the pages are told to
20399
- * read it again when something new was learned. A sign-in already being read is not read twice, and a read that began before the accounts were
20400
- * forgotten (a sign-in finished meanwhile) is dropped. No login, no network request of ours, nothing logged.
20401
- */
20402
- refreshSignInIdentities() {
20403
- const now = Date.now(), fresh = (target) => { const entry = this.signInIdentities.get(`${target.backend}\0${target.profile ?? ""}`); return !!entry && now - entry.at < 5 * 60_000; };
20404
- const started = [];
20405
- for (const target of this.backendSignInTargets()) {
20406
- const key = `${target.backend}\0${target.profile ?? ""}`;
20407
- if (target.state !== "usable" || !authIdentitySupported(target.backend) || fresh(target) || this.signInIdentityReads.has(key))
20408
- continue;
20409
- const generation = this.signInIdentityGeneration;
20410
- const read = this.withSignInIdentitySlot(() => this.readSignInIdentity(target)).then(identity => {
20411
- if (generation !== this.signInIdentityGeneration)
20412
- return false; // forgotten while it ran: its answer may be the old account
20413
- this.signInIdentities.set(key, { identity: identity ?? null, at: Date.now() });
20414
- return true;
20415
- }).catch(() => {
20416
- // A read that threw is an answer too: remembered as "no account" for the same five minutes, so every GET does not try again.
20417
- if (generation === this.signInIdentityGeneration)
20418
- this.signInIdentities.set(key, { identity: null, at: Date.now() });
20419
- return false;
20420
- }).finally(() => { if (this.signInIdentityReads.get(key) === read)
20421
- this.signInIdentityReads.delete(key); });
20422
- this.signInIdentityReads.set(key, read);
20423
- started.push(read);
20424
- }
20425
- if (started.length)
20426
- void Promise.all(started).then(results => {
20427
- if (results.some(Boolean))
20428
- broadcastSseEvent(this.sseClients, "backend_login", {}, err => this.logger.debug({ err }, "backend_login event failed")); // read the list again: it knows more now
20429
- });
20430
- }
20431
- /**
20432
- * #3212 PR 1: the Accounts view (accounts-view.ts) — every CLI's default sign-in and named profiles (used or only on
20433
- * disk), who uses each and its weekly usage. Host rows only; a paired computer's rows come with #3190 Part 3c. Its
20434
- * usage is the usage panel's cached read (never forced), given at most `usageWaitMs` before the view is sent without it.
20435
- */
20436
- async accountsView(usageWaitMs = 1500) {
20437
- const fleet = this.fleetConfig;
20438
- const defaultBackend = fleet?.defaults?.backend ?? "claude-code";
20439
- this.refreshSignInIdentities();
20440
- const targets = this.backendSignInTargets();
20441
- const diskProfiles = new Map();
20442
- for (const backend of PROFILE_CLIS) {
20443
- const rows = [];
20444
- for (const profile of profilesOnDisk(this.dataDir, backend)) {
20445
- let login = "unknown";
20446
- try {
20447
- login = credentialProfileLogin(this.dataDir, backend, profile).state;
20448
- }
20449
- catch { /* unknown */ }
20450
- const signIn = dashboardSignInSupported(backend, profile);
20451
- const hostCommand = signIn ? null : credentialProfileLoginCommand(this.dataDir, backend, profile);
20452
- const identity = this.signInIdentity(backend, profile);
20453
- rows.push({ profile, state: (login === "logged-in" ? "usable" : login === "signed-out" ? "not_signed_in" : "unknown"), signIn,
20454
- ...(hostCommand ? { hostCommand } : {}), ...(identity ? { identity } : {}) });
20455
- }
20456
- diskProfiles.set(backend, rows);
20457
- }
20458
- // Host agents only: an agent on a paired Windows computer runs on that computer's accounts (its own scope, later).
20459
- const agents = Object.keys(fleet?.instances ?? {}).filter(name => !isWindowsInstance(fleet.instances[name])).sort()
20460
- .map(name => { const backend = this.backendNameForInstance(name); return { name, backend, profile: this.credentialProfileOf(name, backend) }; });
20461
- const classic = readClassicBindings(this.dataDir).map(b => {
20462
- const backend = b.backend ?? defaultBackend;
20463
- return { key: b.key, backend, profile: profileName(classicProfile(b.backend_options, fleet?.defaults?.backend_options, backend)) };
20464
- });
20465
- const fleetDefaults = new Map();
20466
- for (const backend of PROFILE_CLIS) {
20467
- const p = profileName(profileOf(fleet?.defaults?.backend_options, backend));
20468
- if (p)
20469
- fleetDefaults.set(backend, p);
20470
- }
20471
- // Weekly usage, from the usage panel's own (cached, never forced) read.
20472
- const usage = new Map();
20473
- const ids = new Map(); // provider id → backend\0profile
20474
- for (const backend of ACCOUNT_CLIS) {
20475
- const pid = usageProviderIdForBackend(backend);
20476
- if (!pid)
20477
- continue;
20478
- ids.set(pid, `${backend}\0`);
20479
- for (const p of new Set([...(diskProfiles.get(backend) ?? []).map(d => d.profile), ...targets.filter(t => t.backend === backend && t.profile).map(t => t.profile)]))
20480
- ids.set(`${pid}:${p}`, `${backend}\0${p}`);
20481
- }
20482
- try {
20483
- const payload = await Promise.race([getUsageSnapshot(false, undefined, { allowStale: true }), new Promise(r => { const tm = setTimeout(() => r(null), usageWaitMs); tm.unref?.(); })]);
20484
- for (const provider of payload?.providers ?? []) {
20485
- const at = ids.get(provider.id);
20486
- const w = at ? weeklyUsage(provider) : undefined;
20487
- if (at && w)
20488
- usage.set(at, w);
20489
- }
20490
- }
20491
- catch { /* the view without usage */ }
20492
- return buildAccountsView({ targets, diskProfiles, agents, classic, fleetDefaults, usage });
20493
- }
20494
17997
  backendSignInTargets() {
20495
17998
  const out = [];
20496
17999
  for (const row of this.backendAvailabilityRows()) {
@@ -20502,7 +18005,6 @@ export class FleetManager {
20502
18005
  ...(flow?.remoteLogin === "unsupported" ? { hostCommand: flow.command } : {}),
20503
18006
  ...(row.state === "not_installed" && install ? { install } : {}),
20504
18007
  ...(row.state === "too_old" && install ? { update: install } : {}),
20505
- ...(this.signInIdentity(row.backend) ? { identity: this.signInIdentity(row.backend) } : {}),
20506
18008
  });
20507
18009
  }
20508
18010
  const seen = new Set();
@@ -20520,7 +18022,7 @@ export class FleetManager {
20520
18022
  const signIn = dashboardSignInSupported(backend, profile);
20521
18023
  const hostCommand = signIn ? null : credentialProfileLoginCommand(this.dataDir, backend, profile);
20522
18024
  out.push({ backend, profile, state: login === "logged-in" ? "usable" : login === "signed-out" ? "not_signed_in" : "unknown", signIn,
20523
- ...(hostCommand ? { hostCommand } : {}), ...(this.signInIdentity(backend, profile) ? { identity: this.signInIdentity(backend, profile) } : {}) });
18025
+ ...(hostCommand ? { hostCommand } : {}) });
20524
18026
  }
20525
18027
  return out;
20526
18028
  }
@@ -21345,8 +18847,6 @@ export class FleetManager {
21345
18847
  this.needsYou?.poke();
21346
18848
  const onError = (err) => this.logger.debug({ err }, "SSE client write failed; evicting");
21347
18849
  if (event === "message" && data && typeof data === "object") {
21348
- // #2861: an Office share's dialogue box hears that someone talked to an instance (never what was said).
21349
- this.officeTalk.noteMessage(data);
21350
18850
  // A chat message: record it, and send it WITH its id so a reconnecting stream can ask for what it missed.
21351
18851
  const m = data;
21352
18852
  const recorded = this.webChatHistory.record({
@@ -21560,7 +19060,36 @@ export class FleetManager {
21560
19060
  "antigravity": ".agents/AGENTS.md",
21561
19061
  "mock": "CLAUDE.md",
21562
19062
  };
21563
- static GENERAL_INSTRUCTIONS = GENERAL_ROLE_INSTRUCTIONS;
19063
+ static GENERAL_INSTRUCTIONS = `# Fleet Coordinator
19064
+
19065
+ You are the fleet coordinator — the central entry point for this AgEnD fleet.
19066
+ You route tasks, manage instances, enforce policies, and synthesize results.
19067
+ Do NOT modify project files directly — delegate file changes to the project's instance.
19068
+ You CAN write code snippets, explain code, and answer technical questions directly.
19069
+
19070
+ ## Task Routing
19071
+
19072
+ - **Handle directly**: no file/exec access needed, answerable from knowledge, ≤2 reasoning steps (Q&A, translation, status queries, code snippets).
19073
+ - **Delegate to 1 instance**: scoped to one project/repo, needs file access or execution.
19074
+ - **Coordinate multiple**: spans repos, outputs feed each other, or parallel helps (max 3 per task).
19075
+
19076
+ Instance discovery: start with list_instances(); follow the guidance in its response. Prefer reuse; never duplicate a running instance.
19077
+
19078
+ ## Reply Contract
19079
+
19080
+ Every final response to the user contains: the result (the actual answer or deliverable) and gaps (anything incomplete — omit if none). Summarize instance reports; omit internal coordination noise.
19081
+
19082
+ ## After Restart
19083
+
19084
+ BEFORE processing any new messages: 1. list_instances() 2. list_teams() 3. list_decisions(). Only then handle requests.
19085
+
19086
+ ## Playbooks (on-demand skills)
19087
+
19088
+ Detailed procedures live in your skills — consult them when the situation comes up rather than from memory:
19089
+ - **delegation-playbook** — delegation protocol, loop prevention, parallel vs sequential, result/failure handling, team management, instance configuration tips.
19090
+ - **development-workflow** — the fleet-wide code-change policy you enforce when delegating code tasks.
19091
+ Plus the operational skills (fleet-health, instance-lifecycle, scheduling, session-management, …).
19092
+ `;
21564
19093
  /** Ensure the general instance has its project instructions file + knowledge */
21565
19094
  ensureGeneralInstructions(workDir, backendName, instanceName) {
21566
19095
  measureSyncWork("fleet.generalInstructions", () => this.ensureGeneralInstructionsSync(workDir, backendName, instanceName));
@@ -22582,8 +20111,6 @@ export class FleetManager {
22582
20111
  : model,
22583
20112
  });
22584
20113
  const fleetInstance = this.fleetConfig?.instances[instanceName];
22585
- if (isWindowsInstance(fleetInstance))
22586
- return fleetInstance.model?.trim() ? done(fleetInstance.model.trim(), "instance") : done("default", "unresolved", "this CLI does not report a default");
22587
20114
  if (fleetInstance) {
22588
20115
  if (fleetInstance.model?.trim())
22589
20116
  return done(fleetInstance.model.trim(), "instance");
@@ -22665,7 +20192,7 @@ export class FleetManager {
22665
20192
  if (this.fleetConfig?.defaults?.backend)
22666
20193
  backends.add(this.fleetConfig.defaults.backend);
22667
20194
  for (const inst of Object.values(this.fleetConfig?.instances ?? {}))
22668
- if (!isWindowsInstance(inst) && inst.backend)
20195
+ if (inst.backend)
22669
20196
  backends.add(inst.backend);
22670
20197
  for (const ch of this.classicChannels?.getAll() ?? [])
22671
20198
  if (ch.backend)
@@ -22680,8 +20207,6 @@ export class FleetManager {
22680
20207
  // here; a ClassicBot room on a profile is probed on its first /model (catalogProfile knows rooms too).
22681
20208
  const profiles = new Set();
22682
20209
  for (const name of Object.keys(this.fleetConfig?.instances ?? {})) {
22683
- if (isWindowsInstance(this.fleetConfig?.instances[name]))
22684
- continue;
22685
20210
  if (this.backendNameForInstance(name) !== "claude-code")
22686
20211
  continue;
22687
20212
  const profile = this.catalogProfile("claude-code", name);
@@ -22702,8 +20227,6 @@ export class FleetManager {
22702
20227
  * instead of presenting an old list as a fresh one.
22703
20228
  */
22704
20229
  async getModelOptionsWithSource(instanceName, opts = {}) {
22705
- if (isWindowsInstance(this.fleetConfig?.instances[instanceName]))
22706
- return { models: (await this.listModelCatalog({ instanceName })).models, source: "cache" };
22707
20230
  const backendName = this.backendNameForInstance(instanceName);
22708
20231
  const profile = this.catalogProfile(backendName, instanceName);
22709
20232
  const cached = this.readCliEnv(backendName, profile);
@@ -22759,21 +20282,6 @@ export class FleetManager {
22759
20282
  */
22760
20283
  async listModelCatalog(opts = {}) {
22761
20284
  const { instanceName } = opts;
22762
- if (instanceName && isWindowsInstance(this.fleetConfig?.instances[instanceName])) {
22763
- const config = this.fleetConfig.instances[instanceName], backend = config.backend ?? "codex";
22764
- // Detection is admin-owned. A read never launches a provider or falls back to this host's account.
22765
- let inventory = null;
22766
- try {
22767
- inventory = await this.runtimeWindowsExecutor().probe({ force: false, machineId: config.machine_id });
22768
- }
22769
- catch { /* offline remains unavailable */ }
22770
- const row = inventory?.clis.find(value => value.id === backend);
22771
- return { backend, scope: "instance", instance: instanceName, current_model: config.model || null,
22772
- models: (row?.models ?? []).map(value => ({ id: value.id, label: value.name ?? value.id, ...(value.description ? { description: value.description } : {}) })),
22773
- source: row?.models?.length ? "cache" : "fallback",
22774
- ...(inventory?.probedAt ? { probed_at: new Date(inventory.probedAt).toISOString() } : {}),
22775
- note: "Windows runtime's last detected inventory; no coordinator account or live probe is used." };
22776
- }
22777
20285
  if (instanceName) {
22778
20286
  const backend = this.backendNameForInstance(instanceName);
22779
20287
  const resolved = this.resolveInstanceModel(instanceName);
@@ -22809,8 +20317,6 @@ export class FleetManager {
22809
20317
  }
22810
20318
  /** Ask a backend for its catalog using ONE instance's real config. Never throws. */
22811
20319
  async instanceScopedModels(instanceName, backend) {
22812
- if (isWindowsInstance(this.fleetConfig?.instances[instanceName]))
22813
- return [];
22814
20320
  try {
22815
20321
  const inst = this.fleetConfig?.instances?.[instanceName];
22816
20322
  const instanceDir = this.getInstanceDir(instanceName);
@@ -23448,8 +20954,6 @@ export class FleetManager {
23448
20954
  }
23449
20955
  /** The instance's backend's effort metadata; agy's levels are its model's (#1825), the one the instance runs. */
23450
20956
  instanceEffortMetadata(instanceName) {
23451
- if (isWindowsInstance(this.fleetConfig?.instances[instanceName]))
23452
- return { strategy: "unsupported", levels: [] };
23453
20957
  const backend = this.backendNameForInstance(instanceName);
23454
20958
  let model = null;
23455
20959
  if (backend === "antigravity") {
@@ -23460,8 +20964,6 @@ export class FleetManager {
23460
20964
  }
23461
20965
  /** Configured effort for an instance: per-instance, else fleet default, else none. */
23462
20966
  resolveInstanceEffort(instanceName) {
23463
- if (isWindowsInstance(this.fleetConfig?.instances[instanceName]))
23464
- return { effort: null, source: "unset" };
23465
20967
  const own = this.fleetConfig?.instances[instanceName]?.effort;
23466
20968
  if (own)
23467
20969
  return { effort: own, source: "instance" };
@@ -23499,11 +21001,9 @@ export class FleetManager {
23499
21001
  * instance respawns.
23500
21002
  */
23501
21003
  async applyEffort(instanceName, requested, opts) {
23502
- // #1490 row 27 (main): a menu's choice is for the lifecycle it was offered to — asked first and fenced into the restart.
21004
+ // #1490 row 27: a menu's choice is for the lifecycle it was offered to — asked first and fenced into the restart.
23503
21005
  if (opts?.current && !opts.current())
23504
21006
  return t("menu.instance_replaced", this.shownInstanceName(instanceName));
23505
- if (isWindowsInstance(this.fleetConfig?.instances[instanceName]))
23506
- return t("effort.unsupported", this.backendNameForInstance(instanceName));
23507
21007
  const level = requested.trim().toLowerCase();
23508
21008
  const backendName = this.backendNameForInstance(instanceName);
23509
21009
  let strategy = "unsupported";
@@ -23551,20 +21051,6 @@ export class FleetManager {
23551
21051
  if (/[\x00-\x1f\x7f]/.test(model)) {
23552
21052
  return t("model.invalid_chars");
23553
21053
  }
23554
- const native = this.fleetConfig?.instances[instanceName];
23555
- if (isWindowsInstance(native)) {
23556
- if (!isNativePaneInstance(native))
23557
- return WINDOWS_CONFIG_REFUSED;
23558
- const catalog = await this.listModelCatalog({ instanceName });
23559
- if (!catalog.models.some(row => row.id === model))
23560
- return t("model.unknown_name", model, catalog.models.map(row => row.id).join(", "));
23561
- const id = native.native_agent_id, machine = native.machine_id;
23562
- await this.updateNativePaneConfig(instanceName, { model }, () => {
23563
- const now = this.fleetConfig?.instances[instanceName];
23564
- return isNativePaneInstance(now) && now.native_agent_id === id && now.machine_id === machine;
23565
- });
23566
- return t("model.restart_success", instanceName, model);
23567
- }
23568
21054
  const backendName = this.backendNameForInstance(instanceName);
23569
21055
  let strategy = "restart";
23570
21056
  try {
@@ -23753,8 +21239,6 @@ export class FleetManager {
23753
21239
  * profile's, when it is named and has one), claude's account catalog included. Read only: never a probe.
23754
21240
  */
23755
21241
  modelCatalogFor(backend, instance) {
23756
- if (instance && isWindowsInstance(this.fleetConfig?.instances[instance]))
23757
- return [];
23758
21242
  if (!KNOWN_BACKENDS.includes(backend) || backend === "mock")
23759
21243
  return [];
23760
21244
  const own = instance && Object.prototype.hasOwnProperty.call(this.fleetConfig?.instances ?? {}, instance) ? instance
@@ -23768,40 +21252,6 @@ export class FleetManager {
23768
21252
  }
23769
21253
  return out;
23770
21254
  }
23771
- /** Host-default fallback only: Grok has no modeled profile home. Never construct a backend or start a probe. */
23772
- async cliModelCatalogFor(backend, instance) {
23773
- if (backend !== "grok")
23774
- return [];
23775
- const fleet = this.fleetConfig;
23776
- const configured = instance && Object.prototype.hasOwnProperty.call(fleet?.instances ?? {}, instance) ? fleet.instances[instance] : undefined;
23777
- const classic = this.classicChannels;
23778
- const room = instance && !configured ? classic?.getAll().find(channel => channel.instanceName === instance) : undefined;
23779
- if (instance && !configured && !room)
23780
- return [];
23781
- const daemon = room ? this.daemons.get(instance) : undefined;
23782
- const grokHome = process.env.GROK_HOME?.trim() || join(homedir(), ".grok");
23783
- const stillHostDefault = () => {
23784
- if (this.fleetConfig !== fleet || (process.env.GROK_HOME?.trim() || join(homedir(), ".grok")) !== grokHome)
23785
- return false;
23786
- if (configured && (isWindowsInstance(configured) || !Object.prototype.hasOwnProperty.call(fleet?.instances ?? {}, instance) || fleet.instances[instance] !== configured))
23787
- return false;
23788
- if (room && (this.classicChannels !== classic || classic?.getAll().find(channel => channel.instanceName === instance) !== room || this.daemons.get(instance) !== daemon))
23789
- return false;
23790
- // The ordinary profile helpers deliberately ignore Grok: it has no credential-home mapping yet.
23791
- // Check raw declarations, including invalid ones, rather than mistaking them for the host login.
23792
- const defaults = fleet?.defaults?.backend_options;
23793
- const profiles = configured
23794
- ? [configured.backend_options?.grok?.credential_profile, defaults?.grok?.credential_profile]
23795
- : room
23796
- ? [mergeBackendOptions(defaults, room.backendOptions)?.grok?.credential_profile, daemon?.getConfigSnapshot?.().backend_options?.grok?.credential_profile]
23797
- : [defaults?.grok?.credential_profile];
23798
- return profiles.every(profile => profile === undefined || profile === null || profile === "");
23799
- };
23800
- if (!stillHostDefault())
23801
- return [];
23802
- const models = await readGrokModelCache(grokHome);
23803
- return stillHostDefault() ? models : [];
23804
- }
23805
21255
  /**
23806
21256
  * #2072 (#2078 review): the snapshot as THIS instance would see it. Its sign-in on a backend is its own credential
23807
21257
  * profile's when it has one there — the host's default login says nothing about it. A profile's login is a file check
@@ -24356,6 +21806,7 @@ export class FleetManager {
24356
21806
  ...this.fleetConfig?.defaults,
24357
21807
  working_directory: workDir,
24358
21808
  lightweight: true,
21809
+ quota_notice: classicIdentity?.quotaNotice,
24359
21810
  tool_progress: toolProgress ?? "off",
24360
21811
  reply_completion_guard: replyCompletionGuard ?? true,
24361
21812
  ...(backend ? { backend } : {}),
@@ -24460,19 +21911,6 @@ export class FleetManager {
24460
21911
  * settles, so a later genuine stop (after a restart) still does the work.
24461
21912
  */
24462
21913
  stopAll() {
24463
- // S-51: no new Hub admission, dispatch or listener work from this tick on; doStopAll awaits the close.
24464
- this.hubEffectsReady = false;
24465
- this.hubRuntime?.fence();
24466
- this.lifecycle.cancelNativeRecovery?.();
24467
- this.nativeMcpStore?.closeAll();
24468
- this.runtimeExecutor?.close();
24469
- this.runtimeExecutor = null;
24470
- this.fleetWindowsRuntime = null;
24471
- this.runtimeHub?.close();
24472
- this.runtimeHub = null;
24473
- this.localPaneRelay?.stop();
24474
- this.localPaneRelay = null;
24475
- closeWindowsBridge(this);
24476
21914
  this.stopAllInFlight ??= this.doStopAll().finally(() => {
24477
21915
  // #1956: after the stop's own tmux work; anything later goes direct. A later startAll starts it again.
24478
21916
  if (this.tmuxExecHelperTimer) {
@@ -24535,6 +21973,9 @@ export class FleetManager {
24535
21973
  // because we asked it to. Set synchronously — doStopAll runs to its first
24536
21974
  // await in the same tick as the signal handler, so no event can slip in.
24537
21975
  this.shuttingDown = true;
21976
+ this.stopQuotaObserver?.();
21977
+ this.stopQuotaObserver = null;
21978
+ const quotaNoticesStopped = this.quotaNotices?.stop();
24538
21979
  // Stop receive admission before the first await or any instance shutdown; outbound
24539
21980
  // adapters remain available below to retire notices and pending prompts.
24540
21981
  for (const state of this.adapterState.values()) {
@@ -24567,7 +22008,6 @@ export class FleetManager {
24567
22008
  const settingsControlStopped = settingsControl?.close();
24568
22009
  this.publicLinkAutoOpen?.stop();
24569
22010
  const publicStopped = this.publicWebLink?.close("fleet shutdown");
24570
- this.webRemoteAccessState?.close(); // #3008: an unsaved switch gets its last write
24571
22011
  const sharesStopped = this.officeSharesInstance?.close(); // #2636: every Office share ends, and its tunnel
24572
22012
  // #1386: every live "Needs you" message says the fleet stopped (capabilities revoked first) — while the
24573
22013
  // adapters can still edit. Bounded: a platform that does not answer must not hold the shutdown.
@@ -24581,17 +22021,6 @@ export class FleetManager {
24581
22021
  // #1565: the web chat's last changes reach the disk (bounded: a slow disk does not hold the shutdown).
24582
22022
  const webChatStopped = this.webChatStore ? Promise.race([this.webChatStore.flush().catch(() => { }), new Promise(r => setTimeout(r, 3_000).unref?.())]) : Promise.resolve();
24583
22023
  const profileStopped = this.runtimeCpuProfiler?.shutdown("fleet shutdown");
24584
- // S-51: the Hub closes in the same drain, bounded so an unanswering listener does not hold the shutdown.
24585
- const hubInit = this.hubRuntimeInit, hubNow = this.hubRuntime;
24586
- this.hubRuntime = null;
24587
- this.hubRuntimeInit = null;
24588
- const hubClose = async () => {
24589
- const runtime = hubNow ?? (hubInit ? await hubInit.catch(() => null) : null);
24590
- await runtime?.close();
24591
- };
24592
- const hubStopped = hubNow || hubInit
24593
- ? Promise.race([hubClose().catch(err => this.logger.warn({ err: err?.message }, "Hub did not close cleanly")), new Promise(r => setTimeout(r, 10_000).unref?.())])
24594
- : Promise.resolve();
24595
22024
  this.ipcStoppingInstances.add("__fleet_stopping__");
24596
22025
  // Release held delivery promises before awaiting daemon shutdown, then
24597
22026
  // reject spawn work which has not started. Otherwise a storm backoff could
@@ -24626,10 +22055,11 @@ export class FleetManager {
24626
22055
  await publicStopped;
24627
22056
  await sharesStopped?.catch(() => { });
24628
22057
  await profileStopped;
24629
- await hubStopped;
24630
22058
  await needsStopped;
24631
22059
  await settingsControlStopped;
24632
22060
  await cacheStopped;
22061
+ await quotaNoticesStopped;
22062
+ this.quotaNotices = null;
24633
22063
  await webChatStopped;
24634
22064
  await this.cpuProfileControl?.close();
24635
22065
  this.cpuProfileControl = null;
@@ -24965,8 +22395,6 @@ export class FleetManager {
24965
22395
  this.savedFleetConfigSnapshot = previousSavedSnapshot;
24966
22396
  this.loadedFleetSource = previousSource;
24967
22397
  this.observeWebAuthority(); // #2453 review: the configuration back in force is the one seen
24968
- this.observeRuntimeSwitches(); // M-2255: and the Windows runtimes' switches
24969
- this.observeHubSwitches(); // S-51: and the Hub's
24970
22398
  this.logger.error({
24971
22399
  oldCount,
24972
22400
  newCount,
@@ -24986,7 +22414,6 @@ export class FleetManager {
24986
22414
  // mark every row done and tell the user "changes applied".
24987
22415
  return { rejected: why };
24988
22416
  }
24989
- this.nativeMcpStore?.observePolicies();
24990
22417
  this.agentCredentialsCache?.observePolicies();
24991
22418
  if (this.loadedFleetSource)
24992
22419
  this.getConfigHistory().accepted("fleet.yaml", this.configPath, this.loadedFleetSource);
@@ -26607,7 +24034,7 @@ export class FleetManager {
26607
24034
  const topicMode = daemonTopicMode(fleet);
26608
24035
  const restartProgress = new RestartProgress(this.runnableStartupCount(fleet, topicMode), restartStartedAt, this.logger);
26609
24036
  // Phase 1: generals first
26610
- const restartEntries = Object.entries(fleet.instances).filter(([, config]) => !isNativeExecInstance(config));
24037
+ const restartEntries = Object.entries(fleet.instances);
26611
24038
  const restartGenerals = restartEntries.filter(([_, cfg]) => cfg.general_topic);
26612
24039
  const restartOthers = restartEntries.filter(([_, cfg]) => !cfg.general_topic);
26613
24040
  for (const [name, cfg] of restartGenerals) {
@@ -27061,14 +24488,6 @@ export class FleetManager {
27061
24488
  return;
27062
24489
  }
27063
24490
  const requestPath = new URL(req.url ?? "/", `http://localhost:${port}`).pathname;
27064
- const publicUrl = new URL(req.url ?? "/", `http://localhost:${port}`);
27065
- if (remoteRuntimeEnabled() && !gatewayRequestContext(req)
27066
- && this.runtimeCoordinator().handlePublicRequest(req, res, publicUrl))
27067
- return;
27068
- // The read-only installer files (/runtime/install.ps1 and the bundle ZIP) grant nothing. They answer 404 while remote runtimes
27069
- // are off or on a public gateway link, and touch no other path.
27070
- if (this.runtimeInstaller().handle(req, res, publicUrl))
27071
- return;
27072
24491
  // Branding is public, after Host admission: automatic favicon requests
27073
24492
  // must not become noisy web-token 401s in the browser console.
27074
24493
  // #2308: the icon routes and the pages' icon links follow the app icon set in Settings.
@@ -27095,8 +24514,7 @@ export class FleetManager {
27095
24514
  if (decision.reason === "no-credential" && req.method === "GET"
27096
24515
  && String(req.headers.accept ?? "").includes("text/html")
27097
24516
  && isWebPageNavigation(requestPath)) {
27098
- const link = gatewayRequestContext(req);
27099
- serveSigninPage(res, 401, { webOnly: this.isWebOnlyFleet(), outside: !!link && this.isOutsideLink(link.exposureId), deviceLogin: !link && this.deviceLoginAvailable(), loginQr: this.loginQrEnabled() });
24517
+ serveSigninPage(res, 401, { webOnly: this.isWebOnlyFleet() });
27100
24518
  return;
27101
24519
  }
27102
24520
  res.writeHead(decision.status);
@@ -27268,7 +24686,6 @@ export class FleetManager {
27268
24686
  res.end(JSON.stringify({
27269
24687
  ...sysInfo,
27270
24688
  version: this.currentVersion,
27271
- build: gatewayRequestContext(req) ? undefined : this.currentBuild,
27272
24689
  instances: enriched,
27273
24690
  publicLink: this.getPublicWebStatus(),
27274
24691
  }));
@@ -27454,12 +24871,6 @@ export class FleetManager {
27454
24871
  if (!this.webToken || !this.webSessions)
27455
24872
  this.initializeWebAuthTokens();
27456
24873
  const server = createServer((req, res) => this.dispatchWebHttp(req, res, port, drain.closing));
27457
- server.on("upgrade", (req, socket, head) => {
27458
- if (!this.shuttingDown && remoteRuntimeEnabled() && isHostAllowed(req.headers.host, allowedHostNames(this.fleetConfig))
27459
- && this.runtimeCoordinator().handleUpgrade(req, socket, head))
27460
- return;
27461
- socket.destroy();
27462
- });
27463
24874
  // #1937: Node's default (300 s to receive a request) would cut a large upload on a slow link. The listener allows
27464
24875
  // more; the upload route keeps its own clocks (no byte for a minute, or 30 minutes in all), and every other request
27465
24876
  // keeps the old 300 s (limitRequestReceive in dispatchWebHttp).
@@ -27684,36 +25095,9 @@ export class FleetManager {
27684
25095
  this.logger.debug({ err }, "bots unreadable (getUiStatus)");
27685
25096
  }
27686
25097
  const instances = names.map(name => {
27687
- const nativeConfig = this.fleetConfig?.instances[name];
27688
- if (isNativeExecInstance(nativeConfig)) {
27689
- let runtime = null;
27690
- let agent = null;
27691
- try {
27692
- runtime = this.nativeInstanceRuntime();
27693
- agent = runtime.get(name);
27694
- }
27695
- catch { /* A damaged native profile is a crashed native row, never a Linux spawn. */ }
27696
- const display_name = nativeConfig.display_name || agent?.name || name;
27697
- const backend = nativeConfig.backend || agent?.cli || "codex";
27698
- return { name, display_name, description: nativeConfig.description || undefined, tags: nativeConfig.tags || [],
27699
- teams: instanceTeams(this.fleetConfig?.teams, name), runtime: "windows", native_agent_id: nativeConfig.native_agent_id,
27700
- ...(nativeConfig.machine_id ? { machine_id: nativeConfig.machine_id, machine_name: this.runtimeHub?.machine(nativeConfig.machine_id)?.name,
27701
- machine_status: this.runtimeHub?.machine(nativeConfig.machine_id)?.status ?? "offline" } : {}),
27702
- native_blocked: !agent || agent.blocked === true,
27703
- native_busy: runtime?.busy(name) ?? false,
27704
- backend,
27705
- model: nativeConfig.model || "default", model_source: nativeConfig.model ? "instance" : "cli-default",
27706
- status: agent && runtime ? runtime.status(name) : "crashed", state: agent && runtime ? runtime.execution(name) : null, execution_state: agent && runtime ? runtime.execution(name) : null,
27707
- interaction: null, interaction_summary: null, context_pct: null, cost: null,
27708
- last_reply: this.webChatHistory.lastAgentReply(name) ?? undefined,
27709
- avatar: avatarFor({ name, displayName: display_name }),
27710
- capabilities: { chat: true, settings: true, pause: true, cancel: true, teams: true, schedules: true, delegation: true, mcp: nativeProviderMcpSupported(backend), channels: false, terminal: false, attachments: false, usage: false, send_while_busy: false },
27711
- ...(!agent ? { runtime_error: "Windows profile is missing or invalid; restore its saved conversation before using this instance" }
27712
- : agent.blocked ? { runtime_error: "Windows process outcome is unknown; verify it stopped before recovery" } : {}) };
27713
- }
27714
25098
  // #1956: read once for this instance; its cost, context and model come from it.
27715
- const statusline = isNativePaneInstance(nativeConfig) ? undefined : readStatuslineJson(this.dataDir, name);
27716
- const cost = isNativePaneInstance(nativeConfig) ? null : statusline?.cost?.total_cost_usd ?? 0;
25099
+ const statusline = readStatuslineJson(this.dataDir, name);
25100
+ const cost = statusline?.cost?.total_cost_usd ?? 0;
27717
25101
  // Align with /ctx: statusline for claude-code, pane scrape for kiro/grok/codex.
27718
25102
  const classic = classicOnly.includes(name);
27719
25103
  const backend = classic
@@ -27721,7 +25105,7 @@ export class FleetManager {
27721
25105
  : (this.fleetConfig?.instances[name]?.backend
27722
25106
  ?? this.fleetConfig?.defaults?.backend
27723
25107
  ?? "claude-code");
27724
- const { context } = isNativePaneInstance(nativeConfig) ? { context: null } : resolveInstanceContext(this.dataDir, name, backend, { source: this.getPaneContextSource(name), statusline });
25108
+ const { context } = resolveInstanceContext(this.dataDir, name, backend, { source: this.getPaneContextSource(name), statusline });
27725
25109
  // context_pct: null when unavailable, not 0
27726
25110
  const context_pct = context ?? null;
27727
25111
  // Model: Only Claude Code has live statusline; others use the effective resolver.
@@ -27752,24 +25136,6 @@ export class FleetManager {
27752
25136
  }
27753
25137
  return {
27754
25138
  name,
27755
- general_topic: nativeConfig?.general_topic === true,
27756
- ...(isNativePaneInstance(nativeConfig) ? { runtime: "windows", native_adapter: "pane", native_agent_id: nativeConfig.native_agent_id,
27757
- ...(() => {
27758
- try {
27759
- const profile = this.nativeInstanceRuntime().get(name), bound = this.daemons.get(name);
27760
- return { native_recovery: profile?.paneRecovery ?? (profile?.paneLaunchId && bound?.usesNativePane?.() && !bound.nativePaneCurrent()
27761
- ? { kind: "exit-unconfirmed", launchId: profile.paneLaunchId } : null) };
27762
- }
27763
- catch {
27764
- return { native_recovery: null };
27765
- }
27766
- })(),
27767
- capabilities: { chat: true, settings: true, pause: true, cancel: true, teams: true, schedules: true, delegation: true, mcp: true,
27768
- commands: true, effort: false, log: true, details: true, channels: false, terminal: false, attachments: false, usage: false, send_while_busy: true },
27769
- // cc3ce4fe: the version this machine's inventory reports for the agent's CLI, and whether AgEnD has audited it.
27770
- ...this.nativeCliStatus(nativeConfig.backend, nativeConfig.machine_id, this.daemons.get(name)),
27771
- ...(nativeConfig.machine_id ? { machine_id: nativeConfig.machine_id, machine_name: this.runtimeHub?.machine(nativeConfig.machine_id)?.name,
27772
- machine_status: this.runtimeHub?.machine(nativeConfig.machine_id)?.status ?? "offline" } : {}) } : {}),
27773
25139
  display_name: identity.display_name || undefined,
27774
25140
  avatar: image.avatar,
27775
25141
  ...(image.accent ? { accent: image.accent } : {}),
@@ -27792,8 +25158,6 @@ export class FleetManager {
27792
25158
  role: identity.role || undefined,
27793
25159
  tags: identity.tags,
27794
25160
  teams: instanceTeams(this.fleetConfig?.teams, name), // #2172: the sidebar groups a team's members under it
27795
- // #3008 G2: a web-only fleet's General (the agent its owner ticked) — the sidebar pins it first.
27796
- ...(this.isWebOnlyFleet() && this.fleetConfig?.instances[name]?.general_topic === true ? { general: true } : {}),
27797
25161
  last_reply: this.webChatHistory.lastAgentReply(name) ?? undefined, // #2179: its last reply to a person (ISO)
27798
25162
  // #2855: its last reply that reached the channel — the only reply time a guest's sidebar gets (guestStatus).
27799
25163
  last_reply_channel: this.webChatHistory.lastAgentReply(name, { channel: true }) ?? undefined,
@@ -27823,7 +25187,6 @@ export class FleetManager {
27823
25187
  uptime: Math.floor((Date.now() - this.startedAt) / 1000),
27824
25188
  // #1519 P7: how many chat connections are configured — 0 is a web-only fleet, which gets the first-run card.
27825
25189
  connections: (this.fleetConfig?.channels ?? (this.fleetConfig?.channel ? [this.fleetConfig.channel] : [])).length,
27826
- windows_cli: localWindowsCliEnabled(),
27827
25190
  // #1665: the host preferences' revision — a page that has another re-reads /ui/prefs (no values in this frame).
27828
25191
  prefs_rev: this.webPrefs.rev(),
27829
25192
  // #1629: the themes' revision — the page re-reads GET /ui/themes when it moves.
@@ -27833,8 +25196,6 @@ export class FleetManager {
27833
25196
  host_memory: this.hostMemoryIndicator(),
27834
25197
  // #2910 D2: a web-only fleet's newest Notice — a polling page (no SSE `notice`) reads the list again when it moves.
27835
25198
  ...(this.webOnlyPromptPlace() ? { notices_newest: this.webNoticeStore()?.newest() ?? 0 } : {}),
27836
- // alpha.2 (M-2300, #2997 review): agents' requests changed — the owner's page reads GET /ui/agent-proposals again.
27837
- ...(this.isWebOnlyFleet() ? { agent_proposals_rev: this.agentProposalsRev } : {}),
27838
25199
  // #2015: the largest processes while it lasts (name, pid, RSS, and whose: an instance, the fleet or tmux).
27839
25200
  ...(this.hostMemoryIndicator() && this.memoryTop ? { host_memory_top: this.memoryTop.processes } : {}),
27840
25201
  };
@@ -28277,12 +25638,4 @@ load();
28277
25638
  </script>
28278
25639
  </body>
28279
25640
  </html>`;
28280
- /** alpha.2 (M-2300): the agent an applied Windows request created (its name, for the page's "Open chat"), or null. */
28281
- export function agentNameOf(view) {
28282
- const r = view.state === "applied" ? view.outcome?.result : null;
28283
- if (!r || typeof r !== "object")
28284
- return null;
28285
- const o = r, name = o.name ?? o.instance_name ?? (o.agent && typeof o.agent === "object" ? o.agent.name : undefined);
28286
- return typeof name === "string" && /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name) ? name : null;
28287
- }
28288
25641
  //# sourceMappingURL=fleet-manager.js.map