@songsid/agend 2.1.12 → 2.2.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/README.md +2 -2
  2. package/README.zh-TW.md +2 -2
  3. package/dist/auth/one-time-code.d.ts +19 -0
  4. package/dist/auth/one-time-code.js +49 -0
  5. package/dist/auth/one-time-code.js.map +1 -0
  6. package/dist/auth-api.d.ts +52 -0
  7. package/dist/auth-api.js +346 -0
  8. package/dist/auth-api.js.map +1 -0
  9. package/dist/backend/claude-code.d.ts +4 -0
  10. package/dist/backend/claude-code.js +97 -0
  11. package/dist/backend/claude-code.js.map +1 -1
  12. package/dist/backend/codex.js +59 -30
  13. package/dist/backend/codex.js.map +1 -1
  14. package/dist/backend/kiro-agent.d.ts +66 -0
  15. package/dist/backend/kiro-agent.js +302 -0
  16. package/dist/backend/kiro-agent.js.map +1 -0
  17. package/dist/backend/kiro-identity.d.ts +80 -0
  18. package/dist/backend/kiro-identity.js +537 -0
  19. package/dist/backend/kiro-identity.js.map +1 -0
  20. package/dist/backend/kiro-v2-store-worker.d.ts +1 -0
  21. package/dist/backend/kiro-v2-store-worker.js +87 -0
  22. package/dist/backend/kiro-v2-store-worker.js.map +1 -0
  23. package/dist/backend/kiro-v2-store.d.ts +54 -0
  24. package/dist/backend/kiro-v2-store.js +131 -0
  25. package/dist/backend/kiro-v2-store.js.map +1 -0
  26. package/dist/backend/kiro-v3-identity.d.ts +5 -0
  27. package/dist/backend/kiro-v3-identity.js +22 -0
  28. package/dist/backend/kiro-v3-identity.js.map +1 -1
  29. package/dist/backend/kiro.d.ts +83 -7
  30. package/dist/backend/kiro.js +418 -91
  31. package/dist/backend/kiro.js.map +1 -1
  32. package/dist/backend/types.d.ts +45 -3
  33. package/dist/backend/types.js.map +1 -1
  34. package/dist/channel/adapters/discord.d.ts +1 -0
  35. package/dist/channel/adapters/discord.js +29 -10
  36. package/dist/channel/adapters/discord.js.map +1 -1
  37. package/dist/channel/adapters/telegram.d.ts +1 -0
  38. package/dist/channel/adapters/telegram.js +13 -1
  39. package/dist/channel/adapters/telegram.js.map +1 -1
  40. package/dist/channel/types.d.ts +9 -1
  41. package/dist/classic-channel-manager.d.ts +18 -10
  42. package/dist/classic-channel-manager.js +45 -18
  43. package/dist/classic-channel-manager.js.map +1 -1
  44. package/dist/cli.js +225 -127
  45. package/dist/cli.js.map +1 -1
  46. package/dist/command-table.d.ts +5 -3
  47. package/dist/command-table.js +29 -27
  48. package/dist/command-table.js.map +1 -1
  49. package/dist/config-validator.js +46 -0
  50. package/dist/config-validator.js.map +1 -1
  51. package/dist/config.js +12 -0
  52. package/dist/config.js.map +1 -1
  53. package/dist/connection-secrets.d.ts +6 -0
  54. package/dist/connection-secrets.js +4 -0
  55. package/dist/connection-secrets.js.map +1 -1
  56. package/dist/daemon.d.ts +89 -4
  57. package/dist/daemon.js +661 -77
  58. package/dist/daemon.js.map +1 -1
  59. package/dist/darwin-memory.d.ts +6 -1
  60. package/dist/darwin-memory.js +17 -6
  61. package/dist/darwin-memory.js.map +1 -1
  62. package/dist/delivery-consumed-watch.d.ts +85 -0
  63. package/dist/delivery-consumed-watch.js +244 -0
  64. package/dist/delivery-consumed-watch.js.map +1 -0
  65. package/dist/delivery-outbox.d.ts +61 -0
  66. package/dist/delivery-outbox.js +120 -2
  67. package/dist/delivery-outbox.js.map +1 -1
  68. package/dist/delivery-reconciliation.d.ts +14 -5
  69. package/dist/delivery-reconciliation.js +27 -4
  70. package/dist/delivery-reconciliation.js.map +1 -1
  71. package/dist/delivery-reconciliation.test.js +35 -1
  72. package/dist/delivery-reconciliation.test.js.map +1 -1
  73. package/dist/event-loop-watch.d.ts +28 -0
  74. package/dist/event-loop-watch.js +75 -3
  75. package/dist/event-loop-watch.js.map +1 -1
  76. package/dist/fleet-context.d.ts +22 -2
  77. package/dist/fleet-level-config.d.ts +2 -2
  78. package/dist/fleet-level-config.js +6 -1
  79. package/dist/fleet-level-config.js.map +1 -1
  80. package/dist/fleet-manager.d.ts +293 -12
  81. package/dist/fleet-manager.js +2364 -449
  82. package/dist/fleet-manager.js.map +1 -1
  83. package/dist/host-memory.d.ts +4 -0
  84. package/dist/host-memory.js.map +1 -1
  85. package/dist/instance-config-impact.js +5 -0
  86. package/dist/instance-config-impact.js.map +1 -1
  87. package/dist/instance-lifecycle.d.ts +10 -5
  88. package/dist/instance-lifecycle.js +162 -53
  89. package/dist/instance-lifecycle.js.map +1 -1
  90. package/dist/locale.js +150 -16
  91. package/dist/locale.js.map +1 -1
  92. package/dist/login-controller.d.ts +2 -0
  93. package/dist/login-controller.js +17 -5
  94. package/dist/login-controller.js.map +1 -1
  95. package/dist/memory-pressure.d.ts +7 -8
  96. package/dist/memory-pressure.js +76 -15
  97. package/dist/memory-pressure.js.map +1 -1
  98. package/dist/monotonic-budget.d.ts +2 -0
  99. package/dist/monotonic-budget.js +28 -0
  100. package/dist/monotonic-budget.js.map +1 -0
  101. package/dist/needs-you-hub.d.ts +114 -0
  102. package/dist/needs-you-hub.js +370 -0
  103. package/dist/needs-you-hub.js.map +1 -0
  104. package/dist/needs-you-live.d.ts +116 -0
  105. package/dist/needs-you-live.js +228 -0
  106. package/dist/needs-you-live.js.map +1 -0
  107. package/dist/needs-you.d.ts +132 -0
  108. package/dist/needs-you.js +164 -0
  109. package/dist/needs-you.js.map +1 -0
  110. package/dist/node-version-guard.d.ts +30 -0
  111. package/dist/node-version-guard.js +50 -0
  112. package/dist/node-version-guard.js.map +1 -0
  113. package/dist/operator-control-paths.d.ts +4 -0
  114. package/dist/operator-control-paths.js +24 -0
  115. package/dist/operator-control-paths.js.map +1 -0
  116. package/dist/outbound-handlers.d.ts +2 -0
  117. package/dist/outbound-handlers.js +9 -2
  118. package/dist/outbound-handlers.js.map +1 -1
  119. package/dist/outbound-schemas.d.ts +5 -5
  120. package/dist/profile-control.js +4 -20
  121. package/dist/profile-control.js.map +1 -1
  122. package/dist/public-web-gateway.d.ts +18 -0
  123. package/dist/public-web-gateway.js +141 -0
  124. package/dist/public-web-gateway.js.map +1 -0
  125. package/dist/public-web-link.d.ts +53 -0
  126. package/dist/public-web-link.js +175 -0
  127. package/dist/public-web-link.js.map +1 -0
  128. package/dist/quickstart-api.d.ts +8 -1
  129. package/dist/quickstart-api.js +86 -48
  130. package/dist/quickstart-api.js.map +1 -1
  131. package/dist/quickstart.d.ts +1 -0
  132. package/dist/quickstart.js +6 -2
  133. package/dist/quickstart.js.map +1 -1
  134. package/dist/scheduler/db.d.ts +7 -1
  135. package/dist/scheduler/db.js +38 -0
  136. package/dist/scheduler/db.js.map +1 -1
  137. package/dist/scheduler/index.d.ts +1 -1
  138. package/dist/scheduler/retry-label.d.ts +11 -0
  139. package/dist/scheduler/retry-label.js +24 -0
  140. package/dist/scheduler/retry-label.js.map +1 -0
  141. package/dist/scheduler/scheduler.d.ts +55 -2
  142. package/dist/scheduler/scheduler.js +220 -8
  143. package/dist/scheduler/scheduler.js.map +1 -1
  144. package/dist/scheduler/types.d.ts +22 -0
  145. package/dist/scheduler/types.js.map +1 -1
  146. package/dist/secret-store.d.ts +5 -0
  147. package/dist/secret-store.js +20 -0
  148. package/dist/secret-store.js.map +1 -1
  149. package/dist/service-activation.d.ts +110 -0
  150. package/dist/service-activation.js +302 -0
  151. package/dist/service-activation.js.map +1 -0
  152. package/dist/service-installer.d.ts +7 -0
  153. package/dist/service-installer.js +7 -4
  154. package/dist/service-installer.js.map +1 -1
  155. package/dist/service-restart-selection.d.ts +6 -5
  156. package/dist/service-restart-selection.js +6 -5
  157. package/dist/service-restart-selection.js.map +1 -1
  158. package/dist/settings-api.d.ts +10 -2
  159. package/dist/settings-api.js +175 -72
  160. package/dist/settings-api.js.map +1 -1
  161. package/dist/settings-baseline.d.ts +20 -0
  162. package/dist/settings-baseline.js +104 -0
  163. package/dist/settings-baseline.js.map +1 -0
  164. package/dist/settings-change.d.ts +21 -0
  165. package/dist/settings-change.js +133 -0
  166. package/dist/settings-change.js.map +1 -0
  167. package/dist/settings-confirmation.d.ts +105 -0
  168. package/dist/settings-confirmation.js +232 -0
  169. package/dist/settings-confirmation.js.map +1 -0
  170. package/dist/settings-control.d.ts +41 -0
  171. package/dist/settings-control.js +294 -0
  172. package/dist/settings-control.js.map +1 -0
  173. package/dist/settings-effect.d.ts +24 -0
  174. package/dist/settings-effect.js +169 -0
  175. package/dist/settings-effect.js.map +1 -0
  176. package/dist/settings-http-confirmation.d.ts +29 -0
  177. package/dist/settings-http-confirmation.js +123 -0
  178. package/dist/settings-http-confirmation.js.map +1 -0
  179. package/dist/settings-instance-patch.d.ts +4 -0
  180. package/dist/settings-instance-patch.js +26 -0
  181. package/dist/settings-instance-patch.js.map +1 -0
  182. package/dist/settings-request-capability.d.ts +16 -0
  183. package/dist/settings-request-capability.js +32 -0
  184. package/dist/settings-request-capability.js.map +1 -0
  185. package/dist/settings-transaction.d.ts +65 -0
  186. package/dist/settings-transaction.js +223 -0
  187. package/dist/settings-transaction.js.map +1 -0
  188. package/dist/setup-auth.d.ts +10 -18
  189. package/dist/setup-auth.js +14 -38
  190. package/dist/setup-auth.js.map +1 -1
  191. package/dist/setup-form.d.ts +1 -1
  192. package/dist/setup-form.js +75 -18
  193. package/dist/setup-form.js.map +1 -1
  194. package/dist/setup-guide.d.ts +13 -0
  195. package/dist/setup-guide.js +25 -0
  196. package/dist/setup-guide.js.map +1 -0
  197. package/dist/setup-host.d.ts +16 -0
  198. package/dist/setup-host.js +197 -32
  199. package/dist/setup-host.js.map +1 -1
  200. package/dist/setup-wizard.js +3 -0
  201. package/dist/setup-wizard.js.map +1 -1
  202. package/dist/slash-authz.d.ts +17 -3
  203. package/dist/slash-authz.js +5 -0
  204. package/dist/slash-authz.js.map +1 -1
  205. package/dist/spawn-gate.js +3 -3
  206. package/dist/spawn-gate.js.map +1 -1
  207. package/dist/statusline-watcher.d.ts +5 -0
  208. package/dist/statusline-watcher.js +2 -0
  209. package/dist/statusline-watcher.js.map +1 -1
  210. package/dist/steer-capability.d.ts +6 -0
  211. package/dist/steer-capability.js +8 -0
  212. package/dist/steer-capability.js.map +1 -1
  213. package/dist/sync-work-attribution.d.ts +14 -0
  214. package/dist/sync-work-attribution.js +43 -8
  215. package/dist/sync-work-attribution.js.map +1 -1
  216. package/dist/tips.js +10 -5
  217. package/dist/tips.js.map +1 -1
  218. package/dist/tmux-control.d.ts +35 -5
  219. package/dist/tmux-control.js +291 -57
  220. package/dist/tmux-control.js.map +1 -1
  221. package/dist/tmux-manager.d.ts +20 -6
  222. package/dist/tmux-manager.js +90 -47
  223. package/dist/tmux-manager.js.map +1 -1
  224. package/dist/tmux-read.d.ts +59 -0
  225. package/dist/tmux-read.js +213 -0
  226. package/dist/tmux-read.js.map +1 -0
  227. package/dist/topic-commands.d.ts +21 -10
  228. package/dist/topic-commands.js +191 -58
  229. package/dist/topic-commands.js.map +1 -1
  230. package/dist/transcript-delta-reader.js +2 -1
  231. package/dist/transcript-delta-reader.js.map +1 -1
  232. package/dist/tunnel/cloudflared-install.d.ts +2 -0
  233. package/dist/tunnel/cloudflared-install.js +1 -1
  234. package/dist/tunnel/cloudflared-install.js.map +1 -1
  235. package/dist/tunnel/purpose-lane.d.ts +14 -0
  236. package/dist/tunnel/purpose-lane.js +43 -0
  237. package/dist/tunnel/purpose-lane.js.map +1 -0
  238. package/dist/turn-reply-guard.d.ts +2 -0
  239. package/dist/turn-reply-guard.js +10 -1
  240. package/dist/turn-reply-guard.js.map +1 -1
  241. package/dist/types.d.ts +33 -0
  242. package/dist/ui/app.html +18 -0
  243. package/dist/ui/chat-render.js +478 -0
  244. package/dist/ui/chat-store.js +289 -0
  245. package/dist/ui/chat-strings.js +69 -0
  246. package/dist/ui/chat-thread.js +209 -0
  247. package/dist/ui/chat-tour.js +116 -0
  248. package/dist/ui/fleet-org.js +142 -0
  249. package/dist/ui/panel-chat.js +418 -0
  250. package/dist/ui/panel-fleet.js +413 -0
  251. package/dist/ui/panel-needs.js +202 -0
  252. package/dist/ui/panel-settings.js +750 -0
  253. package/dist/ui/preview.js +266 -0
  254. package/dist/ui/settings-apply.js +239 -0
  255. package/dist/ui/settings-confirm.js +186 -0
  256. package/dist/ui/settings-dialogs.js +546 -0
  257. package/dist/ui/settings-model.js +277 -0
  258. package/dist/ui/settings-strings.js +652 -0
  259. package/dist/ui/settings-wizard.js +140 -0
  260. package/dist/ui/shared/agend-auth.js +93 -0
  261. package/dist/ui/shared/app-ctx.js +80 -0
  262. package/dist/ui/shared/app-html.js +9 -0
  263. package/dist/ui/shared/app-i18n.js +95 -0
  264. package/dist/ui/shared/app-nav.js +73 -0
  265. package/dist/ui/shared/app-needs.js +114 -0
  266. package/dist/ui/shared/app-route.js +73 -0
  267. package/dist/ui/shared/app-session.js +66 -0
  268. package/dist/ui/shared/app-shell.js +344 -0
  269. package/dist/ui/shared/app-store.js +51 -0
  270. package/dist/ui/shared/app-stream.js +201 -0
  271. package/dist/ui/shared/app.css +676 -0
  272. package/dist/ui/shared/app.js +88 -0
  273. package/dist/ui/shared/fonts/OFL.txt +92 -0
  274. package/dist/ui/shared/fonts/README.md +21 -0
  275. package/dist/ui/shared/fonts/inter.woff2 +0 -0
  276. package/dist/ui/shared/htm.module.js +1 -0
  277. package/dist/ui/shared/panel-view.js +655 -0
  278. package/dist/ui/shared/preact-hooks.module.js +2 -0
  279. package/dist/ui/shared/preact.module.js +2 -0
  280. package/dist/ui/shared/signin.js +130 -0
  281. package/dist/ui/shared/theme.js +29 -0
  282. package/dist/ui/shared/tokens.css +153 -0
  283. package/dist/ui/shared/ui-confirm.js +43 -0
  284. package/dist/ui/shared/ui-dialog.js +45 -0
  285. package/dist/ui/shared/ui-icons.js +61 -0
  286. package/dist/ui/shared/ui-menu.js +39 -0
  287. package/dist/ui/shared/ui-states.js +20 -0
  288. package/dist/ui/shared/ui-toast.js +18 -0
  289. package/dist/ui/shared/vendor/htm.LICENSE +202 -0
  290. package/dist/ui/shared/vendor/preact.LICENSE +21 -0
  291. package/dist/ui/shared/vendor/vendor.json +39 -0
  292. package/dist/ui/shared/view-strings.js +309 -0
  293. package/dist/ui/signin.html +51 -0
  294. package/dist/update-check.d.ts +6 -2
  295. package/dist/update-check.js +12 -3
  296. package/dist/update-check.js.map +1 -1
  297. package/dist/update-install.d.ts +71 -0
  298. package/dist/update-install.js +162 -0
  299. package/dist/update-install.js.map +1 -0
  300. package/dist/upgrade-notices.d.ts +17 -0
  301. package/dist/upgrade-notices.js +93 -0
  302. package/dist/upgrade-notices.js.map +1 -0
  303. package/dist/usage/statusline-usage.d.ts +2 -0
  304. package/dist/usage/statusline-usage.js +14 -10
  305. package/dist/usage/statusline-usage.js.map +1 -1
  306. package/dist/view-api.d.ts +6 -1
  307. package/dist/view-api.js +62 -51
  308. package/dist/view-api.js.map +1 -1
  309. package/dist/web-api.d.ts +71 -14
  310. package/dist/web-api.js +492 -77
  311. package/dist/web-api.js.map +1 -1
  312. package/dist/web-auth.d.ts +71 -27
  313. package/dist/web-auth.js +169 -69
  314. package/dist/web-auth.js.map +1 -1
  315. package/dist/web-body.d.ts +3 -0
  316. package/dist/web-body.js +55 -0
  317. package/dist/web-body.js.map +1 -0
  318. package/dist/web-channel-echo.d.ts +9 -0
  319. package/dist/web-channel-echo.js +55 -0
  320. package/dist/web-channel-echo.js.map +1 -0
  321. package/dist/web-chat-history.d.ts +125 -0
  322. package/dist/web-chat-history.js +184 -0
  323. package/dist/web-chat-history.js.map +1 -0
  324. package/dist/web-continuation.d.ts +7 -0
  325. package/dist/web-continuation.js +22 -0
  326. package/dist/web-continuation.js.map +1 -0
  327. package/dist/web-host-guard.d.ts +34 -0
  328. package/dist/web-host-guard.js +57 -1
  329. package/dist/web-host-guard.js.map +1 -1
  330. package/dist/web-login.d.ts +72 -0
  331. package/dist/web-login.js +132 -0
  332. package/dist/web-login.js.map +1 -0
  333. package/dist/web-org.d.ts +55 -0
  334. package/dist/web-org.js +67 -0
  335. package/dist/web-org.js.map +1 -0
  336. package/dist/web-preview.d.ts +77 -0
  337. package/dist/web-preview.js +240 -0
  338. package/dist/web-preview.js.map +1 -0
  339. package/dist/web-request-context.d.ts +10 -0
  340. package/dist/web-request-context.js +13 -0
  341. package/dist/web-request-context.js.map +1 -0
  342. package/dist/web-session.d.ts +167 -0
  343. package/dist/web-session.js +389 -0
  344. package/dist/web-session.js.map +1 -0
  345. package/dist/web-shell-routes.d.ts +59 -0
  346. package/dist/web-shell-routes.js +96 -0
  347. package/dist/web-shell-routes.js.map +1 -0
  348. package/dist/web-upload.d.ts +183 -0
  349. package/dist/web-upload.js +468 -0
  350. package/dist/web-upload.js.map +1 -0
  351. package/dist/website-tips-renderer.d.ts +2 -2
  352. package/dist/website-tips-renderer.js +3 -3
  353. package/dist/website-tips-renderer.js.map +1 -1
  354. package/package.json +8 -5
  355. package/scripts/preinstall-guard.cjs +36 -0
  356. package/dist/ui/dashboard.html +0 -774
  357. package/dist/ui/settings.html +0 -2165
  358. 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();
