@songsid/agend 2.2.0-beta.2 → 2.2.0-beta.20

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 (389) hide show
  1. package/README.md +40 -24
  2. package/README.zh-TW.md +40 -24
  3. package/dist/agent-cli-instructions.md +6 -2
  4. package/dist/agent-cli.js +15 -7
  5. package/dist/agent-cli.js.map +1 -1
  6. package/dist/agent-endpoint.js +3 -0
  7. package/dist/agent-endpoint.js.map +1 -1
  8. package/dist/agent-whats-new.js +16 -4
  9. package/dist/agent-whats-new.js.map +1 -1
  10. package/dist/auth-api.d.ts +3 -1
  11. package/dist/auth-api.js +17 -1
  12. package/dist/auth-api.js.map +1 -1
  13. package/dist/backend/antigravity.d.ts +33 -0
  14. package/dist/backend/antigravity.js +126 -18
  15. package/dist/backend/antigravity.js.map +1 -1
  16. package/dist/backend/claude-carry.d.ts +22 -0
  17. package/dist/backend/claude-carry.js +170 -0
  18. package/dist/backend/claude-carry.js.map +1 -0
  19. package/dist/backend/claude-code.d.ts +65 -2
  20. package/dist/backend/claude-code.js +226 -33
  21. package/dist/backend/claude-code.js.map +1 -1
  22. package/dist/backend/codex-metadata.d.ts +33 -0
  23. package/dist/backend/codex-metadata.js +122 -18
  24. package/dist/backend/codex-metadata.js.map +1 -1
  25. package/dist/backend/codex.d.ts +3 -0
  26. package/dist/backend/codex.js +17 -1
  27. package/dist/backend/codex.js.map +1 -1
  28. package/dist/backend/credential-profile.d.ts +40 -3
  29. package/dist/backend/credential-profile.js +113 -5
  30. package/dist/backend/credential-profile.js.map +1 -1
  31. package/dist/backend/effort-metadata.d.ts +37 -1
  32. package/dist/backend/effort-metadata.js +86 -15
  33. package/dist/backend/effort-metadata.js.map +1 -1
  34. package/dist/backend/grok.d.ts +4 -0
  35. package/dist/backend/grok.js +14 -0
  36. package/dist/backend/grok.js.map +1 -1
  37. package/dist/backend/kiro.d.ts +27 -1
  38. package/dist/backend/kiro.js +63 -10
  39. package/dist/backend/kiro.js.map +1 -1
  40. package/dist/backend/launch-effort.d.ts +61 -0
  41. package/dist/backend/launch-effort.js +170 -0
  42. package/dist/backend/launch-effort.js.map +1 -0
  43. package/dist/backend/muse.d.ts +84 -0
  44. package/dist/backend/muse.js +449 -53
  45. package/dist/backend/muse.js.map +1 -1
  46. package/dist/backend/types.d.ts +48 -2
  47. package/dist/backend/types.js +12 -1
  48. package/dist/backend/types.js.map +1 -1
  49. package/dist/backend-command.d.ts +91 -0
  50. package/dist/backend-command.js +122 -0
  51. package/dist/backend-command.js.map +1 -0
  52. package/dist/background-work-ended.d.ts +18 -0
  53. package/dist/background-work-ended.js +30 -0
  54. package/dist/background-work-ended.js.map +1 -0
  55. package/dist/cache-service.d.ts +2 -0
  56. package/dist/cache-service.js +2 -1
  57. package/dist/cache-service.js.map +1 -1
  58. package/dist/channel/adapters/discord.d.ts +26 -1
  59. package/dist/channel/adapters/discord.js +103 -3
  60. package/dist/channel/adapters/discord.js.map +1 -1
  61. package/dist/channel/adapters/telegram.d.ts +35 -3
  62. package/dist/channel/adapters/telegram.js +122 -23
  63. package/dist/channel/adapters/telegram.js.map +1 -1
  64. package/dist/channel/mcp-tools.js +5 -3
  65. package/dist/channel/mcp-tools.js.map +1 -1
  66. package/dist/channel/message-edit-errors.d.ts +5 -0
  67. package/dist/channel/message-edit-errors.js +24 -0
  68. package/dist/channel/message-edit-errors.js.map +1 -0
  69. package/dist/channel/telegram-markdown.d.ts +6 -0
  70. package/dist/channel/telegram-markdown.js +104 -0
  71. package/dist/channel/telegram-markdown.js.map +1 -0
  72. package/dist/channel/tool-router.d.ts +2 -0
  73. package/dist/channel/tool-router.js +6 -5
  74. package/dist/channel/tool-router.js.map +1 -1
  75. package/dist/channel/types.d.ts +46 -4
  76. package/dist/cli.js +184 -34
  77. package/dist/cli.js.map +1 -1
  78. package/dist/command-table.d.ts +2 -2
  79. package/dist/command-table.js +5 -2
  80. package/dist/command-table.js.map +1 -1
  81. package/dist/compact-watch.d.ts +86 -0
  82. package/dist/compact-watch.js +288 -0
  83. package/dist/compact-watch.js.map +1 -0
  84. package/dist/config-validator.js +55 -5
  85. package/dist/config-validator.js.map +1 -1
  86. package/dist/config.d.ts +1 -0
  87. package/dist/config.js +2 -1
  88. package/dist/config.js.map +1 -1
  89. package/dist/cross-instance-envelope.js +2 -0
  90. package/dist/cross-instance-envelope.js.map +1 -1
  91. package/dist/daemon.d.ts +129 -1
  92. package/dist/daemon.js +754 -94
  93. package/dist/daemon.js.map +1 -1
  94. package/dist/delivery-outbox.d.ts +64 -8
  95. package/dist/delivery-outbox.js +315 -24
  96. package/dist/delivery-outbox.js.map +1 -1
  97. package/dist/delivery-reconciliation.d.ts +18 -1
  98. package/dist/delivery-reconciliation.js +67 -2
  99. package/dist/delivery-reconciliation.js.map +1 -1
  100. package/dist/doctor.js +7 -0
  101. package/dist/doctor.js.map +1 -1
  102. package/dist/fleet-context.d.ts +12 -1
  103. package/dist/fleet-manager.d.ts +392 -19
  104. package/dist/fleet-manager.js +2030 -275
  105. package/dist/fleet-manager.js.map +1 -1
  106. package/dist/forged-envelope-scan.d.ts +4 -3
  107. package/dist/forged-envelope-scan.js +4 -3
  108. package/dist/forged-envelope-scan.js.map +1 -1
  109. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +84 -16
  110. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +16 -1
  111. package/dist/general-knowledge/skills/model-discovery/SKILL.md +4 -1
  112. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +4 -0
  113. package/dist/host-memory.d.ts +10 -0
  114. package/dist/host-memory.js +33 -1
  115. package/dist/host-memory.js.map +1 -1
  116. package/dist/human-delivery-receipts.d.ts +83 -0
  117. package/dist/human-delivery-receipts.js +194 -0
  118. package/dist/human-delivery-receipts.js.map +1 -0
  119. package/dist/install-lock.d.ts +18 -1
  120. package/dist/install-lock.js +30 -3
  121. package/dist/install-lock.js.map +1 -1
  122. package/dist/instance-avatar.d.ts +5 -4
  123. package/dist/instance-avatar.js +12 -8
  124. package/dist/instance-avatar.js.map +1 -1
  125. package/dist/instance-config-impact.js +1 -0
  126. package/dist/instance-config-impact.js.map +1 -1
  127. package/dist/instance-lifecycle.d.ts +25 -6
  128. package/dist/instance-lifecycle.js +76 -27
  129. package/dist/instance-lifecycle.js.map +1 -1
  130. package/dist/instructions.d.ts +1 -1
  131. package/dist/instructions.js +3 -3
  132. package/dist/instructions.js.map +1 -1
  133. package/dist/kiro-compact-pane.d.ts +38 -0
  134. package/dist/kiro-compact-pane.js +57 -0
  135. package/dist/kiro-compact-pane.js.map +1 -0
  136. package/dist/kiro-log-gate.d.ts +42 -0
  137. package/dist/kiro-log-gate.js +39 -0
  138. package/dist/kiro-log-gate.js.map +1 -0
  139. package/dist/launchd-handoff.d.ts +43 -0
  140. package/dist/launchd-handoff.js +56 -0
  141. package/dist/launchd-handoff.js.map +1 -0
  142. package/dist/locale.js +268 -42
  143. package/dist/locale.js.map +1 -1
  144. package/dist/log-service.d.ts +99 -0
  145. package/dist/log-service.js +243 -0
  146. package/dist/log-service.js.map +1 -0
  147. package/dist/log-steps.d.ts +169 -0
  148. package/dist/log-steps.js +858 -0
  149. package/dist/log-steps.js.map +1 -0
  150. package/dist/login-controller.d.ts +20 -3
  151. package/dist/login-controller.js +71 -19
  152. package/dist/login-controller.js.map +1 -1
  153. package/dist/login-flows.d.ts +5 -2
  154. package/dist/login-flows.js +5 -4
  155. package/dist/login-flows.js.map +1 -1
  156. package/dist/login-shell.d.ts +21 -0
  157. package/dist/login-shell.js +98 -0
  158. package/dist/login-shell.js.map +1 -0
  159. package/dist/memory-pressure.d.ts +13 -1
  160. package/dist/memory-pressure.js +50 -10
  161. package/dist/memory-pressure.js.map +1 -1
  162. package/dist/needs-you-hub.js +2 -0
  163. package/dist/needs-you-hub.js.map +1 -1
  164. package/dist/needs-you-live.d.ts +6 -0
  165. package/dist/needs-you-live.js +23 -3
  166. package/dist/needs-you-live.js.map +1 -1
  167. package/dist/outbound-handlers.d.ts +100 -0
  168. package/dist/outbound-handlers.js +277 -93
  169. package/dist/outbound-handlers.js.map +1 -1
  170. package/dist/outbound-schemas.d.ts +30 -15
  171. package/dist/outbound-schemas.js +23 -5
  172. package/dist/outbound-schemas.js.map +1 -1
  173. package/dist/provider-probe.d.ts +29 -0
  174. package/dist/provider-probe.js +198 -2
  175. package/dist/provider-probe.js.map +1 -1
  176. package/dist/public-link-progress.d.ts +1 -1
  177. package/dist/public-link-progress.js +8 -2
  178. package/dist/public-link-progress.js.map +1 -1
  179. package/dist/public-preview.d.ts +101 -0
  180. package/dist/public-preview.js +236 -0
  181. package/dist/public-preview.js.map +1 -0
  182. package/dist/public-web-gateway.d.ts +2 -0
  183. package/dist/public-web-gateway.js +20 -6
  184. package/dist/public-web-gateway.js.map +1 -1
  185. package/dist/public-web-link.d.ts +24 -1
  186. package/dist/public-web-link.js +63 -9
  187. package/dist/public-web-link.js.map +1 -1
  188. package/dist/quickstart-api.d.ts +1 -1
  189. package/dist/quickstart-api.js +8 -6
  190. package/dist/quickstart-api.js.map +1 -1
  191. package/dist/quickstart.d.ts +12 -0
  192. package/dist/quickstart.js +36 -6
  193. package/dist/quickstart.js.map +1 -1
  194. package/dist/reply-buttons-controller.d.ts +89 -5
  195. package/dist/reply-buttons-controller.js +273 -8
  196. package/dist/reply-buttons-controller.js.map +1 -1
  197. package/dist/reply-buttons.d.ts +48 -3
  198. package/dist/reply-buttons.js +105 -10
  199. package/dist/reply-buttons.js.map +1 -1
  200. package/dist/reply-polls.d.ts +85 -0
  201. package/dist/reply-polls.js +154 -0
  202. package/dist/reply-polls.js.map +1 -0
  203. package/dist/restart-guard.d.ts +6 -3
  204. package/dist/restart-guard.js +6 -3
  205. package/dist/restart-guard.js.map +1 -1
  206. package/dist/restart-progress.d.ts +33 -2
  207. package/dist/restart-progress.js +30 -2
  208. package/dist/restart-progress.js.map +1 -1
  209. package/dist/sd-notify.d.ts +22 -1
  210. package/dist/sd-notify.js +63 -10
  211. package/dist/sd-notify.js.map +1 -1
  212. package/dist/service-activation.d.ts +15 -7
  213. package/dist/service-activation.js +42 -26
  214. package/dist/service-activation.js.map +1 -1
  215. package/dist/service-installer.d.ts +55 -0
  216. package/dist/service-installer.js +90 -3
  217. package/dist/service-installer.js.map +1 -1
  218. package/dist/service-plan.d.ts +11 -0
  219. package/dist/service-plan.js +23 -0
  220. package/dist/service-plan.js.map +1 -1
  221. package/dist/settings-api.js +14 -4
  222. package/dist/settings-api.js.map +1 -1
  223. package/dist/settings-change.js +4 -2
  224. package/dist/settings-change.js.map +1 -1
  225. package/dist/settings-effect.js +2 -0
  226. package/dist/settings-effect.js.map +1 -1
  227. package/dist/spawn-gate.js +5 -1
  228. package/dist/spawn-gate.js.map +1 -1
  229. package/dist/spawn-hold-reply.d.ts +12 -0
  230. package/dist/spawn-hold-reply.js +39 -0
  231. package/dist/spawn-hold-reply.js.map +1 -0
  232. package/dist/systemd-peer-bus.d.ts +95 -0
  233. package/dist/systemd-peer-bus.js +430 -0
  234. package/dist/systemd-peer-bus.js.map +1 -0
  235. package/dist/systemd-runtime.d.ts +27 -1
  236. package/dist/systemd-runtime.js +69 -7
  237. package/dist/systemd-runtime.js.map +1 -1
  238. package/dist/tips.js +1 -1
  239. package/dist/tips.js.map +1 -1
  240. package/dist/tool-permissions.js +3 -3
  241. package/dist/tool-permissions.js.map +1 -1
  242. package/dist/tool-progress.js +3 -2
  243. package/dist/tool-progress.js.map +1 -1
  244. package/dist/topic-commands.d.ts +71 -4
  245. package/dist/topic-commands.js +167 -34
  246. package/dist/topic-commands.js.map +1 -1
  247. package/dist/transcript-delta-reader.d.ts +3 -3
  248. package/dist/transcript-delta-reader.js +8 -8
  249. package/dist/transcript-delta-reader.js.map +1 -1
  250. package/dist/transcript-jsonl.d.ts +85 -3
  251. package/dist/transcript-jsonl.js +156 -13
  252. package/dist/transcript-jsonl.js.map +1 -1
  253. package/dist/transcript-monitor.d.ts +11 -0
  254. package/dist/transcript-monitor.js +61 -13
  255. package/dist/transcript-monitor.js.map +1 -1
  256. package/dist/transcript-sources.d.ts +24 -0
  257. package/dist/transcript-sources.js +211 -56
  258. package/dist/transcript-sources.js.map +1 -1
  259. package/dist/tunnel/cloudflared.d.ts +7 -15
  260. package/dist/tunnel/cloudflared.js +36 -1
  261. package/dist/tunnel/cloudflared.js.map +1 -1
  262. package/dist/tunnel/purpose-lane.d.ts +5 -1
  263. package/dist/tunnel/purpose-lane.js +14 -7
  264. package/dist/tunnel/purpose-lane.js.map +1 -1
  265. package/dist/tunnel/types.d.ts +5 -1
  266. package/dist/tunnel/types.js.map +1 -1
  267. package/dist/turn-resume.d.ts +2 -0
  268. package/dist/turn-resume.js +1 -1
  269. package/dist/turn-resume.js.map +1 -1
  270. package/dist/types.d.ts +22 -0
  271. package/dist/ui/chat-commands.js +1 -0
  272. package/dist/ui/chat-mentions.js +63 -0
  273. package/dist/ui/chat-render.js +80 -10
  274. package/dist/ui/chat-store.js +70 -6
  275. package/dist/ui/chat-strings.js +62 -32
  276. package/dist/ui/chat-thread.js +199 -24
  277. package/dist/ui/chat-time.js +80 -0
  278. package/dist/ui/chat-tour.js +17 -14
  279. package/dist/ui/panel-chat.js +45 -21
  280. package/dist/ui/panel-details.js +2 -0
  281. package/dist/ui/panel-fleet.js +213 -58
  282. package/dist/ui/panel-log.js +428 -0
  283. package/dist/ui/panel-needs.js +14 -10
  284. package/dist/ui/panel-settings.js +82 -17
  285. package/dist/ui/preview-panel.js +55 -15
  286. package/dist/ui/preview-status.js +28 -0
  287. package/dist/ui/preview.js +44 -3
  288. package/dist/ui/settings-dialogs.js +11 -6
  289. package/dist/ui/settings-model.js +3 -1
  290. package/dist/ui/settings-strings.js +32 -12
  291. package/dist/ui/settings-token.js +8 -1
  292. package/dist/ui/settings-wizard.js +5 -3
  293. package/dist/ui/shared/app-i18n.js +41 -15
  294. package/dist/ui/shared/app-prefs.js +250 -0
  295. package/dist/ui/shared/app-route.js +6 -1
  296. package/dist/ui/shared/app-shell.js +165 -8
  297. package/dist/ui/shared/app-store.js +5 -1
  298. package/dist/ui/shared/app-stream.js +26 -5
  299. package/dist/ui/shared/app.css +170 -30
  300. package/dist/ui/shared/app.js +11 -1
  301. package/dist/ui/shared/fonts/README.md +1 -1
  302. package/dist/ui/shared/header-tools.js +83 -13
  303. package/dist/ui/shared/instance-nav.js +178 -51
  304. package/dist/ui/shared/instance-switch.js +8 -5
  305. package/dist/ui/shared/panel-view.js +21 -4
  306. package/dist/ui/shared/signin.js +15 -9
  307. package/dist/ui/shared/theme.js +39 -7
  308. package/dist/ui/shared/tokens.css +1 -1
  309. package/dist/ui/shared/ui-icons.js +2 -0
  310. package/dist/ui/shared/usage-dialog.js +23 -5
  311. package/dist/ui/shared/view-strings.js +40 -4
  312. package/dist/ui/signin.html +8 -3
  313. package/dist/unrecognised-prompt.d.ts +23 -0
  314. package/dist/unrecognised-prompt.js +74 -0
  315. package/dist/unrecognised-prompt.js.map +1 -0
  316. package/dist/update-check.d.ts +31 -0
  317. package/dist/update-check.js +47 -0
  318. package/dist/update-check.js.map +1 -1
  319. package/dist/update-install.d.ts +13 -1
  320. package/dist/update-install.js +16 -4
  321. package/dist/update-install.js.map +1 -1
  322. package/dist/update-launch.d.ts +48 -1
  323. package/dist/update-launch.js +159 -5
  324. package/dist/update-launch.js.map +1 -1
  325. package/dist/update-marker.d.ts +51 -0
  326. package/dist/update-marker.js +61 -0
  327. package/dist/update-marker.js.map +1 -1
  328. package/dist/usage/i18n-keys.d.ts +1 -1
  329. package/dist/usage/i18n-keys.js +1 -1
  330. package/dist/usage/i18n-keys.js.map +1 -1
  331. package/dist/usage/providers.d.ts +62 -13
  332. package/dist/usage/providers.js +256 -68
  333. package/dist/usage/providers.js.map +1 -1
  334. package/dist/usage/statusline-usage.d.ts +6 -2
  335. package/dist/usage/statusline-usage.js +10 -4
  336. package/dist/usage/statusline-usage.js.map +1 -1
  337. package/dist/view-api.js +31 -9
  338. package/dist/view-api.js.map +1 -1
  339. package/dist/web-api.d.ts +82 -4
  340. package/dist/web-api.js +632 -88
  341. package/dist/web-api.js.map +1 -1
  342. package/dist/web-auth.js +5 -2
  343. package/dist/web-auth.js.map +1 -1
  344. package/dist/web-chat-history.d.ts +26 -1
  345. package/dist/web-chat-history.js +47 -8
  346. package/dist/web-chat-history.js.map +1 -1
  347. package/dist/web-commands.d.ts +41 -5
  348. package/dist/web-commands.js +25 -7
  349. package/dist/web-commands.js.map +1 -1
  350. package/dist/web-login.d.ts +14 -0
  351. package/dist/web-login.js +4 -2
  352. package/dist/web-login.js.map +1 -1
  353. package/dist/web-mentions.d.ts +44 -0
  354. package/dist/web-mentions.js +75 -0
  355. package/dist/web-mentions.js.map +1 -0
  356. package/dist/web-prefs.d.ts +81 -0
  357. package/dist/web-prefs.js +297 -0
  358. package/dist/web-prefs.js.map +1 -0
  359. package/dist/web-preview.d.ts +14 -1
  360. package/dist/web-preview.js +35 -13
  361. package/dist/web-preview.js.map +1 -1
  362. package/dist/web-profile.d.ts +32 -0
  363. package/dist/web-profile.js +132 -0
  364. package/dist/web-profile.js.map +1 -0
  365. package/dist/web-reply-files.d.ts +45 -0
  366. package/dist/web-reply-files.js +223 -0
  367. package/dist/web-reply-files.js.map +1 -0
  368. package/dist/web-schedules.d.ts +12 -0
  369. package/dist/web-schedules.js +34 -0
  370. package/dist/web-schedules.js.map +1 -0
  371. package/dist/web-server-drain.d.ts +10 -0
  372. package/dist/web-server-drain.js +50 -0
  373. package/dist/web-server-drain.js.map +1 -0
  374. package/dist/web-session.d.ts +14 -0
  375. package/dist/web-session.js +11 -1
  376. package/dist/web-session.js.map +1 -1
  377. package/dist/web-settings-patch.d.ts +3 -1
  378. package/dist/web-settings-patch.js +4 -1
  379. package/dist/web-settings-patch.js.map +1 -1
  380. package/dist/web-shell-routes.d.ts +4 -0
  381. package/dist/web-shell-routes.js +9 -0
  382. package/dist/web-shell-routes.js.map +1 -1
  383. package/dist/web-upload.d.ts +135 -7
  384. package/dist/web-upload.js +315 -51
  385. package/dist/web-upload.js.map +1 -1
  386. package/launcher/install-admission.cjs +61 -6
  387. package/package.json +3 -3
  388. package/templates/launchd.plist.ejs +3 -1
  389. package/templates/systemd.service.ejs +2 -1
