@songsid/agend 2.1.12 → 2.2.0-alpha.2

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 (436) hide show
  1. package/README.md +4 -2
  2. package/README.zh-TW.md +4 -2
  3. package/dist/agent-cli.js +6 -1
  4. package/dist/agent-cli.js.map +1 -1
  5. package/dist/agent-endpoint.js +68 -12
  6. package/dist/agent-endpoint.js.map +1 -1
  7. package/dist/agent-token-header.d.ts +13 -0
  8. package/dist/agent-token-header.js +16 -0
  9. package/dist/agent-token-header.js.map +1 -0
  10. package/dist/auth/one-time-code.d.ts +19 -0
  11. package/dist/auth/one-time-code.js +49 -0
  12. package/dist/auth/one-time-code.js.map +1 -0
  13. package/dist/auth-api.d.ts +52 -0
  14. package/dist/auth-api.js +346 -0
  15. package/dist/auth-api.js.map +1 -0
  16. package/dist/backend/antigravity.js +4 -3
  17. package/dist/backend/antigravity.js.map +1 -1
  18. package/dist/backend/claude-code.d.ts +4 -0
  19. package/dist/backend/claude-code.js +97 -0
  20. package/dist/backend/claude-code.js.map +1 -1
  21. package/dist/backend/codex.js +59 -30
  22. package/dist/backend/codex.js.map +1 -1
  23. package/dist/backend/grok.js +1 -1
  24. package/dist/backend/grok.js.map +1 -1
  25. package/dist/backend/kiro-agent.d.ts +66 -0
  26. package/dist/backend/kiro-agent.js +302 -0
  27. package/dist/backend/kiro-agent.js.map +1 -0
  28. package/dist/backend/kiro-identity.d.ts +80 -0
  29. package/dist/backend/kiro-identity.js +537 -0
  30. package/dist/backend/kiro-identity.js.map +1 -0
  31. package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
  32. package/dist/backend/kiro-v2-store-worker.js +87 -0
  33. package/dist/backend/kiro-v2-store-worker.js.map +1 -0
  34. package/dist/backend/kiro-v2-store.d.ts +54 -0
  35. package/dist/backend/kiro-v2-store.js +131 -0
  36. package/dist/backend/kiro-v2-store.js.map +1 -0
  37. package/dist/backend/kiro-v3-identity.d.ts +5 -0
  38. package/dist/backend/kiro-v3-identity.js +22 -0
  39. package/dist/backend/kiro-v3-identity.js.map +1 -1
  40. package/dist/backend/kiro.d.ts +90 -7
  41. package/dist/backend/kiro.js +500 -124
  42. package/dist/backend/kiro.js.map +1 -1
  43. package/dist/backend/muse.js +1 -1
  44. package/dist/backend/muse.js.map +1 -1
  45. package/dist/backend/types.d.ts +51 -3
  46. package/dist/backend/types.js.map +1 -1
  47. package/dist/cache-analysis.d.ts +92 -0
  48. package/dist/cache-analysis.js +147 -0
  49. package/dist/cache-analysis.js.map +1 -0
  50. package/dist/cache-ledger.d.ts +108 -0
  51. package/dist/cache-ledger.js +230 -0
  52. package/dist/cache-ledger.js.map +1 -0
  53. package/dist/cache-prices.d.ts +32 -0
  54. package/dist/cache-prices.js +73 -0
  55. package/dist/cache-prices.js.map +1 -0
  56. package/dist/cache-scan.d.ts +39 -0
  57. package/dist/cache-scan.js +115 -0
  58. package/dist/cache-scan.js.map +1 -0
  59. package/dist/cache-service.d.ts +115 -0
  60. package/dist/cache-service.js +417 -0
  61. package/dist/cache-service.js.map +1 -0
  62. package/dist/channel/adapters/discord.d.ts +1 -0
  63. package/dist/channel/adapters/discord.js +29 -10
  64. package/dist/channel/adapters/discord.js.map +1 -1
  65. package/dist/channel/adapters/telegram.d.ts +1 -0
  66. package/dist/channel/adapters/telegram.js +13 -1
  67. package/dist/channel/adapters/telegram.js.map +1 -1
  68. package/dist/channel/types.d.ts +9 -1
  69. package/dist/classic-channel-manager.d.ts +18 -10
  70. package/dist/classic-channel-manager.js +45 -18
  71. package/dist/classic-channel-manager.js.map +1 -1
  72. package/dist/cli-entry.d.ts +19 -0
  73. package/dist/cli-entry.js +36 -0
  74. package/dist/cli-entry.js.map +1 -0
  75. package/dist/cli.js +471 -146
  76. package/dist/cli.js.map +1 -1
  77. package/dist/command-table.d.ts +5 -3
  78. package/dist/command-table.js +29 -27
  79. package/dist/command-table.js.map +1 -1
  80. package/dist/config-validator.js +46 -0
  81. package/dist/config-validator.js.map +1 -1
  82. package/dist/config.js +12 -0
  83. package/dist/config.js.map +1 -1
  84. package/dist/connection-secrets.d.ts +6 -0
  85. package/dist/connection-secrets.js +4 -0
  86. package/dist/connection-secrets.js.map +1 -1
  87. package/dist/daemon.d.ts +95 -4
  88. package/dist/daemon.js +748 -111
  89. package/dist/daemon.js.map +1 -1
  90. package/dist/darwin-memory.d.ts +6 -1
  91. package/dist/darwin-memory.js +17 -6
  92. package/dist/darwin-memory.js.map +1 -1
  93. package/dist/delivery-consumed-watch.d.ts +85 -0
  94. package/dist/delivery-consumed-watch.js +244 -0
  95. package/dist/delivery-consumed-watch.js.map +1 -0
  96. package/dist/delivery-outbox.d.ts +61 -0
  97. package/dist/delivery-outbox.js +120 -2
  98. package/dist/delivery-outbox.js.map +1 -1
  99. package/dist/delivery-reconciliation.d.ts +14 -5
  100. package/dist/delivery-reconciliation.js +27 -4
  101. package/dist/delivery-reconciliation.js.map +1 -1
  102. package/dist/delivery-reconciliation.test.js +35 -1
  103. package/dist/delivery-reconciliation.test.js.map +1 -1
  104. package/dist/event-log.d.ts +4 -1
  105. package/dist/event-log.js +6 -3
  106. package/dist/event-log.js.map +1 -1
  107. package/dist/event-loop-watch.d.ts +28 -0
  108. package/dist/event-loop-watch.js +75 -3
  109. package/dist/event-loop-watch.js.map +1 -1
  110. package/dist/fleet-context.d.ts +22 -2
  111. package/dist/fleet-control-audit.d.ts +5 -0
  112. package/dist/fleet-control-audit.js +7 -0
  113. package/dist/fleet-control-audit.js.map +1 -1
  114. package/dist/fleet-level-config.d.ts +2 -2
  115. package/dist/fleet-level-config.js +28 -1
  116. package/dist/fleet-level-config.js.map +1 -1
  117. package/dist/fleet-manager.d.ts +339 -12
  118. package/dist/fleet-manager.js +2623 -461
  119. package/dist/fleet-manager.js.map +1 -1
  120. package/dist/full-restart.js +2 -3
  121. package/dist/full-restart.js.map +1 -1
  122. package/dist/general-knowledge/skills/persona-emoji/SKILL.md +3 -1
  123. package/dist/host-memory.d.ts +4 -0
  124. package/dist/host-memory.js.map +1 -1
  125. package/dist/install-lock.d.ts +49 -0
  126. package/dist/install-lock.js +200 -0
  127. package/dist/install-lock.js.map +1 -0
  128. package/dist/instance-config-impact.js +8 -2
  129. package/dist/instance-config-impact.js.map +1 -1
  130. package/dist/instance-lifecycle.d.ts +10 -5
  131. package/dist/instance-lifecycle.js +162 -53
  132. package/dist/instance-lifecycle.js.map +1 -1
  133. package/dist/locale.js +164 -16
  134. package/dist/locale.js.map +1 -1
  135. package/dist/login-controller.d.ts +2 -0
  136. package/dist/login-controller.js +17 -5
  137. package/dist/login-controller.js.map +1 -1
  138. package/dist/memory-pressure.d.ts +7 -8
  139. package/dist/memory-pressure.js +76 -15
  140. package/dist/memory-pressure.js.map +1 -1
  141. package/dist/monotonic-budget.d.ts +2 -0
  142. package/dist/monotonic-budget.js +28 -0
  143. package/dist/monotonic-budget.js.map +1 -0
  144. package/dist/needs-you-hub.d.ts +114 -0
  145. package/dist/needs-you-hub.js +370 -0
  146. package/dist/needs-you-hub.js.map +1 -0
  147. package/dist/needs-you-live.d.ts +116 -0
  148. package/dist/needs-you-live.js +228 -0
  149. package/dist/needs-you-live.js.map +1 -0
  150. package/dist/needs-you.d.ts +132 -0
  151. package/dist/needs-you.js +164 -0
  152. package/dist/needs-you.js.map +1 -0
  153. package/dist/node-version-guard.d.ts +30 -0
  154. package/dist/node-version-guard.js +50 -0
  155. package/dist/node-version-guard.js.map +1 -0
  156. package/dist/operator-control-paths.d.ts +4 -0
  157. package/dist/operator-control-paths.js +24 -0
  158. package/dist/operator-control-paths.js.map +1 -0
  159. package/dist/outbound-handlers.d.ts +2 -0
  160. package/dist/outbound-handlers.js +9 -2
  161. package/dist/outbound-handlers.js.map +1 -1
  162. package/dist/outbound-schemas.d.ts +5 -5
  163. package/dist/outbound-schemas.js +1 -1
  164. package/dist/outbound-schemas.js.map +1 -1
  165. package/dist/package-preimage.d.ts +26 -0
  166. package/dist/package-preimage.js +96 -0
  167. package/dist/package-preimage.js.map +1 -0
  168. package/dist/profile-control.js +4 -20
  169. package/dist/profile-control.js.map +1 -1
  170. package/dist/public-web-gateway.d.ts +18 -0
  171. package/dist/public-web-gateway.js +141 -0
  172. package/dist/public-web-gateway.js.map +1 -0
  173. package/dist/public-web-link.d.ts +53 -0
  174. package/dist/public-web-link.js +175 -0
  175. package/dist/public-web-link.js.map +1 -0
  176. package/dist/quickstart-api.d.ts +8 -1
  177. package/dist/quickstart-api.js +86 -48
  178. package/dist/quickstart-api.js.map +1 -1
  179. package/dist/quickstart.d.ts +1 -0
  180. package/dist/quickstart.js +16 -6
  181. package/dist/quickstart.js.map +1 -1
  182. package/dist/restart-guard.d.ts +52 -0
  183. package/dist/restart-guard.js +109 -0
  184. package/dist/restart-guard.js.map +1 -0
  185. package/dist/scheduler/db.d.ts +7 -1
  186. package/dist/scheduler/db.js +44 -2
  187. package/dist/scheduler/db.js.map +1 -1
  188. package/dist/scheduler/index.d.ts +1 -1
  189. package/dist/scheduler/retry-label.d.ts +11 -0
  190. package/dist/scheduler/retry-label.js +24 -0
  191. package/dist/scheduler/retry-label.js.map +1 -0
  192. package/dist/scheduler/scheduler.d.ts +55 -2
  193. package/dist/scheduler/scheduler.js +220 -8
  194. package/dist/scheduler/scheduler.js.map +1 -1
  195. package/dist/scheduler/types.d.ts +22 -0
  196. package/dist/scheduler/types.js.map +1 -1
  197. package/dist/secret-store.d.ts +5 -0
  198. package/dist/secret-store.js +20 -0
  199. package/dist/secret-store.js.map +1 -1
  200. package/dist/service-activation.d.ts +152 -0
  201. package/dist/service-activation.js +407 -0
  202. package/dist/service-activation.js.map +1 -0
  203. package/dist/service-installer.d.ts +63 -1
  204. package/dist/service-installer.js +141 -16
  205. package/dist/service-installer.js.map +1 -1
  206. package/dist/service-plan.d.ts +65 -0
  207. package/dist/service-plan.js +108 -0
  208. package/dist/service-plan.js.map +1 -0
  209. package/dist/service-restart-selection.d.ts +6 -5
  210. package/dist/service-restart-selection.js +6 -5
  211. package/dist/service-restart-selection.js.map +1 -1
  212. package/dist/settings-api.d.ts +10 -2
  213. package/dist/settings-api.js +216 -72
  214. package/dist/settings-api.js.map +1 -1
  215. package/dist/settings-baseline.d.ts +20 -0
  216. package/dist/settings-baseline.js +104 -0
  217. package/dist/settings-baseline.js.map +1 -0
  218. package/dist/settings-change.d.ts +21 -0
  219. package/dist/settings-change.js +161 -0
  220. package/dist/settings-change.js.map +1 -0
  221. package/dist/settings-confirmation.d.ts +105 -0
  222. package/dist/settings-confirmation.js +232 -0
  223. package/dist/settings-confirmation.js.map +1 -0
  224. package/dist/settings-control.d.ts +41 -0
  225. package/dist/settings-control.js +294 -0
  226. package/dist/settings-control.js.map +1 -0
  227. package/dist/settings-effect.d.ts +24 -0
  228. package/dist/settings-effect.js +169 -0
  229. package/dist/settings-effect.js.map +1 -0
  230. package/dist/settings-http-confirmation.d.ts +29 -0
  231. package/dist/settings-http-confirmation.js +123 -0
  232. package/dist/settings-http-confirmation.js.map +1 -0
  233. package/dist/settings-instance-patch.d.ts +4 -0
  234. package/dist/settings-instance-patch.js +26 -0
  235. package/dist/settings-instance-patch.js.map +1 -0
  236. package/dist/settings-request-capability.d.ts +16 -0
  237. package/dist/settings-request-capability.js +32 -0
  238. package/dist/settings-request-capability.js.map +1 -0
  239. package/dist/settings-transaction.d.ts +65 -0
  240. package/dist/settings-transaction.js +223 -0
  241. package/dist/settings-transaction.js.map +1 -0
  242. package/dist/setup-auth.d.ts +10 -18
  243. package/dist/setup-auth.js +14 -38
  244. package/dist/setup-auth.js.map +1 -1
  245. package/dist/setup-form.d.ts +1 -1
  246. package/dist/setup-form.js +75 -18
  247. package/dist/setup-form.js.map +1 -1
  248. package/dist/setup-guide.d.ts +13 -0
  249. package/dist/setup-guide.js +25 -0
  250. package/dist/setup-guide.js.map +1 -0
  251. package/dist/setup-host.d.ts +16 -0
  252. package/dist/setup-host.js +199 -35
  253. package/dist/setup-host.js.map +1 -1
  254. package/dist/setup-wizard.js +5 -1
  255. package/dist/setup-wizard.js.map +1 -1
  256. package/dist/slash-authz.d.ts +17 -3
  257. package/dist/slash-authz.js +5 -0
  258. package/dist/slash-authz.js.map +1 -1
  259. package/dist/spawn-gate.js +3 -3
  260. package/dist/spawn-gate.js.map +1 -1
  261. package/dist/sqlite-open-errors.d.ts +12 -0
  262. package/dist/sqlite-open-errors.js +13 -0
  263. package/dist/sqlite-open-errors.js.map +1 -0
  264. package/dist/statusline-watcher.d.ts +5 -0
  265. package/dist/statusline-watcher.js +2 -0
  266. package/dist/statusline-watcher.js.map +1 -1
  267. package/dist/steer-capability.d.ts +6 -0
  268. package/dist/steer-capability.js +8 -0
  269. package/dist/steer-capability.js.map +1 -1
  270. package/dist/sync-work-attribution.d.ts +14 -0
  271. package/dist/sync-work-attribution.js +43 -8
  272. package/dist/sync-work-attribution.js.map +1 -1
  273. package/dist/tips.js +10 -5
  274. package/dist/tips.js.map +1 -1
  275. package/dist/tmux-control.d.ts +57 -5
  276. package/dist/tmux-control.js +379 -61
  277. package/dist/tmux-control.js.map +1 -1
  278. package/dist/tmux-manager.d.ts +20 -6
  279. package/dist/tmux-manager.js +90 -47
  280. package/dist/tmux-manager.js.map +1 -1
  281. package/dist/tmux-read.d.ts +59 -0
  282. package/dist/tmux-read.js +213 -0
  283. package/dist/tmux-read.js.map +1 -0
  284. package/dist/topic-commands.d.ts +21 -10
  285. package/dist/topic-commands.js +205 -63
  286. package/dist/topic-commands.js.map +1 -1
  287. package/dist/transcript-delta-reader.js +2 -1
  288. package/dist/transcript-delta-reader.js.map +1 -1
  289. package/dist/tunnel/cloudflared-install.d.ts +2 -0
  290. package/dist/tunnel/cloudflared-install.js +1 -1
  291. package/dist/tunnel/cloudflared-install.js.map +1 -1
  292. package/dist/tunnel/purpose-lane.d.ts +14 -0
  293. package/dist/tunnel/purpose-lane.js +43 -0
  294. package/dist/tunnel/purpose-lane.js.map +1 -0
  295. package/dist/turn-reply-guard.d.ts +2 -0
  296. package/dist/turn-reply-guard.js +10 -1
  297. package/dist/turn-reply-guard.js.map +1 -1
  298. package/dist/types.d.ts +33 -0
  299. package/dist/ui/app.html +18 -0
  300. package/dist/ui/chat-commands.js +43 -0
  301. package/dist/ui/chat-render.js +478 -0
  302. package/dist/ui/chat-store.js +318 -0
  303. package/dist/ui/chat-strings.js +89 -0
  304. package/dist/ui/chat-thread.js +246 -0
  305. package/dist/ui/chat-tour.js +116 -0
  306. package/dist/ui/fleet-cache.js +168 -0
  307. package/dist/ui/fleet-org.js +142 -0
  308. package/dist/ui/panel-chat.js +504 -0
  309. package/dist/ui/panel-fleet.js +427 -0
  310. package/dist/ui/panel-needs.js +202 -0
  311. package/dist/ui/panel-settings.js +754 -0
  312. package/dist/ui/preview-panel.js +202 -0
  313. package/dist/ui/preview.js +270 -0
  314. package/dist/ui/settings-apply.js +239 -0
  315. package/dist/ui/settings-confirm.js +186 -0
  316. package/dist/ui/settings-dialogs.js +546 -0
  317. package/dist/ui/settings-model.js +277 -0
  318. package/dist/ui/settings-strings.js +654 -0
  319. package/dist/ui/settings-wizard.js +140 -0
  320. package/dist/ui/shared/agend-auth.js +93 -0
  321. package/dist/ui/shared/app-ctx.js +80 -0
  322. package/dist/ui/shared/app-html.js +9 -0
  323. package/dist/ui/shared/app-i18n.js +95 -0
  324. package/dist/ui/shared/app-nav.js +73 -0
  325. package/dist/ui/shared/app-needs.js +114 -0
  326. package/dist/ui/shared/app-route.js +73 -0
  327. package/dist/ui/shared/app-session.js +66 -0
  328. package/dist/ui/shared/app-shell.js +346 -0
  329. package/dist/ui/shared/app-store.js +51 -0
  330. package/dist/ui/shared/app-stream.js +201 -0
  331. package/dist/ui/shared/app.css +820 -0
  332. package/dist/ui/shared/app.js +88 -0
  333. package/dist/ui/shared/fonts/OFL.txt +92 -0
  334. package/dist/ui/shared/fonts/README.md +21 -0
  335. package/dist/ui/shared/fonts/inter.woff2 +0 -0
  336. package/dist/ui/shared/htm.module.js +1 -0
  337. package/dist/ui/shared/panel-view.js +655 -0
  338. package/dist/ui/shared/preact-hooks.module.js +2 -0
  339. package/dist/ui/shared/preact.module.js +2 -0
  340. package/dist/ui/shared/signin.js +142 -0
  341. package/dist/ui/shared/theme.js +29 -0
  342. package/dist/ui/shared/tokens.css +153 -0
  343. package/dist/ui/shared/ui-confirm.js +43 -0
  344. package/dist/ui/shared/ui-dialog.js +45 -0
  345. package/dist/ui/shared/ui-icons.js +63 -0
  346. package/dist/ui/shared/ui-menu.js +39 -0
  347. package/dist/ui/shared/ui-states.js +20 -0
  348. package/dist/ui/shared/ui-toast.js +18 -0
  349. package/dist/ui/shared/vendor/htm.LICENSE +202 -0
  350. package/dist/ui/shared/vendor/preact.LICENSE +21 -0
  351. package/dist/ui/shared/vendor/vendor.json +39 -0
  352. package/dist/ui/shared/view-strings.js +309 -0
  353. package/dist/ui/signin.html +52 -0
  354. package/dist/update-check.d.ts +6 -2
  355. package/dist/update-check.js +12 -3
  356. package/dist/update-check.js.map +1 -1
  357. package/dist/update-dispatch.d.ts +28 -0
  358. package/dist/update-dispatch.js +73 -0
  359. package/dist/update-dispatch.js.map +1 -0
  360. package/dist/update-install.d.ts +129 -0
  361. package/dist/update-install.js +262 -0
  362. package/dist/update-install.js.map +1 -0
  363. package/dist/upgrade-notices.d.ts +17 -0
  364. package/dist/upgrade-notices.js +93 -0
  365. package/dist/upgrade-notices.js.map +1 -0
  366. package/dist/usage/statusline-usage.d.ts +2 -0
  367. package/dist/usage/statusline-usage.js +14 -10
  368. package/dist/usage/statusline-usage.js.map +1 -1
  369. package/dist/view-api.d.ts +6 -1
  370. package/dist/view-api.js +62 -51
  371. package/dist/view-api.js.map +1 -1
  372. package/dist/web-api.d.ts +85 -14
  373. package/dist/web-api.js +557 -77
  374. package/dist/web-api.js.map +1 -1
  375. package/dist/web-auth.d.ts +71 -27
  376. package/dist/web-auth.js +169 -69
  377. package/dist/web-auth.js.map +1 -1
  378. package/dist/web-body.d.ts +3 -0
  379. package/dist/web-body.js +55 -0
  380. package/dist/web-body.js.map +1 -0
  381. package/dist/web-channel-echo.d.ts +9 -0
  382. package/dist/web-channel-echo.js +55 -0
  383. package/dist/web-channel-echo.js.map +1 -0
  384. package/dist/web-chat-history.d.ts +125 -0
  385. package/dist/web-chat-history.js +184 -0
  386. package/dist/web-chat-history.js.map +1 -0
  387. package/dist/web-commands.d.ts +77 -0
  388. package/dist/web-commands.js +81 -0
  389. package/dist/web-commands.js.map +1 -0
  390. package/dist/web-continuation.d.ts +7 -0
  391. package/dist/web-continuation.js +22 -0
  392. package/dist/web-continuation.js.map +1 -0
  393. package/dist/web-host-guard.d.ts +34 -0
  394. package/dist/web-host-guard.js +57 -1
  395. package/dist/web-host-guard.js.map +1 -1
  396. package/dist/web-login.d.ts +72 -0
  397. package/dist/web-login.js +132 -0
  398. package/dist/web-login.js.map +1 -0
  399. package/dist/web-org.d.ts +55 -0
  400. package/dist/web-org.js +67 -0
  401. package/dist/web-org.js.map +1 -0
  402. package/dist/web-preview.d.ts +77 -0
  403. package/dist/web-preview.js +240 -0
  404. package/dist/web-preview.js.map +1 -0
  405. package/dist/web-request-context.d.ts +10 -0
  406. package/dist/web-request-context.js +13 -0
  407. package/dist/web-request-context.js.map +1 -0
  408. package/dist/web-session.d.ts +167 -0
  409. package/dist/web-session.js +389 -0
  410. package/dist/web-session.js.map +1 -0
  411. package/dist/web-shell-routes.d.ts +59 -0
  412. package/dist/web-shell-routes.js +96 -0
  413. package/dist/web-shell-routes.js.map +1 -0
  414. package/dist/web-upload.d.ts +183 -0
  415. package/dist/web-upload.js +468 -0
  416. package/dist/web-upload.js.map +1 -0
  417. package/dist/website-tips-renderer.d.ts +2 -2
  418. package/dist/website-tips-renderer.js +3 -3
  419. package/dist/website-tips-renderer.js.map +1 -1
  420. package/launcher/agend +110 -0
  421. package/launcher/agend-agent +110 -0
  422. package/launcher/agend-agent.cjs +4 -0
  423. package/launcher/agend.cjs +4 -0
  424. package/launcher/install-admission.cjs +105 -0
  425. package/launcher/launch.cjs +64 -0
  426. package/launcher/postinstall.cjs +114 -0
  427. package/launcher/run-in-process.cjs +13 -0
  428. package/launcher/runtime-platform.cjs +86 -0
  429. package/launcher/runtime-select.cjs +217 -0
  430. package/package.json +18 -7
  431. package/scripts/preinstall-guard.cjs +55 -0
  432. package/templates/launchd.plist.ejs +2 -2
  433. package/templates/systemd.service.ejs +1 -1
  434. package/dist/ui/dashboard.html +0 -774
  435. package/dist/ui/settings.html +0 -2165
  436. package/dist/ui/view.html +0 -1139