@@ -346,6 +430,7 @@ function compatibilityFromVersion(version, parsed) {
346
430
  : has("2.3.0") ? ["rust", "kas"]
347
431
  : null,
348
432
  supportsEffortFlag: has(KIRO_EFFORT_FLAG_MIN),
433
+ supportsInstanceAgent: has(KIRO_INSTANCE_AGENT_MIN),
349
434
  source: "version",
350
435
  };
351
436
  }
@@ -359,6 +444,10 @@ function compatibilityFromHelp(version, help) {
359
444
  supportsV3: helpAdvertisesFlag(help, "--v3"),
360
445
  agentEngines: parseKiroAgentEngines(help),
361
446
  supportsEffortFlag: helpAdvertisesFlag(help, "--effort"),
447
+ // The flags are listed long before 2.21, but the behaviour #906 relies on was verified from 2.21 on: an unknown or
448
+ // older version keeps today's command.
449
+ supportsInstanceAgent: helpAdvertisesFlag(help, "--agent") && helpAdvertisesFlag(help, "--resume-id")
450
+ && (() => { const v = parseSemver(version); return !!v && versionAtLeast(v, KIRO_INSTANCE_AGENT_MIN); })(),
362
451
  source: "help",
363
452
  };
364
453
  }
@@ -401,14 +490,78 @@ function nextPatch(version) {
401
490
  const [major, minor, patch] = parseSemver(version);
402
491
  return `${major}.${minor}.${patch + 1}`;
403
492
  }