package/dist/cli.js CHANGED
@@ -16,14 +16,14 @@ import { Command } from "commander";
16
16
  import { join, dirname } from "node:path";
17
17
  import { SchedulerDb } from "./scheduler/db.js";
18
18
  import { Cron } from "croner";
19
- import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, } from "node:fs";
19
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, unlinkSync, mkdirSync, chmodSync, readdirSync, rmSync, statSync, realpathSync, openSync, writeSync, closeSync, } from "node:fs";
20
20
  import { homedir, totalmem, freemem, userInfo } from "node:os";
21
21
  import { fileURLToPath } from "node:url";
22
22
  import { spawn, spawnSync, execSync, execFileSync } from "node:child_process";
23
- import { getAgendHome, getTmuxSocketName } from "./paths.js";
23
+ import { getAgendHome, getTmuxSocketName, isDefaultAgendHome } from "./paths.js";
24
24
  import { hasPausedMarker } from "./pause-marker.js";
25
25
  import { COMPLETION_SHELLS, completionScript } from "./completion.js";
26
- import { getUpdateSelector, isExactVersion, isUnrequestedDowngrade, lookupTargetVersion, reportUpdateRestart, shouldSkipUpdate, runningFleetPredatesInstall, processStartMs, } from "./update-check.js";
26
+ import { getUpdateSelector, isExactVersion, isUnrequestedDowngrade, lookupTargetVersion, reportUpdateRestart, shouldSkipUpdate, runningFleetPredatesInstall, fleetProvenStopped, readFleetPidEvidence, pidAlive, processStartMs, } from "./update-check.js";
27
27
  import { clearUpdateMarker, markUpdateInProgress, setUpdateProgressStage } from "./update-marker.js";
