@songsid/agend 2.3.0-beta.9 → 2.3.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 (743) hide show
  1. package/README.md +16 -8
  2. package/README.zh-TW.md +16 -8
  3. package/dist/adapter-world.js.map +1 -1
  4. package/dist/agend-home.d.ts +5 -0
  5. package/dist/agend-home.js +7 -0
  6. package/dist/agend-home.js.map +1 -0
  7. package/dist/agent-cli-instructions.md +7 -1
  8. package/dist/agent-cli.js +41 -0
  9. package/dist/agent-cli.js.map +1 -1
  10. package/dist/agent-endpoint.d.ts +9 -0
  11. package/dist/agent-endpoint.js +23 -0
  12. package/dist/agent-endpoint.js.map +1 -1
  13. package/dist/agent-file-read.js +1 -1
  14. package/dist/agent-file-read.js.map +1 -1
  15. package/dist/agent-whats-new.d.ts +16 -0
  16. package/dist/agent-whats-new.js +53 -12
  17. package/dist/agent-whats-new.js.map +1 -1
  18. package/dist/apply-job.js.map +1 -1
  19. package/dist/atomic-write.js.map +1 -1
  20. package/dist/auth-api.d.ts +12 -0
  21. package/dist/auth-api.js +77 -22
  22. package/dist/auth-api.js.map +1 -1
  23. package/dist/authority/authority-state.d.ts +4 -0
  24. package/dist/authority/authority-state.js +17 -8
  25. package/dist/authority/authority-state.js.map +1 -1
  26. package/dist/authority/session-authorizations.js.map +1 -1
  27. package/dist/authority/web-authority.d.ts +16 -8
  28. package/dist/authority/web-authority.js +19 -5
  29. package/dist/authority/web-authority.js.map +1 -1
  30. package/dist/backend/antigravity.d.ts +2 -2
  31. package/dist/backend/antigravity.js +2 -1
  32. package/dist/backend/antigravity.js.map +1 -1
  33. package/dist/backend/claude-code.js +13 -4
  34. package/dist/backend/claude-code.js.map +1 -1
  35. package/dist/backend/codex-metadata.d.ts +8 -0
  36. package/dist/backend/codex-metadata.js +74 -17
  37. package/dist/backend/codex-metadata.js.map +1 -1
  38. package/dist/backend/codex-package.d.ts +30 -0
  39. package/dist/backend/codex-package.js +133 -0
  40. package/dist/backend/codex-package.js.map +1 -0
  41. package/dist/backend/codex-session-lookup.js.map +1 -1
  42. package/dist/backend/codex.d.ts +2 -0
  43. package/dist/backend/codex.js +25 -5
  44. package/dist/backend/codex.js.map +1 -1
  45. package/dist/backend/credential-profile.d.ts +2 -0
  46. package/dist/backend/credential-profile.js +2 -0
  47. package/dist/backend/credential-profile.js.map +1 -1
  48. package/dist/backend/effort-metadata.js.map +1 -1
  49. package/dist/backend/grok.js +58 -3
  50. package/dist/backend/grok.js.map +1 -1
  51. package/dist/backend/kiro-engine-ledger.js.map +1 -1
  52. package/dist/backend/kiro-kas-store.js.map +1 -1
  53. package/dist/backend/kiro-v2-store.js.map +1 -1
  54. package/dist/backend/kiro-v3-identity.js.map +1 -1
  55. package/dist/backend/kiro.d.ts +9 -1
  56. package/dist/backend/kiro.js +102 -1
  57. package/dist/backend/kiro.js.map +1 -1
  58. package/dist/backend/launch-effort.js.map +1 -1
  59. package/dist/backend/mock.js.map +1 -1
  60. package/dist/backend/muse.d.ts +20 -2
  61. package/dist/backend/muse.js +154 -16
  62. package/dist/backend/muse.js.map +1 -1
  63. package/dist/backend/opencode-permission.js +1 -1
  64. package/dist/backend/opencode-permission.js.map +1 -1
  65. package/dist/backend/opencode.js +2 -1
  66. package/dist/backend/opencode.js.map +1 -1
  67. package/dist/backend/session-signals.js.map +1 -1
  68. package/dist/backend-availability.js.map +1 -1
  69. package/dist/backend-command.d.ts +2 -0
  70. package/dist/backend-command.js +3 -1
  71. package/dist/backend-command.js.map +1 -1
  72. package/dist/backend-outage.js.map +1 -1
  73. package/dist/binary-probe.js +1 -1
  74. package/dist/binary-probe.js.map +1 -1
  75. package/dist/bug-report-cli.d.ts +30 -0
  76. package/dist/bug-report-cli.js +139 -0
  77. package/dist/bug-report-cli.js.map +1 -0
  78. package/dist/bug-report.d.ts +47 -0
  79. package/dist/bug-report.js +137 -0
  80. package/dist/bug-report.js.map +1 -0
  81. package/dist/bundled-skills.d.ts +18 -0
  82. package/dist/bundled-skills.js +38 -0
  83. package/dist/bundled-skills.js.map +1 -0
  84. package/dist/cache-service.js +1 -1
  85. package/dist/cache-service.js.map +1 -1
  86. package/dist/channel/adapters/discord-client-retirement.d.ts +5 -0
  87. package/dist/channel/adapters/discord-client-retirement.js +57 -0
  88. package/dist/channel/adapters/discord-client-retirement.js.map +1 -0
  89. package/dist/channel/adapters/discord.d.ts +4 -0
  90. package/dist/channel/adapters/discord.js +57 -12
  91. package/dist/channel/adapters/discord.js.map +1 -1
  92. package/dist/channel/adapters/telegram.d.ts +7 -1
  93. package/dist/channel/adapters/telegram.js +57 -49
  94. package/dist/channel/adapters/telegram.js.map +1 -1
  95. package/dist/channel/mcp-server.js +21 -4
  96. package/dist/channel/mcp-server.js.map +1 -1
  97. package/dist/channel/mcp-tools.d.ts +3 -1
  98. package/dist/channel/mcp-tools.js +41 -4
  99. package/dist/channel/mcp-tools.js.map +1 -1
  100. package/dist/channel/message-queue.js.map +1 -1
  101. package/dist/channel/sticker-download.js.map +1 -1
  102. package/dist/channel/tool-router.js.map +1 -1
  103. package/dist/child-process.d.ts +21 -0
  104. package/dist/child-process.js +54 -0
  105. package/dist/child-process.js.map +1 -0
  106. package/dist/classic-channel-manager.js +3 -2
  107. package/dist/classic-channel-manager.js.map +1 -1
  108. package/dist/cli-entry.js.map +1 -1
  109. package/dist/cli-flags.js.map +1 -1
  110. package/dist/cli.d.ts +1 -0
  111. package/dist/cli.js +448 -313
  112. package/dist/cli.js.map +1 -1
  113. package/dist/command-table.d.ts +1 -1
  114. package/dist/command-table.js +1 -1
  115. package/dist/command-table.js.map +1 -1
  116. package/dist/compact-watch.js.map +1 -1
  117. package/dist/completion-install.js.map +1 -1
  118. package/dist/completion.js.map +1 -1
  119. package/dist/config-validator.d.ts +6 -0
  120. package/dist/config-validator.js +81 -8
  121. package/dist/config-validator.js.map +1 -1
  122. package/dist/config.d.ts +15 -3
  123. package/dist/config.js +41 -10
  124. package/dist/config.js.map +1 -1
  125. package/dist/connection-secrets.js.map +1 -1
  126. package/dist/context-guardian.js.map +1 -1
  127. package/dist/coredump-filter.js.map +1 -1
  128. package/dist/cpu-profile.js.map +1 -1
  129. package/dist/cross-instance-envelope.js.map +1 -1
  130. package/dist/cross-instance-notice.d.ts +7 -0
  131. package/dist/cross-instance-notice.js +10 -0
  132. package/dist/cross-instance-notice.js.map +1 -1
  133. package/dist/daemon.d.ts +52 -1
  134. package/dist/daemon.js +385 -27
  135. package/dist/daemon.js.map +1 -1
  136. package/dist/daily-summary.js.map +1 -1
  137. package/dist/darwin-memory.js +1 -1
  138. package/dist/darwin-memory.js.map +1 -1
  139. package/dist/dashboard-live.d.ts +8 -0
  140. package/dist/dashboard-live.js +35 -0
  141. package/dist/dashboard-live.js.map +1 -0
  142. package/dist/dashboard-permissions.d.ts +69 -0
  143. package/dist/dashboard-permissions.js +138 -0
  144. package/dist/dashboard-permissions.js.map +1 -0
  145. package/dist/decision-list.js.map +1 -1
  146. package/dist/delivery-consumed-watch.js.map +1 -1
  147. package/dist/delivery-outbox.d.ts +11 -0
  148. package/dist/delivery-outbox.js +52 -2
  149. package/dist/delivery-outbox.js.map +1 -1
  150. package/dist/delivery-outbox.test.js.map +1 -1
  151. package/dist/delivery-reconciliation.test.js.map +1 -1
  152. package/dist/diagnostic-redaction.d.ts +10 -0
  153. package/dist/diagnostic-redaction.js +167 -0
  154. package/dist/diagnostic-redaction.js.map +1 -0
  155. package/dist/doctor.d.ts +8 -0
  156. package/dist/doctor.js +52 -0
  157. package/dist/doctor.js.map +1 -1
  158. package/dist/e2e-isolation.js.map +1 -1
  159. package/dist/event-log.js.map +1 -1
  160. package/dist/export-import.js +7 -5
  161. package/dist/export-import.js.map +1 -1
  162. package/dist/fleet-context.d.ts +1 -1
  163. package/dist/fleet-control-audit.js.map +1 -1
  164. package/dist/fleet-label.js.map +1 -1
  165. package/dist/fleet-level-config.d.ts +1 -1
  166. package/dist/fleet-level-config.js +2 -0
  167. package/dist/fleet-level-config.js.map +1 -1
  168. package/dist/fleet-lock.d.ts +6 -0
  169. package/dist/fleet-lock.js +22 -2
  170. package/dist/fleet-lock.js.map +1 -1
  171. package/dist/fleet-manager.d.ts +283 -15
  172. package/dist/fleet-manager.js +1825 -331
  173. package/dist/fleet-manager.js.map +1 -1
  174. package/dist/fleet-process-handlers.d.ts +18 -0
  175. package/dist/fleet-process-handlers.js +70 -0
  176. package/dist/fleet-process-handlers.js.map +1 -0
  177. package/dist/fleet-startup.d.ts +14 -0
  178. package/dist/fleet-startup.js +30 -0
  179. package/dist/fleet-startup.js.map +1 -0
  180. package/dist/fleet-traffic.js.map +1 -1
  181. package/dist/forged-envelope-scan.d.ts +0 -20
  182. package/dist/forged-envelope-scan.js +3 -2
  183. package/dist/forged-envelope-scan.js.map +1 -1
  184. package/dist/full-restart.d.ts +5 -5
  185. package/dist/full-restart.js +5 -5
  186. package/dist/full-restart.js.map +1 -1
  187. package/dist/general-knowledge/skills/bug-report/SKILL.md +31 -0
  188. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +52 -11
  189. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +5 -1
  190. package/dist/general-knowledge/skills/fleet-config/SKILL.md +3 -1
  191. package/dist/general-knowledge/skills/fleet-restart/SKILL.md +8 -9
  192. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +11 -0
  193. package/dist/general-knowledge/skills/multi-channel/SKILL.md +1 -1
  194. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +1 -1
  195. package/dist/general-knowledge/skills/scheduling/SKILL.md +4 -0
  196. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +321 -0
  197. package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -4
  198. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +554 -0
  199. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +156 -32
  200. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  201. package/dist/general-knowledge/steering/core-rules.md +1 -1
  202. package/dist/host-memory.js.map +1 -1
  203. package/dist/idle-gate-log.d.ts +19 -0
  204. package/dist/idle-gate-log.js +15 -0
  205. package/dist/idle-gate-log.js.map +1 -0
  206. package/dist/instance-config-impact.js +25 -4
  207. package/dist/instance-config-impact.js.map +1 -1
  208. package/dist/instance-lifecycle.d.ts +11 -1
  209. package/dist/instance-lifecycle.js +97 -25
  210. package/dist/instance-lifecycle.js.map +1 -1
  211. package/dist/instructions.d.ts +12 -6
  212. package/dist/instructions.js +19 -10
  213. package/dist/instructions.js.map +1 -1
  214. package/dist/kiro-compact-pane.js.map +1 -1
  215. package/dist/kiro-db-reader.js.map +1 -1
  216. package/dist/kiro-engine-status.js.map +1 -1
  217. package/dist/kiro-log-gate.js.map +1 -1
  218. package/dist/kiro-transcript-lane.js.map +1 -1
  219. package/dist/locale.js +450 -20
  220. package/dist/locale.js.map +1 -1
  221. package/dist/log-service.js +1 -0
  222. package/dist/log-service.js.map +1 -1
  223. package/dist/log-steps.d.ts +1 -0
  224. package/dist/log-steps.js +15 -0
  225. package/dist/log-steps.js.map +1 -1
  226. package/dist/logger.js +3 -2
  227. package/dist/logger.js.map +1 -1
  228. package/dist/login-controller.d.ts +45 -0
  229. package/dist/login-controller.js +101 -5
  230. package/dist/login-controller.js.map +1 -1
  231. package/dist/login-flows.js +1 -1
  232. package/dist/login-manager.js.map +1 -1
  233. package/dist/login-shell.js +3 -1
  234. package/dist/login-shell.js.map +1 -1
  235. package/dist/ls-attach-resolve.d.ts +96 -0
  236. package/dist/ls-attach-resolve.js +180 -0
  237. package/dist/ls-attach-resolve.js.map +1 -0
  238. package/dist/ls-rows.d.ts +12 -0
  239. package/dist/ls-rows.js +83 -4
  240. package/dist/ls-rows.js.map +1 -1
  241. package/dist/memory-pressure.js.map +1 -1
  242. package/dist/muse-usage-relay.js.map +1 -1
  243. package/dist/needs-you-hub.d.ts +98 -1
  244. package/dist/needs-you-hub.js +313 -44
  245. package/dist/needs-you-hub.js.map +1 -1
  246. package/dist/needs-you-live.d.ts +5 -2
  247. package/dist/needs-you-live.js +6 -3
  248. package/dist/needs-you-live.js.map +1 -1
  249. package/dist/needs-you.d.ts +68 -3
  250. package/dist/needs-you.js +39 -4
  251. package/dist/needs-you.js.map +1 -1
  252. package/dist/network-family.js.map +1 -1
  253. package/dist/operator-control-paths.js +3 -4
  254. package/dist/operator-control-paths.js.map +1 -1
  255. package/dist/outbound-handlers.d.ts +5 -2
  256. package/dist/outbound-handlers.js +81 -24
  257. package/dist/outbound-handlers.js.map +1 -1
  258. package/dist/outbound-schemas.d.ts +106 -60
  259. package/dist/outbound-schemas.js +33 -10
  260. package/dist/outbound-schemas.js.map +1 -1
  261. package/dist/outbox-open-error.js.map +1 -1
  262. package/dist/pane-context-cache.js.map +1 -1
  263. package/dist/paths.js.map +1 -1
  264. package/dist/private-dir.js.map +1 -1
  265. package/dist/probe-worker-pool.js.map +1 -1
  266. package/dist/profile-control.js.map +1 -1
  267. package/dist/prompt-file-ref.js.map +1 -1
  268. package/dist/provider-probe.js.map +1 -1
  269. package/dist/provider-secret-registry.d.ts +5 -3
  270. package/dist/provider-secret-registry.js +8 -2
  271. package/dist/provider-secret-registry.js.map +1 -1
  272. package/dist/public-link-auto-open.d.ts +32 -0
  273. package/dist/public-link-auto-open.js +86 -0
  274. package/dist/public-link-auto-open.js.map +1 -0
  275. package/dist/public-link-owner.d.ts +10 -0
  276. package/dist/public-link-owner.js +36 -0
  277. package/dist/public-link-owner.js.map +1 -0
  278. package/dist/public-link-progress.d.ts +12 -1
  279. package/dist/public-link-progress.js +5 -10
  280. package/dist/public-link-progress.js.map +1 -1
  281. package/dist/public-preview.js.map +1 -1
  282. package/dist/public-tunnel-config.d.ts +11 -0
  283. package/dist/public-tunnel-config.js +68 -0
  284. package/dist/public-tunnel-config.js.map +1 -0
  285. package/dist/public-web-gateway.d.ts +2 -0
  286. package/dist/public-web-gateway.js +19 -6
  287. package/dist/public-web-gateway.js.map +1 -1
  288. package/dist/public-web-link.d.ts +48 -3
  289. package/dist/public-web-link.js +177 -51
  290. package/dist/public-web-link.js.map +1 -1
  291. package/dist/quickstart-api.d.ts +9 -0
  292. package/dist/quickstart-api.js +82 -0
  293. package/dist/quickstart-api.js.map +1 -1
  294. package/dist/quickstart.d.ts +5 -0
  295. package/dist/quickstart.js +40 -25
  296. package/dist/quickstart.js.map +1 -1
  297. package/dist/reply-buttons-controller.js.map +1 -1
  298. package/dist/reply-dedup.js.map +1 -1
  299. package/dist/resource-report.js +1 -1
  300. package/dist/resource-report.js.map +1 -1
  301. package/dist/restart-guard.js.map +1 -1
  302. package/dist/restart-progress.d.ts +80 -1
  303. package/dist/restart-progress.js +306 -21
  304. package/dist/restart-progress.js.map +1 -1
  305. package/dist/rollout-index.js.map +1 -1
  306. package/dist/runtime-cpu-profile.js.map +1 -1
  307. package/dist/scheduler/db.d.ts +40 -13
  308. package/dist/scheduler/db.js +123 -89
  309. package/dist/scheduler/db.js.map +1 -1
  310. package/dist/scheduler/scheduler.js.map +1 -1
  311. package/dist/scheduler/task-board-text.d.ts +26 -0
  312. package/dist/scheduler/task-board-text.js +140 -0
  313. package/dist/scheduler/task-board-text.js.map +1 -0
  314. package/dist/scheduler/task-board.d.ts +730 -0
  315. package/dist/scheduler/task-board.js +2179 -0
  316. package/dist/scheduler/task-board.js.map +1 -0
  317. package/dist/scheduler/types.d.ts +6 -1
  318. package/dist/sd-notify.js +2 -1
  319. package/dist/sd-notify.js.map +1 -1
  320. package/dist/search/search-docs.d.ts +58 -0
  321. package/dist/search/search-docs.js +63 -0
  322. package/dist/search/search-docs.js.map +1 -0
  323. package/dist/search/search-index.d.ts +149 -0
  324. package/dist/search/search-index.js +453 -0
  325. package/dist/search/search-index.js.map +1 -0
  326. package/dist/search/search-store.d.ts +122 -0
  327. package/dist/search/search-store.js +236 -0
  328. package/dist/search/search-store.js.map +1 -0
  329. package/dist/search/search-worker.d.ts +1 -0
  330. package/dist/search/search-worker.js +163 -0
  331. package/dist/search/search-worker.js.map +1 -0
  332. package/dist/secret-file.js.map +1 -1
  333. package/dist/secret-store.d.ts +2 -0
  334. package/dist/secret-store.js +21 -0
  335. package/dist/secret-store.js.map +1 -1
  336. package/dist/service-installer.d.ts +98 -1
  337. package/dist/service-installer.js +246 -11
  338. package/dist/service-installer.js.map +1 -1
  339. package/dist/service-owner.d.ts +44 -0
  340. package/dist/service-owner.js +48 -0
  341. package/dist/service-owner.js.map +1 -0
  342. package/dist/service-plan.d.ts +39 -2
  343. package/dist/service-plan.js +188 -6
  344. package/dist/service-plan.js.map +1 -1
  345. package/dist/session-oom-stop.d.ts +45 -0
  346. package/dist/session-oom-stop.js +259 -0
  347. package/dist/session-oom-stop.js.map +1 -0
  348. package/dist/session-oom.d.ts +34 -0
  349. package/dist/session-oom.js +251 -0
  350. package/dist/session-oom.js.map +1 -0
  351. package/dist/settings-api.d.ts +14 -1
  352. package/dist/settings-api.js +74 -13
  353. package/dist/settings-api.js.map +1 -1
  354. package/dist/settings-baseline.js.map +1 -1
  355. package/dist/settings-change.d.ts +5 -0
  356. package/dist/settings-change.js +167 -6
  357. package/dist/settings-change.js.map +1 -1
  358. package/dist/settings-confirmation.d.ts +9 -1
  359. package/dist/settings-confirmation.js +18 -8
  360. package/dist/settings-confirmation.js.map +1 -1
  361. package/dist/settings-control.js +2 -1
  362. package/dist/settings-control.js.map +1 -1
  363. package/dist/settings-effect.js +29 -4
  364. package/dist/settings-effect.js.map +1 -1
  365. package/dist/settings-http-confirmation.d.ts +13 -0
  366. package/dist/settings-http-confirmation.js +15 -3
  367. package/dist/settings-http-confirmation.js.map +1 -1
  368. package/dist/settings-instance-patch.js +13 -1
  369. package/dist/settings-instance-patch.js.map +1 -1
  370. package/dist/settings-request-capability.d.ts +4 -1
  371. package/dist/settings-request-capability.js +11 -3
  372. package/dist/settings-request-capability.js.map +1 -1
  373. package/dist/settings-transaction.js.map +1 -1
  374. package/dist/setup-auth.d.ts +1 -0
  375. package/dist/setup-auth.js +2 -1
  376. package/dist/setup-auth.js.map +1 -1
  377. package/dist/setup-form.d.ts +1 -1
  378. package/dist/setup-form.js +23 -7
  379. package/dist/setup-form.js.map +1 -1
  380. package/dist/setup-host.d.ts +6 -1
  381. package/dist/setup-host.js +15 -4
  382. package/dist/setup-host.js.map +1 -1
  383. package/dist/setup-wizard.js +6 -3
  384. package/dist/setup-wizard.js.map +1 -1
  385. package/dist/spawn-gate.d.ts +7 -0
  386. package/dist/spawn-gate.js +45 -12
  387. package/dist/spawn-gate.js.map +1 -1
  388. package/dist/start-attempt.d.ts +11 -0
  389. package/dist/start-attempt.js +29 -0
  390. package/dist/start-attempt.js.map +1 -0
  391. package/dist/start-failure-chat.d.ts +37 -0
  392. package/dist/start-failure-chat.js +142 -0
  393. package/dist/start-failure-chat.js.map +1 -0
  394. package/dist/start-failure-notice.d.ts +4 -0
  395. package/dist/start-failure-notice.js +20 -0
  396. package/dist/start-failure-notice.js.map +1 -0
  397. package/dist/start-failure.d.ts +171 -0
  398. package/dist/start-failure.js +348 -0
  399. package/dist/start-failure.js.map +1 -0
  400. package/dist/startup-memory-deadline.d.ts +23 -0
  401. package/dist/startup-memory-deadline.js +55 -0
  402. package/dist/startup-memory-deadline.js.map +1 -0
  403. package/dist/statusline-watcher.js.map +1 -1
  404. package/dist/storm-window.js.map +1 -1
  405. package/dist/systemd-peer-bus.js.map +1 -1
  406. package/dist/target-queue-worker.js.map +1 -1
  407. package/dist/task-board-summary.d.ts +101 -0
  408. package/dist/task-board-summary.js +177 -0
  409. package/dist/task-board-summary.js.map +1 -0
  410. package/dist/task-board-tool.d.ts +25 -0
  411. package/dist/task-board-tool.js +155 -0
  412. package/dist/task-board-tool.js.map +1 -0
  413. package/dist/task-board-waker.d.ts +51 -0
  414. package/dist/task-board-waker.js +141 -0
  415. package/dist/task-board-waker.js.map +1 -0
  416. package/dist/telegram-api-root.d.ts +1 -0
  417. package/dist/telegram-api-root.js +40 -0
  418. package/dist/telegram-api-root.js.map +1 -0
  419. package/dist/tips.js.map +1 -1
  420. package/dist/tmux-control.js +1 -1
  421. package/dist/tmux-control.js.map +1 -1
  422. package/dist/tmux-exec-helper.d.ts +104 -0
  423. package/dist/tmux-exec-helper.js +412 -0
  424. package/dist/tmux-exec-helper.js.map +1 -0
  425. package/dist/tmux-manager.d.ts +1 -0
  426. package/dist/tmux-manager.js +29 -13
  427. package/dist/tmux-manager.js.map +1 -1
  428. package/dist/tmux-read.js +2 -2
  429. package/dist/tmux-read.js.map +1 -1
  430. package/dist/tool-permissions.js.map +1 -1
  431. package/dist/tool-progress.js.map +1 -1
  432. package/dist/topic-archiver.js.map +1 -1
  433. package/dist/topic-commands.d.ts +13 -0
  434. package/dist/topic-commands.js +56 -33
  435. package/dist/topic-commands.js.map +1 -1
  436. package/dist/topic-link.js.map +1 -1
  437. package/dist/transcript-delta-reader.js.map +1 -1
  438. package/dist/transcript-jsonl.js.map +1 -1
  439. package/dist/transcript-monitor.js +5 -0
  440. package/dist/transcript-monitor.js.map +1 -1
  441. package/dist/transcript-sources.d.ts +2 -0
  442. package/dist/transcript-sources.js +4 -0
  443. package/dist/transcript-sources.js.map +1 -1
  444. package/dist/tunnel/cloudflared-install.js +3 -1
  445. package/dist/tunnel/cloudflared-install.js.map +1 -1
  446. package/dist/tunnel/cloudflared.d.ts +7 -16
  447. package/dist/tunnel/cloudflared.js +100 -13
  448. package/dist/tunnel/cloudflared.js.map +1 -1
  449. package/dist/tunnel/lease.js +3 -2
  450. package/dist/tunnel/lease.js.map +1 -1
  451. package/dist/tunnel/manager.js.map +1 -1
  452. package/dist/tunnel/named-diagnostics.d.ts +13 -0
  453. package/dist/tunnel/named-diagnostics.js +52 -0
  454. package/dist/tunnel/named-diagnostics.js.map +1 -0
  455. package/dist/tunnel/public-fetch.js.map +1 -1
  456. package/dist/tunnel/purpose-lane.js.map +1 -1
  457. package/dist/tunnel/types.d.ts +4 -1
  458. package/dist/tunnel/types.js.map +1 -1
  459. package/dist/types.d.ts +56 -2
  460. package/dist/ui/app-effects.js +23 -5
  461. package/dist/ui/app.html +2 -0
  462. package/dist/ui/backend-signin.js +297 -0
  463. package/dist/ui/chat-effects.js +10 -3
  464. package/dist/ui/chat-msg-actions.js +176 -0
  465. package/dist/ui/chat-render.js +62 -18
  466. package/dist/ui/chat-store.js +218 -48
  467. package/dist/ui/chat-strings.js +40 -8
  468. package/dist/ui/chat-thread.js +119 -10
  469. package/dist/ui/effect-trial-offer.js +2 -31
  470. package/dist/ui/effects-waker.js +2 -0
  471. package/dist/ui/fleet-canvas-layout.js +13 -4
  472. package/dist/ui/fleet-canvas.js +69 -28
  473. package/dist/ui/fleet-office-editor.js +149 -0
  474. package/dist/ui/fleet-office-model.js +358 -0
  475. package/dist/ui/fleet-office-pack.js +199 -0
  476. package/dist/ui/fleet-office-skin.js +474 -0
  477. package/dist/ui/fleet-office-store.js +49 -0
  478. package/dist/ui/fleet-office-zones.js +177 -0
  479. package/dist/ui/fleet-office.js +434 -0
  480. package/dist/ui/fleet-org.js +18 -5
  481. package/dist/ui/fleet-route-scene.js +272 -0
  482. package/dist/ui/fleet-route-worker.js +21 -0
  483. package/dist/ui/fleet-route.js +88 -0
  484. package/dist/ui/fleet-task-card.js +436 -0
  485. package/dist/ui/fleet-task-common.js +482 -0
  486. package/dist/ui/fleet-task-work.js +107 -0
  487. package/dist/ui/fleet-tasks.js +569 -0
  488. package/dist/ui/fleet-traffic.js +81 -18
  489. package/dist/ui/fleet-usage.js +36 -2
  490. package/dist/ui/guest-refused.html +41 -0
  491. package/dist/ui/icons/icon-192.png +0 -0
  492. package/dist/ui/icons/icon-512.png +0 -0
  493. package/dist/ui/image-lightbox.js +45 -1
  494. package/dist/ui/image-zoom.js +242 -0
  495. package/dist/ui/panel-chat.js +214 -57
  496. package/dist/ui/panel-details.js +2 -0
  497. package/dist/ui/panel-fleet.js +32 -71
  498. package/dist/ui/panel-log.js +3 -3
  499. package/dist/ui/panel-needs.js +23 -6
  500. package/dist/ui/panel-settings.js +166 -33
  501. package/dist/ui/preview-panel.js +2 -1
  502. package/dist/ui/search-dialog.js +350 -0
  503. package/dist/ui/settings-appearance.js +170 -10
  504. package/dist/ui/settings-apply.js +3 -3
  505. package/dist/ui/settings-confirm.js +7 -2
  506. package/dist/ui/settings-dialogs.js +46 -21
  507. package/dist/ui/settings-discord.js +6 -2
  508. package/dist/ui/settings-guests.js +2 -1
  509. package/dist/ui/settings-learn.js +1 -0
  510. package/dist/ui/settings-model.js +51 -18
  511. package/dist/ui/settings-mods.js +8 -3
  512. package/dist/ui/settings-owner-effects.js +6 -4
  513. package/dist/ui/settings-strings.js +186 -6
  514. package/dist/ui/settings-token.js +3 -1
  515. package/dist/ui/settings-web-authority.js +13 -5
  516. package/dist/ui/settings-wizard.js +13 -7
  517. package/dist/ui/shared/agend-auth.js +2 -0
  518. package/dist/ui/shared/app-i18n.js +12 -5
  519. package/dist/ui/shared/app-needs.js +2 -2
  520. package/dist/ui/shared/app-prefs.js +15 -0
  521. package/dist/ui/shared/app-route.js +18 -0
  522. package/dist/ui/shared/app-shell.js +189 -28
  523. package/dist/ui/shared/app-store.js +3 -2
  524. package/dist/ui/shared/app-stream.js +5 -1
  525. package/dist/ui/shared/app-themes.js +116 -1
  526. package/dist/ui/shared/app.css +668 -48
  527. package/dist/ui/shared/app.js +9 -1
  528. package/dist/ui/shared/card-strings.js +4 -0
  529. package/dist/ui/shared/effect-app-runtime.js +7 -5
  530. package/dist/ui/shared/effect-chat-runtime.js +2 -7
  531. package/dist/ui/shared/effect-cursor.js +3 -2
  532. package/dist/ui/shared/effect-display-metadata.js +39 -0
  533. package/dist/ui/shared/effect-owner-source.js +8 -1
  534. package/dist/ui/shared/effect-registry.js +14 -4
  535. package/dist/ui/shared/effect-runtime-loader.js +3 -1
  536. package/dist/ui/shared/effect-stock-trust.js +5 -5
  537. package/dist/ui/shared/feature-flags.js +127 -0
  538. package/dist/ui/shared/guest-refused.js +35 -0
  539. package/dist/ui/shared/header-tools.js +2 -2
  540. package/dist/ui/shared/identity-card.js +26 -8
  541. package/dist/ui/shared/identity-color.js +17 -10
  542. package/dist/ui/shared/instance-nav.js +183 -28
  543. package/dist/ui/shared/owner-css.js +30 -1
  544. package/dist/ui/shared/panel-view.js +14 -29
  545. package/dist/ui/shared/text-undo.js +210 -0
  546. package/dist/ui/shared/tokens.css +4 -0
  547. package/dist/ui/shared/ui-dialog.js +5 -4
  548. package/dist/ui/shared/ui-icons.js +4 -0
  549. package/dist/ui/shared/ui-menu.js +20 -7
  550. package/dist/ui/shared/ui-toast.js +16 -3
  551. package/dist/ui/shared/usage-dialog.js +216 -16
  552. package/dist/ui/shared/view-strings.js +70 -4
  553. package/dist/ui/shared/web-authorize.js +50 -1
  554. package/dist/ui/signin.html +2 -0
  555. package/dist/ui/web-terminal/terminal.html +10 -10
  556. package/dist/ui/web-terminal/terminal.js +58 -12
  557. package/dist/update-check.js.map +1 -1
  558. package/dist/update-dispatch.js +1 -1
  559. package/dist/update-dispatch.js.map +1 -1
  560. package/dist/update-install.js.map +1 -1
  561. package/dist/update-launch.d.ts +10 -1
  562. package/dist/update-launch.js +39 -14
  563. package/dist/update-launch.js.map +1 -1
  564. package/dist/update-marker.js.map +1 -1
  565. package/dist/update-preflight.js +1 -1
  566. package/dist/update-preflight.js.map +1 -1
  567. package/dist/update-progress.d.ts +3 -1
  568. package/dist/update-progress.js +3 -1
  569. package/dist/update-progress.js.map +1 -1
  570. package/dist/usage/format-rich.js +6 -3
  571. package/dist/usage/format-rich.js.map +1 -1
  572. package/dist/usage/i18n-keys.d.ts +1 -1
  573. package/dist/usage/i18n-keys.js +2 -1
  574. package/dist/usage/i18n-keys.js.map +1 -1
  575. package/dist/usage/i18n.d.ts +13 -1
  576. package/dist/usage/i18n.js +29 -0
  577. package/dist/usage/i18n.js.map +1 -1
  578. package/dist/usage/providers.d.ts +89 -2
  579. package/dist/usage/providers.js +247 -41
  580. package/dist/usage/providers.js.map +1 -1
  581. package/dist/usage/statusline-usage.js.map +1 -1
  582. package/dist/usage/usage-api.d.ts +5 -1
  583. package/dist/usage/usage-api.js +185 -58
  584. package/dist/usage/usage-api.js.map +1 -1
  585. package/dist/usage-report.js.map +1 -1
  586. package/dist/usage-rollup.js +1 -1
  587. package/dist/usage-rollup.js.map +1 -1
  588. package/dist/user-systemd.d.ts +24 -0
  589. package/dist/user-systemd.js +75 -0
  590. package/dist/user-systemd.js.map +1 -0
  591. package/dist/view-api.d.ts +7 -0
  592. package/dist/view-api.js +16 -4
  593. package/dist/view-api.js.map +1 -1
  594. package/dist/wake-coordinator.js.map +1 -1
  595. package/dist/web-ambient.js.map +1 -1
  596. package/dist/web-api.d.ts +58 -11
  597. package/dist/web-api.js +1104 -71
  598. package/dist/web-api.js.map +1 -1
  599. package/dist/web-app-icon-worker.d.ts +1 -0
  600. package/dist/web-app-icon-worker.js +16 -0
  601. package/dist/web-app-icon-worker.js.map +1 -0
  602. package/dist/web-app-icon.d.ts +109 -0
  603. package/dist/web-app-icon.js +257 -0
  604. package/dist/web-app-icon.js.map +1 -0
  605. package/dist/web-appearance-policy.js.map +1 -1
  606. package/dist/web-appearance-tools.d.ts +1 -0
  607. package/dist/web-appearance-tools.js +7 -3
  608. package/dist/web-appearance-tools.js.map +1 -1
  609. package/dist/web-appearance-view.js.map +1 -1
  610. package/dist/web-appearance.d.ts +4 -10
  611. package/dist/web-appearance.js +7 -4
  612. package/dist/web-appearance.js.map +1 -1
  613. package/dist/web-auth.d.ts +8 -4
  614. package/dist/web-auth.js +33 -21
  615. package/dist/web-auth.js.map +1 -1
  616. package/dist/web-backend-login.d.ts +130 -0
  617. package/dist/web-backend-login.js +225 -0
  618. package/dist/web-backend-login.js.map +1 -0
  619. package/dist/web-card.js.map +1 -1
  620. package/dist/web-channel-echo.js.map +1 -1
  621. package/dist/web-chat-history.d.ts +118 -8
  622. package/dist/web-chat-history.js +220 -25
  623. package/dist/web-chat-history.js.map +1 -1
  624. package/dist/web-commands.d.ts +5 -1
  625. package/dist/web-commands.js +1 -1
  626. package/dist/web-commands.js.map +1 -1
  627. package/dist/web-cookie-names.d.ts +5 -0
  628. package/dist/web-cookie-names.js +12 -0
  629. package/dist/web-cookie-names.js.map +1 -0
  630. package/dist/web-effect-bundle.d.ts +2 -2
  631. package/dist/web-effect-library.js.map +1 -1
  632. package/dist/web-effect-playback.js.map +1 -1
  633. package/dist/web-effect-profiles.d.ts +16 -16
  634. package/dist/web-effect-profiles.js +3 -2
  635. package/dist/web-effect-profiles.js.map +1 -1
  636. package/dist/web-effect-service.js.map +1 -1
  637. package/dist/web-effect-tools.js.map +1 -1
  638. package/dist/web-effect-trials.js.map +1 -1
  639. package/dist/web-effect-upload.js.map +1 -1
  640. package/dist/web-effect.d.ts +6 -6
  641. package/dist/web-effect.js.map +1 -1
  642. package/dist/web-experimental-http.d.ts +2 -0
  643. package/dist/web-experimental-http.js +12 -0
  644. package/dist/web-experimental-http.js.map +1 -0
  645. package/dist/web-experimental.d.ts +11 -0
  646. package/dist/web-experimental.js +49 -0
  647. package/dist/web-experimental.js.map +1 -0
  648. package/dist/web-guest-access.d.ts +4 -2
  649. package/dist/web-guest-access.js +22 -10
  650. package/dist/web-guest-access.js.map +1 -1
  651. package/dist/web-guest-rooms.js.map +1 -1
  652. package/dist/web-host-guard.d.ts +11 -0
  653. package/dist/web-host-guard.js +11 -0
  654. package/dist/web-host-guard.js.map +1 -1
  655. package/dist/web-icons.d.ts +33 -1
  656. package/dist/web-icons.js +83 -11
  657. package/dist/web-icons.js.map +1 -1
  658. package/dist/web-identity-color.js.map +1 -1
  659. package/dist/web-image-meta.d.ts +13 -0
  660. package/dist/web-image-meta.js +62 -0
  661. package/dist/web-image-meta.js.map +1 -1
  662. package/dist/web-images.js.map +1 -1
  663. package/dist/web-libavoid.d.ts +14 -0
  664. package/dist/web-libavoid.js +46 -0
  665. package/dist/web-libavoid.js.map +1 -0
  666. package/dist/web-login.js.map +1 -1
  667. package/dist/web-mentions.js.map +1 -1
  668. package/dist/web-mod-agent.js.map +1 -1
  669. package/dist/web-mod-store.d.ts +2 -0
  670. package/dist/web-mod-store.js +10 -0
  671. package/dist/web-mod-store.js.map +1 -1
  672. package/dist/web-office-store.d.ts +104 -0
  673. package/dist/web-office-store.js +236 -0
  674. package/dist/web-office-store.js.map +1 -0
  675. package/dist/web-owner-css-store.d.ts +44 -1
  676. package/dist/web-owner-css-store.js +70 -9
  677. package/dist/web-owner-css-store.js.map +1 -1
  678. package/dist/web-owner-css.js.map +1 -1
  679. package/dist/web-png.d.ts +33 -0
  680. package/dist/web-png.js +329 -0
  681. package/dist/web-png.js.map +1 -0
  682. package/dist/web-prefs.d.ts +6 -0
  683. package/dist/web-prefs.js +29 -3
  684. package/dist/web-prefs.js.map +1 -1
  685. package/dist/web-preview.d.ts +2 -0
  686. package/dist/web-preview.js +2 -0
  687. package/dist/web-preview.js.map +1 -1
  688. package/dist/web-profile.js.map +1 -1
  689. package/dist/web-reactions.d.ts +35 -0
  690. package/dist/web-reactions.js +47 -0
  691. package/dist/web-reactions.js.map +1 -0
  692. package/dist/web-request-context.d.ts +5 -0
  693. package/dist/web-request-context.js.map +1 -1
  694. package/dist/web-returning-device.d.ts +4 -2
  695. package/dist/web-returning-device.js +13 -6
  696. package/dist/web-returning-device.js.map +1 -1
  697. package/dist/web-schedules.js.map +1 -1
  698. package/dist/web-send-dedupe.d.ts +36 -0
  699. package/dist/web-send-dedupe.js +69 -0
  700. package/dist/web-send-dedupe.js.map +1 -0
  701. package/dist/web-session-authorization.js.map +1 -1
  702. package/dist/web-session.js +1 -1
  703. package/dist/web-session.js.map +1 -1
  704. package/dist/web-settings-patch.d.ts +3 -1
  705. package/dist/web-settings-patch.js +11 -5
  706. package/dist/web-settings-patch.js.map +1 -1
  707. package/dist/web-settings-tools.d.ts +6 -0
  708. package/dist/web-settings-tools.js +52 -17
  709. package/dist/web-settings-tools.js.map +1 -1
  710. package/dist/web-terminal-http.js +15 -2
  711. package/dist/web-terminal-http.js.map +1 -1
  712. package/dist/web-terminal.js +1 -1
  713. package/dist/web-terminal.js.map +1 -1
  714. package/dist/web-theme-store.d.ts +2 -2
  715. package/dist/web-theme-store.js +6 -3
  716. package/dist/web-theme-store.js.map +1 -1
  717. package/dist/web-theme.d.ts +29 -0
  718. package/dist/web-theme.js +89 -1
  719. package/dist/web-theme.js.map +1 -1
  720. package/dist/web-upload.d.ts +18 -1
  721. package/dist/web-upload.js +45 -7
  722. package/dist/web-upload.js.map +1 -1
  723. package/dist/web-widget.js.map +1 -1
  724. package/dist/webhook-emitter.js.map +1 -1
  725. package/dist/windows-shortcuts.d.ts +78 -0
  726. package/dist/windows-shortcuts.js +203 -0
  727. package/dist/windows-shortcuts.js.map +1 -0
  728. package/dist/workflow-templates/default.md +3 -0
  729. package/dist/working-state-journal.d.ts +68 -0
  730. package/dist/working-state-journal.js +93 -0
  731. package/dist/working-state-journal.js.map +1 -0
  732. package/dist/ws-server.js.map +1 -1
  733. package/dist/yaml-shared-nodes.d.ts +25 -0
  734. package/dist/yaml-shared-nodes.js +185 -0
  735. package/dist/yaml-shared-nodes.js.map +1 -0
  736. package/launcher/agend-install.cjs +828 -0
  737. package/launcher/install-admission.cjs +4 -39
  738. package/launcher/postinstall.cjs +14 -12
  739. package/launcher/private-home.cjs +43 -0
  740. package/package.json +16 -13
  741. package/scripts/preinstall-guard.cjs +2 -5
  742. package/templates/windows-shortcuts.ps1 +181 -0
  743. package/launcher/old-updater-note.cjs +0 -48