@@ -1,16 +1,20 @@
1
1
  import { measureSyncWork } from "../sync-work-attribution.js";
2
2
  import { EFFORT_CAPABILITIES } from "./effort-metadata.js";
3
- import { join } from "node:path";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { homedir } from "node:os";
4
5
  import { getAgendHome } from "../paths.js";
5
- import { credentialHomeSpec, credentialProfileHome, prepareCredentialProfileHome, resolveCredentialProfile, } from "./credential-profile.js";
6
+ import { credentialHomeSpec, credentialProfileHome, credentialProfileStoreHome, prepareCredentialProfileHome, resolveCredentialProfile, } from "./credential-profile.js";
7
+ import { kiroAgentName, kiroFleetTag, writeKiroAgent, removeKiroAgent, writeSharedKiroMcpEntries, removeSharedKiroMcpEntries, writeTaggedKiroSteering, removeTaggedKiroSteering, } from "./kiro-agent.js";
8
+ import { resolveKiroIdentity, listKiroV1Sessions, listKiroV2Sessions, confirmKiroAgentSwitch, kiroAgentConfirmed, forgetKiroIdentity, kiroIdentityNeedsStore, kiroCanonicalDirectory, KiroIdentityError, } from "./kiro-identity.js";
9
+ import { readKiroLedger } from "./kiro-engine-ledger.js";
6
10
  import { execFileSync } from "node:child_process";