28
28
  import { describeSignalSource, forceAllowed, gateFleetControl, recordInstanceControl, withOrigin } from "./fleet-control-audit.js";
29
29
  import { FLEET_STOP_TIMEOUT_MS, stopDetachedOwner, systemdStopTimeoutMs, detachedProcessStart, detachedProcessExited } from "./fleet-stop-budget.js";
@@ -38,6 +38,7 @@ import { DeliveryStatusArgs } from "./outbound-schemas.js";
38
38
  import { requestCpuProfile } from "./profile-control.js";
39
39
  import { requestSettingsConfirmation } from "./settings-control.js";
40
40
  import { canonicalCliEntry, selfCommand } from "./cli-entry.js";
41
+ import { handOff, shouldHandOff } from "./launchd-handoff.js";
41
42
  /** Prefix tmux args with -L when socket isolation is active. */
42
43
  function tmuxArgs(args) {
43
44
  const socket = getTmuxSocketName();
@@ -79,6 +80,28 @@ function planDeps() {
79
80
  now: () => new Date(),
80
81
  };
81
82
  }
83
+ /**
84
+ * #1871: hand this command to a detached copy that outlives the fleet's stop (see launchd-handoff.ts). True when it
85
+ * did; false to carry on here (not an agent session, or the copy could not be started — said so).
86
+ */
87
+ async function handOffLaunchdActivation(action) {
88
+ if (!shouldHandOff(process.env))
89
+ return false;
90
+ const handed = await handOff({ agendHome: DATA_DIR, self: selfCommand(process.argv.slice(2)), env: process.env, action }, {
91
+ open: path => openSync(path, "a", 0o600),
92
+ write: (fd, text) => writeSync(fd, text),
93
+ close: fd => closeSync(fd),
94
+ spawn: (command, args, options) => spawn(command, args, options),
95
+ now: () => new Date(),
96
+ });
97
+ if (handed.ok) {
98
+ console.log(` ↪ This session belongs to the fleet that launchd is about to stop, so \`agend ${action}\` goes on in the background.`);
99
+ console.log(` Its output: ${handed.log}`);
100
+ return true;
101
+ }
102
+ console.log(` ⚠ Could not continue in the background (${handed.reason}); going on in this session.`);
103
+ return false;
104
+ }
82
105
  const FLEET_CONFIG_PATH = join(DATA_DIR, "fleet.yaml");