package/dist/daemon.js CHANGED
@@ -1,4 +1,4 @@
1
- import { AGENT_WHATS_NEW, agendVersion, planWhatsNew, readWhatsNewRecord, recordWhatsNew, whatsNewNotice } from "./agent-whats-new.js";
1
+ import { agentWhatsNew, agendVersion, planWhatsNew, readWhatsNewRecord, recordWhatsNew, whatsNewNotice } from "./agent-whats-new.js";
2
2
  import { join, dirname, resolve } from "node:path";
3
3
  import { checkoutTarget, readCheckouts, releaseCandidate, reposRoot, verifyWorktree, writeCheckouts } from "./repo-worktree-guard.js";
4
4
  import { mkdirSync, writeFileSync, readFileSync, readdirSync, existsSync, unlinkSync, rmSync, appendFileSync, statSync, renameSync } from "node:fs";
@@ -10,6 +10,8 @@ import { createHash, randomBytes, randomUUID } from "node:crypto";
10
10
  import { EventEmitter } from "node:events";
11
11
  import { rotateLogIfNeededAsync } from "./logger.js";
12
12
  import { coredumpFilterLaunchPrefix } from "./coredump-filter.js";
13
+ import { observeCliOomIdentity, confirmedCliOom, consecutiveSessionOoms, describeSelfOom } from "./session-oom.js";
14
+ import { assertNoSessionOomStop, readSessionOomStop, writeSessionOomStop, SessionOomStoppedError, sessionOomStopHint } from "./session-oom-stop.js";
13
15
  import { mcpServerState } from "./mcp-liveness.js";