7
- import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync, unlinkSync, statSync } from "node:fs";
11
+ import { writeFileSync, chmodSync, statSync } from "node:fs";
8
12
  import { UnsupportedCliError, resolveBinary, shellQuote, validateEffort, validateModel, warnIfModelMismatch } from "./types.js";
9
13
  import { PIE_CLASS } from "../tui-glyphs.js";
10
14
  import { KIRO_EXPIRED_LOGIN_SCREEN } from "../login-flows.js";
11
15
  import { t } from "../locale.js";
12
16
  import { recordKiroLaunch } from "./kiro-engine-ledger.js";
13
- import { resolveKiroV3Resume } from "./kiro-v3-identity.js";
17
+ import { forgetKiroV3Identity, resolveKiroV3Resume } from "./kiro-v3-identity.js";
14
18
  // Kiro CLI feature gates. These are deliberately separate: the flags shipped
15
19
  // in different releases, so one broad "old Kiro" check would still crash some
16
20
  // supported versions with an unknown argument. Every row below was read from
@@ -26,18 +30,98 @@ export const KIRO_LEGACY_UI_MIN = "1.27.0";
26
30
  export const KIRO_EFFORT_FLAG_MIN = "2.6.0";
27
31
  /** Oldest kiro-cli AgEnD claims: older ones still launch, with a warning. */
28
32
  export const KIRO_SUPPORTED_MIN = "2.21.0";