493
+ /** Binary generations already told they cannot isolate (once each). */
494
+ const warnedNoInstanceAgentKeys = new Set();
495
+ /** Instances already told about an untagged steering file (once each). */
496
+ const warnedUntaggedSteering = new Set();
497
+ /** The classic (v1) conversation store an instance launches with: its credential profile's, else the shared one. */
498
+ export function kiroV1DbPath(credentialProfile) {
499
+ const home = credentialProfile
500
+ ? credentialProfileStoreHome(getAgendHome(), "kiro-cli", credentialProfile)
501
+ : join(process.env.XDG_DATA_HOME || join(homedir(), ".local", "share"), "kiro-cli");
502
+ return join(home, "data.sqlite3");
503
+ }
504
+ /** The TUI (v2) session files: `<KIRO_HOME or ~/.kiro>/sessions/cli` — not per credential profile. */
505
+ export function kiroV2SessionsDir() {
506
+ return join(process.env.KIRO_HOME?.trim() || join(homedir(), ".kiro"), "sessions", "cli");
507
+ }
404
508
  /**
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.
509
+ * Adoption evidence (#906 §2): the engine ledger shows this instance launched here — same directory, credential
510
+ * profile and engine — before #906 (no "--agent" in that launch). A new instance has no such record and starts fresh
511
+ * instead of taking a sibling's conversation.
411
512
  */