14
16
  import { clearPausedMarker, writePausedMarker } from "./pause-marker.js";
15
17
  import { writeBackgroundWorkEndedMarker } from "./background-work-ended.js";
@@ -19,7 +21,9 @@ import { createTranscriptSource } from "./transcript-sources.js";
19
21
  import { TranscriptTurnLedger } from "./transcript-turns.js";
20
22
  import { credentialProfileStoreHome, resolveCredentialProfile } from "./backend/credential-profile.js";
21
23
  import { resolveToolSet } from "./tool-permissions.js";
24
+ import { dashboardAccessProblem, dashboardFeaturesOff } from "./dashboard-permissions.js";
22
25
  import { getAgendHome } from "./paths.js";
26
+ import { readCanvasWorkerEnabled, scopedFleetConfigEnvironment } from "./web-experimental.js";
23
27
  import { readAgentEffect, sendEffectInParts } from "./web-effect-upload.js";
24
28
  import { readAgentFile } from "./agent-file-read.js";
25
29
  import { ASSET_UPLOAD_MAX_BYTES, MOD_FILE, MOD_MAX_PARTS, MOD_UPLOAD_MAX_BYTES, receiveFiles, receivePreview, sendAssetInParts, writeModFile, writePreview } from "./web-asset-parts.js";