33
+ /**
34
+ * Lowest version whose `--agent` and `--resume-id` the per-instance agent and conversation (#906) were verified on
35
+ * (2.21.0 and 2.28.0, docs/design/kiro-per-instance-agent.md). Below it an instance launches as before.
36
+ */
37
+ export const KIRO_INSTANCE_AGENT_MIN = "2.21.0";
29
38
  /**
30
39
  * Newest kiro-cli run live under AgEnD. Above it the launch flags come from
31
40
  * the binary's own --help instead of the version table, and the operator is
32
41
  * told the version is unverified.
33
42
  */
34
43
  export const KIRO_TESTED_MAX = "2.27.0";
44
+ /**
45
+ * kiro-cli versions, per TUI front-end, whose mid-turn steering AgEnD drives (#1405). Only kiro's TUI steers — typed
46
+ * input while a turn runs is injected into it ("steer", the default) or held for its end ("queue", Ctrl+S toggles); the
47
+ * legacy UI swallows it. A version/front-end is listed once a live run of it — a real account, a steer and a
48
+ * queued message mid-tool — has been captured (tests/fixtures/kiro-steer-1405, 2026-10-08: 2.27.1 `--tui
49
+ * --agent-engine=v2` and `--v3`, 2.28.0 `--tui --agent-engine=v2`; identical rows on all three). Any other version,
50
+ * 2.28.0's v3, the legacy UI and an undetected version all deliver a steer as an ordinary message after the turn.
51
+ */
52
+ export const KIRO_STEER_VERIFIED = {
53
+ "2.27.1": ["tui", "v3"],
54
+ "2.28.0": ["tui"],
55
+ };
56
+ /**
57
+ * The TUI composer's interrupt mode, read off its own row: the LAST row starting at column 0 with `›` (transcript user
58
+ * rows are indented), below which only blank rows or right-aligned hints (`/copy to clipboard`) may follow. Its text is
59
+ * the empty composer's placeholder (tui.js, identical in 2.21.0, 2.27.1 and 2.28.0; 2.21.0 shows no elapsed time):
60
+ *
61
+ * `› Kiro is working · 12s · Type to steer · Ctrl+S to queue` → "steer"
62
+ * `› Kiro is working · 12s · Type to queue · Ctrl+S to steer` → "queue" (also `· Type to queue` alone: a spec task run)
63
+ * `› ask a question or describe a task ↵` → "idle" (exactly; also plan mode's and tangent's
64
+ * complete forms — never a prefix of typed text)
65
+ *
66
+ * Any other text on the composer's row is typed text: "text" — positive evidence the box holds something. The goal,
67
+ * editing, initializing, spec-description and shell placeholders are other modes, and the ASCII glyph set (`.`/`enter`,
68
+ * never seen on a real pane of ours) is not read: null, like a screen with no composer row (a dialog) — null is never
69
+ * evidence of either an empty or a full box. The toggle key's label is whatever the user bound; it is read past, never
70
+ * pressed.
71
+ */
72
+ function readKiroComposer(pane) {
73
+ const rows = pane.split("\n").map(row => row.replace(/\s+$/, ""));
74
+ let i = rows.length - 1;
75
+ while (i >= 0 && (rows[i] === "" || /^[ \t]{20,}\S/.test(rows[i])))
76
+ i--;
77
+ // Typed text of more than one line continues below the `›` row, each row indented two spaces (live, 2.27.1 and
78
+ // 2.28.0, TUI v2 and v3): the composer starts at the `›` row above them.
79
+ let continued = 0;
80
+ while (i >= 0 && /^ {2,19}\S/.test(rows[i])) {
81
+ i--;
82
+ continued++;
83
+ }
84
+ const row = rows[i];
85
+ if (row === undefined || !/^›[ \t]+\S/.test(row))
86
+ return null;
87
+ // A placeholder is one row: with continuation rows below it, this is typed text — unless its first row is another
88
+ // mode's placeholder, which is then not ours to read.
89
+ if (continued > 0) {
90
+ if (/^›[ \t]+(?:Kiro is working\b|ask a question or describe a task\b|Goal (?:Active|Paused):|Editing queued message \d|Initializing\b|describe what "|running shell command\b)/.test(row))
91
+ return null;
92
+ return { mode: "text", text: row.replace(/^›[ \t]+/, "") };
93
+ }
94
+ const working = String.raw `^›[ \t]+Kiro is working(?:[ \t]+·[ \t]+[^·]+?)?[ \t]+·[ \t]+`;
95
+ const text = row.replace(/^›[ \t]+/, "");
96
+ if (new RegExp(`${working}Type to steer[ \\t]+·[ \\t]+[^·]+?[ \\t]+to queue$`).test(row))
97
+ return { mode: "steer", text };
98
+ if (new RegExp(`${working}Type to queue(?:[ \\t]+·[ \\t]+[^·]+?[ \\t]+to steer)?$`).test(row))
99
+ return { mode: "queue", text };
100
+ const idle = String.raw `^›[ \t]+ask a question or describe a task`;
101
+ if (new RegExp(`${idle}[ \\t]+↵$`).test(row)
102
+ || new RegExp(`${idle}[ \\t]+↵[ \\t]+·[ \\t]+exit plan mode: shift\\+tab$`).test(row)
103
+ || new RegExp(`${idle}[ \\t]+·[ \\t]+/tangent to go back[ \\t]+·[ \\t]+/tangent ls to view$`).test(row))
104
+ return { mode: "idle", text };
105
+ // Other placeholders (and anything the ASCII glyph set paints) are not typed text.
106
+ if (/^›[ \t]+(?:Kiro is working\b|Goal (?:Active|Paused):|Editing queued message \d|Initializing\b|describe what "|running shell command\b|ask a question or describe a task (?:\.|enter\b))/.test(row))
107
+ return null;
108
+ return { mode: "text", text };
109
+ }
110
+ export function readKiroSteerComposer(pane) {
111
+ return readKiroComposer(pane)?.mode ?? null;
112
+ }
113
+ /** The typed text on the composer's row when it reads "text" (its first row, as kiro paints it); else null. */
114
+ export function readKiroComposerText(pane) {
115
+ const c = readKiroComposer(pane);
116
+ return c?.mode === "text" ? c.text : null;
117
+ }
35
118
  const UNKNOWN_KIRO_COMPATIBILITY = {
36
119
  supportsLegacyUi: false,
37
120
  supportsTui: false,
38
121
  supportsV3: false,
39
122
  agentEngines: null,
40
123
  supportsEffortFlag: false,
124
+ supportsInstanceAgent: false,
41
125
  source: "unknown",
42
126
  };
43
127
  const compatibilityCache = new Map();
@@ -65,26 +149,87 @@ function kiroModelPickerAtTail(pane) {
65
149
  return false;
66
150
  return tail.some(line => /^\s*[>❯›]\s*\*?\s*\S/.test(line));
67
151
  }
68
- /** Never navigate a stale trust phrase in scrollback or an unknown menu. */
69
- function kiroTrustDialogActive(pane) {
152
+ const KIRO_TRUST_HEADER = "Warning: Kiro is running in trust all tools mode";
153
+ const KIRO_TRUST_PATTERN = /^\s*(?:Warning: Kiro is running in trust all tools mode|Do you trust the files\?)\s*$/m;
154
+ const KIRO_TRUST_OPTIONS = ["No, exit", "Yes, I accept", "Yes, and don't ask again"];
155
+ /**
156
+ * #849A: only the captured 2.27.1 TUI trust-all-tools layout can receive a
157
+ * key. Unknown layouts remain held. A live composer or model picker below
158
+ * the header makes it history, not a consent request. The old synthetic
159
+ * workspace-trust shape is observable but never auto-accepted.
160
+ */
161
+ export function kiroTrustPromptState(pane) {
162
+ const none = { active: false, cursor: null };
70
163
  if (kiroModelPickerAtTail(pane))
71
- return false;
72
- const lines = pane.replace(/\r/g, "").split("\n");
73
- let selected = -1;
74
- for (let index = lines.length - 1; index >= 0; index--) {
75
- if (/^\s*[❯›]\s*No, exit\s*$/.test(lines[index])) {
76
- selected = index;
164
+ return none;
165
+ const rows = pane.replace(/\r/g, "").split("\n").map(row => row.trimEnd());
166
+ let header = -1;
167
+ for (let index = rows.length - 1; index >= 0; index--) {
168
+ if (KIRO_TRUST_PATTERN.test(rows[index])) {
169
+ header = index;
77
170
  break;
78
171
  }
79
172
  }
80
- if (selected < 0)
81
- return false;
82
- const yes = lines.findIndex((line, index) => index > selected && index <= selected + 3
83
- && /^\s*Yes, I accept\s*$/.test(line));
84
- if (yes < 0)
85
- return false;
86
- const afterChoices = lines.slice(yes + 1).filter(line => line.trim());
87
- return afterChoices.every(line => /^\s*(?:enter|esc|press|use)\b/i.test(line));
173
+ if (header < 0)
174
+ return none;
175
+ const body = rows.slice(header + 1);
176
+ if (body.some(row => KIRO_COMPOSER_ROW.test(row)))
177
+ return none;
178
+ const choice = (row, label) => {
179
+ if (row.trim() === label)
180
+ return { glyph: null };
181
+ const hit = /^\s*(\S)\s+(.+)$/.exec(row);
182
+ return hit?.[2] === label ? { glyph: hit[1] } : null;
183
+ };
184
+ const held = { active: true, cursor: null };
185
+ if (rows[header].trim() !== KIRO_TRUST_HEADER) {
186
+ // A question alone is not permission evidence. A structurally selected
187
+ // two-choice workspace prompt is held for a human, not navigated.
188
+ return body.some(row => choice(row, KIRO_TRUST_OPTIONS[0]))
189
+ && body.some(row => choice(row, KIRO_TRUST_OPTIONS[1])) ? held : none;
190
+ }
191
+ const first = body.findIndex(row => choice(row, KIRO_TRUST_OPTIONS[0]) !== null);
192
+ if (first < 0)
193
+ return held;
194
+ if (body.slice(0, first).some(row => KIRO_TRUST_OPTIONS.some(label => choice(row, label))))
195
+ return held;
196
+ const options = body.slice(first).filter(row => row.trim());
197
+ // Exact labels/order, divider and key-hint tail from the native fixture.
198
+ // No inferred two-option layout, added option, or arbitrary output suffix.
199
+ if (options.length !== 5 || !/^─{3,}$/.test(options[3].trim())
200
+ || !/^esc to cancel\s*·\s*↑↓ to navigate\s*·\s*↵ to select$/.test(options[4].trim()))
201
+ return held;
202
+ const hits = KIRO_TRUST_OPTIONS.map((label, index) => choice(options[index], label));
203
+ if (hits.some(hit => !hit))
204
+ return held;
205
+ const marked = hits.flatMap((hit, index) => hit.glyph === null ? [] : [index]);
206
+ if (marked.length !== 1 || !KIRO_PROMPT_CURSOR.test(hits[marked[0]].glyph))
207
+ return held;
208
+ return { active: true, cursor: marked[0] };
209
+ }
210
+ function kiroTrustPromptDialogs(trustAll) {
211
+ const guarded = { pattern: KIRO_TRUST_PATTERN, blocksDelivery: true, inputBlocked: true, verifyAfterKeys: true, oncePerLaunch: true };
212
+ const at = (pane, cursor) => {
213
+ const state = kiroTrustPromptState(pane);
214
+ return trustAll && state.active && state.cursor === cursor;
215
+ };
216
+ return [
217
+ {
218
+ ...guarded, keys: ["Down"], autoResolutionKey: "kiro-trust-all-tools-step",
219
+ description: "Kiro trust confirmation — one step off 'No, exit'",
220
+ isActive: pane => at(pane, 0),
221
+ },
222
+ {
223
+ ...guarded, keys: ["Enter"], autoResolutionKey: "kiro-trust-all-tools-confirm",
224
+ description: "Kiro trust confirmation — confirm per-session 'Yes, I accept'",
225
+ isActive: pane => at(pane, 1),
226
+ },
227
+ {
228
+ pattern: KIRO_TRUST_PATTERN, keys: [], holdOnly: true, blocksDelivery: true, inputBlocked: true,
229
+ description: "Kiro trust confirmation — unsafe or unverified choice; holding for a human",
230
+ isActive: pane => kiroTrustPromptState(pane).active,
231
+ },
232
+ ];
88
233
  }
89
234
  /**
90
235
  * Kiro's service-unavailable path opens the same picker as `/model`, but only
@@ -207,8 +352,8 @@ export function kiroLaunchPromptState(pane, spec) {
207
352
  return { active: true, cursor: null };
208
353
  return { active: true, cursor: marked[0] };
209
354
  }
210
- /** kiro's input row: legacy `12% !>` / `[agent] 3% λ !>`, or a bare `>`/`❯`. */
211
- const KIRO_COMPOSER_ROW = /^\s*(?:\[[^\]]*\]\s*)?\d+%\s*\S{0,2}\s*!?\s*[❯>]|^\s*[!❯>]\s*$/;
355
+ /** Kiro's legacy input row, or the archived TUI's column-zero `›` composer (including typed text). */
356
+ const KIRO_COMPOSER_ROW = /^\s*(?:\[[^\]]*\]\s*)?\d+%\s*\S{0,2}\s*!?\s*[❯>]|^\s*[!❯>]\s*$|^›(?:[ \t]|$)/;
212
357
  /**
213
358
  * Launch prompts that would move an instance off its engine (#1109). Text
214
359
  * from the kiro-cli 2.27.0 binary (crates/chat-cli/src/launch/v3_ease_in.rs,
@@ -346,6 +491,7 @@ function compatibilityFromVersion(version, parsed) {
346
491
  : has("2.3.0") ? ["rust", "kas"]
347
492
  : null,
348
493
  supportsEffortFlag: has(KIRO_EFFORT_FLAG_MIN),
494
+ supportsInstanceAgent: has(KIRO_INSTANCE_AGENT_MIN),
349
495
  source: "version",
350
496
  };
351
497
  }
@@ -359,6 +505,10 @@ function compatibilityFromHelp(version, help) {
359
505
  supportsV3: helpAdvertisesFlag(help, "--v3"),
360
506
  agentEngines: parseKiroAgentEngines(help),
361
507
  supportsEffortFlag: helpAdvertisesFlag(help, "--effort"),
508
+ // The flags are listed long before 2.21, but the behaviour #906 relies on was verified from 2.21 on: an unknown or
509
+ // older version keeps today's command.
510
+ supportsInstanceAgent: helpAdvertisesFlag(help, "--agent") && helpAdvertisesFlag(help, "--resume-id")
511
+ && (() => { const v = parseSemver(version); return !!v && versionAtLeast(v, KIRO_INSTANCE_AGENT_MIN); })(),
362
512
  source: "help",
363
513
  };
364
514
  }
@@ -401,14 +551,78 @@ function nextPatch(version) {
401
551
  const [major, minor, patch] = parseSemver(version);
402
552
  return `${major}.${minor}.${patch + 1}`;
403
553
  }
554
+ /** Binary generations already told they cannot isolate (once each). */
555
+ const warnedNoInstanceAgentKeys = new Set();
556
+ /** Instances already told about an untagged steering file (once each). */
557
+ const warnedUntaggedSteering = new Set();
558
+ /** The classic (v1) conversation store an instance launches with: its credential profile's, else the shared one. */
559
+ export function kiroV1DbPath(credentialProfile) {
560
+ const home = credentialProfile
561
+ ? credentialProfileStoreHome(getAgendHome(), "kiro-cli", credentialProfile)
562
+ : join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "kiro-cli");
563
+ return join(home, "data.sqlite3");
564
+ }
565
+ /** The TUI (v2) session files: `<KIRO_HOME or ~/.kiro>/sessions/cli` — not per credential profile. */
566
+ export function kiroV2SessionsDir() {
567
+ return join(process.env.KIRO_HOME?.trim() || join(homedir(), ".kiro"), "sessions", "cli");
568
+ }
569
+ /**
570
+ * Adoption evidence (#906 §2): the engine ledger shows this instance launched here — same directory, credential
571
+ * profile and engine — before #906 (no "--agent" in that launch). A new instance has no such record and starts fresh
572
+ * instead of taking a sibling's conversation.
573
+ */
574
+ export function kiroLaunchedHereBefore(instance, workingDirectory, credentialProfile, engine, ledgerPath) {
575
+ const entry = readKiroLedger(ledgerPath)[instance];
576
+ if (!entry || resolve(entry.workingDirectory) !== resolve(workingDirectory) || entry.credentialProfile !== credentialProfile)
577
+ return false;
578
+ return entry.lastLaunch.ui === (engine === "v1" ? "legacy" : "tui") && !entry.lastLaunch.flags.includes("--agent");
579
+ }
580
+ /** The legacy UI's prompt row: an optional `[agent]`, the context percentage, an optional mode glyph, the marker. */
581
+ const LEGACY_PROMPT_ROW = /^\s*(?:\[([^\]]+)\]\s*)?\d+%\s*(?:[^\s\d%!❯>]{1,2}\s+)?(?:!\s?[❯>]|❯|>)/;
582
+ /** A TUI status row: `<agent> · <model> · …`. */
583
+ const TUI_STATUS_ROW = /^\s*(\S+) · /;
584
+ const RULE_ROW = /^[\s─━│┃╭╮╰╯┌┐└┘]*$/;
585
+ /** The right-aligned hint under the TUI input row: far right, a slash command or two. */
586
+ const TUI_HINT_ROW = /^\s{20,}\/[\w-]+(?: [\w-]+)*(?: · \/[\w-]+(?: [\w-]+)*)*$/;
404
587
  /**
405
- * Pin the UI AND the engine on every launch (#1109). A flag outranks kiro's
406
- * persisted `chat.agentEngine`, which the V3 ease-in prompt writes for the
407
- * whole machine and which kiro 3.0 may default to V3. An instance's
408
- * conversation lives in its engine's store — classic sqlite, V2 JSON, V3 KAS —
409
- * and moving between them forks it one way, so a launch AgEnD cannot pin to
410
- * the instance's own engine is refused, never run on whatever kiro defaults to.
588
+ * The agent the live layout shows (#906 §3) — never a name quoted in the conversation above it:
589
+ * - legacy: the pane's last non-empty row, only when it is the prompt row; `[name]` on it, or the default agent;
590
+ * - TUI / v3: the status row directly above the input row (the last `›` row; earlier `›` rows are the user's past
591
+ * messages, which sit above it), skipping rule rows.
592
+ * Null when the layout cannot be read.
411
593
  */
594
+ export function readActiveKiroAgent(pane, ui) {
595
+ const rows = pane.split("\n").map(r => r.replace(/\s+$/, ""));
596
+ if (ui === "legacy") {
597
+ const last = [...rows].reverse().find(r => r.trim() !== "");
598
+ const m = last ? LEGACY_PROMPT_ROW.exec(last) : null;
599
+ return m ? (m[1] ?? "kiro_default") : null;
600
+ }
601
+ let input = -1;
602
+ for (let i = rows.length - 1; i >= 0; i--)
603
+ if (/^\s*›/.test(rows[i])) {
604
+ input = i;
605
+ break;
606
+ }
607
+ if (input < 0)
608
+ return null;
609
+ // The live bottom layout ends with the input row: below it only blank or rule rows and the right-aligned hint
610
+ // (`/copy to clipboard`, `/sessions to resume · …`). Anything else — a modal, a panel, output — means the pair
611
+ // above it is not the live one (#1416 review).
612
+ for (let i = input + 1; i < rows.length; i++) {
613
+ const row = rows[i];
614
+ if (row.trim() === "" || RULE_ROW.test(row) || TUI_HINT_ROW.test(row))
615
+ continue;
616
+ return null;
617
+ }
618
+ for (let i = input - 1; i >= 0; i--) {
619
+ if (RULE_ROW.test(rows[i]))
620
+ continue;
621
+ const m = TUI_STATUS_ROW.exec(rows[i]);
622
+ return m ? m[1] : null;
623
+ }
624
+ return null;
625
+ }
412
626
  export function planKiroLaunch(ui, compat) {
413
627
  const version = compat.version ?? "of unknown version";
414
628
  if (ui === "v3")
@@ -539,6 +753,46 @@ export class KiroBackend {
539
753
  activeTrustAll = true;
540
754
  /** Version-gate notice for the launch just built (consumeLaunchWarning). */
541
755
  launchWarning = null;
756
+ /**
757
+ * What this launch attempt resumes and as which agent (#906), resolved once per attempt in writeConfig — it claims
758
+ * conversations and records fresh starts — and read by the buildCommand that follows it.
759
+ */
760
+ launchPlan = null;
761
+ /** The plan of the command last built: what agentSwitch() works from. */
762
+ activePlan = null;
763
+ preparedV2Store = null;
764
+ storePreparation = 0;
765
+ storeKey(config) {
766
+ return JSON.stringify([config.instanceName, config.workingDirectory, kiroCanonicalDirectory(config.workingDirectory), config.kiroUi,
767
+ resolveCredentialProfile(config.backendOptions), !!config.skipResume, getAgendHome(), kiroV2SessionsDir(), kiroCanonicalDirectory(kiroV2SessionsDir())]);
768
+ }
769
+ readPreparedV2Store(config) {
770
+ if (this.preparedV2Store?.key !== this.storeKey(config)) {
771
+ throw new KiroIdentityError("v2 session discovery was not prepared for this launch");
772
+ }
773
+ return this.preparedV2Store.store;
774
+ }
775
+ /** Metadata only: identity claims/config writes remain synchronous, after
776
+ * the daemon rechecks its launch admission following this await. */
777
+ async prepareLaunch(config) {
778
+ const preparation = ++this.storePreparation;
779
+ this.preparedV2Store = null;
780
+ if (!config || config.kiroUi !== "tui")
781
+ return;
782
+ if (!this.fixedCompatibility)
783
+ this.compatibility = cachedKiroCliCompatibility(this.binaryPath).compatibility;
784
+ const key = this.storeKey(config);
785
+ const plan = planKiroLaunch("tui", this.compatibility);
786
+ if (plan.kind !== "launch" || !plan.flags.includes("--agent-engine=v2") || !this.compatibility.supportsInstanceAgent)
787
+ return;
788
+ const opts = { instance: config.instanceName, engine: "v2", workingDirectory: config.workingDirectory,
789
+ credentialProfile: resolveCredentialProfile(config.backendOptions), skipResume: config.skipResume, agendHome: getAgendHome() };
790
+ if (kiroIdentityNeedsStore(opts)) {
791
+ const store = await listKiroV2Sessions(config.workingDirectory, kiroV2SessionsDir());
792
+ if (preparation === this.storePreparation && key === this.storeKey(config))
793
+ this.preparedV2Store = { key, store };
794
+ }
795
+ }
542
796
  constructor(instanceDir, compatibility) {
543
797
  this.instanceDir = instanceDir;
544
798
  this.binaryPath = resolveBinary("kiro-cli");
@@ -577,6 +831,22 @@ export class KiroBackend {
577
831
  // their ready marker is verified.
578
832
  return this.activeUi === "legacy" && this.activeTrustAll;
579
833
  }
834
+ /** #1405: the TUI front-end this instance was launched with, on a version listed in KIRO_STEER_VERIFIED. */
835
+ supportsSteer() {
836
+ if (this.activeUi === "legacy")
837
+ return false;
838
+ // compatibility.version is kiro-cli's own `--version` line ("kiro-cli 2.27.1").
839
+ const version = parseSemver(this.compatibility.version)?.join(".");
840
+ return version !== undefined && Object.hasOwn(KIRO_STEER_VERIFIED, version)
841
+ && KIRO_STEER_VERIFIED[version].includes(this.activeUi);
842
+ }
843
+ /** #1405: the TUI composer's mode; never read on the legacy UI, whose prompt row has no such mode. */
844
+ readSteerComposer(pane) {
845
+ return this.activeUi === "legacy" ? null : readKiroSteerComposer(pane);
846
+ }
847
+ readSteerComposerText(pane) {
848
+ return this.activeUi === "legacy" ? null : readKiroComposerText(pane);
849
+ }
580
850
  /**
581
851
  * Live evidence (kiro-cli 2.21.0, 2026-09-03): `--resume` prints nothing
582
852
  * until the conversation comes back from the backend ("Picking up where we
@@ -647,13 +917,21 @@ export class KiroBackend {
647
917
  cmd += ` ${flag}`;
648
918
  this.noteVersionGate();
649
919
  const credentialProfile = resolveCredentialProfile(config.backendOptions);
920
+ // #906: the plan writeConfig resolved for this attempt (a direct buildCommand, as in tests, resolves its own).
921
+ const lp = this.launchPlan && this.launchPlan.cwd === config.workingDirectory && this.launchPlan.skipResume === !!config.skipResume
922
+ ? this.launchPlan : this.planInstanceLaunch(config);
923
+ this.launchPlan = null;
924
+ this.activePlan = lp;
925
+ this.activeSpec = lp.mode === "legacy" ? null : this.agentSpec(config);
926
+ this.activeInstance = config.instanceName;
650
927
  recordKiroLaunch({
651
928
  instance: config.instanceName,
652
929
  workingDirectory: config.workingDirectory,
653
930
  credentialProfile,
654
931
  kiroVersion: this.compatibility.version ?? null,
655
932
  ui,
656
- flags: plan.flags,
933
+ // "--agent" marks a launch made after #906: such an instance is never adopted again (kiroLaunchedHereBefore).
934
+ flags: lp.mode !== "legacy" ? [...plan.flags, "--agent"] : plan.flags,
657
935
  });
658
936
  // Record what is actually being launched for the delivery gate (see
659
937
  // dropsEnterWhileBusy): the legacy prompt row exists only under
@@ -662,30 +940,24 @@ export class KiroBackend {
662
940
  this.activeTrustAll = config.skipPermissions !== false;
663
941
  if (config.skipPermissions !== false)
664
942
  cmd += " --trust-all-tools";
665
- // --resume is boolean: Kiro auto-resumes latest conversation for this working directory.
666
- //
667
- // Deliberately NOT `--resume-id <SESSION_ID>`, which kiro-cli 2.22 also
668
- // offers (alongside --resume-picker). Conversations live in the same
669
- // data.sqlite3 as the login, keyed by working directory — so a credential
670
- // profile has its own set of them, and a session id recorded under one
671
- // profile does not exist under another. Holding an id would mean carrying a
672
- // stale one across a subscription switch and resuming into nothing; the
673
- // boolean form simply finds no conversation for this directory in a new
674
- // store, which is the same thing every brand-new instance does on its first
675
- // launch. Switching profiles also skips resume outright (crash-state
676
- // resumeDisabled), so this never fires against a store that just changed.
677
- //
678
- // V3 is the exception, and resumes only the session it owns, by id: its
679
- // `--resume` takes the newest conversation in the directory from ANY
680
- // engine and converts a classic one into a new V3 copy — on every launch.
681
- // A skipped resume is recorded there too, so the session given up is not
682
- // taken back on the next launch. See kiro-v3-identity.ts.
683
- if (ui === "v3") {
943
+ if (lp.mode !== "legacy") {
944
+ // Its own agent: on a fresh start it takes effect at once; on a resume kiro brings back the conversation's saved
945
+ // agent instead (E6), and the daemon switches it on screen (agentSwitch).
946
+ cmd += ` --agent ${shellQuote(lp.agent)}`;
947
+ if (lp.mode === "resume")
948
+ cmd += ` --resume-id ${shellQuote(lp.id)}`;
949
+ }
950
+ else if (ui === "v3") {
951
+ // Legacy mode, V3: it resumes only the session it owns, by id (kiro-v3-identity.ts) — its `--resume` would take
952
+ // the newest conversation in the directory from ANY engine and convert a classic one into a new V3 copy.
684
953
  const id = resolveKiroV3Resume(config.instanceName, config.workingDirectory, credentialProfile, { skipResume: config.skipResume });
685
954
  if (id)
686
955
  cmd += ` --resume-id ${shellQuote(id)}`;
687
956
  }
688
957
  else if (!config.skipResume) {
958
+ // Legacy mode, classic/TUI: the boolean form — kiro resumes the directory's newest conversation. Not isolated
959
+ // between instances sharing the directory (#1410); an instance launches like this only without the
960
+ // per-instance plan (an old kiro-cli, or a conversation store AgEnD could not read on adoption).
689
961
  cmd += " --resume";
690
962
  }
691
963
  if (config.model) {
@@ -756,7 +1028,8 @@ export class KiroBackend {
756
1028
  if (!warning)
757
1029
  return;
758
1030
  warnedVersionGateCacheKeys.add(key);
759
- this.launchWarning = warning;
1031
+ // Appended: the launch's isolation warnings (#906, recorded by writeConfig) must not be overwritten by this one.
1032
+ this.addLaunchWarning(warning);
760
1033
  }
761
1034
  consumeLaunchWarning() {
762
1035
  const warning = this.launchWarning;
@@ -776,36 +1049,11 @@ export class KiroBackend {
776
1049
  return true;
777
1050
  }
778
1051
  writeConfig(config) {
779
- // Kiro CLI reads workspace MCP config from .kiro/settings/mcp.json
780
- // Format: { "mcpServers": { "name": { command, args, env } } }
781
- //
782
- // WORKAROUND: kiro-cli ignores the "env" block in mcp.json — the MCP server
783
- // subprocess inherits the fleet manager's process env, which has a stale
784
- // AGEND_SOCKET_PATH from whichever daemon wrote to it last.
785
- // Fix: generate a wrapper script that exports the correct env vars before
786
- // exec-ing the real MCP server.
787
- const mcpDir = join(config.workingDirectory, ".kiro", "settings");
788
- mkdirSync(mcpDir, { recursive: true });
789
- const mcpConfigPath = join(mcpDir, "mcp.json");
790
- let mcpConfig = {};
791
- try {
792
- mcpConfig = JSON.parse(readFileSync(mcpConfigPath, "utf-8"));
793
- }
794
- catch { /* new file */ }
795
- const servers = (mcpConfig.mcpServers ?? {});
796
- // Remove stale agend entries whose wrapper scripts no longer exist
797
- for (const [key, val] of Object.entries(servers)) {
798
- if (key.startsWith("agend-")) {
799
- const cmd = val?.command;
800
- if (typeof cmd === "string" && !existsSync(cmd)) {
801
- delete servers[key];
802
- }
803
- }
804
- }
1052
+ // WORKAROUND: kiro-cli ignores the "env" block of an MCP server entry — the server subprocess inherits the fleet
1053
+ // manager's env, with a stale AGEND_SOCKET_PATH from whichever daemon wrote last. Each server therefore runs
1054
+ // through a wrapper script that exports this instance's env and execs the real server.
805
1055
  for (const [name, entry] of Object.entries(config.mcpServers)) {
806
- const instanceKey = `${name}-${config.instanceName}`;
807
1056
  const allEnv = { ...entry.env, AGEND_INSTANCE_NAME: config.instanceName };
808
- // Write a wrapper script that sets env vars explicitly
809
1057
  const wrapperPath = join(this.instanceDir, `mcp-wrapper-${name}.sh`);
810
1058
  const envExports = Object.entries(allEnv)
811
1059
  .map(([k, v]) => `export ${k}='${String(v).replace(/'/g, "'\\''")}'`)
@@ -813,29 +1061,170 @@ export class KiroBackend {
813
1061
  // 0o700 (owner-only rwx): wrapper inlines sensitive env (tokens, socket paths).
814
1062
  // Other users on the host must not be able to read it. Set mode at creation
815
1063
  // to avoid a world-readable window between writeFileSync and chmodSync.
816
- writeFileSync(wrapperPath, `#!/bin/bash\n${envExports}\n# Wait for IPC socket to be ready (up to 10s)\nfor i in $(seq 1 20); do [ -S "$AGEND_SOCKET_PATH" ] && break; sleep 0.5; done\nexec ${entry.command} ${entry.args.map((a) => JSON.stringify(a)).join(" ")}\n`, { mode: 0o700 });
1064
+ writeFileSync(wrapperPath, `#!/bin/bash\n${envExports}\n# Wait for IPC socket to be ready (up to 10s)\nfor i in $(seq 1 20); do [ -S "$AGEND_SOCKET_PATH" ] && break; sleep 0.5; done\nexec ${[entry.command, ...entry.args].map(shellQuote).join(" ")}\n`, { mode: 0o700 });
817
1065
  // Re-chmod in case the file already existed with looser permissions (writeFileSync's
818
1066
  // mode only applies on create).
819
1067
  chmodSync(wrapperPath, 0o700);
820
- servers[instanceKey] = {
821
- command: wrapperPath,
822
- args: [],
823
- };
824
1068
  }
825
- // Clean up old non-namespaced key if present
826
- delete servers["agend"];
827
- mcpConfig.mcpServers = servers;
828
- writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
829
- // Write fleet instructions to .kiro/steering/ (auto-loaded by Kiro CLI)
830
- if (config.instructions) {
831
- try {
832
- const steeringDir = join(config.workingDirectory, ".kiro", "steering");
833
- mkdirSync(steeringDir, { recursive: true });
834
- writeFileSync(join(steeringDir, `agend-${config.instanceName}.md`), config.instructions);
835
- }
836
- catch { /* best effort */ }
1069
+ // #906: which conversation this attempt resumes, and as which agent — resolved once, here.
1070
+ const plan = this.planInstanceLaunch(config);
1071
+ this.preparedV2Store = null;
1072
+ this.launchPlan = plan;
1073
+ const spec = this.agentSpec(config);
1074
+ const root = dirname(this.instanceDir);
1075
+ if (plan.mode === "legacy") {
1076
+ // Not isolated (an old kiro-cli, or adoption with an unreadable store): the shared files as before, each
1077
+ // written only where it is free, and removed only by provenance.
1078
+ if (plan.reason)
1079
+ this.addLaunchWarning(plan.reason);
1080
+ this.noteShared("mcp.json", writeSharedKiroMcpEntries(spec));
1081
+ if (config.instructions)
1082
+ this.noteShared("steering", writeTaggedKiroSteering(spec, config.instructions));
1083
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, false));
1084
+ return;
1085
+ }
1086
+ // Throws when a file that is not this instance's agent is in the way: never overwritten, launch refused.
1087
+ writeKiroAgent(spec, config.instructions);
1088
+ if (plan.mode === "resume" && !plan.agentConfirmed) {
1089
+ // The resumed conversation comes back as its saved agent (`--agent` is ignored on resume), so until the switch
1090
+ // is confirmed on screen it gets its AgEnD server and instructions from the shared files (design §3).
1091
+ this.noteShared("mcp.json", writeSharedKiroMcpEntries(spec));
1092
+ if (config.instructions)
1093
+ this.noteShared("steering", writeTaggedKiroSteering(spec, config.instructions));
1094
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, false));
1095
+ return;
1096
+ }
1097
+ // Running as its own agent: nothing of this instance belongs in the shared files any more.
1098
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, true));
1099
+ this.noteShared("steering", removeTaggedKiroSteering(spec));
1100
+ }
1101
+ /** This instance's agent, by its fleet and its own wrapper scripts. */
1102
+ agentSpec(config) {
1103
+ return {
1104
+ workingDirectory: config.workingDirectory,
1105
+ instance: config.instanceName,
1106
+ fleet: kiroFleetTag(getAgendHome()),
1107
+ instanceDir: this.instanceDir,
1108
+ serverNames: Object.keys(config.mcpServers),
1109
+ };
1110
+ }
1111
+ /** A shared file AgEnD could not bring to the state isolation needs: said, never reported as isolated. */
1112
+ noteShared(file, outcome) {
1113
+ if (outcome === "conflict")
1114
+ this.addLaunchWarning(t("kiro.shared_conflict", file));
1115
+ else if (outcome === "unreadable" || outcome === "failed")
1116
+ this.addLaunchWarning(t("kiro.shared_unreadable", file));
1117
+ else if (outcome === "untagged-legacy" && !warnedUntaggedSteering.has(this.instanceDir)) {
1118
+ warnedUntaggedSteering.add(this.instanceDir);
1119
+ this.addLaunchWarning(t("kiro.untagged_steering"));
1120
+ }
1121
+ }
1122
+ addLaunchWarning(message) {
1123
+ this.launchWarning = this.launchWarning ? `${this.launchWarning}\n${message}` : message;
1124
+ }
1125
+ /**
1126
+ * The launch plan of one attempt (#906, design §2/§5): the engine the plan pins, and — when this kiro-cli has
1127
+ * `--agent` and `--resume-id` — the conversation this instance owns and whether it already runs as its agent.
1128
+ * Anything else is legacy mode: today's command, not isolated.
1129
+ */
1130
+ planInstanceLaunch(config) {
1131
+ const ui = config.kiroUi ?? "legacy";
1132
+ if (!this.fixedCompatibility) {
1133
+ const current = cachedKiroCliCompatibility(this.binaryPath);
1134
+ this.compatibility = current.compatibility;
1135
+ this.compatibilityCacheKey = current.cacheKey;
837
1136
  }
1137
+ const plan = planKiroLaunch(ui, this.compatibility);
1138
+ const flags = plan.kind === "launch" ? plan.flags : [];
1139
+ const engine = flags.includes("--agent-engine=v1") ? "v1"
1140
+ : flags.includes("--agent-engine=v2") ? "v2" : ui === "v3" ? "v3" : null;
1141
+ const credentialProfile = resolveCredentialProfile(config.backendOptions);
1142
+ const base = { cwd: config.workingDirectory, skipResume: !!config.skipResume, engine, credentialProfile };
1143
+ const legacy = (reason) => ({ ...base, mode: "legacy", reason, agent: null, agentConfirmed: false });
1144
+ if (plan.kind !== "launch" || this.compatibility.source === "unknown")
1145
+ return legacy(null); // buildCommand refuses
1146
+ if (!this.compatibility.supportsInstanceAgent || !engine) {
1147
+ const key = this.compatibilityCacheKey ?? this.binaryPath;
1148
+ if (warnedNoInstanceAgentKeys.has(key))
1149
+ return legacy(null);
1150
+ warnedNoInstanceAgentKeys.add(key);
1151
+ return legacy(t("kiro.no_instance_agent", this.compatibility.version ?? "?", KIRO_INSTANCE_AGENT_MIN));
1152
+ }
1153
+ const agent = kiroAgentName(config.instanceName, kiroFleetTag(getAgendHome()));
1154
+ const agendHome = getAgendHome();
1155
+ let decision;
1156
+ if (engine === "v3") {
1157
+ // V3 keeps its own identity (kiro-v3-identity.ts); only whether it runs as the agent is recorded here.
1158
+ const id = resolveKiroV3Resume(config.instanceName, config.workingDirectory, credentialProfile, { skipResume: config.skipResume });
1159
+ decision = id
1160
+ ? { mode: "resume", id, agentConfirmed: kiroAgentConfirmed(agendHome, config.instanceName, "v3", config.workingDirectory, credentialProfile, id) }
1161
+ : { mode: "fresh" };
1162
+ }
1163
+ else {
1164
+ decision = resolveKiroIdentity({
1165
+ instance: config.instanceName,
1166
+ engine,
1167
+ workingDirectory: config.workingDirectory,
1168
+ credentialProfile,
1169
+ skipResume: config.skipResume,
1170
+ agendHome,
1171
+ readStore: () => engine === "v1"
1172
+ ? listKiroV1Sessions(config.workingDirectory, kiroV1DbPath(credentialProfile))
1173
+ : this.readPreparedV2Store(config),
1174
+ launchedBefore: () => kiroLaunchedHereBefore(config.instanceName, config.workingDirectory, credentialProfile, engine),
1175
+ });
1176
+ }
1177
+ if (decision.mode === "legacy")
1178
+ return legacy(t("kiro.identity_legacy", decision.reason));
1179
+ return decision.mode === "resume"
1180
+ ? { ...base, mode: "resume", id: decision.id, agent, agentConfirmed: decision.agentConfirmed, reason: null }
1181
+ : { ...base, mode: "fresh", agent, agentConfirmed: true, reason: null };
1182
+ }
1183
+ /**
1184
+ * After a resume as the instance's agent (#906 §3): what the daemon needs to read the active agent off the live
1185
+ * layout and, when it is not ours, switch to it with `/agent swap` and confirm. Null when there is nothing to do.
1186
+ */
1187
+ agentSwitch() {
1188
+ const plan = this.activePlan;
1189
+ if (!plan || plan.mode !== "resume" || !plan.agent || !plan.id || !plan.engine)
1190
+ return null;
1191
+ const { agent, id, engine, cwd, credentialProfile } = plan;
1192
+ const ui = this.activeUi;
1193
+ return {
1194
+ agent,
1195
+ alreadyConfirmed: plan.agentConfirmed,
1196
+ readActive: (pane) => readActiveKiroAgent(pane, ui),
1197
+ command: `/agent swap ${agent}`,
1198
+ confirm: () => {
1199
+ const warnings = [];
1200
+ if (!this.activeInstance || !confirmKiroAgentSwitch(getAgendHome(), this.activeInstance, engine, cwd, credentialProfile, id)) {
1201
+ warnings.push(t("kiro.switch_unrecorded"));
1202
+ return warnings;
1203
+ }
1204
+ plan.agentConfirmed = true;
1205
+ const spec = this.activeSpec;
1206
+ if (spec) {
1207
+ const mcp = removeSharedKiroMcpEntries(spec, dirname(this.instanceDir), true);
1208
+ if (mcp === "unreadable" || mcp === "failed")
1209
+ warnings.push(t("kiro.shared_unreadable", "mcp.json"));
1210
+ const steering = removeTaggedKiroSteering(spec);
1211
+ if (steering === "failed" || steering === "unreadable")
1212
+ warnings.push(t("kiro.shared_unreadable", "steering"));
1213
+ }
1214
+ return warnings;
1215
+ },
1216
+ };
838
1217
  }