83
106
  // CLI commands run outside FleetManager. Honor an explicit fleet locale while
84
107
  // keeping the historical English CLI default when config is missing/unset.
@@ -1446,6 +1469,12 @@ program
1446
1469
  }
1447
1470
  if (!gateFleetControl(DATA_DIR, "update", { yes: opts.yes }))
1448
1471
  process.exit(1);
1472
+ // #1871: on launchd the activation boots out the fleet this agent session runs in; the update must outlive that.
1473
+ {
1474
+ const { getServicePath, getSystemServicePath, detectPlatform } = await import("./service-installer.js");
1475
+ if (detectPlatform() === "macos" && !getSystemServicePath() && getServicePath() && await handOffLaunchdActivation("update"))
1476
+ return;
1477
+ }
1449
1478
  const { spawnSync } = await import("node:child_process");
1450
1479
  // The channel follows the version being replaced — this CLI's own package — so an alpha or beta stays on its channel.
1451
1480
  const tag = getUpdateSelector(opts, pkgVersion);
@@ -1495,7 +1524,7 @@ program
1495
1524
  const { newAgendInvocation, retireSystemCopy, activationSettled } = await import("./update-install.js");
1496
1525
  // What the restart came to — carried, never inferred from the exit code (pending ≠ restarted).
1497
1526
  let restartResult = null;