@@ -53,7 +57,9 @@ import { measureSyncWork, noteSyncWork } from "./sync-work-attribution.js";
53
57
  import { TranscriptDeltaReader } from "./transcript-delta-reader.js";
54
58
  import { buildResumeContinuation, clearInFlightTurnMarker, consumeInFlightTurnMarker, decideTurnResume, normalizeResumeBackend, readResumeFingerprint, RESUME_BLOCKING_DELIVERY_STATES, writeInFlightTurnMarker, } from "./turn-resume.js";
55
59
  import { codexLiveRowMatches } from "./backend/codex.js";
60
+ import { codexPackageProblemMessage } from "./backend/codex-package.js";
56
61
  import { claudeConfigDirFor, claudeProfileConfigDir, claudeSwitchDialog, claudeSwitchTargetMatches } from "./backend/claude-code.js";
62
+ import { MuseBackend } from "./backend/muse.js";
57
63
  import { InteractionObservation, INTERACTION_CONFIRM_MS, sameInteractionOwner } from "./interaction-observation.js";
58
64
  import { unrecognisedPromptShape } from "./unrecognised-prompt.js";
59
65
  /** #2140: deliveries that may carry an unwritten what's-new notice before it is recorded anyway. */
@@ -1157,6 +1163,37 @@ export class Daemon extends EventEmitter {
1157
1163
  // injected by FleetManager below.
1158
1164
  healthCheckTimer = null;
1159
1165
  crashCount = 0;
1166
+ cliOomObservation = null;
1167
+ consecutiveOoms = null;
1168
+ collateralOomRecovery = false;
1169
+ unclearOomStopped = false;
1170
+ sessionOomStopUnpersisted = false;
1171
+ assertSessionOomAdmission() {
1172
+ if (this.sessionOomStopUnpersisted)
1173
+ throw new SessionOomStoppedError(false, { blocked: true, record: null, revision: null, confirmedOom: true });
1174
+ assertNoSessionOomStop(this.instanceDir);
1175
+ }
1176
+ isSessionOomStopped() {
1177
+ return this.sessionOomStopUnpersisted || readSessionOomStop(this.instanceDir).blocked;
1178
+ }
1179
+ isUnclearOomStopped() {
1180
+ return this.unclearOomStopped && this.healthCheckPaused && this.processStatus === "crashed";
1181
+ }
1182
+ stopUnclearOom(exitCode) {
1183
+ this.unclearOomStopped = true;
1184
+ this.healthCheckPaused = true;
1185
+ this.emitSupervisionEnded(t("inst.oom_unclear_stopped"), t("inst.session_oom_remedy"), exitCode, "oom_unclear");
1186
+ }
1187
+ oomSessionKey() {
1188
+ // Claude's per-instance statusline owns its ID. Other current getters
1189
+ // either return null or select by cwd (Muse/Grok): those are not proof
1190
+ // of this pane's session and must not trigger this destructive decision.
1191
+ if ((this.config.backend ?? "claude-code") !== "claude-code")
1192
+ return null;
1193
+ const session = this.backend?.getSessionId?.();
1194
+ return typeof session === "string" && session.trim() && session.length <= 512
1195
+ ? JSON.stringify([this.config.backend ?? "claude-code", session.trim()]) : null;
1196
+ }
1160
1197
  lastCrashAt = 0;
1161
1198
  lastSpawnAt = 0;
1162
1199
  crashTimestamps = [];
@@ -1565,6 +1602,7 @@ export class Daemon extends EventEmitter {
1565
1602
  windowQueryFailureTicks = 0;
1566
1603
  mcpRestartPending = false;
1567
1604
  mcpRestartStaleTimer = null;
1605
+ mcpRestartCooldownTimer = null;
1568
1606
  /** In-flight replacement grace; also the guard against three triggers stacking. */
1569
1607
  mcpRestartGraceTimer = null;
1570
1608
  /**
@@ -1601,6 +1639,20 @@ export class Daemon extends EventEmitter {
1601
1639
  get isErrorState() {
1602
1640
  return this.errorWaitingForRecovery || (this.healthCheckPaused && !this.isPaused) || this.stormWindow?.isActive() === true;
1603
1641
  }
1642
+ /**
1643
+ * #2327: which of isErrorState's conditions holds, so a warning names the one actually observed. A CLI no longer
1644
+ * supervised outranks a tmux recovery, which outranks a detected error pattern — one match is enough for that last
1645
+ * one, so it proves neither a pause nor a repetition.
1646
+ */
1647
+ get errorStateCause() {
1648
+ if (this.healthCheckPaused && !this.isPaused)
1649
+ return "unsupervised";
1650
+ if (this.stormWindow?.isActive() === true)
1651
+ return "tmux_recovering";
1652
+ if (this.errorWaitingForRecovery)
1653
+ return "error_detected";
1654
+ return null;
1655
+ }
1604
1656
  get isPaused() { return this.pauseWakeState !== "active"; }
1605
1657
  /** Current CLI process state for status surfaces and recovery commands. */
1606
1658
  getProcessStatus() {
@@ -1779,6 +1831,12 @@ export class Daemon extends EventEmitter {
1779
1831
  model: config.model ?? "default",
1780
1832
  };
1781
1833
  this.logger = rootLogger.child({ instance: name }, { level: config.log_level });
1834
+ if (backend instanceof MuseBackend) {
1835
+ const log = this.logger;
1836
+ backend.setPlaceholderDebug((rec) => {
1837
+ log.debug(rec, "muse empty-composer placeholder classified as empty");
1838
+ });
1839
+ }
1782
1840
  this.tmuxSessionName = getTmuxSession();
1783
1841
  this.messageBus = new MessageBus();
1784
1842
  this.messageBus.setLogger(this.logger);
@@ -1791,7 +1849,10 @@ export class Daemon extends EventEmitter {
1791
1849
  // finished turn can never resume after a later restart. reset() is
1792
1850
  // deliberately NOT hooked: stop/spawn abandon the in-memory turn while
1793
1851
  // the on-disk interruption must survive for the resume gate.
1794
- this.turnReplyGuard.onComplete = () => clearInFlightTurnMarker(this.instanceDir);
1852
+ this.turnReplyGuard.onComplete = () => {
1853
+ clearInFlightTurnMarker(this.instanceDir);
1854
+ this.publishWorkingState("turn_closed");
1855
+ };
1795
1856
  }
1796
1857
  /** FleetManager and this daemon share a process, so durable begin/ACK is a direct synchronous store call. */
1797
1858
  setDeliveryOutboxPort(port) {
@@ -2086,6 +2147,11 @@ export class Daemon extends EventEmitter {
2086
2147
  * daemon's own state only; it never inspects the pane itself.
2087
2148
  */
2088
2149
  notAcceptingReason() {
2150
+ if (this.sessionOomStopUnpersisted)
2151
+ return t("inst.session_oom_hold");
2152
+ const oomStop = readSessionOomStop(this.instanceDir);
2153
+ if (oomStop.blocked)
2154
+ return sessionOomStopHint(oomStop);
2089
2155
  if (this.pauseWakeState !== "active")
2090
2156
  return t("delivery.hold_state", this.pauseWakeState);
2091
2157
  if (this.inputBlockedDialogKey !== null)
@@ -2136,6 +2202,7 @@ export class Daemon extends EventEmitter {
2136
2202
  }
2137
2203
  }
2138
2204
  async startAdmitted() {
2205
+ this.assertSessionOomAdmission();
2139
2206
  ensureInstanceDir(this.instanceDir);
2140
2207
  writeFileSync(join(this.instanceDir, "daemon.pid"), String(process.pid));
2141
2208
  this.logger.info(`Starting ${this.name}`);
@@ -2356,6 +2423,7 @@ export class Daemon extends EventEmitter {
2356
2423
  ackIfPending();
2357
2424
  this.recordRecentEvent({ type: "assistant_text", preview: text.slice(0, 100) });
2358
2425
  });
2426
+ this.transcriptMonitor.on("codex_package_missing_host", (root) => this.noteCodexPackageMissingHost(root));
2359
2427
  this.transcriptMonitor.startPolling();
2360
2428
  // HangDetector is the notification bridge to the fleet manager; pane state
2361
2429
  // transitions below are the sole source of hang events (see hang-detector.ts).
@@ -2583,6 +2651,24 @@ export class Daemon extends EventEmitter {
2583
2651
  this.fireMcpRestartRequest("already_idle");
2584
2652
  }
2585
2653
  }
2654
+ /** Keep a cooldown-suppressed revival alive without bypassing the idle gate. */
2655
+ deferMcpRestart(delayMs) {
2656
+ this.clearMcpRestartRequest();
2657
+ if (this.config.mcp_auto_restart === false || this.authFailureUnresolved
2658
+ || this.mcpRestartCancelledByRecovery("cooldown"))
2659
+ return;
2660
+ this.mcpRestartPending = true;
2661
+ this.mcpRestartCooldownTimer = setTimeout(() => {
2662
+ this.mcpRestartCooldownTimer = null;
2663
+ if (this.mcpRestartCancelledByRecovery("cooldown_expired"))
2664
+ return;
2665
+ this.clearMcpRestartRequest();
2666
+ if (this.config.mcp_auto_restart === false || this.authFailureUnresolved || this.mcpServerAlive().alive)
2667
+ return;
2668
+ this.armMcpRestartWhenIdle();
2669
+ }, Math.max(0, delayMs));
2670
+ this.mcpRestartCooldownTimer.unref?.();
2671
+ }
2586
2672
  /**
2587
2673
  * Single chokepoint for all three revival triggers (already_idle, idle_edge,
2588
2674
  * stale_timeout) — and therefore the one place worth re-checking reality.
@@ -2600,6 +2686,8 @@ export class Daemon extends EventEmitter {
2600
2686
  fireMcpRestartRequest(trigger) {
2601
2687
  if (this.mcpRestartCancelledByRecovery(trigger))
2602
2688
  return;
2689
+ if (this.mcpRestartCooldownTimer)
2690
+ return;
2603
2691
  // Authoritative re-read — the CLI may have replaced the server since the
2604
2692
  // health tick that armed this.
2605
2693
  if (this.mcpServerAlive().alive) {
@@ -2794,6 +2882,10 @@ export class Daemon extends EventEmitter {
2794
2882
  }
2795
2883
  clearMcpRestartRequest() {
2796
2884
  this.mcpRestartPending = false;
2885
+ if (this.mcpRestartCooldownTimer) {
2886
+ clearTimeout(this.mcpRestartCooldownTimer);
2887
+ this.mcpRestartCooldownTimer = null;
2888
+ }
2797
2889
  if (this.mcpRestartStaleTimer) {
2798
2890
  clearTimeout(this.mcpRestartStaleTimer);
2799
2891
  this.mcpRestartStaleTimer = null;
@@ -2862,6 +2954,19 @@ export class Daemon extends EventEmitter {
2862
2954
  if (discardStaleHealth())
2863
2955
  return;
2864
2956
  if (paneStatus?.alive) {
2957
+ // Capture only a known primary while it lives. No extra tmux call: PID comes
2958
+ // from this same status read. Fast/unobserved deaths remain ordinary crashes.
2959
+ const key = this.oomSessionKey();
2960
+ if (this.cliOomObservation?.key !== key)
2961
+ this.cliOomObservation = null;
2962
+ if (paneStatus.pid && key && !this.cliOomObservation) {
2963
+ const identity = await observeCliOomIdentity(paneStatus.pid, cliLabel);
2964
+ if (discardStaleHealth())
2965
+ return;
2966
+ if (identity && this.oomSessionKey() === key) {
2967
+ this.cliOomObservation = { identity, key, owner: healthOwner, tmux: healthTmux };
2968
+ }
2969
+ }
2865
2970
  this.windowQueryFailureTicks = 0;
2866
2971
  this.stormWindow?.noteWindowAlive(this.name);
2867
2972
  // Instance output.log is fed by tmux pipe-pane and was previously never
@@ -3016,6 +3121,47 @@ export class Daemon extends EventEmitter {
3016
3121
  return;
3017
3122
  this.setProcessStatus("crashed");
3018
3123
  this.logPaneDeath(cliLabel, exitCode, lastOutput);
3124
+ const observed = this.cliOomObservation;
3125
+ const key = this.oomSessionKey();
3126
+ const oom = paneStatus && exitCode === 137 && observed && observed.key === key
3127
+ && observed.tmux === healthTmux && sameInteractionOwner(observed.owner, healthOwner)
3128
+ ? await confirmedCliOom(observed.identity, Date.now()) : null;
3129
+ if (discardStaleHealth())
3130
+ return;
3131
+ this.cliOomObservation = null;
3132
+ const decisionKey = this.oomSessionKey();
3133
+ this.consecutiveOoms = consecutiveSessionOoms(this.consecutiveOoms, decisionKey, decisionKey === key && oom?.attribution === "self");
3134
+ const collateralOom = oom?.attribution === "collateral";
3135
+ if (collateralOom)
3136
+ this.logger.warn({ attribution: oom.attribution, pid: oom.pid }, "OOM victim without self-caused proof — preserving session; waiting for memory recovery");
3137
+ if (this.consecutiveOoms && this.consecutiveOoms.count >= 2) {
3138
+ // Stop supervision, not an idle pause: queued work must not auto-wake
3139
+ // the same session. An explicit restart is the person's retry choice.
3140
+ // No await after the final captured health owner/session check. The
3141
+ // stop is authoritative before observers can see supervision end.
3142
+ try {
3143
+ writeSessionOomStop(this.instanceDir, { owner: healthOwner, sessionKey: decisionKey,
3144
+ configuration: this.getWorkingStateObservation().configuration, stoppedAt: Date.now(),
3145
+ count: this.consecutiveOoms.count, victim: { pid: oom.pid, at: oom.at }, evidence: oom.evidence });
3146
+ }
3147
+ catch {
3148
+ // Storage failure cannot revive this CLI in the current process.
3149
+ // Say explicitly that a fleet restart cannot yet rely on durability.
3150
+ this.sessionOomStopUnpersisted = true;
3151
+ this.logger.error("Could not persist session OOM stop; fix storage before restarting the fleet");
3152
+ }
3153
+ this.healthCheckPaused = true;
3154
+ try {
3155
+ this.appendCrashHistory({ exitCode, lastOutput, crashType, reason: "session_oom", oomEvidence: oom.evidence });
3156
+ }
3157
+ catch (error) {
3158
+ this.logger.warn({ error }, "Could not append session OOM history");
3159
+ }
3160
+ this.logger.error({ pid: oom.pid, count: this.consecutiveOoms.count }, "Repeated confirmed CLI OOM — automatic resume stopped; session preserved");
3161
+ this.emitSupervisionEnded(`${t("inst.session_oom_stopped")} (${describeSelfOom(oom.evidence)})`, t("inst.session_oom_remedy")
3162
+ + (this.sessionOomStopUnpersisted ? ` ${t("inst.session_oom_save_failed")}` : ""), exitCode, "session_oom");
3163
+ return;
3164
+ }
3019
3165
  // Kill the dead window (remain-on-exit keeps it around) before respawn
3020
3166
  if (paneStatus) {
3021
3167
  await healthTmux.killWindow();
@@ -3023,7 +3169,7 @@ export class Daemon extends EventEmitter {
3023
3169
  return;
3024
3170
  }
3025
3171
  // Detect claude-code background session conflict — recover without counting as crash
3026
- if (lastOutput && (lastOutput.includes("background agent") || lastOutput.includes("Session is currently running"))) {
3172
+ if (!oom && lastOutput && (lastOutput.includes("background agent") || lastOutput.includes("Session is currently running"))) {
3027
3173
  if (!this.backgroundSessionRecoveryAttempted) {
3028
3174
  this.backgroundSessionRecoveryAttempted = true;
3029
3175
  this.logger.warn("Detected lingering background agent session — starting fresh (no resume)");
@@ -3063,7 +3209,7 @@ export class Daemon extends EventEmitter {
3063
3209
  // --continue again and crash in the same way → loop. Clear the session id
3064
3210
  // and skip resume so the next spawn starts fresh. (skipResume also stops
3065
3211
  // saveSessionId below from resurrecting the id from statusline.json.)
3066
- if (this.paneSaysNoConversation(lastOutput)) {
3212
+ if (!oom && this.paneSaysNoConversation(lastOutput)) {
3067
3213
  this.setSessionAside("cli_reported_no_conversation");
3068
3214
  this.skipResume = true;
3069
3215
  }
@@ -3084,12 +3230,21 @@ export class Daemon extends EventEmitter {
3084
3230
  return;
3085
3231
  }
3086
3232
  // Detect rapid crash: sliding window — 3+ crashes in 5 minutes
3087
- this.crashTimestamps.push(Date.now());
3233
+ if (!collateralOom)
3234
+ this.crashTimestamps.push(Date.now());
3235
+ else
3236
+ this.crashTimestamps = [];
3088
3237
  const crashWindowMs = 5 * 60_000;
3089
3238
  this.crashTimestamps = this.crashTimestamps.filter(t => t > Date.now() - crashWindowMs);
3090
3239
  if (this.crashTimestamps.length >= 3) {
3091
3240
  this.healthCheckPaused = true;
3092
3241
  this.logger.error({ crashesInWindow: this.crashTimestamps.length, lastExitCode: exitCode ?? null, lastOutput: paneTail(lastOutput) ?? null }, "3+ crashes in 5 minutes — pausing respawn");
3242
+ // Unknown cause is bounded like an ordinary crash, never a
3243
+ // self-OOM verdict. Preserve the session for the person's choice.
3244
+ if (oom?.attribution === "unknown") {
3245
+ this.stopUnclearOom(exitCode);
3246
+ return;
3247
+ }
3093
3248
  // P1: Persist crash state so next process restart skips resume
3094
3249
  try {
3095
3250
  writeFileSync(join(this.instanceDir, "crash-state.json"), JSON.stringify({
@@ -3106,18 +3261,23 @@ export class Daemon extends EventEmitter {
3106
3261
  if (reset_after > 0 && Date.now() - this.lastCrashAt > reset_after) {
3107
3262
  this.crashCount = 0;
3108
3263
  }
3109
- this.crashCount++;
3264
+ if (!collateralOom)
3265
+ this.crashCount++;
3110
3266
  this.lastCrashAt = Date.now();
3111
- if (this.crashCount > max_retries) {
3267
+ if (!collateralOom && this.crashCount > max_retries) {
3112
3268
  this.logger.error({ crashCount: this.crashCount, maxRetries: max_retries, lastExitCode: exitCode ?? null, lastOutput: paneTail(lastOutput) ?? null }, "Max crash retries exceeded — not respawning");
3113
3269
  this.healthCheckPaused = true;
3270
+ if (oom?.attribution === "unknown") {
3271
+ this.stopUnclearOom(exitCode);
3272
+ return;
3273
+ }
3114
3274
  this.emitSupervisionEnded(`it crashed ${this.crashCount} times, exceeding restart_policy.max_retries (${max_retries})`, "Check the logs for the cause, then restart it.");
3115
3275
  return; // don't schedule next — given up
3116
3276
  }
3117
3277
  // Calculate backoff delay
3118
3278
  const delay = backoff === "exponential"
3119
- ? Math.min(1000 * Math.pow(2, this.crashCount - 1), 60_000)
3120
- : 1000 * this.crashCount;
3279
+ ? Math.min(1000 * Math.pow(2, Math.max(1, this.crashCount) - 1), 60_000)
3280
+ : 1000 * Math.max(1, this.crashCount);
3121
3281
  this.logger.warn({ crashCount: this.crashCount, delay }, `${cliLabel} window died — respawning after backoff`);
3122
3282
  await new Promise(r => setTimeout(r, delay));
3123
3283
  try {
@@ -3126,14 +3286,14 @@ export class Daemon extends EventEmitter {
3126
3286
  // (bounded) session lookup. They only stop LATER ticks; this continuation is already
3127
3287
  // past that check and must not clear the process/window or respawn an instance that
3128
3288
  // was just stopped or paused (#1160 review).
3129
- if (this.runtimeMonitorsFrozen || this.healthCheckPaused)
3289
+ if (discardStaleHealth())
3130
3290
  return;
3131
3291
  // Reset is synchronous for file sources, but Kiro's baseline is
3132
3292
  // read off-thread. Finish it before starting the replacement CLI,
3133
3293
  // otherwise its first work could become part of that baseline.
3134
3294
  if (!await this.resetTranscriptBeforeAdmission())
3135
3295
  return;
3136
- if (this.runtimeMonitorsFrozen || this.healthCheckPaused)
3296
+ if (discardStaleHealth())
3137
3297
  return;
3138
3298
  // Kill orphan MCP server from the crashed CLI session.
3139
3299
  // MCP server writes its PID to channel.mcp.pid on startup.
@@ -3157,10 +3317,19 @@ export class Daemon extends EventEmitter {
3157
3317
  }
3158
3318
  }
3159
3319
  catch { /* tmux server may be dead — ensureSession in trySpawn will recover */ }
3320
+ if (discardStaleHealth())
3321
+ return;
3160
3322
  // Write snapshot before spawn — consumed only if resume fails
3161
3323
  this.writeRotationSnapshot("crash");
3162
3324
  // Try --resume first; spawnClaudeWindow falls back to fresh session if resume fails
3163
- const resumed = await this.spawnClaudeWindow();
3325
+ this.collateralOomRecovery = collateralOom;
3326
+ let resumed;
3327
+ try {
3328
+ resumed = await this.spawnClaudeWindow();
3329
+ }
3330
+ finally {
3331
+ this.collateralOomRecovery = false;
3332
+ }
3164
3333
  if (!resumed) {
3165
3334
  // Resume failed → fresh session → inject snapshot for context
3166
3335
  await this.injectSnapshotMessage();
@@ -3254,6 +3423,7 @@ export class Daemon extends EventEmitter {
3254
3423
  * four, carrying a human-readable cause and the operator's next step.
3255
3424
  */
3256
3425
  emitSupervisionEnded(reason, remedy, exitCode, cause) {
3426
+ this.publishWorkingState("supervision_ended");
3257
3427
  this.emit("supervision_ended", {
3258
3428
  name: this.name,
3259
3429
  reason,
@@ -3266,6 +3436,7 @@ export class Daemon extends EventEmitter {
3266
3436
  if (this.processStatus === status)
3267
3437
  return;
3268
3438
  this.processStatus = status;
3439
+ this.publishWorkingState("process");
3269
3440
  if (status === "running") {
3270
3441
  // A successful crash respawn is a new pane generation. It is ready by
3271
3442
  // this point, so an error gate inherited from the dead pane must not keep
@@ -3651,6 +3822,17 @@ export class Daemon extends EventEmitter {
3651
3822
  const key = Daemon.errorPatternKey(ep);
3652
3823
  const count = countMatches(ep.pattern);
3653
3824
  const seen = this.lastErrorCount.get(key) ?? 0;
3825
+ // #2282: a resumed exhausted429 row is history, not even a new
3826
+ // notification/failover incident. Wait for the first live composer to
3827
+ // establish its baseline; later appended occurrences remain eligible.
3828
+ if (ep.type === "rate_limit" && ep.action === "nudge_continue") {
3829
+ const baseline = this.nudgeBaselines?.get(key);
3830
+ if (baseline === undefined || count <= baseline) {
3831
+ if (baseline !== undefined)
3832
+ this.lastErrorCount.set(key, count);
3833
+ continue;
3834
+ }
3835
+ }
3654
3836
  // The generic Codex usage-limit line remains in scrollback after the
3655
3837
  // user selects Luna Reserve. What proves that is NOT a live composer —
3656
3838
  // codex paints one after a plain hit too, and the old live-composer guard
@@ -3803,6 +3985,7 @@ export class Daemon extends EventEmitter {
3803
3985
  this.deliveryEpoch = fleetEpoch === undefined
3804
3986
  ? this.deliveryEpoch + 1
3805
3987
  : Math.max(this.deliveryEpoch, fleetEpoch);
3988
+ this.publishWorkingState("cancelled");
3806
3989
  this.logger.info({ deliveryEpoch: this.deliveryEpoch }, "Pending delivery queue cleared by user cancel");
3807
3990
  }
3808
3991
  /** Board 907c7899: the layout a web /compact's end can be read from on this pane (kiro classic or TUI V2), or null. */
@@ -4371,6 +4554,7 @@ export class Daemon extends EventEmitter {
4371
4554
  return true;
4372
4555
  }
4373
4556
  async stop() {
4557
+ this.publishWorkingState("before_stop");
4374
4558
  this.logger.info("Stopping daemon instance");
4375
4559
  this.clearQuitRelaunchWatch();
4376
4560
  this.fenceDeliveryWritesForStop();
@@ -4519,6 +4703,31 @@ export class Daemon extends EventEmitter {
4519
4703
  getInteractionSnapshot() {
4520
4704
  return this.interactionObservation.snapshot(this.interactionOwner());
4521
4705
  }
4706
+ workingTurnDelivery = null;
4707
+ workingBackgroundObservation = null;
4708
+ /** Pure metadata read: never discovers sessions, captures a pane or reads backend files. */
4709
+ getWorkingStateObservation() {
4710
+ const turn = this.turnReplyGuard.snapshot();
4711
+ const backend = this.config.backend ?? this.backend?.binaryName ?? "unknown";
4712
+ const profile = resolveCredentialProfile(this.config.backend_options?.[backend]);
4713
+ return {
4714
+ owner: this.interactionOwner(), backend,
4715
+ configuration: createHash("sha256").update(JSON.stringify([backend, this.config.working_directory, profile])).digest("hex"),
4716
+ executionState: this.processStatus === "running" ? this.isPaused ? "paused" : this.instanceState : null,
4717
+ processStatus: this.getProcessStatus(), sessionId: null, backendTurnId: null,
4718
+ backgroundWork: this.workingBackgroundObservation,
4719
+ turn: turn ? { generation: turn.generation,
4720
+ deliveryId: this.workingTurnDelivery?.generation === turn.generation ? this.workingTurnDelivery.deliveryId : null,
4721
+ correlationId: turn.target.correlationId ?? null, messageId: turn.target.messageId ?? null,
4722
+ cancelled: turn.cancelledByUser, completionDelivered: turn.completionDelivered } : null,
4723
+ };
4724
+ }
4725
+ publishWorkingState(reason) {
4726
+ try {
4727
+ this.emit("working_state", { reason, observation: this.getWorkingStateObservation() });
4728
+ }
4729
+ catch { /* Journal observers cannot change daemon behavior. */ }
4730
+ }
4522
4731
  /** Key of the last interaction snapshot actually emitted + broadcast (#1219). */
4523
4732
  lastPublishedInteractionKey = null;
4524
4733
  publishInteraction() {
@@ -4677,7 +4886,9 @@ export class Daemon extends EventEmitter {
4677
4886
  static AUTH_RECONFIRM_ROWS = 15;
4678
4887
  /** Why the deferred pause was requested; what its marker records (Phase 2a). */
4679
4888
  pendingPauseReason = "auth";
4680
- async pause(reason = "operator") {
4889
+ async pause(reason = "operator", admission) {
4890
+ if (admission && !admission.current())
4891
+ return;
4681
4892
  if (this.pauseWakeState === "paused")
4682
4893
  return;
4683
4894
  if (this.pauseWakeState === "pausing" || this.pauseWakeState === "waking") {
@@ -4685,6 +4896,8 @@ export class Daemon extends EventEmitter {
4685
4896
  if (this.getPauseWakeState() !== "active")
4686
4897
  return;
4687
4898
  }
4899
+ if (admission && !admission.current())
4900
+ return;
4688
4901
  // `pauseAllowStuck` is consumed here, not merely read: it authorises exactly
4689
4902
  // one pause from a stuck pane (the auth-deferred one) and must never leak
4690
4903
  // into an ordinary idle-timeout pause of a busy instance.
@@ -4704,7 +4917,7 @@ export class Daemon extends EventEmitter {
4704
4917
  // this cycle while the backend reports background work; the next idle
4705
4918
  // tick retries once the task ends. A pane-capture failure proceeds (the
4706
4919
  // quit-menu handler below stays the backstop), never wedges unpaused.
4707
- if (this.backend?.backgroundWorkRunning && this.tmux) {
4920
+ if ((admission || this.backend?.backgroundWorkRunning) && this.tmux) {
4708
4921
  const tmux = this.tmux;
4709
4922
  const windowId = tmux.getWindowId?.();
4710
4923
  const generation = this.spawnGeneration;
@@ -4716,7 +4929,8 @@ export class Daemon extends EventEmitter {
4716
4929
  // #1730 P2-2: the await yields — the pane read is only valid for the
4717
4930
  // world it was taken from. Revalidate owner, phase, state and queue
4718
4931
  // before acting on it; anything that moved defers this cycle.
4719
- if (this.tmux !== tmux
4932
+ if ((admission && (!admission.current() || pane === null || !admission.accepts(pane)))
4933
+ || this.tmux !== tmux
4720
4934
  || this.tmux?.getWindowId?.() !== windowId
4721
4935
  || this.spawnGeneration !== generation
4722
4936
  || this.getPauseWakeState() !== "active"
@@ -4766,6 +4980,7 @@ export class Daemon extends EventEmitter {
4766
4980
  this.pauseWakeState = "paused";
4767
4981
  this.autoPauseController.markPaused();
4768
4982
  writePausedMarker(this.instanceDir, this.lastPausedAt ?? Date.now(), reason);
4983
+ this.publishWorkingState("execution");
4769
4984
  this.logger.info({ pausedAt: this.lastPausedAt }, "Instance auto-paused");
4770
4985
  this.ipcServer?.broadcast({
4771
4986
  type: "instance_state", instanceName: this.name, state: "paused", pausedAt: this.lastPausedAt,
@@ -4791,6 +5006,7 @@ export class Daemon extends EventEmitter {
4791
5006
  }
4792
5007
  /** Respawn the CLI in the preserved window and block until its prompt is ready. */
4793
5008
  async wake(timeoutMs = 30_000) {
5009
+ this.assertSessionOomAdmission();
4794
5010
  // An explicit wake (e.g. after the user re-logs in) cancels a deferred
4795
5011
  // auth pause — otherwise the instance would pause again the moment it idles.
4796
5012
  this.pausePending = false;
@@ -4858,6 +5074,7 @@ export class Daemon extends EventEmitter {
4858
5074
  this.clearErrorRecoveryGate();
4859
5075
  clearPausedMarker(this.instanceDir);
4860
5076
  this.resumeRuntimeMonitors();
5077
+ this.publishWorkingState("execution");
4861
5078
  this.logger.info("Instance auto-woke");
4862
5079
  this.ipcServer?.broadcast({
4863
5080
  type: "instance_state", instanceName: this.name, state: this.instanceState, pausedAt: null,
@@ -4913,7 +5130,9 @@ export class Daemon extends EventEmitter {
4913
5130
  this.logger.info({ notLogged: this.stateEdgesNotLogged }, "Instance execution state edges were not logged while it was flapping");
4914
5131
  this.stateEdgesNotLogged = 0;
4915
5132
  }
4916
- this.logger.info(fields, "Instance execution state changed");
5133
+ // #2482: idle ↔ working is every turn (40% of daemon.log): debug. Anything involving stuck stays INFO.
5134
+ const routine = previous !== "stuck" && snapshot.state !== "stuck";
5135
+ this.logger[routine ? "debug" : "info"](fields, "Instance execution state changed");
4917
5136
  return;
4918
5137
  }
4919
5138
  if (this.stateEdgesNotLogged === 0) {
@@ -4957,6 +5176,17 @@ export class Daemon extends EventEmitter {
4957
5176
  }
4958
5177
  this.instanceState = snapshot.state;
4959
5178
  this.acceptedInstanceSnapshot = { ...snapshot };
5179
+ // Reuse an already-captured pane and the backend's pure parser, never capture for the journal.
5180
+ if (typeof pane === "string" && this.backend?.backgroundWorkRunning) {
5181
+ try {
5182
+ const background = this.backend.backgroundWorkRunning(pane);
5183
+ if (background !== this.workingBackgroundObservation) {
5184
+ this.workingBackgroundObservation = background;
5185
+ this.publishWorkingState("background");
5186
+ }
5187
+ }
5188
+ catch { /* Observational parsing cannot change accepted execution state. */ }
5189
+ }
4960
5190
  // OpenCode creates its session lazily on the first submitted message.
4961
5191
  // Waiting until stop/pause to persist that id loses resume state when the
4962
5192
  // fleet is SIGKILLed or the host reboots.
@@ -5001,6 +5231,7 @@ export class Daemon extends EventEmitter {
5001
5231
  }
5002
5232
  if (snapshot.state !== previous) {
5003
5233
  this.logStateEdge(previous, snapshot);
5234
+ this.publishWorkingState("execution");
5004
5235
  this.emit("instance_state", { name: this.name, ...snapshot });
5005
5236
  this.ipcServer?.broadcast({ type: "instance_state", instanceName: this.name, ...snapshot });
5006
5237
  if (snapshot.state === "stuck" && pane && this.instanceStateReadyPattern) {
@@ -5083,6 +5314,8 @@ export class Daemon extends EventEmitter {
5083
5314
  deliveredText,
5084
5315
  deliveredAt,
5085
5316
  }, this.pendingWork.lastInboundTimestamp());
5317
+ this.workingTurnDelivery = { generation: this.turnReplyGuard.snapshot().generation, deliveryId: meta.delivery_id || null };
5318
+ this.publishWorkingState("turn_started");
5086
5319
  // #1209: persist the in-flight turn with a seam checkpoint. A marker
5087
5320
  // that survives a restart is an interrupted turn by construction —
5088
5321
  // completion (via the guard's onComplete) and cancel paths delete it.
@@ -6353,6 +6586,15 @@ export class Daemon extends EventEmitter {
6353
6586
  this.config.cross_instance_visibility = update.cross_instance_visibility;
6354
6587
  else if (update.cross_instance_visibility === null)
6355
6588
  delete this.config.cross_instance_visibility;
6589
+ if (Number.isInteger(update.board_cards_at_once) && update.board_cards_at_once >= 1)
6590
+ this.config.board_cards_at_once = update.board_cards_at_once;
6591
+ else if (update.board_cards_at_once === null)
6592
+ delete this.config.board_cards_at_once;
6593
+ // The fleet reads it at each tool call; kept current here for the next spawn's tool menu.
6594
+ if (update.dashboard && typeof update.dashboard === "object" && !dashboardAccessProblem(update.dashboard))
6595
+ this.config.dashboard = update.dashboard;
6596
+ else if (update.dashboard === null)
6597
+ delete this.config.dashboard;
6356
6598
  if (["trace", "debug", "info", "warn", "error"].includes(String(update.log_level))) {
6357
6599
  const level = update.log_level;
6358
6600
  if (this.config.log_level !== level) {
@@ -7612,6 +7854,46 @@ export class Daemon extends EventEmitter {
7612
7854
  this.logger.info({ delayMs }, "Model-capacity nudge armed");
7613
7855
  return true;
7614
7856
  }
7857
+ /**
7858
+ * Consume an exhausted nudge only if its failed turn still owns an empty,
7859
+ * idle pane. Unlike arming, this verifies all input/work gates before pause.
7860
+ * The returned owner survives pause's own monitor freeze (which advances the
7861
+ * launch fence), but never another spawn/launch, cancel, delivery or episode.
7862
+ */
7863
+ async prepareCapacityNudgePause() {
7864
+ const nudge = this.capacityNudge, episode = this.lastErrorEpisode;
7865
+ const tmux = this.tmux, attempt = this.launchAttempt, boot = this.bootId;
7866
+ this.cancelCapacityNudge("retries exhausted");
7867
+ if (!nudge || !episode || !tmux)
7868
+ return null;
7869
+ const window = tmux.getWindowId?.();
7870
+ const current = () => this.tmux === tmux && tmux.getWindowId?.() === window
7871
+ && boot === this.bootId && nudge.spawn === this.spawnGeneration && attempt === this.launchAttempt
7872
+ && episode === this.lastErrorEpisode && this.isDeliveryEpochCurrent(nudge.epoch)
7873
+ && !this.startupAborted && !this.spawning && !this.isPaused
7874
+ && this.getProcessStatus() === "running" && this.instanceState === "idle" && this.pasteQueueDepth === 0;
7875
+ const accepts = (pane) => pane === nudge.pane && this.paneAuthoritativelyIdle(pane)
7876
+ && this.backend?.isPeriodicRedrawIdlePane?.(pane) === true
7877
+ && codexLiveRowMatches(pane, nudge.pattern) && !this.backend?.backgroundWorkRunning?.(pane);
7878
+ const revalidate = () => this.paneWriteLock.run(async () => {
7879
+ if (!current())
7880
+ return false;
7881
+ try {
7882
+ const pane = await tmux.capturePane();
7883
+ return current() && accepts(pane);
7884
+ }
7885
+ catch {
7886
+ return false;
7887
+ }
7888
+ });
7889
+ // Initial admission must also respect the captured monitor fence. Later
7890
+ // revalidation deliberately tolerates only pause's own fence advancement.
7891
+ if (nudge.fence !== this.launchFenceEpoch || this.healthCheckPaused || this.runtimeMonitorsFrozen)
7892
+ return null;
7893
+ if (!await revalidate() || nudge.fence !== this.launchFenceEpoch || this.healthCheckPaused || this.runtimeMonitorsFrozen)
7894
+ return null;
7895
+ return { current, accepts, revalidate };
7896
+ }
7615
7897
  /** Drop an armed nudge (the user stopped / paused the instance, or the lifecycle gave up on it). */
7616
7898
  cancelCapacityNudge(reason) {
7617
7899
  if (!this.capacityNudge)
@@ -7954,6 +8236,21 @@ export class Daemon extends EventEmitter {
7954
8236
  this.footerFallbackEpoch = null;
7955
8237
  }
7956
8238
  footerFallbackWarnedGeneration = null;
8239
+ /** #2357: the launch (spawn generation and attempt) the missing code-mode host was last reported for. */
8240
+ codexPackageNoticeEpoch = null;
8241
+ /**
8242
+ * #2357: codex recorded that a tool call could not start its code-mode host. Every tool call of a code-mode-only
8243
+ * account fails this way (AgEnD's reply too) while the pane shows only `⚠ N warnings · f2 to view`. Told once per
8244
+ * launch however often the model retries; the launch check (CodexBackend.buildCommand) may already have said so.
8245
+ */
8246
+ noteCodexPackageMissingHost(root) {
8247
+ const epoch = `${this.spawnGeneration}:${this.launchAttempt}`;
8248
+ if (this.codexPackageNoticeEpoch === epoch)
8249
+ return;
8250
+ this.codexPackageNoticeEpoch = epoch;
8251
+ this.logger.warn({ root }, "Codex tool call failed: code-mode host missing");
8252
+ this.emit("backend_launch_warning", { name: this.name, message: codexPackageProblemMessage(root, { file: "bin/codex-code-mode-host", reason: "missing" }) });
8253
+ }
7957
8254
  /**
7958
8255
  * The input-row proof used before a paste (#931/#947), with one fallback
7959
8256
  * (#1031). Codex sometimes paints an idle composer without its Context
@@ -9918,8 +10215,10 @@ export class Daemon extends EventEmitter {
9918
10215
  // purposes even before the idle edge completes the guard. Without
9919
10216
  // this, a restart in the reply-to-idle window would re-inject a
9920
10217
  // continuation for an already-answered turn.
9921
- if (replyAttempt?.completionAction)
10218
+ if (replyAttempt?.completionAction) {
9922
10219
  clearInFlightTurnMarker(this.instanceDir);
10220
+ this.publishWorkingState("completion_delivered");
10221
+ }
9923
10222
  // #1377: a confirmed completion action (reply/react/edit_message) ends
9924
10223
  // the human-facing obligation immediately — the idle edge does not need
9925
10224
  // to confirm it. Clear any pending confirmation window so a later
@@ -10414,7 +10713,11 @@ export class Daemon extends EventEmitter {
10414
10713
  }
10415
10714
  }
10416
10715
  // ── MCP server env (dual-track: still passes env vars for MCP instructions fallback) ──
10716
+ const configEnvironment = scopedFleetConfigEnvironment(join(this.instanceDir, "..", ".."));
10417
10717
  const mcpEnv = {
10718
+ // Empty values clear an unrelated reference inherited by the CLI or tmux server.
10719
+ AGEND_FLEET_DATA_DIR: configEnvironment.AGEND_FLEET_DATA_DIR ?? "",
10720
+ AGEND_FLEET_CONFIG_PATH: configEnvironment.AGEND_FLEET_CONFIG_PATH ?? "",
10418
10721
  AGEND_SOCKET_PATH: sockPath,
10419
10722
  AGEND_INSTANCE_NAME: this.name,
10420
10723
  AGEND_WORKING_DIR: this.config.working_directory,
@@ -10437,6 +10740,11 @@ export class Daemon extends EventEmitter {
10437
10740
  ? resolveToolSet(this.config, this.name, "general")
10438
10741
  : resolveToolSet(this.config, this.name);
10439
10742
  mcpEnv.AGEND_TOOL_SET = toolSet;
10743
+ // The dashboard features the person switched off: out of the tool menu from this start (the fleet refuses them
10744
+ // at once, whatever the menu still shows).
10745
+ const dashboardOff = dashboardFeaturesOff(this.config.dashboard);
10746
+ if (dashboardOff.length)
10747
+ mcpEnv.AGEND_DASHBOARD_OFF = dashboardOff.join(",");
10440
10748
  if (this.config.display_name)
10441
10749
  mcpEnv.AGEND_DISPLAY_NAME = this.config.display_name;
10442
10750
  if (this.config.description)
@@ -10469,6 +10777,7 @@ export class Daemon extends EventEmitter {
10469
10777
  catch { /* fallback to empty */ }
10470
10778
  instructions = buildFleetInstructions({
10471
10779
  instanceName: this.name,
10780
+ taskBoard: !dashboardOff.includes("task_board"),
10472
10781
  workingDirectory: this.config.working_directory,
10473
10782
  runtimeIdentity: this.runtimeIdentity,
10474
10783
  displayName: this.config.display_name,
@@ -10483,6 +10792,7 @@ export class Daemon extends EventEmitter {
10483
10792
  else {
10484
10793
  instructions = buildFleetInstructions({
10485
10794
  instanceName: this.name,
10795
+ taskBoard: !dashboardOff.includes("task_board"),
10486
10796
  workingDirectory: this.config.working_directory,
10487
10797
  runtimeIdentity: this.runtimeIdentity,
10488
10798
  displayName: this.config.display_name,
@@ -10571,6 +10881,9 @@ export class Daemon extends EventEmitter {
10571
10881
  * into exactly the window this exists to close.
10572
10882
  */
10573
10883
  beginSpawn() {
10884
+ this.unclearOomStopped = false;
10885
+ this.publishWorkingState("before_spawn");
10886
+ this.workingBackgroundObservation = null;
10574
10887
  this.dialogAnswers = null;
10575
10888
  this.capacityNudge = null;
10576
10889
  this.lastErrorEpisode = null;
@@ -10585,6 +10898,7 @@ export class Daemon extends EventEmitter {
10585
10898
  this.clearReplyGuardConfirm();
10586
10899
  this.sawBlockedInput = false;
10587
10900
  if (this.spawnDepth === 0) {
10901
+ this.cliOomObservation = null;
10588
10902
  this.spawnGeneration++;
10589
10903
  this.interactionObservation.reset(this.interactionOwner());
10590
10904
  this.clearInteractionConfirmation();
@@ -10785,6 +11099,7 @@ export class Daemon extends EventEmitter {
10785
11099
  catch { /* best effort */ }
10786
11100
  }
10787
11101
  async spawnClaudeWindow() {
11102
+ this.assertSessionOomAdmission();
10788
11103
  this.beginSpawn();
10789
11104
  let resumedSuccessfully = false;
10790
11105
  try {
@@ -10888,6 +11203,10 @@ export class Daemon extends EventEmitter {
10888
11203
  // is reachable again (a fresh prompt is local and proves nothing).
10889
11204
  this.backendOutage?.clear(this.backendKey());
10890
11205
  }
11206
+ // A pre-existing fresh fallback is not another resume of the old session.
11207
+ // Providers may not have rewritten their session reading yet.
11208
+ if (!resumedSuccessfully)
11209
+ this.consecutiveOoms = null;
10891
11210
  this.lastSpawnAt = Date.now();
10892
11211
  // #1217: whatever session this CLI writes from here on is this backend's.
10893
11212
  this.recordSessionOwner();
@@ -11171,11 +11490,20 @@ export class Daemon extends EventEmitter {
11171
11490
  * Returns true if CLI is ready, false if it failed or got stuck.
11172
11491
  */
11173
11492
  async trySpawn(reuseWindow = false, startupTimeoutMs) {
11174
- const ready = !this.spawnGate ? await this.trySpawnInsideGate(reuseWindow, startupTimeoutMs) : await this.spawnGate.run({
11493
+ const recoveryOwner = this.collateralOomRecovery ? this.interactionOwner() : null;
11494
+ const enter = () => {
11495
+ if (recoveryOwner && (!sameInteractionOwner(recoveryOwner, this.interactionOwner())
11496
+ || this.startupAborted || this.deliveryWritesStopping || this.runtimeMonitorsFrozen || this.healthCheckPaused)) {
11497
+ throw new Error("OOM recovery cancelled: the instance was stopped, paused or replaced while memory was unavailable");
11498
+ }
11499
+ return this.trySpawnInsideGate(reuseWindow, startupTimeoutMs);
11500
+ };
11501
+ const ready = !this.spawnGate ? await enter() : await this.spawnGate.run({
11175
11502
  instanceName: this.name,
11176
11503
  workingDirectory: this.config.working_directory,
11177
11504
  reason: reuseWindow ? "wake" : this.lastSpawnAt > 0 ? "recovery" : "startup",
11178
- }, () => this.trySpawnInsideGate(reuseWindow, startupTimeoutMs));
11505
+ requireNormalMemory: this.collateralOomRecovery,
11506
+ }, enter);
11179
11507
  // #906: a resumed kiro conversation comes back as the agent it was saved under. Every launch path that reaches a
11180
11508
  // ready CLI (start, recovery, wake) switches it to this instance's own agent here, while the spawn still holds
11181
11509
  // deliveries — and outside the spawn gate, which it does not need.
@@ -11404,16 +11732,41 @@ export class Daemon extends EventEmitter {
11404
11732
  this.pendingWhatsNew = null;
11405
11733
  return null;
11406
11734
  }
11407
- if (ids.length === pending.ids.length)
11408
- return pending;
11409
- const narrowed = { notice: whatsNewNotice(pending.version, AGENT_WHATS_NEW.filter(e => ids.includes(e.id))), ids, version: pending.version };
11735
+ // Rebuild even when ids are unchanged: an experimental switch may have changed
11736
+ // while this delivery waited. Never paste the old enabled capability text.
11737
+ // #2163: rebuilt as planned — only the entries this instance's roles are told; the rest are recorded with them.
11738
+ const roles = this.whatsNewRoles();
11739
+ const shown = agentWhatsNew(readCanvasWorkerEnabled(join(this.instanceDir, "..", ".."))).filter(e => ids.includes(e.id) && (!e.audience || e.audience.some(r => roles.includes(r))));
11740
+ if (!shown.length) {
11741
+ this.pendingWhatsNew = null;
11742
+ try {
11743
+ recordWhatsNew(this.instanceDir, ids, pending.version);
11744
+ }
11745
+ catch (err) {
11746
+ this.logger.warn({ err }, "what's-new record not written — the notice may repeat once");
11747
+ }
11748
+ return null;
11749
+ }
11750
+ const narrowed = { notice: whatsNewNotice(pending.version, shown), ids, version: pending.version };
11410
11751
  this.pendingWhatsNew = narrowed;
11411
11752
  return narrowed;
11412
11753
  }
11754
+ /**
11755
+ * #2163: who this instance is to the what's-new notices — a ClassicBot room's agent, General, or a fleet worker (with
11756
+ * `coordinator` too when its tool set is coordinator or full, the ones that carry get_/set_web_settings).
11757
+ */
11758
+ whatsNewRoles() {
11759
+ if (this.runtimeIdentity?.kind === "classic")
11760
+ return ["classic"];
11761
+ if (this.config.general_topic === true || this.name === "general")
11762
+ return ["general"];
11763
+ const set = resolveToolSet(this.config, this.name);
11764
+ return set === "coordinator" || set === "full" ? ["worker", "coordinator"] : ["worker"];
11765
+ }
11413
11766
  /** #1633: what this instance has not been told about its AgEnD version yet (see agent-whats-new.ts). */
11414
11767
  prepareWhatsNew(fresh, version = agendVersion()) {
11415
11768
  try {
11416
- const plan = planWhatsNew({ record: readWhatsNewRecord(this.instanceDir), fresh, version });
11769
+ const plan = planWhatsNew({ record: readWhatsNewRecord(this.instanceDir), fresh, version, roles: this.whatsNewRoles(), canvasWorker: readCanvasWorkerEnabled(join(this.instanceDir, "..", "..")) });
11417
11770
  if (!plan) {
11418
11771
  this.pendingWhatsNew = null;
11419
11772
  return;
@@ -11432,6 +11785,7 @@ export class Daemon extends EventEmitter {
11432
11785
  }
11433
11786
  }
11434
11787
  async trySpawnInsideGate(reuseWindow = false, startupTimeoutMs) {
11788
+ this.assertSessionOomAdmission();
11435
11789
  this.startupAdmission?.();
11436
11790
  const launchFence = this.launchFenceEpoch;
11437
11791
  const backendConfig = this.buildBackendConfig();
@@ -11480,6 +11834,7 @@ export class Daemon extends EventEmitter {
11480
11834
  // Thrown, not `false`: a false verdict reads as "the CLI failed to start" and sends the startup path
11481
11835
  // into its resume-retry / set-the-session-aside handling, for an instance somebody just stopped.
11482
11836
  this.startupAdmission?.();
11837
+ this.assertSessionOomAdmission();
11483
11838
  if (launchFence !== this.launchFenceEpoch)
11484
11839
  throw new Error("Launch cancelled: the instance was stopped or paused while the launch was being prepared");
11485
11840
  this.backend.writeConfig(backendConfig);
@@ -11505,7 +11860,8 @@ export class Daemon extends EventEmitter {
11505
11860
  // AGEND_HOME points the child's agent-cli at the same data dir the daemon
11506
11861
  // is using, so it can locate <instanceDir>/agent.token.
11507
11862
  const agendHome = join(this.instanceDir, "..", "..");
11508
- let envPrefix = `TERM=xterm-256color AGEND_INSTANCE_NAME=${shellQuote(this.name)} AGEND_HOME=${shellQuote(agendHome)}`;
11863
+ const configEnvironment = scopedFleetConfigEnvironment(agendHome);
11864
+ let envPrefix = `TERM=xterm-256color AGEND_INSTANCE_NAME=${shellQuote(this.name)} AGEND_HOME=${shellQuote(agendHome)} AGEND_FLEET_DATA_DIR=${shellQuote(configEnvironment.AGEND_FLEET_DATA_DIR ?? "")} AGEND_FLEET_CONFIG_PATH=${shellQuote(configEnvironment.AGEND_FLEET_CONFIG_PATH ?? "")}`;
11509
11865
  if (backendConfig.agentMode === "cli" && backendConfig.agentPort) {
11510
11866
  envPrefix += ` AGEND_PORT=${backendConfig.agentPort}`;
11511
11867
  }
@@ -11565,6 +11921,7 @@ export class Daemon extends EventEmitter {
11565
11921
  }
11566
11922
  this.startupAdmission?.();
11567
11923
  let windowId;
11924
+ this.assertSessionOomAdmission();
11568
11925
  if (reuseWindow) {
11569
11926
  this.controlClient?.unregisterWindow(this.tmux.getWindowId());
11570
11927
  await this.tmux.respawnWindow(cmd, resolvedCwd);
@@ -12199,6 +12556,7 @@ export class Daemon extends EventEmitter {
12199
12556
  crashType: data.crashType,
12200
12557
  exitCode: data.exitCode,
12201
12558
  reason: data.reason,
12559
+ ...(data.oomEvidence ? { oomEvidence: data.oomEvidence } : {}),
12202
12560
  lastOutput: data.lastOutput,
12203
12561
  crashCount: this.crashCount + 1,
12204
12562
  crashesInWindow: this.crashTimestamps.length,
@@ -12228,7 +12586,7 @@ export class Daemon extends EventEmitter {
12228
12586
  }
12229
12587
  // ── Repo Checkout ─────────────────────────────────────────
12230
12588
  async handleCheckoutRepo(args, respond) {
12231
- const { execFile: execFileCb } = await import("node:child_process");
12589
+ const { execFile: execFileCb } = await import("./child-process.js");
12232
12590
  const { promisify } = await import("node:util");
12233
12591
  const execFileAsync = promisify(execFileCb);
12234
12592
  const rawSource = args.source;
@@ -12264,7 +12622,7 @@ export class Daemon extends EventEmitter {
12264
12622
  respond(null, `Not a git repository: ${source}`);
12265
12623
  return;
12266
12624
  }
12267
- mkdirSync(join(this.instanceDir, "repos"), { recursive: true });
12625
+ mkdirSync(join(this.instanceDir, "repos"), { recursive: true, mode: 0o700 });
12268
12626
  // Only a fresh direct child of a real repos/ directory (never through a symlinked repos/, never an existing path).
12269
12627
  const target = checkoutTarget(this.instanceDir, source, branch);
12270
12628
  if (!target.ok) {
@@ -12306,7 +12664,7 @@ export class Daemon extends EventEmitter {
12306
12664
  respond(null, `Cannot release: ${root.reason}`);
12307
12665
  return;
12308
12666
  }
12309
- const { execFile: execFileCb } = await import("node:child_process");
12667
+ const { execFile: execFileCb } = await import("./child-process.js");
12310
12668
  const { promisify } = await import("node:util");
12311
12669
  const execFileAsync = promisify(execFileCb);
12312
12670
  const git = async (gitArgs, cwd) => (await execFileAsync("git", gitArgs, { cwd })).stdout;