1218
+ /**
1219
+ * An instance running as its own agent reads its instructions from the agent file's prompt (#906). Until a resumed
1220
+ * conversation's switch is confirmed it still runs as its saved agent, reading the steering file (the default).
1221
+ */
1222
+ instructionsSource() {
1223
+ const plan = this.activePlan;
1224
+ return plan && plan.mode !== "legacy" && plan.agent && plan.agentConfirmed ? `.kiro/agents/${plan.agent}.json (its "prompt" field)` : null;
1225
+ }
1226
+ activeSpec = null;
1227
+ activeInstance = null;
839
1228
  getReadyPattern() {
840
1229
  // Startup: trust/banner text. Daily prompt: "22% !>" / "8% ❯";
841
1230
  // Kiro may insert mode glyphs between them, e.g. "20% λ !>".
@@ -1028,13 +1417,7 @@ export class KiroBackend {
1028
1417
  getStartupDialogs() {
1029
1418
  return [
1030
1419
  ...KIRO_ENGINE_PROMPT_DIALOGS,
1031
- {
1032
- // Kiro CLI --trust-all-tools now shows a confirmation prompt.
1033
- // Default cursor is on "No, exit" — press Down then Enter to select "Yes, I accept".
1034
- pattern: /[❯›]\s*No, exit/m,
1035
- keys: ["Down", "Enter"],
1036
- description: "Kiro --trust-all-tools confirmation — navigate to 'Yes, I accept'",
1037
- },
1420
+ ...kiroTrustPromptDialogs(this.activeTrustAll),
1038
1421
  ];
1039
1422
  }
1040
1423
  getRuntimeDialogs() {
@@ -1066,13 +1449,7 @@ export class KiroBackend {
1066
1449
  inputBlocked: true,
1067
1450
  isActive: kiroModelPickerAtTail,
1068
1451
  },
1069
- {
1070
- // Same trust prompt can also appear mid-session if Kiro re-validates.
1071
- pattern: /Do you trust the files|Yes, I accept[\s\S]*No, exit/m,
1072
- keys: ["Down", "Enter"],
1073
- description: "Kiro trust confirmation dialog — auto-accept",
1074
- isActive: kiroTrustDialogActive,
1075
- },
1452
+ ...kiroTrustPromptDialogs(this.activeTrustAll),
1076
1453
  ];
1077
1454
  }
1078
1455
  getContextUsage() {
@@ -1138,26 +1515,25 @@ export class KiroBackend {
1138
1515
  getEffortStrategy() { return EFFORT_CAPABILITIES["kiro-cli"].strategy; }
1139
1516
  getEffortLevels() { return [...EFFORT_CAPABILITIES["kiro-cli"].levels]; }
1140
1517
  cleanup(config) {
1141
- // Only remove namespaced keys — non-namespaced "agend" key may belong to
1142
- // another instance sharing this working directory.
1518
+ // By provenance only (#906 §4/§6): the agent file and its .bak when they are this instance's, the shared mcp.json
1519
+ // entries run by this instance's own wrappers, and a steering file carrying this fleet's tag for it. The user's,
1520
+ // another fleet's, and untagged steering from before the tag existed are kept.
1143
1521
  try {
1144
- const mcpConfigPath = join(config.workingDirectory, ".kiro", "settings", "mcp.json");
1145
- if (existsSync(mcpConfigPath)) {
1146
- const mcpConfig = JSON.parse(readFileSync(mcpConfigPath, "utf-8"));
1147
- if (mcpConfig.mcpServers) {
1148
- for (const name of Object.keys(config.mcpServers)) {
1149
- delete mcpConfig.mcpServers[`${name}-${config.instanceName}`];
1150
- }
1151
- writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
1152
- }
1153
- }
1522
+ const spec = this.agentSpec(config);
1523
+ removeKiroAgent(spec);
1524
+ removeSharedKiroMcpEntries(spec, dirname(this.instanceDir), true);
1525
+ removeTaggedKiroSteering(spec);
1526
+ }
1527
+ catch { /* best effort */ }
1528
+ }
1529
+ /** Delete or replace: forget which conversations this instance owns (they stay in kiro's store). */
1530
+ forget(instanceName) {
1531
+ try {
1532
+ forgetKiroIdentity(getAgendHome(), instanceName);
1154
1533
  }
1155
1534
  catch { /* best effort */ }
1156
- // Remove fleet instructions steering file
1157
1535
  try {
1158
- const steeringFile = join(config.workingDirectory, ".kiro", "steering", `agend-${config.instanceName}.md`);
1159
- if (existsSync(steeringFile))
1160
- unlinkSync(steeringFile);
1536
+ forgetKiroV3Identity(instanceName, getAgendHome());
1161
1537
  }
1162
1538
  catch { /* best effort */ }
1163
1539
  }