1498
- const { activateService } = await import("./service-activation.js");
1527
+ const { activateService, systemdPeerBus } = await import("./service-activation.js");
1499
1528
  const { getServicePath, getSystemServicePath, detectPlatform } = await import("./service-installer.js");
1500
1529
  const { accessSync, constants } = await import("node:fs");
1501
1530
  const newAgend = newAgendInvocation({ viaNvm, nvmSh }, verified.agendPath);
@@ -1518,6 +1547,18 @@ program
1518
1547
  const result = spawnSync(command, args, { encoding: "utf-8", timeout: 15_000, stdio: ["ignore", "pipe", "pipe"] });
1519
1548
  return { status: result.status, signal: result.signal, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
1520
1549
  };
1550
+ // #1660: the retire outcome lands after this process's restart returns —
1551
+ // after the new fleet may have consumed this receipt. Capture it now so
1552
+ // the cleanup write later can prove it still owns the marker (checklist
1553
+ // 3/5). A missed capture only skips the marker write; retire still runs.
1554
+ let cleanupReceipt = null;
1555
+ if (verified.retireSystemCopy) {
1556
+ try {
1557
+ const { markCleanupPending } = await import("./update-marker.js");
1558
+ cleanupReceipt = markCleanupPending(DATA_DIR);
1559
+ }
1560
+ catch { /* no receipt: retire still runs, the marker write is skipped */ }
1561
+ }
1521
1562
  const outcome = activateService(manager, verified, {
1522
1563
  run: capture,
1523
1564
  readFile: path => { try {
@@ -1567,6 +1608,7 @@ program
1567
1608
  log: message => console.log(message),
1568
1609
  restorePackage,
1569
1610
  systemdRecovery: restoreChecked ? { restorePackage: restoreChecked } : undefined,
1611
+ peerBus: systemdPeerBus,
1570
1612
  });
1571
1613
  // Settled: after a success only this transition's preimage is kept (for a repair); a failure consumed it.
1572
1614
  if (activationSettled(outcome, restartResult) && rollback)
@@ -1575,10 +1617,41 @@ program
1575
1617
  // new install (activationSettled) is it removed; pending, failed or refused, it stays, so the old unit still starts.
1576
1618
  if (verified.retireSystemCopy) {
1577
1619
  if (activationSettled(outcome, restartResult)) {
1578
- const retireResult = retireSystemCopy({ run: (command, args) => capture(command, args), log: message => console.log(message) }, verified.npmPath);
1620
+ // #1735: the old copy is the package this updater runs from (the nvm route is taken because its prefix is not
1621
+ // writable); the warning names it.
1622
+ const oldCopyDir = (() => { try {
1623
+ return realpathSync(dirname(__dirname));
1624
+ }
1625
+ catch {
1626
+ return undefined;
1627
+ } })();
1628
+ const retireResult = retireSystemCopy({ run: (command, args) => capture(command, args), log: message => console.log(message) }, verified.npmPath, oldCopyDir);
1629
+ // The marker write proves the pre-restart receipt still owns the
1630
+ // marker: refused when it is gone or belongs to another operation.
1631
+ const recordCleanup = async (result) => {
1632
+ if (!cleanupReceipt)
1633
+ return;
1634
+ try {
1635
+ const { writeCleanupOutcome } = await import("./update-marker.js");
1636
+ writeCleanupOutcome(DATA_DIR, cleanupReceipt, result);
1637
+ }
1638
+ catch { /* receipt unverifiable: retire already ran; stdout stands */ }
1639
+ };
1579
1640
  if (!retireResult.ok) {
1580
1641
  console.error(` ✗ Old system install was not removed: ${retireResult.reason}`);
1581
1642
  process.exitCode = 1;
1643
+ // Resolve the pending flag so no watch waits out the max age.
1644
+ await recordCleanup(null);
1645
+ }
1646
+ else if (retireResult.warning) {
1647
+ // #1660: cleanup only — the new version runs. Exit 0; the single
1648
+ // print lives here (retireSystemCopy stays silent, P3-1).
1649
+ console.log(` ⚠️ ${retireResult.warning}`
1650
+ + (retireResult.cleanupCommand ? `\n $ ${retireResult.cleanupCommand}` : ""));
1651
+ await recordCleanup({ warning: retireResult.warning, cleanupCommand: retireResult.cleanupCommand });
1652
+ }
1653
+ else {
1654
+ await recordCleanup(null);
1582
1655
  }
1583
1656
  }
1584
1657
  else {
@@ -1623,15 +1696,9 @@ program
1623
1696
  installedAtMs = statSync(fileURLToPath(import.meta.url)).mtimeMs;
1624
1697
  }
1625
1698
  catch { /* unknown */ }
1626
- if (runningFleetPredatesInstall({
1627
- pid: fleetPid,
1628
- installedAtMs,
1629
- processStartMs: pid => processStartMs(pid),
1630
- isFleetProcess: pid => isFleetStartCommandLine(readProcessCommandLine(pid)),
1631
- })) {
1632
- console.log(`\n v${pkgVersion} is installed, but the running fleet started before it was — verifying it before restarting onto it.`);
1633
- // The earlier update may have stopped exactly because this install failed verification (#1449 review): a
1634
- // second `agend update` must not restart onto it unchecked.
1699
+ // The earlier update may have stopped exactly because this install failed verification (#1449 review): a
1700
+ // second `agend update` must not restart onto it — nor call it up to date — unchecked.
1701
+ const verifyInstalled = async () => {
1635
1702
  const { verifyInstalledPackage } = await import("./update-install.js");
1636
1703
  const verified = verifyInstalledPackage({ pkg: `@songsid/agend@${pkgVersion}`, targetVersion: pkgVersion, viaNvm: false, nvmSh }, {
1637
1704
  run: (command, args, options = {}) => {
@@ -1645,13 +1712,42 @@ program
1645
1712
  console.error(message);
1646
1713
  setUpdateProgressStage(DATA_DIR, "failed", { error: message.trim() });
1647
1714
  process.exitCode = 1;
1648
- return;
1649
1715
  }
1716
+ return verified;
1717
+ };
1718
+ if (runningFleetPredatesInstall({
1719
+ pid: fleetPid,
1720
+ installedAtMs,
1721
+ processStartMs: pid => processStartMs(pid),
1722
+ isFleetProcess: pid => isFleetStartCommandLine(readProcessCommandLine(pid)),
1723
+ })) {
1724
+ console.log(`\n v${pkgVersion} is installed, but the running fleet started before it was — verifying it before restarting onto it.`);
1725
+ const verified = await verifyInstalled();
1726
+ if (!verified.ok)
1727
+ return;
1650
1728
  console.log(` ✓ v${pkgVersion} verified — restarting the fleet onto it.\n`);
1651
1729
  markUpdateInProgress(DATA_DIR);
1652
1730
  await activateVerified(verified, false);
1653
1731
  return;
1654
1732
  }
1733
+ // #1829 ⑥: installed, but no fleet runs (a restart that never finished, a stopped service): not "up to date".
1734
+ // Verified first, then the exact next step — never started from here, so nothing bypasses `agend start`'s checks.
1735
+ // Only a fleet.pid that is missing, or names a pid proven gone / not a fleet, says so (review: unreadable,
1736
+ // empty or malformed is unknown, and keeps the old answer).
1737
+ const pidEvidence = readFleetPidEvidence(join(DATA_DIR, "fleet.pid"));
1738
+ if (pidEvidence.kind !== "unknown" && fleetProvenStopped({
1739
+ pid: pidEvidence.kind === "valid" ? pidEvidence.pid : null,
1740
+ alive: pid => pidAlive(pid),
1741
+ commandLine: pid => readProcessCommandLine(pid),
1742
+ isFleetProcess: line => isFleetStartCommandLine(line),
1743
+ })) {
1744
+ const verified = await verifyInstalled();
1745
+ if (!verified.ok)
1746
+ return;
1747
+ console.error(`\n ✗ AgEnD v${pkgVersion} is installed but the fleet is not running — run: agend start\n`);
1748
+ process.exitCode = 1;
1749
+ return;
1750
+ }
1655
1751
  console.log(`\n ✓ Already up to date (v${pkgVersion})\n`);
1656
1752
  setUpdateProgressStage(DATA_DIR, "complete", { version: pkgVersion });
1657
1753
  return;
@@ -1757,7 +1853,7 @@ program
1757
1853
  : {}),
1758
1854
  lock: prefix => {
1759
1855
  const lock = acquireInstallLock(prefix, { spec: pkg, agendHome: DATA_DIR }, {
1760
- pid: process.pid, processStart: admission.processStart, newToken: () => randomBytes(16).toString("hex"),
1856
+ pid: process.pid, processStart: admission.processStart, startTicks: admission.startTicks, bootId: admission.bootId, newToken: () => randomBytes(16).toString("hex"),
1761
1857
  now: () => new Date(), log: message => console.log(message),
1762
1858
  });
1763
1859
  if (!lock.ok)
@@ -1813,7 +1909,7 @@ program
1813
1909
  .option("--activate", "Deprecated; service activation is now the default")
1814
1910
  .option("--no-activate", "Write the service file without activating it")
1815
1911
  .action(async (opts) => {
1816
- const { installService, activateService, detectPlatform, renderLaunchdPlist, launchdPlistPath, getServicePath, getSystemServicePath } = await import("./service-installer.js");
1912
+ const { installService, activateService, detectPlatform, renderLaunchdPlist, launchdPlistPath, getServicePath, getSystemServicePath, serviceHomeFor } = await import("./service-installer.js");
1817
1913
  // #1631 B: an older updater (2.2.0-beta.1 and before) refreshes a hand-started fleet's service too, through this
1818
1914
  // command — which would CREATE a unit or plist, and every later update would then take a manager it may not reach
1819
1915
  // as the authority. When no service exists and it is certain this is that updater's refresh, write nothing. A
@@ -1827,13 +1923,20 @@ program
1827
1923
  }
1828
1924
  }
1829
1925
  const execPath = canonicalCliEntry();
1830
- const serviceVars = {
1831
- label: "com.agend.fleet",
1832
- execPath,
1833
- path: process.env.PATH,
1834
- workingDirectory: DATA_DIR,
1835
- logPath: join(DATA_DIR, "fleet.log"),
1836
- };
1926
+ // A fleet on a custom AGEND_HOME must come back on it after a service restart or an update: the definition names
1927
+ // it (the default home adds nothing). A refresh from the default home keeps an existing definition's home.
1928
+ const existingPath = getServicePath();
1929
+ const service = serviceHomeFor(DATA_DIR, {
1930
+ refresh: opts.activate === false,
1931
+ existingDefinition: existingPath ? (() => { try {
1932
+ return readFileSync(existingPath, "utf-8");
1933
+ }
1934
+ catch {
1935
+ return null;
1936
+ } })() : null,
1937
+ isDefault: isDefaultAgendHome,
1938
+ });
1939
+ const serviceVars = { label: "com.agend.fleet", execPath, path: process.env.PATH, ...service };
1837
1940
  // #1450 C6: on launchd, writing a new plist without activating is a PLANNED activation — proven, recorded, and
1838
1941
  // left for `agend restart` (a reload there would itself start the job). See service-plan.ts.
1839
1942
  if (opts.activate === false && detectPlatform() === "macos") {
@@ -1863,6 +1966,25 @@ program
1863
1966
  console.log(` ${outcome.message}`);
1864
1967
  return;
1865
1968
  }
1969
+ // #1884: a legacy system unit is the service a root fleet runs under; the refresh brings its ExecStart to this
1970
+ // install's selection too, or `agend restart` refuses it after an update (see refreshSystemUnitExecStart).
1971
+ const systemUnit = opts.activate === false && detectPlatform() !== "macos" ? getSystemServicePath() : null;
1972
+ if (systemUnit) {
1973
+ const { refreshSystemUnitExecStart } = await import("./service-installer.js");
1974
+ try {
1975
+ const refreshed = refreshSystemUnitExecStart(systemUnit, serviceVars, dirname(dirname(execPath)));
1976
+ if (refreshed.kind === "updated")
1977
+ console.log(` ✓ ${systemUnit}: ExecStart=${refreshed.execStart}`);
1978
+ else if (refreshed.kind === "foreign")
1979
+ console.log(` ℹ ${systemUnit} left as it is: ${refreshed.reason}.`);
1980
+ else if (refreshed.kind === "unreadable")
1981
+ console.log(` ⚠ ${systemUnit} could not be read; left as it is.`);
1982
+ }
1983
+ catch (err) {
1984
+ console.error(` ✗ ${systemUnit} could not be updated (${err.message}); \`agend restart\` will refuse until its ExecStart starts this install. Run the update as root, or: sudo agend install --no-activate`);
1985
+ process.exitCode = 1;
1986
+ }
1987
+ }
1866
1988
  const svcPath = installService(serviceVars);
1867
1989
  console.log(t("install.path", svcPath));
1868
1990
  if (opts.activate !== false) {
@@ -2233,7 +2355,8 @@ program
2233
2355
  }
2234
2356
  if (filterCustom)
2235
2357
  console.log(` ⚠ ${unitPath} sets its own CoredumpFilter; left as is. CoredumpFilter=0 keeps crash dumps to a few KB (#1113).`);
2236
- if (expectation.ok && refuses(guard.guardSystemd(captureRun, systemdTarget.user, systemdTarget.unit, expectation.expected, guardDeps)))
2358
+ const { systemdPeerBus } = await import("./service-activation.js");
2359
+ if (expectation.ok && refuses(guard.guardSystemd(captureRun, systemdTarget.user, systemdTarget.unit, expectation.expected, guardDeps, systemdPeerBus)))
2237
2360
  return;
2238
2361
  // Clears the failed state AND the start-limit counter (#1113:
2239
2362
  // StartLimitBurst=4 in 30min) for whichever unit this restart targets,
@@ -2272,8 +2395,13 @@ program
2272
2395
  if (printed.status === 0) {
2273
2396
  const plistPath = getServicePath() ?? join(homedir(), "Library", "LaunchAgents", `${label}.plist`);
2274
2397
  // Path 2 (#1450 C6): a proven new plist waits on disk with its plan — exactly one bootout + bootstrap.
2275
- const { readPlan, admitPlan, planPath } = await import("./service-plan.js");
2276
- const plan = opts.force ? null : readPlan(DATA_DIR, deps);
2398
+ const { readPlan, admitPlan, planPath, planCarriedOut } = await import("./service-plan.js");
2399
+ const recorded = opts.force ? null : readPlan(DATA_DIR, deps);
2400
+ // #1871: an interrupted update's plan whose job launchd already runs (after `agend start`) is done: restart that
2401
+ // job as an unchanged one (path 1) instead of refusing it for its new pid.
2402
+ const plan = recorded && planCarriedOut(recorded, target, deps) ? null : recorded;
2403
+ if (recorded && !plan)
2404
+ console.log(" ℹ launchd already runs the job an interrupted update planned; restarting it as it is.");
2277
2405
  if (plan && expectation.ok) {
2278
2406
  const pkgDir = dirname(dirname(expectation.expected.entry));
2279
2407
  const admitted = admitPlan(plan, { target, expected: expectation.expected, pkgDir }, deps);
@@ -2282,6 +2410,8 @@ program
2282
2410
  process.exitCode = 1;
2283
2411
  return;
2284
2412
  }
2413
+ if (await handOffLaunchdActivation("restart"))
2414
+ return;
2285
2415
  const { activateService } = await import("./service-activation.js");
2286
2416
  const outcome = activateService({ kind: "launchd", label, plistPath: plan.plistPath, domain: `gui/${uid}` }, { bin: expectation.expected.entry, entry: expectation.expected.entry, node: expectation.expected.node, dir: pkgDir }, {
2287
2417
  ...deps,
@@ -2555,6 +2685,16 @@ program
2555
2685
  const { runSetupWizard } = await import("./setup-wizard.js");
2556
2686
  await runSetupWizard();
2557
2687
  });
2688
+ // The opener is optional (a server or WSL without xdg-open): a spawn failure is an `error` event, and unhandled it
2689
+ // crashed the command after the link was already printed (#1854). The printed link still works, so say so instead.
2690
+ function openInBrowser(url) {
2691
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
2692
+ const child = spawn(cmd, [url], { detached: true, stdio: "ignore" });
2693
+ child.on("error", (err) => {
2694
+ console.error(`Could not open a browser (${cmd}: ${err.code ?? err.message}). Open the link above yourself.`);
2695
+ });
2696
+ child.unref();
2697
+ }
2558
2698
  program
2559
2699
  .command("web")
2560
2700
  .description("Open the Web UI sign-in page in your browser and print a one-time sign-in code")
@@ -2597,9 +2737,7 @@ program
2597
2737
  console.log(`Code: ${code} (works once, valid ~${minutes} min)`);
2598
2738
  if (opts.code)
2599
2739
  return;
2600
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
2601
- const child = spawn(cmd, [signin], { detached: true, stdio: "ignore" });
2602
- child.unref();
2740
+ openInBrowser(signin);
2603
2741
  });
2604
2742
  program
2605
2743
  .command("web-token")
@@ -2629,9 +2767,7 @@ program
2629
2767
  const port = fleet.health_port ?? 19280;
2630
2768
  const url = `http://localhost:${port}/view`;
2631
2769
  console.log(`Opening ${url}`);
2632
- const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "explorer" : "xdg-open";
2633
- const child = spawn(cmd, [url], { detached: true, stdio: "ignore" });
2634
- child.unref();
2770
+ openInBrowser(url);
2635
2771
  });
2636
2772
  program
2637
2773
  .command("validate")
@@ -2940,7 +3076,21 @@ async function fuzzyMatch(query, names) {
2940
3076
  return null;
2941
3077
  }
2942
3078
  async function resolveInstance(query, config) {
2943
- const names = await listInstanceNames(config);
3079
+ const namesRaw = await listInstanceNames(config);
3080
+ const names = [...new Set(namesRaw)]; // P3-3: deduplicate (a classicBot instanceName can equal a fleet name)
3081
+ const { resolveName } = await import("./topic-commands.js");
3082
+ const entries = names.map((name) => ({
3083
+ name,
3084
+ displayName: config.instances[name]?.display_name,
3085
+ }));
3086
+ const result = resolveName(query, entries);
3087
+ if (result.kind === "name")
3088
+ return result.realName;
3089
+ if (result.kind === "ambiguous") {
3090
+ console.error(`"${query}" matches multiple instances: ${result.realNames.join(", ")}. Use the real name.`);
3091
+ process.exit(1);
3092
+ }
3093
+ // Fall through to the original fuzzy match on real names.
2944
3094
  const match = await fuzzyMatch(query, names);
2945
3095
  if (!match) {
2946
3096
  console.error(`No instance matching "${query}". Available: ${names.join(", ")}`);