513
+ export function kiroLaunchedHereBefore(instance, workingDirectory, credentialProfile, engine, ledgerPath) {
514
+ const entry = readKiroLedger(ledgerPath)[instance];
515
+ if (!entry || resolve(entry.workingDirectory) !== resolve(workingDirectory) || entry.credentialProfile !== credentialProfile)
516
+ return false;
517
+ return entry.lastLaunch.ui === (engine === "v1" ? "legacy" : "tui") && !entry.lastLaunch.flags.includes("--agent");
518
+ }
519
+ /** The legacy UI's prompt row: an optional `[agent]`, the context percentage, an optional mode glyph, the marker. */
520
+ const LEGACY_PROMPT_ROW = /^\s*(?:\[([^\]]+)\]\s*)?\d+%\s*(?:[^\s\d%!❯>]{1,2}\s+)?(?:!\s?[❯>]|❯|>)/;
521
+ /** A TUI status row: `<agent> · <model> · …`. */
522
+ const TUI_STATUS_ROW = /^\s*(\S+) · /;
523
+ const RULE_ROW = /^[\s─━│┃╭╮╰╯┌┐└┘]*$/;
524
+ /** The right-aligned hint under the TUI input row: far right, a slash command or two. */
525
+ const TUI_HINT_ROW = /^\s{20,}\/[\w-]+(?: [\w-]+)*(?: · \/[\w-]+(?: [\w-]+)*)*$/;
526
+ /**
527
+ * The agent the live layout shows (#906 §3) — never a name quoted in the conversation above it:
528
+ * - legacy: the pane's last non-empty row, only when it is the prompt row; `[name]` on it, or the default agent;
529
+ * - TUI / v3: the status row directly above the input row (the last `›` row; earlier `›` rows are the user's past
530
+ * messages, which sit above it), skipping rule rows.
531
+ * Null when the layout cannot be read.
532
+ */
533
+ export function readActiveKiroAgent(pane, ui) {
534
+ const rows = pane.split("\n").map(r => r.replace(/\s+$/, ""));
535
+ if (ui === "legacy") {
536
+ const last = [...rows].reverse().find(r => r.trim() !== "");
537
+ const m = last ? LEGACY_PROMPT_ROW.exec(last) : null;
538
+ return m ? (m[1] ?? "kiro_default") : null;
539
+ }
540
+ let input = -1;
541
+ for (let i = rows.length - 1; i >= 0; i--)
542
+ if (/^\s*›/.test(rows[i])) {
543
+ input = i;
544
+ break;
545
+ }
546
+ if (input < 0)
547
+ return null;
548
+ // The live bottom layout ends with the input row: below it only blank or rule rows and the right-aligned hint
549
+ // (`/copy to clipboard`, `/sessions to resume · …`). Anything else — a modal, a panel, output — means the pair
550
+ // above it is not the live one (#1416 review).
551
+ for (let i = input + 1; i < rows.length; i++) {
552
+ const row = rows[i];
553
+ if (row.trim() === "" || RULE_ROW.test(row) || TUI_HINT_ROW.test(row))
554
+ continue;
555
+ return null;
556
+ }
557
+ for (let i = input - 1; i >= 0; i--) {
558
+ if (RULE_ROW.test(rows[i]))
559
+ continue;
560
+ const m = TUI_STATUS_ROW.exec(rows[i]);
561
+ return m ? m[1] : null;
562
+ }
563
+ return null;
564
+ }
412
565
  export function planKiroLaunch(ui, compat) {
413
566
  const version = compat.version ?? "of unknown version";
414
567
  if (ui === "v3")
@@ -539,6 +692,46 @@ export class KiroBackend {
539
692
  activeTrustAll = true;
540
693
  /** Version-gate notice for the launch just built (consumeLaunchWarning). */
541
694
  launchWarning = null;
695
+ /**
696
+ * What this launch attempt resumes and as which agent (#906), resolved once per attempt in writeConfig — it claims
697
+ * conversations and records fresh starts — and read by the buildCommand that follows it.
698
+ */
699
+ launchPlan = null;
700
+ /** The plan of the command last built: what agentSwitch() works from. */
701
+ activePlan = null;
702
+ preparedV2Store = null;
703
+ storePreparation = 0;
704
+ storeKey(config) {
705
+ return JSON.stringify([config.instanceName, config.workingDirectory, kiroCanonicalDirectory(config.workingDirectory), config.kiroUi,
706
+ resolveCredentialProfile(config.backendOptions), !!config.skipResume, getAgendHome(), kiroV2SessionsDir(), kiroCanonicalDirectory(kiroV2SessionsDir())]);
707
+ }
708
+ readPreparedV2Store(config) {
709
+ if (this.preparedV2Store?.key !== this.storeKey(config)) {
710
+ throw new KiroIdentityError("v2 session discovery was not prepared for this launch");
711
+ }
712
+ return this.preparedV2Store.store;
713
+ }
714
+ /** Metadata only: identity claims/config writes remain synchronous, after
715
+ * the daemon rechecks its launch admission following this await. */
716
+ async prepareLaunch(config) {
717
+ const preparation = ++this.storePreparation;
718
+ this.preparedV2Store = null;
719
+ if (!config || config.kiroUi !== "tui")
720
+ return;
721
+ if (!this.fixedCompatibility)
722
+ this.compatibility = cachedKiroCliCompatibility(this.binaryPath).compatibility;
723
+ const key = this.storeKey(config);
724
+ const plan = planKiroLaunch("tui", this.compatibility);
725
+ if (plan.kind !== "launch" || !plan.flags.includes("--agent-engine=v2") || !this.compatibility.supportsInstanceAgent)
726
+ return;
727
+ const opts = { instance: config.instanceName, engine: "v2", workingDirectory: config.workingDirectory,
728
+ credentialProfile: resolveCredentialProfile(config.backendOptions), skipResume: config.skipResume, agendHome: getAgendHome() };
729
+ if (kiroIdentityNeedsStore(opts)) {
730
+ const store = await listKiroV2Sessions(config.workingDirectory, kiroV2SessionsDir());
731
+ if (preparation === this.storePreparation && key === this.storeKey(config))
732
+ this.preparedV2Store = { key, store };
733
+ }
734
+ }
542
735
  constructor(instanceDir, compatibility) {
543
736
  this.instanceDir = instanceDir;
544
737
  this.binaryPath = resolveBinary("kiro-cli");
@@ -577,6 +770,22 @@ export class KiroBackend {
577
770
  // their ready marker is verified.
578
771
  return this.activeUi === "legacy" && this.activeTrustAll;
579
772
  }
773
+ /** #1405: the TUI front-end this instance was launched with, on a version listed in KIRO_STEER_VERIFIED. */
774
+ supportsSteer() {
775
+ if (this.activeUi === "legacy")
776
+ return false;
777
+ // compatibility.version is kiro-cli's own `--version` line ("kiro-cli 2.27.1").
778
+ const version = parseSemver(this.compatibility.version)?.join(".");
779
+ return version !== undefined && Object.hasOwn(KIRO_STEER_VERIFIED, version)
780
+ && KIRO_STEER_VERIFIED[version].includes(this.activeUi);
781
+ }
782
+ /** #1405: the TUI composer's mode; never read on the legacy UI, whose prompt row has no such mode. */
783
+ readSteerComposer(pane) {
784
+ return this.activeUi === "legacy" ? null : readKiroSteerComposer(pane);
785
+ }
786
+ readSteerComposerText(pane) {
787
+ return this.activeUi === "legacy" ? null : readKiroComposerText(pane);
788
+ }
580
789
  /**
581
790
  * Live evidence (kiro-cli 2.21.0, 2026-09-03): `--resume` prints nothing
582
791
  * until the conversation comes back from the backend ("Picking up where we
@@ -647,13 +856,21 @@ export class KiroBackend {
647
856
  cmd += ` ${flag}`;
648
857
  this.noteVersionGate();
649
858
  const credentialProfile = resolveCredentialProfile(config.backendOptions);
859
+ // #906: the plan writeConfig resolved for this attempt (a direct buildCommand, as in tests, resolves its own).
860
+ const lp = this.launchPlan && this.launchPlan.cwd === config.workingDirectory && this.launchPlan.skipResume === !!config.skipResume
861
+ ? this.launchPlan : this.planInstanceLaunch(config);
862
+ this.launchPlan = null;
863
+ this.activePlan = lp;
864
+ this.activeSpec = lp.mode === "legacy" ? null : this.agentSpec(config);
865
+ this.activeInstance = config.instanceName;
650
866
  recordKiroLaunch({
651
867
  instance: config.instanceName,
652
868
  workingDirectory: config.workingDirectory,
653
869
  credentialProfile,
654
870
  kiroVersion: this.compatibility.version ?? null,
655
871
  ui,
656
- flags: plan.flags,
872
+ // "--agent" marks a launch made after #906: such an instance is never adopted again (kiroLaunchedHereBefore).
873
+ flags: lp.mode !== "legacy" ? [...plan.flags, "--agent"] : plan.flags,
657
874
  });
658
875
  // Record what is actually being launched for the delivery gate (see
659
876
  // dropsEnterWhileBusy): the legacy prompt row exists only under
@@ -662,30 +879,24 @@ export class KiroBackend {
662
879
  this.activeTrustAll = config.skipPermissions !== false;
663
880
  if (config.skipPermissions !== false)
664
881
  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") {
882
+ if (lp.mode !== "legacy") {
883
+ // Its own agent: on a fresh start it takes effect at once; on a resume kiro brings back the conversation's saved
884
+ // agent instead (E6), and the daemon switches it on screen (agentSwitch).
885
+ cmd += ` --agent ${shellQuote(lp.agent)}`;
886
+ if (lp.mode === "resume")
887
+ cmd += ` --resume-id ${shellQuote(lp.id)}`;
888
+ }
889
+ else if (ui === "v3") {
890
+ // Legacy mode, V3: it resumes only the session it owns, by id (kiro-v3-identity.ts) — its `--resume` would take
891
+ // the newest conversation in the directory from ANY engine and convert a classic one into a new V3 copy.
684
892
  const id = resolveKiroV3Resume(config.instanceName, config.workingDirectory, credentialProfile, { skipResume: config.skipResume });
685
893
  if (id)
686
894
  cmd += ` --resume-id ${shellQuote(id)}`;
687
895
  }
688
896
  else if (!config.skipResume) {
897
+ // Legacy mode, classic/TUI: the boolean form — kiro resumes the directory's newest conversation. Not isolated
898
+ // between instances sharing the directory (#1410); an instance launches like this only without the
899
+ // per-instance plan (an old kiro-cli, or a conversation store AgEnD could not read on adoption).
689
900
  cmd += " --resume";
690
901
  }
691
902
  if (config.model) {
@@ -756,7 +967,8 @@ export class KiroBackend {
756
967
  if (!warning)
757
968
  return;
758
969
  warnedVersionGateCacheKeys.add(key);
759
- this.launchWarning = warning;
970
+ // Appended: the launch's isolation warnings (#906, recorded by writeConfig) must not be overwritten by this one.
971
+ this.addLaunchWarning(warning);
760
972
  }
761
973
  consumeLaunchWarning() {
762
974
  const warning = this.launchWarning;
@@ -776,36 +988,11 @@ export class KiroBackend {
776
988
  return true;
777
989
  }
778
990
  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
- }
991
+ // WORKAROUND: kiro-cli ignores the "env" block of an MCP server entry — the server subprocess inherits the fleet
992
+ // manager's env, with a stale AGEND_SOCKET_PATH from whichever daemon wrote last. Each server therefore runs
993
+ // through a wrapper script that exports this instance's env and execs the real server.
805
994
  for (const [name, entry] of Object.entries(config.mcpServers)) {
806
- const instanceKey = `${name}-${config.instanceName}`;
807
995
  const allEnv = { ...entry.env, AGEND_INSTANCE_NAME: config.instanceName };
808
- // Write a wrapper script that sets env vars explicitly
809
996
  const wrapperPath = join(this.instanceDir, `mcp-wrapper-${name}.sh`);
810
997
  const envExports = Object.entries(allEnv)
811
998
  .map(([k, v]) => `export ${k}='${String(v).replace(/'/g, "'\\''")}'`)
@@ -817,25 +1004,166 @@ export class KiroBackend {
817
1004
  // Re-chmod in case the file already existed with looser permissions (writeFileSync's
818
1005
  // mode only applies on create).
819
1006
  chmodSync(wrapperPath, 0o700);
820
- servers[instanceKey] = {
821
- command: wrapperPath,
822
- args: [],
823
- };
824
1007
  }
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 */ }
1008
+ // #906: which conversation this attempt resumes, and as which agent — resolved once, here.
1009
+ const plan = this.planInstanceLaunch(config);
1010
+ this.preparedV2Store = null;
1011
+ this.launchPlan = plan;
1012
+ const spec = this.agentSpec(config);
1013
+ const root = dirname(this.instanceDir);
1014
+ if (plan.mode === "legacy") {
1015
+ // Not isolated (an old kiro-cli, or adoption with an unreadable store): the shared files as before, each
1016
+ // written only where it is free, and removed only by provenance.
1017
+ if (plan.reason)
1018
+ this.addLaunchWarning(plan.reason);
1019
+ this.noteShared("mcp.json", writeSharedKiroMcpEntries(spec));
1020
+ if (config.instructions)
1021
+ this.noteShared("steering", writeTaggedKiroSteering(spec, config.instructions));
1022
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, false));
1023
+ return;
1024
+ }
1025
+ // Throws when a file that is not this instance's agent is in the way: never overwritten, launch refused.
1026
+ writeKiroAgent(spec, config.instructions);
1027
+ if (plan.mode === "resume" && !plan.agentConfirmed) {
1028
+ // The resumed conversation comes back as its saved agent (`--agent` is ignored on resume), so until the switch
1029
+ // is confirmed on screen it gets its AgEnD server and instructions from the shared files (design §3).
1030
+ this.noteShared("mcp.json", writeSharedKiroMcpEntries(spec));
1031
+ if (config.instructions)
1032
+ this.noteShared("steering", writeTaggedKiroSteering(spec, config.instructions));
1033
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, false));
1034
+ return;
1035
+ }
1036
+ // Running as its own agent: nothing of this instance belongs in the shared files any more.
1037
+ this.noteShared("mcp.json", removeSharedKiroMcpEntries(spec, root, true));
1038
+ this.noteShared("steering", removeTaggedKiroSteering(spec));
1039
+ }
1040
+ /** This instance's agent, by its fleet and its own wrapper scripts. */
1041
+ agentSpec(config) {
1042
+ return {
1043
+ workingDirectory: config.workingDirectory,
1044
+ instance: config.instanceName,
1045
+ fleet: kiroFleetTag(getAgendHome()),
1046
+ instanceDir: this.instanceDir,
1047
+ serverNames: Object.keys(config.mcpServers),
1048
+ };
1049
+ }
1050
+ /** A shared file AgEnD could not bring to the state isolation needs: said, never reported as isolated. */
1051
+ noteShared(file, outcome) {
1052
+ if (outcome === "conflict")
1053
+ this.addLaunchWarning(t("kiro.shared_conflict", file));
1054
+ else if (outcome === "unreadable" || outcome === "failed")
1055
+ this.addLaunchWarning(t("kiro.shared_unreadable", file));
1056
+ else if (outcome === "untagged-legacy" && !warnedUntaggedSteering.has(this.instanceDir)) {
1057
+ warnedUntaggedSteering.add(this.instanceDir);
1058
+ this.addLaunchWarning(t("kiro.untagged_steering"));
837
1059
  }
838
1060
  }
1061
+ addLaunchWarning(message) {
1062
+ this.launchWarning = this.launchWarning ? `${this.launchWarning}\n${message}` : message;
1063
+ }
1064
+ /**
1065
+ * The launch plan of one attempt (#906, design §2/§5): the engine the plan pins, and — when this kiro-cli has
1066
+ * `--agent` and `--resume-id` — the conversation this instance owns and whether it already runs as its agent.
1067
+ * Anything else is legacy mode: today's command, not isolated.
1068
+ */
1069
+ planInstanceLaunch(config) {
1070
+ const ui = config.kiroUi ?? "legacy";
1071
+ if (!this.fixedCompatibility) {
1072
+ const current = cachedKiroCliCompatibility(this.binaryPath);
1073
+ this.compatibility = current.compatibility;
1074
+ this.compatibilityCacheKey = current.cacheKey;
1075
+ }
1076
+ const plan = planKiroLaunch(ui, this.compatibility);
1077
+ const flags = plan.kind === "launch" ? plan.flags : [];
1078
+ const engine = flags.includes("--agent-engine=v1") ? "v1"
1079
+ : flags.includes("--agent-engine=v2") ? "v2" : ui === "v3" ? "v3" : null;
1080
+ const credentialProfile = resolveCredentialProfile(config.backendOptions);
1081
+ const base = { cwd: config.workingDirectory, skipResume: !!config.skipResume, engine, credentialProfile };
1082
+ const legacy = (reason) => ({ ...base, mode: "legacy", reason, agent: null, agentConfirmed: false });
1083
+ if (plan.kind !== "launch" || this.compatibility.source === "unknown")
1084
+ return legacy(null); // buildCommand refuses
1085
+ if (!this.compatibility.supportsInstanceAgent || !engine) {
1086
+ const key = this.compatibilityCacheKey ?? this.binaryPath;
1087
+ if (warnedNoInstanceAgentKeys.has(key))
1088
+ return legacy(null);
1089
+ warnedNoInstanceAgentKeys.add(key);
1090
+ return legacy(t("kiro.no_instance_agent", this.compatibility.version ?? "?", KIRO_INSTANCE_AGENT_MIN));
1091
+ }
1092
+ const agent = kiroAgentName(config.instanceName, kiroFleetTag(getAgendHome()));
1093
+ const agendHome = getAgendHome();
1094
+ let decision;
1095
+ if (engine === "v3") {
1096
+ // V3 keeps its own identity (kiro-v3-identity.ts); only whether it runs as the agent is recorded here.
1097
+ const id = resolveKiroV3Resume(config.instanceName, config.workingDirectory, credentialProfile, { skipResume: config.skipResume });
1098
+ decision = id
1099
+ ? { mode: "resume", id, agentConfirmed: kiroAgentConfirmed(agendHome, config.instanceName, "v3", config.workingDirectory, credentialProfile, id) }
1100
+ : { mode: "fresh" };
1101
+ }
1102
+ else {
1103
+ decision = resolveKiroIdentity({
1104
+ instance: config.instanceName,
1105
+ engine,
1106
+ workingDirectory: config.workingDirectory,
1107
+ credentialProfile,
1108
+ skipResume: config.skipResume,
1109
+ agendHome,
1110
+ readStore: () => engine === "v1"
1111
+ ? listKiroV1Sessions(config.workingDirectory, kiroV1DbPath(credentialProfile))
1112
+ : this.readPreparedV2Store(config),
1113
+ launchedBefore: () => kiroLaunchedHereBefore(config.instanceName, config.workingDirectory, credentialProfile, engine),
1114
+ });
1115
+ }
1116
+ if (decision.mode === "legacy")
1117
+ return legacy(t("kiro.identity_legacy", decision.reason));
1118
+ return decision.mode === "resume"
1119
+ ? { ...base, mode: "resume", id: decision.id, agent, agentConfirmed: decision.agentConfirmed, reason: null }
1120
+ : { ...base, mode: "fresh", agent, agentConfirmed: true, reason: null };
1121
+ }
1122
+ /**
1123
+ * After a resume as the instance's agent (#906 §3): what the daemon needs to read the active agent off the live
1124
+ * layout and, when it is not ours, switch to it with `/agent swap` and confirm. Null when there is nothing to do.
1125
+ */
1126
+ agentSwitch() {
1127
+ const plan = this.activePlan;
1128
+ if (!plan || plan.mode !== "resume" || !plan.agent || !plan.id || !plan.engine)
1129
+ return null;
1130
+ const { agent, id, engine, cwd, credentialProfile } = plan;
1131
+ const ui = this.activeUi;
1132
+ return {
1133
+ agent,
1134
+ alreadyConfirmed: plan.agentConfirmed,
1135
+ readActive: (pane) => readActiveKiroAgent(pane, ui),
1136
+ command: `/agent swap ${agent}`,
1137
+ confirm: () => {
1138
+ const warnings = [];
1139
+ if (!this.activeInstance || !confirmKiroAgentSwitch(getAgendHome(), this.activeInstance, engine, cwd, credentialProfile, id)) {
1140
+ warnings.push(t("kiro.switch_unrecorded"));
1141
+ return warnings;
1142
+ }
1143
+ plan.agentConfirmed = true;
1144
+ const spec = this.activeSpec;
1145
+ if (spec) {
1146
+ const mcp = removeSharedKiroMcpEntries(spec, dirname(this.instanceDir), true);
1147
+ if (mcp === "unreadable" || mcp === "failed")
1148
+ warnings.push(t("kiro.shared_unreadable", "mcp.json"));
1149
+ const steering = removeTaggedKiroSteering(spec);
1150
+ if (steering === "failed" || steering === "unreadable")
1151
+ warnings.push(t("kiro.shared_unreadable", "steering"));
1152
+ }
1153
+ return warnings;
1154
+ },
1155
+ };
1156
+ }
1157
+ /**
1158
+ * An instance running as its own agent reads its instructions from the agent file's prompt (#906). Until a resumed
1159
+ * conversation's switch is confirmed it still runs as its saved agent, reading the steering file (the default).
1160
+ */
1161
+ instructionsSource() {
1162
+ const plan = this.activePlan;
1163
+ return plan && plan.mode !== "legacy" && plan.agent && plan.agentConfirmed ? `.kiro/agents/${plan.agent}.json (its "prompt" field)` : null;
1164
+ }
1165
+ activeSpec = null;
1166
+ activeInstance = null;
839
1167
  getReadyPattern() {
840
1168
  // Startup: trust/banner text. Daily prompt: "22% !>" / "8% ❯";
841
1169
  // Kiro may insert mode glyphs between them, e.g. "20% λ !>".
@@ -1138,26 +1466,25 @@ export class KiroBackend {
1138
1466
  getEffortStrategy() { return EFFORT_CAPABILITIES["kiro-cli"].strategy; }
1139
1467
  getEffortLevels() { return [...EFFORT_CAPABILITIES["kiro-cli"].levels]; }
1140
1468
  cleanup(config) {
1141
- // Only remove namespaced keys — non-namespaced "agend" key may belong to
1142
- // another instance sharing this working directory.
1469
+ // By provenance only (#906 §4/§6): the agent file and its .bak when they are this instance's, the shared mcp.json
1470
+ // entries run by this instance's own wrappers, and a steering file carrying this fleet's tag for it. The user's,
1471
+ // another fleet's, and untagged steering from before the tag existed are kept.
1143
1472
  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
- }
1473
+ const spec = this.agentSpec(config);
1474
+ removeKiroAgent(spec);
1475
+ removeSharedKiroMcpEntries(spec, dirname(this.instanceDir), true);
1476
+ removeTaggedKiroSteering(spec);
1477
+ }
1478
+ catch { /* best effort */ }
1479
+ }
1480
+ /** Delete or replace: forget which conversations this instance owns (they stay in kiro's store). */
1481
+ forget(instanceName) {
1482
+ try {
1483
+ forgetKiroIdentity(getAgendHome(), instanceName);
1154
1484
  }
1155
1485
  catch { /* best effort */ }
1156
- // Remove fleet instructions steering file
1157
1486
  try {
1158
- const steeringFile = join(config.workingDirectory, ".kiro", "steering", `agend-${config.instanceName}.md`);
1159
- if (existsSync(steeringFile))
1160
- unlinkSync(steeringFile);
1487
+ forgetKiroV3Identity(instanceName, getAgendHome());
1161
1488
  }
1162
1489
  catch { /* best effort */ }
1163
1490
  }