@songsid/agend 2.3.2-beta.1 → 2.3.2-beta.10

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 (266) hide show
  1. package/THIRD_PARTY_NOTICES.md +40 -0
  2. package/dist/agent-cli.js +21 -2
  3. package/dist/agent-cli.js.map +1 -1
  4. package/dist/agent-endpoint.js +5 -0
  5. package/dist/agent-endpoint.js.map +1 -1
  6. package/dist/antigravity-usage-scan.d.ts +58 -0
  7. package/dist/antigravity-usage-scan.js +257 -0
  8. package/dist/antigravity-usage-scan.js.map +1 -0
  9. package/dist/antigravity-usage-worker.d.ts +1 -0
  10. package/dist/antigravity-usage-worker.js +10 -0
  11. package/dist/antigravity-usage-worker.js.map +1 -0
  12. package/dist/auth-api.js +4 -0
  13. package/dist/auth-api.js.map +1 -1
  14. package/dist/backend/kiro-identity.d.ts +64 -0
  15. package/dist/backend/kiro-identity.js +101 -0
  16. package/dist/backend/kiro-identity.js.map +1 -1
  17. package/dist/backend/kiro-retired-model.d.ts +62 -0
  18. package/dist/backend/kiro-retired-model.js +160 -0
  19. package/dist/backend/kiro-retired-model.js.map +1 -0
  20. package/dist/backend/kiro.d.ts +71 -2
  21. package/dist/backend/kiro.js +352 -11
  22. package/dist/backend/kiro.js.map +1 -1
  23. package/dist/backend/types.d.ts +59 -0
  24. package/dist/backend/types.js.map +1 -1
  25. package/dist/backend-signin-guide.d.ts +1 -0
  26. package/dist/backend-signin-guide.js +9 -0
  27. package/dist/backend-signin-guide.js.map +1 -0
  28. package/dist/backend-signin-pending.d.ts +10 -0
  29. package/dist/backend-signin-pending.js +44 -0
  30. package/dist/backend-signin-pending.js.map +1 -0
  31. package/dist/browser-backend-signins.d.ts +56 -0
  32. package/dist/browser-backend-signins.js +189 -0
  33. package/dist/browser-backend-signins.js.map +1 -0
  34. package/dist/cache-ledger.d.ts +38 -2
  35. package/dist/cache-ledger.js +133 -8
  36. package/dist/cache-ledger.js.map +1 -1
  37. package/dist/cache-prices.d.ts +2 -1
  38. package/dist/cache-prices.js +5 -2
  39. package/dist/cache-prices.js.map +1 -1
  40. package/dist/cache-service.d.ts +68 -0
  41. package/dist/cache-service.js +355 -13
  42. package/dist/cache-service.js.map +1 -1
  43. package/dist/channel/adapters/discord.d.ts +11 -0
  44. package/dist/channel/adapters/discord.js +71 -14
  45. package/dist/channel/adapters/discord.js.map +1 -1
  46. package/dist/channel/mcp-tools.js +29 -2
  47. package/dist/channel/mcp-tools.js.map +1 -1
  48. package/dist/channel/tool-router.js +2 -1
  49. package/dist/channel/tool-router.js.map +1 -1
  50. package/dist/classic-channel-manager.d.ts +11 -0
  51. package/dist/classic-channel-manager.js +39 -1
  52. package/dist/classic-channel-manager.js.map +1 -1
  53. package/dist/cli.js +21 -2
  54. package/dist/cli.js.map +1 -1
  55. package/dist/config-validator.d.ts +7 -1
  56. package/dist/config-validator.js +53 -10
  57. package/dist/config-validator.js.map +1 -1
  58. package/dist/config.d.ts +19 -0
  59. package/dist/config.js +31 -1
  60. package/dist/config.js.map +1 -1
  61. package/dist/cpu-quota.d.ts +23 -0
  62. package/dist/cpu-quota.js +86 -0
  63. package/dist/cpu-quota.js.map +1 -0
  64. package/dist/daemon.d.ts +23 -0
  65. package/dist/daemon.js +106 -7
  66. package/dist/daemon.js.map +1 -1
  67. package/dist/dashboard-permissions.d.ts +20 -6
  68. package/dist/dashboard-permissions.js +58 -9
  69. package/dist/dashboard-permissions.js.map +1 -1
  70. package/dist/doctor.js +2 -1
  71. package/dist/doctor.js.map +1 -1
  72. package/dist/fleet-manager.d.ts +72 -0
  73. package/dist/fleet-manager.js +507 -89
  74. package/dist/fleet-manager.js.map +1 -1
  75. package/dist/forged-envelope-scan.d.ts +9 -0
  76. package/dist/forged-envelope-scan.js +30 -5
  77. package/dist/forged-envelope-scan.js.map +1 -1
  78. package/dist/general-knowledge/skills/credential-profiles/SKILL.md +5 -2
  79. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +14 -1
  80. package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +24 -1
  81. package/dist/general-knowledge/skills/web-appearance/SKILL.md +24 -1
  82. package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +37 -9
  83. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +44 -11
  84. package/dist/instance-config-impact.js +10 -5
  85. package/dist/instance-config-impact.js.map +1 -1
  86. package/dist/instance-lifecycle.d.ts +19 -0
  87. package/dist/instance-lifecycle.js +55 -12
  88. package/dist/instance-lifecycle.js.map +1 -1
  89. package/dist/kiro-db-reader.d.ts +5 -0
  90. package/dist/kiro-db-reader.js +18 -0
  91. package/dist/kiro-db-reader.js.map +1 -1
  92. package/dist/kiro-engine-status.d.ts +2 -0
  93. package/dist/kiro-engine-status.js +9 -0
  94. package/dist/kiro-engine-status.js.map +1 -1
  95. package/dist/kiro-migrate.d.ts +49 -0
  96. package/dist/kiro-migrate.js +105 -0
  97. package/dist/kiro-migrate.js.map +1 -0
  98. package/dist/kiro-transcript-lane.d.ts +2 -0
  99. package/dist/kiro-transcript-lane.js.map +1 -1
  100. package/dist/kiro-transcript-worker.js +1 -0
  101. package/dist/kiro-transcript-worker.js.map +1 -1
  102. package/dist/kiro-tui-forensic.d.ts +7 -0
  103. package/dist/kiro-tui-forensic.js +110 -0
  104. package/dist/kiro-tui-forensic.js.map +1 -0
  105. package/dist/kiro-usage-run.d.ts +7 -0
  106. package/dist/kiro-usage-run.js +39 -0
  107. package/dist/kiro-usage-run.js.map +1 -0
  108. package/dist/kiro-usage-scan.d.ts +49 -0
  109. package/dist/kiro-usage-scan.js +223 -0
  110. package/dist/kiro-usage-scan.js.map +1 -0
  111. package/dist/kiro-usage-worker.d.ts +1 -0
  112. package/dist/kiro-usage-worker.js +10 -0
  113. package/dist/kiro-usage-worker.js.map +1 -0
  114. package/dist/kiro-usage.d.ts +58 -0
  115. package/dist/kiro-usage.js +310 -0
  116. package/dist/kiro-usage.js.map +1 -0
  117. package/dist/locale.js +88 -8
  118. package/dist/locale.js.map +1 -1
  119. package/dist/log-service.js +1 -1
  120. package/dist/log-service.js.map +1 -1
  121. package/dist/login-url.d.ts +4 -0
  122. package/dist/login-url.js +22 -0
  123. package/dist/login-url.js.map +1 -0
  124. package/dist/needs-you-hub.d.ts +2 -1
  125. package/dist/needs-you-hub.js +1 -1
  126. package/dist/needs-you-hub.js.map +1 -1
  127. package/dist/needs-you.d.ts +8 -2
  128. package/dist/needs-you.js +6 -0
  129. package/dist/needs-you.js.map +1 -1
  130. package/dist/notice-delivery.d.ts +16 -0
  131. package/dist/notice-delivery.js +35 -0
  132. package/dist/notice-delivery.js.map +1 -0
  133. package/dist/office-share-page.d.ts +16 -0
  134. package/dist/office-share-page.js +85 -0
  135. package/dist/office-share-page.js.map +1 -0
  136. package/dist/office-share-service.d.ts +91 -0
  137. package/dist/office-share-service.js +176 -0
  138. package/dist/office-share-service.js.map +1 -0
  139. package/dist/office-share.d.ts +258 -0
  140. package/dist/office-share.js +549 -0
  141. package/dist/office-share.js.map +1 -0
  142. package/dist/opencode-usage-scan.d.ts +28 -0
  143. package/dist/opencode-usage-scan.js +92 -0
  144. package/dist/opencode-usage-scan.js.map +1 -0
  145. package/dist/opencode-usage-worker.d.ts +1 -0
  146. package/dist/opencode-usage-worker.js +10 -0
  147. package/dist/opencode-usage-worker.js.map +1 -0
  148. package/dist/outbound-handlers.js +22 -2
  149. package/dist/outbound-handlers.js.map +1 -1
  150. package/dist/outbound-schemas.d.ts +26 -2
  151. package/dist/outbound-schemas.js +27 -2
  152. package/dist/outbound-schemas.js.map +1 -1
  153. package/dist/public-web-gateway.js +7 -1
  154. package/dist/public-web-gateway.js.map +1 -1
  155. package/dist/quickstart-api.js +13 -2
  156. package/dist/quickstart-api.js.map +1 -1
  157. package/dist/quickstart.js +2 -1
  158. package/dist/quickstart.js.map +1 -1
  159. package/dist/remote-card-answer.d.ts +83 -0
  160. package/dist/remote-card-answer.js +208 -0
  161. package/dist/remote-card-answer.js.map +1 -0
  162. package/dist/reply-triggers.d.ts +2 -0
  163. package/dist/reply-triggers.js +3 -0
  164. package/dist/reply-triggers.js.map +1 -0
  165. package/dist/settings-api.d.ts +2 -0
  166. package/dist/settings-api.js +41 -16
  167. package/dist/settings-api.js.map +1 -1
  168. package/dist/settings-change.js +17 -14
  169. package/dist/settings-change.js.map +1 -1
  170. package/dist/settings-effect.js +7 -1
  171. package/dist/settings-effect.js.map +1 -1
  172. package/dist/settings-instance-patch.js +4 -1
  173. package/dist/settings-instance-patch.js.map +1 -1
  174. package/dist/setup-form.d.ts +1 -1
  175. package/dist/setup-form.js +55 -6
  176. package/dist/setup-form.js.map +1 -1
  177. package/dist/setup-host.d.ts +9 -0
  178. package/dist/setup-host.js +127 -6
  179. package/dist/setup-host.js.map +1 -1
  180. package/dist/setup-signin-assets.d.ts +2 -0
  181. package/dist/setup-signin-assets.js +6 -0
  182. package/dist/setup-signin-assets.js.map +1 -0
  183. package/dist/setup-wizard.js +2 -0
  184. package/dist/setup-wizard.js.map +1 -1
  185. package/dist/structured-signin-transport.d.ts +8 -0
  186. package/dist/structured-signin-transport.js +79 -0
  187. package/dist/structured-signin-transport.js.map +1 -0
  188. package/dist/structured-signin.d.ts +131 -0
  189. package/dist/structured-signin.js +388 -0
  190. package/dist/structured-signin.js.map +1 -0
  191. package/dist/tool-permissions.js +12 -0
  192. package/dist/tool-permissions.js.map +1 -1
  193. package/dist/topic-commands.js +6 -2
  194. package/dist/topic-commands.js.map +1 -1
  195. package/dist/transcript-sources.d.ts +26 -2
  196. package/dist/transcript-sources.js +103 -40
  197. package/dist/transcript-sources.js.map +1 -1
  198. package/dist/turn-reply-guard.d.ts +1 -1
  199. package/dist/turn-reply-guard.js +1 -1
  200. package/dist/turn-reply-guard.js.map +1 -1
  201. package/dist/types.d.ts +28 -8
  202. package/dist/ui/backend-signin.js +63 -10
  203. package/dist/ui/chat-strings.js +2 -2
  204. package/dist/ui/fleet-office-editor.js +14 -9
  205. package/dist/ui/fleet-office-layout.js +70 -32
  206. package/dist/ui/fleet-office-model.js +6 -3
  207. package/dist/ui/fleet-office-overview.js +167 -0
  208. package/dist/ui/fleet-office-pack.d.ts +7 -2
  209. package/dist/ui/fleet-office-pack.js +119 -15
  210. package/dist/ui/fleet-office-skin.js +98 -23
  211. package/dist/ui/fleet-office-store.js +39 -5
  212. package/dist/ui/fleet-office-view.js +5 -4
  213. package/dist/ui/fleet-office-zones.js +172 -12
  214. package/dist/ui/fleet-office.js +547 -186
  215. package/dist/ui/fleet-usage.js +19 -15
  216. package/dist/ui/office-share-dialog.js +145 -0
  217. package/dist/ui/office-share-page.js +164 -0
  218. package/dist/ui/panel-chat.js +8 -1
  219. package/dist/ui/panel-log.js +2 -2
  220. package/dist/ui/panel-needs.js +4 -4
  221. package/dist/ui/panel-settings.js +37 -11
  222. package/dist/ui/settings-dialogs.js +8 -1
  223. package/dist/ui/settings-guests.js +17 -7
  224. package/dist/ui/settings-model.js +14 -6
  225. package/dist/ui/settings-registry.js +4 -0
  226. package/dist/ui/settings-strings.js +16 -2
  227. package/dist/ui/shared/app-i18n.js +4 -2
  228. package/dist/ui/shared/app-shell.js +14 -6
  229. package/dist/ui/shared/app-stream.js +2 -1
  230. package/dist/ui/shared/app.css +67 -2
  231. package/dist/ui/shared/office/cozy.png +0 -0
  232. package/dist/ui/shared/side-panel.js +95 -0
  233. package/dist/ui/structured-signin.js +115 -0
  234. package/dist/usage-report.d.ts +20 -0
  235. package/dist/usage-report.js +54 -17
  236. package/dist/usage-report.js.map +1 -1
  237. package/dist/usage-rollup.d.ts +9 -1
  238. package/dist/usage-rollup.js +20 -4
  239. package/dist/usage-rollup.js.map +1 -1
  240. package/dist/web-api.d.ts +18 -0
  241. package/dist/web-api.js +201 -0
  242. package/dist/web-api.js.map +1 -1
  243. package/dist/web-auth.js +1 -1
  244. package/dist/web-auth.js.map +1 -1
  245. package/dist/web-backend-login.d.ts +1 -7
  246. package/dist/web-backend-login.js +2 -24
  247. package/dist/web-backend-login.js.map +1 -1
  248. package/dist/web-office-acts.d.ts +173 -0
  249. package/dist/web-office-acts.js +285 -0
  250. package/dist/web-office-acts.js.map +1 -0
  251. package/dist/web-office-skins.d.ts +3 -2
  252. package/dist/web-office-skins.js +3 -2
  253. package/dist/web-office-skins.js.map +1 -1
  254. package/dist/web-office-store.js +40 -7
  255. package/dist/web-office-store.js.map +1 -1
  256. package/dist/web-settings-patch.d.ts +2 -1
  257. package/dist/web-settings-patch.js +4 -1
  258. package/dist/web-settings-patch.js.map +1 -1
  259. package/dist/working-recovery.d.ts +2 -2
  260. package/dist/working-recovery.js +43 -8
  261. package/dist/working-recovery.js.map +1 -1
  262. package/dist/working-state-journal.d.ts +2 -0
  263. package/dist/working-state-journal.js +16 -1
  264. package/dist/working-state-journal.js.map +1 -1
  265. package/package.json +3 -2
  266. package/templates/windows-shortcuts.ps1 +155 -140
@@ -5,7 +5,7 @@ import { homedir } from "node:os";
5
5
  import { getAgendHome } from "../paths.js";
6
6
  import { credentialHomeSpec, credentialProfileHome, credentialProfileStoreHome, prepareCredentialProfileHome, resolveCredentialProfile, } from "./credential-profile.js";
7
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";
8
+ import { resolveKiroIdentity, listKiroV1Sessions, listKiroV2Sessions, confirmKiroAgentSwitch, kiroAgentConfirmed, forgetKiroIdentity, kiroIdentityNeedsStore, kiroRecordedConversation, kiroCanonicalDirectory, KiroIdentityError, kiroEngineFloor, } from "./kiro-identity.js";
9
9
  import { readKiroLedger, kiroHasNoLaunchRecord } from "./kiro-engine-ledger.js";
10
10
  import { execFileSync } from "node:child_process";
11
11
  import { writeFileSync, chmodSync, statSync } from "node:fs";
@@ -13,6 +13,7 @@ import { UnsupportedCliError, resolveBinary, shellQuote, validateEffort, validat
13
13
  import { PIE_CLASS } from "../tui-glyphs.js";
14
14
  import { KIRO_EXPIRED_LOGIN_SCREEN } from "../login-flows.js";
15
15
  import { t } from "../locale.js";
16
+ import { decideKiroModelError, readKiroKnownModels, readKiroModelRecords, readKiroV1SavedModel, readKiroV2SavedModel, writeKiroModelRecords, KIRO_FALLBACK_MODEL } from "./kiro-retired-model.js";
16
17
  import { recordKiroLaunch } from "./kiro-engine-ledger.js";
17
18
  import { forgetKiroV3Identity, resolveKiroV3Resume } from "./kiro-v3-identity.js";
18
19
  // Kiro CLI feature gates. These are deliberately separate: the flags shipped
@@ -40,7 +41,7 @@ export const KIRO_INSTANCE_AGENT_MIN = "2.21.0";
40
41
  * the binary's own --help instead of the version table, and the operator is
41
42
  * told the version is unverified.
42
43
  */
43
- export const KIRO_TESTED_MAX = "2.33.0";
44
+ export const KIRO_TESTED_MAX = "2.34.0";
44
45
  /**
45
46
  * kiro-cli versions, per TUI front-end, whose mid-turn steering AgEnD drives (#1405). Only kiro's TUI steers — typed
46
47
  * input while a turn runs is injected into it ("steer", the default) or held for its end ("queue", Ctrl+S toggles); the
@@ -48,10 +49,13 @@ export const KIRO_TESTED_MAX = "2.33.0";
48
49
  * queued message mid-tool — has been captured (tests/fixtures/kiro-steer-1405, 2026-10-08: 2.27.1 `--tui
49
50
  * --agent-engine=v2` and `--v3`, 2.28.0 `--tui --agent-engine=v2`; identical rows on all three). Any other version,
50
51
  * 2.28.0's v3, the legacy UI and an undetected version all deliver a steer as an ordinary message after the turn.
52
+ * 2.34.0 TUI was checked on the user-authorized #2598 D5 run (tests/fixtures/kiro-2.34.0-tui-d5):
53
+ * native steer during a read-only tool, a fleet message queued mid-tool, and a native queue entry in the busy turn.
51
54
  */
52
55
  export const KIRO_STEER_VERIFIED = {
53
56
  "2.27.1": ["tui", "v3"],
54
57
  "2.28.0": ["tui"],
58
+ "2.34.0": ["tui"],
55
59
  };
56
60
  /**
57
61
  * The TUI composer's interrupt mode, read off its own row: the LAST row starting at column 0 with `›` (transcript user
@@ -133,11 +137,12 @@ const warnedVersionGateCacheKeys = new Set();
133
137
  * This broad test is only a safety hold: it must never select an option or
134
138
  * claim a service outage. A ready prompt below the picker makes it history.
135
139
  */
140
+ const KIRO_MODEL_PICKER_HEADER = /^\s*Select model(?: \(type to search\):|:\s+type to search)\s*$/m;
136
141
  function kiroModelPickerAtTail(pane) {
137
142
  const lines = pane.replace(/\r/g, "").split("\n");
138
143
  let pickerIndex = -1;
139
144
  for (let index = lines.length - 1; index >= 0; index--) {
140
- if (/^\s*Select model \(type to search\):\s*$/.test(lines[index])) {
145
+ if (KIRO_MODEL_PICKER_HEADER.test(lines[index])) {
141
146
  pickerIndex = index;
142
147
  break;
143
148
  }
@@ -145,7 +150,8 @@ function kiroModelPickerAtTail(pane) {
145
150
  if (pickerIndex < 0)
146
151
  return false;
147
152
  const tail = lines.slice(pickerIndex + 1);
148
- if (tail.some(line => /^\s*\d+%[^\n]*[!❯>]\s+\S/.test(line)))
153
+ const composer = readKiroSteerComposer(tail.join("\n"));
154
+ if (tail.some(line => /^\s*\d+%[^\n]*[!❯>]\s+\S/.test(line)) || (composer !== null && composer !== "text"))
149
155
  return false;
150
156
  return tail.some(line => /^\s*[>❯›]\s*\*?\s*\S/.test(line));
151
157
  }
@@ -330,6 +336,205 @@ function kiroWorkspaceTrustDialogs(trustAll) {
330
336
  },
331
337
  ];
332
338
  }
339
+ const KIRO_MCP_INPUT_HEADER = /^\s*Input requested · \S.*$/;
340
+ const KIRO_MCP_INPUT_FOOTER = /^\s*esc to cancel · ↑↓ to navigate · enter to select(?: · ctrl\+s to submit)?$/;
341
+ /**
342
+ * #2587: kiro-cli 2.34.0's V3 engine answers an MCP server's `elicitation/create` (a tool asking the user for input)
343
+ * with a card in place of the composer: a divider, `Input requested · <server>`, a divider, the server's message and
344
+ * one row per form field then Submit / Decline (form mode) — or Host, URL and Open in browser / Copy URL / I'll open it
345
+ * myself / Decline / Cancel (url mode) — with exactly one `❯` cursor row, a divider, and the key hints as the last row
346
+ * (tests/fixtures/kiro-2.34.0-audit). Bottom-anchored: the card is live only while it is what the pane ends with. The
347
+ * answer can carry credentials or a sign-in, so it is the person's: never a key, and no delivery's Enter may land in it.
348
+ */
349
+ export function kiroMcpInputRequestActive(pane) {
350
+ const rows = pane.replace(/\r/g, "").split("\n").map(row => row.trimEnd());
351
+ let last = rows.length - 1;
352
+ while (last >= 0 && !rows[last].trim())
353
+ last--;
354
+ if (last < 0 || !KIRO_MCP_INPUT_FOOTER.test(rows[last]))
355
+ return false;
356
+ const divider = (row) => row !== undefined && /^─{3,}$/.test(row.trim());
357
+ if (!divider(rows[last - 1]))
358
+ return false;
359
+ let header = -1;
360
+ for (let index = last - 2; index >= 1; index--) {
361
+ if (KIRO_MCP_INPUT_HEADER.test(rows[index])) {
362
+ header = index;
363
+ break;
364
+ }
365
+ }
366
+ if (header < 0 || !divider(rows[header - 1]) || !divider(rows[header + 1]))
367
+ return false;
368
+ const body = rows.slice(header + 2, last - 1);
369
+ // A V3 composer (`›` at column 0) inside means the card is history. A lone `>` is a form field's own input caret.
370
+ if (body.some(row => divider(row) || /^›(?:[ \t]|$)/.test(row)))
371
+ return false;
372
+ return body.filter(row => /^\s*❯\s+\S/.test(row)).length === 1;
373
+ }
374
+ const KIRO_QUESTION_HEADER = /^\s*(?:\S.* > )?Question$/;
375
+ const KIRO_QUESTION_FOOTER = /^\s*esc to (?:cancel|go back) · ↑↓ to navigate(?: · space to toggle)? · ↵ to submit$/;
376
+ /**
377
+ * #2592: kiro's V3 engine shows the agent's question to the user (`question_request`) as a card in place of the
378
+ * composer: a divider, `Question` (or `<name> > Question`), the question, numbered options (a description row under
379
+ * one, `(recommended)` after one) and `Type a different answer…`, a divider, and the key hints as the last row — or, in
380
+ * an option's sub-options, `[✓] <title>` rows and `esc to go back · … · space to toggle · ↵ to submit`
381
+ * (tests/fixtures/kiro-question-2592; unchanged from 2.33.1 to 2.34.0). Any typed character turns the card into a free
382
+ * answer and Enter submits it, so a delivered message would become the answer to a question nobody read. The person
383
+ * answers it in the pane (the user's decision on #2592); it is never keyed. Bottom-anchored like
384
+ * kiroMcpInputRequestActive: the header is the row right under the nearest divider above the key hints' divider.
385
+ */
386
+ export function kiroQuestionCardActive(pane) {
387
+ const rows = pane.replace(/\r/g, "").split("\n").map(row => row.trimEnd());
388
+ let last = rows.length - 1;
389
+ while (last >= 0 && !rows[last].trim())
390
+ last--;
391
+ if (last < 0 || !KIRO_QUESTION_FOOTER.test(rows[last]))
392
+ return false;
393
+ const divider = (row) => row !== undefined && /^─{3,}$/.test(row.trim());
394
+ if (!divider(rows[last - 1]))
395
+ return false;
396
+ let top = last - 2;
397
+ while (top >= 0 && !divider(rows[top]))
398
+ top--;
399
+ if (top < 0 || !KIRO_QUESTION_HEADER.test(rows[top + 1] ?? ""))
400
+ return false;
401
+ const body = rows.slice(top + 2, last - 1);
402
+ // The question, then the options: a V3 composer (`›` at column 0) among them means the card is history.
403
+ if (body.length < 2 || body.some(row => /^›(?:[ \t]|$)/.test(row)))
404
+ return false;
405
+ return body.filter(row => /^\s*❯\s+\S/.test(row)).length === 1;
406
+ }
407
+ /** #2675: the live card's rows — the header row, the body between it and the key hints' divider, and the key hints. */
408
+ function kiroCardRows(pane, headerAt) {
409
+ const rows = pane.replace(/\r/g, "").split("\n").map(row => row.trimEnd());
410
+ let last = rows.length - 1;
411
+ while (last >= 0 && !rows[last].trim())
412
+ last--;
413
+ const header = headerAt(rows, last);
414
+ return header < 0 ? null : { header: rows[header].trim(), body: rows.slice(header + 1, last - 1), footer: rows[last].trim(), width: rows[last - 1].trim().length };
415
+ }
416
+ const KIRO_QUESTION_OPTION_ROW = /^ (❯| ) (\d+)\. (.*?)( \(recommended\))?$/;
417
+ const KIRO_QUESTION_SUBOPTION_ROW = /^ (❯| ) \[(✓| )\] (.*)$/;
418
+ const KIRO_QUESTION_FREE_ROW = /^ (❯| ) (Type a different answer…|or type feedback to request changes)$/;
419
+ /**
420
+ * #2675: the live Question card (see kiroQuestionCardActive), read for a person to answer from the chat: the question,
421
+ * the numbered options (`(recommended)` and the description row under one), the typed-answer row, and where the `❯`
422
+ * is (tui.js `A0`; tests/fixtures/kiro-question-2592). On a sub-options page the rows are the `[✓]` boxes; while an
423
+ * answer is being typed the last row is `❯ <text>`. A row the shapes above do not account for is a wrapped title (the
424
+ * card's own indent) or description (deeper), joined to the row it continues; one before any option belongs to the
425
+ * question. Null when the card is not live, or when it is in a shape this reader does not know.
426
+ */
427
+ export function readKiroQuestionCard(pane) {
428
+ if (!kiroQuestionCardActive(pane))
429
+ return null;
430
+ const card = kiroCardRows(pane, (rows, last) => {
431
+ let top = last - 2;
432
+ while (top >= 0 && !/^─{3,}$/.test(rows[top].trim()))
433
+ top--;
434
+ return top + 1;
435
+ });
436
+ if (!card)
437
+ return null;
438
+ const page = /space to toggle/.test(card.footer) ? "suboptions" : "options";
439
+ const question = [];
440
+ const options = [];
441
+ let cursor = null, freeText = false, typed = null, freeRow = false;
442
+ for (const row of card.body) {
443
+ const option = page === "options" ? KIRO_QUESTION_OPTION_ROW.exec(row) : null;
444
+ const sub = page === "suboptions" ? KIRO_QUESTION_SUBOPTION_ROW.exec(row) : null;
445
+ const free = page === "options" ? KIRO_QUESTION_FREE_ROW.exec(row) : null;
446
+ if (freeRow)
447
+ return null; // nothing follows the typed-answer row
448
+ if (option && Number(option[2]) === options.length + 1) {
449
+ if (option[1] === "❯")
450
+ cursor = options.length;
451
+ options.push({ n: options.length + 1, title: option[3], recommended: option[4] !== undefined, description: null });
452
+ }
453
+ else if (sub) {
454
+ if (sub[1] === "❯")
455
+ cursor = options.length;
456
+ options.push({ n: options.length + 1, title: sub[3], recommended: false, description: null, checked: sub[2] === "✓" });
457
+ }
458
+ else if (free) {
459
+ freeText = true;
460
+ freeRow = true;
461
+ if (free[1] === "❯")
462
+ cursor = options.length;
463
+ }
464
+ else if (page === "options" && options.length > 0 && /^ ❯ /.test(row)) {
465
+ freeText = true;
466
+ freeRow = true;
467
+ typed = row.slice(3);
468
+ }
469
+ else if (!row.trim()) {
470
+ continue;
471
+ }
472
+ else if (options.length === 0) {
473
+ question.push(row.replace(/^ /, ""));
474
+ }
475
+ else {
476
+ const lastOption = options[options.length - 1];
477
+ if (/^ {5,}\S/.test(row))
478
+ lastOption.description = lastOption.description === null ? row.trim() : `${lastOption.description} ${row.trim()}`;
479
+ else if (lastOption.description === null)
480
+ lastOption.title = `${lastOption.title} ${row.trim()}`;
481
+ else
482
+ return null;
483
+ }
484
+ }
485
+ if (options.length === 0 || question.length === 0)
486
+ return null;
487
+ const typing = typed !== null;
488
+ if (typing ? cursor !== null : cursor === null)
489
+ return null;
490
+ const titles = options.map(option => `${option.n}. ${option.title}`).join("\n");
491
+ return {
492
+ kind: "question", identity: `${card.header}\n${question.join("\n")}\n${page === "suboptions" ? "sub" : "options"}\n${titles}`,
493
+ title: card.header, question: question.join("\n").trim(), page: typing ? "freetext" : page, options, freeText, cursor, typed, rowWidth: card.width,
494
+ };
495
+ }
496
+ const KIRO_MCP_INPUT_ACTIONS = new Set(["Submit", "Decline", "Open in browser", "Copy URL", "I'll open it myself", "Cancel"]);
497
+ /**
498
+ * #2675: the live MCP `Input requested` card (see kiroMcpInputRequestActive), read for the chat: the server, the body
499
+ * as painted, with the `URL:` row masked (a sign-in link stays in the pane), its action rows, and the `❯` row. Its
500
+ * identity is the server, the mode, the message's first row and the actions: moving or filling in a field keeps it.
501
+ */
502
+ export function readKiroMcpInputCard(pane) {
503
+ if (!kiroMcpInputRequestActive(pane))
504
+ return null;
505
+ const card = kiroCardRows(pane, (rows, last) => {
506
+ for (let index = last - 2; index >= 1; index--)
507
+ if (KIRO_MCP_INPUT_HEADER.test(rows[index]))
508
+ return index;
509
+ return -1;
510
+ });
511
+ if (!card)
512
+ return null;
513
+ // The URL row is masked, and so is every row after it up to the next action row: a long URL wraps (#2677 review).
514
+ let inUrl = false;
515
+ const body = card.body.slice(1).flatMap(row => {
516
+ if (/^\s*URL: /.test(row)) {
517
+ inUrl = true;
518
+ return [row.replace(/URL: .*$/, "URL: (shown in the instance pane)")];
519
+ }
520
+ if (inUrl && KIRO_MCP_INPUT_ACTIONS.has(row.replace(/^ ❯ /, " ").trim()))
521
+ inUrl = false;
522
+ return inUrl ? [] : [row];
523
+ });
524
+ const strip = (row) => row.replace(/^ ❯ /, " ").trim();
525
+ const cursorRow = body.find(row => /^\s*❯\s+\S/.test(row));
526
+ if (!cursorRow)
527
+ return null;
528
+ return {
529
+ kind: "input", server: card.header.replace(/^Input requested · /, ""),
530
+ mode: /ctrl\+s to submit/.test(card.footer) ? "form" : "url",
531
+ rows: body.map(row => row.replace(/^ ❯ /, " ").replace(/^ {3}/, "")).filter(row => row.trim()),
532
+ actions: body.map(strip).filter(row => KIRO_MCP_INPUT_ACTIONS.has(row)),
533
+ cursorRow: strip(cursorRow),
534
+ // The focused field paints extra rows (its value, its caret) and a typed value changes its row: neither is another request.
535
+ identity: `${card.header}\n${/ctrl\+s to submit/.test(card.footer) ? "form" : "url"}\n${strip(body[0] ?? "")}\n${body.map(strip).filter(row => KIRO_MCP_INPUT_ACTIONS.has(row)).join("|")}`,
536
+ };
537
+ }
333
538
  /**
334
539
  * Kiro's service-unavailable path opens the same picker as `/model`, but only
335
540
  * the former needs incident escalation. The runtime scanner and delivery gate
@@ -342,7 +547,7 @@ export function kiroUnavailableModelPickerActive(pane) {
342
547
  const lines = pane.replace(/\r/g, "").split("\n");
343
548
  let pickerIndex = -1;
344
549
  for (let i = lines.length - 1; i >= 0; i--) {
345
- if (/^\s*Select model \(type to search\):\s*$/.test(lines[i])) {
550
+ if (KIRO_MODEL_PICKER_HEADER.test(lines[i])) {
346
551
  pickerIndex = i;
347
552
  break;
348
553
  }
@@ -634,6 +839,17 @@ function compatibilityFromHelp(version, help) {
634
839
  source: "help",
635
840
  };
636
841
  }
842
+ /**
843
+ * #2598 (D1): kiro-cli-chat skips its self-update when this is set (2.30.0 and 2.34.0 both read it). AgEnD pins the
844
+ * kiro version it has audited; an in-place update can swap the engine a running fleet depends on (3.0 drops legacy).
845
+ * Every chat AgEnD starts carries it: the instance launch (a shell prefix) and the probes (their child env). The
846
+ * host-wide `app.disableAutoupdates` setting is the operator's half; this holds even where that was never set.
847
+ */
848
+ export const KIRO_NO_AUTO_UPDATE = "KIRO_NO_AUTO_UPDATE";
849
+ /** A kiro child's environment: the given one (default: ours) with self-update off. */
850
+ export function kiroChildEnv(base = process.env) {
851
+ return { ...base, [KIRO_NO_AUTO_UPDATE]: "1" };
852
+ }
637
853
  /**
638
854
  * Probe once per binary generation. A version AgEnD has run (<= TESTED_MAX)
639
855
  * is answered from the table without a second CLI call; anything newer, or a
@@ -645,6 +861,7 @@ export function probeKiroCliCompatibility(binaryPath, run = (binary, args) => me
645
861
  encoding: "utf-8",
646
862
  timeout: 5000,
647
863
  stdio: ["ignore", "pipe", "ignore"],
864
+ env: kiroChildEnv(),
648
865
  }))) {
649
866
  return measureSyncWork("kiro.compatibilitySync", () => probeKiroCliCompatibilitySync(binaryPath, run));
650
867
  }
@@ -673,6 +890,8 @@ function nextPatch(version) {
673
890
  const [major, minor, patch] = parseSemver(version);
674
891
  return `${major}.${minor}.${patch + 1}`;
675
892
  }
893
+ /** #2598 B3: engines in the order a conversation can move between them (never back). */
894
+ const KIRO_ENGINE_RANK = { legacy: 1, tui: 2, v3: 3 };
676
895
  /** Binary generations already told they cannot isolate (once each). */
677
896
  const warnedNoInstanceAgentKeys = new Set();
678
897
  /** Instances already told about an untagged steering file (once each). */
@@ -913,6 +1132,8 @@ export class KiroBackend {
913
1132
  activeTrustAll = true;
914
1133
  /** Version-gate notice for the launch just built (consumeLaunchWarning). */
915
1134
  launchWarning = null;
1135
+ /** #2648: what this launch's model came from, for a model error to be judged by (decideKiroModelError). */
1136
+ launchModels = null;
916
1137
  /**
917
1138
  * What this launch attempt resumes and as which agent (#906), resolved once per attempt in writeConfig — it claims
918
1139
  * conversations and records fresh starts — and read by the buildCommand that follows it.
@@ -923,6 +1144,8 @@ export class KiroBackend {
923
1144
  /** #1218: the last launch built in full (cleared when a build starts, set only when it returns). */
924
1145
  builtLaunch = null;
925
1146
  preparedV2Store = null;
1147
+ /** #2648: the saved model of the v2 conversation this instance has recorded, read in prepareLaunch (off the loop). */
1148
+ preparedV2Model = null;
926
1149
  storePreparation = 0;
927
1150
  storeKey(config) {
928
1151
  return JSON.stringify([config.instanceName, config.workingDirectory, kiroCanonicalDirectory(config.workingDirectory), config.kiroUi,
@@ -939,6 +1162,7 @@ export class KiroBackend {
939
1162
  async prepareLaunch(config) {
940
1163
  const preparation = ++this.storePreparation;
941
1164
  this.preparedV2Store = null;
1165
+ this.preparedV2Model = null;
942
1166
  if (!config || config.kiroUi !== "tui")
943
1167
  return;
944
1168
  if (!this.fixedCompatibility)
@@ -954,6 +1178,14 @@ export class KiroBackend {
954
1178
  if (preparation === this.storePreparation && key === this.storeKey(config))
955
1179
  this.preparedV2Store = { key, store };
956
1180
  }
1181
+ // #2648: only for an instance with no model of its own, and only the conversation it has recorded (the resolver
1182
+ // in writeConfig may pick another — then nothing is foreseen, and a model error is handled after the launch).
1183
+ const recorded = !config.model && !config.skipResume ? kiroRecordedConversation(opts) : null;
1184
+ if (recorded) {
1185
+ const model = await readKiroV2SavedModel(kiroV2SessionsDir(), recorded);
1186
+ if (preparation === this.storePreparation && key === this.storeKey(config))
1187
+ this.preparedV2Model = { key, id: recorded, model };
1188
+ }
957
1189
  }
958
1190
  constructor(instanceDir, compatibility) {
959
1191
  this.instanceDir = instanceDir;
@@ -1055,6 +1287,12 @@ export class KiroBackend {
1055
1287
  this.replyGuardOptedIn = false;
1056
1288
  this.builtLaunch = null;
1057
1289
  const ui = config.kiroUi ?? "legacy";
1290
+ // #2598 B3: never back onto an older engine than this instance has run on — that resumes the frozen copy and loses
1291
+ // everything done since. Before any probe: no kiro-cli flag or version changes the answer.
1292
+ const floor = kiroEngineFloor(getAgendHome(), config.instanceName);
1293
+ if (floor && KIRO_ENGINE_RANK[ui] < KIRO_ENGINE_RANK[floor === "v2" ? "tui" : "v3"]) {
1294
+ throw new UnsupportedCliError(t("kiro.engine_floor", config.instanceName, ui, floor === "v2" ? "tui" : "v3"));
1295
+ }
1058
1296
  // Every launch re-reads the binary generation: a crash-respawn reuses this
1059
1297
  // backend, and kiro-cli may have replaced itself in place since the last
1060
1298
  // launch (an auto-update to a release without this instance's engine).
@@ -1075,7 +1313,8 @@ export class KiroBackend {
1075
1313
  const plan = planKiroLaunch(ui, this.compatibility);
1076
1314
  if (plan.kind === "refuse")
1077
1315
  throw new UnsupportedCliError(plan.reason);
1078
- let cmd = `${this.binaryPath} chat`;
1316
+ // #2598: a shell assignment, so it reaches kiro-cli-chat through `kiro-cli chat` and any credential-home prefix.
1317
+ let cmd = `${KIRO_NO_AUTO_UPDATE}=1 ${this.binaryPath} chat`;
1079
1318
  for (const flag of plan.flags)
1080
1319
  cmd += ` ${flag}`;
1081
1320
  this.noteVersionGate();
@@ -1119,17 +1358,32 @@ export class KiroBackend {
1119
1358
  if (id)
1120
1359
  cmd += ` --resume-id ${shellQuote(id)}`;
1121
1360
  }
1122
- else if (!config.skipResume) {
1123
- // Legacy mode, classic/TUI: the boolean form — kiro resumes the directory's newest conversation. Not isolated
1361
+ else if (!config.skipResume && ui === "legacy") {
1362
+ // Legacy mode, classic: the boolean form — kiro resumes the directory's newest conversation. Not isolated
1124
1363
  // between instances sharing the directory (#1410); an instance launches like this only without the
1125
1364
  // per-instance plan (an old kiro-cli, or a conversation store AgEnD could not read on adoption).
1126
1365
  cmd += " --resume";
1127
1366
  }
1367
+ else if (!config.skipResume && ui === "tui") {
1368
+ // #2598 B3: never the boolean form under the TUI — it takes the directory's newest conversation from EITHER
1369
+ // engine, and on a classic one makes a TUI fork of it (the wrong conversation, possibly another instance's). A
1370
+ // TUI launch without its per-instance plan starts fresh instead, and says so.
1371
+ this.addLaunchWarning(t("kiro.tui_no_plan_fresh"));
1372
+ }
1373
+ const resumed = lp.mode === "resume" && lp.id && (lp.engine === "v1" || lp.engine === "v2") ? { engine: lp.engine, id: lp.id } : null;
1374
+ const fallbackModel = !config.model && resumed && config.instanceDir
1375
+ ? this.retiredModelFallback(config, resumed, credentialProfile) : null;
1376
+ this.launchModels = config.instanceDir
1377
+ ? { instanceDir: config.instanceDir, ownModel: config.model ?? null, launchModel: config.model ?? fallbackModel, conversation: resumed?.id ?? null }
1378
+ : null;
1128
1379
  if (config.model) {
1129
1380
  const model = validateModel(config.model);
1130
1381
  warnIfModelMismatch("kiro-cli", model);
1131
1382
  cmd += ` --model ${shellQuote(model)}`;
1132
1383
  }
1384
+ else if (fallbackModel) {
1385
+ cmd += ` --model ${shellQuote(validateModel(fallbackModel))}`;
1386
+ }
1133
1387
  if (config.effort) {
1134
1388
  const effort = validateEffort(config.effort);
1135
1389
  if (this.compatibility.supportsEffortFlag) {
@@ -1290,6 +1544,59 @@ export class KiroBackend {
1290
1544
  this.addLaunchWarning(t("kiro.untagged_steering"));
1291
1545
  }
1292
1546
  }
1547
+ /**
1548
+ * #2648 steps 1 and 3: the model a resumed conversation is launched on instead of its saved one, or null to launch as
1549
+ * before. Only for an instance with no model of its own. The conversation's current saved model decides, every
1550
+ * launch: one kiro's cached list no longer has is switched to kiro's default; one it has — or, without a list, one
1551
+ * other than the model that failed (a later /model choice, or the default kiro saved after a switch) — is launched
1552
+ * as saved, even after an earlier switch (#2649 review). Only when the saved model cannot be read does a recorded
1553
+ * switch (step 2's restart) still apply. Announced once per conversation; the record also keeps that dedupe.
1554
+ */
1555
+ retiredModelFallback(config, resumed, credentialProfile) {
1556
+ const records = readKiroModelRecords(config.instanceDir);
1557
+ let entry = records.switched[resumed.id];
1558
+ const known = readKiroKnownModels(getAgendHome());
1559
+ const prepared = this.preparedV2Model;
1560
+ const saved = resumed.engine === "v1"
1561
+ ? readKiroV1SavedModel({ id: resumed.id, workingDirectory: config.workingDirectory, dbPath: kiroV1DbPath(credentialProfile) })
1562
+ : prepared && prepared.key === this.storeKey(config) && prepared.id === resumed.id ? prepared.model : null;
1563
+ if (saved) {
1564
+ // Step 2's restart recorded no model: the one saved now is the one that failed — unless it is the default the
1565
+ // switch put there (kiro saves the model a launch ran on).
1566
+ if (entry && entry.from === null && saved !== entry.to)
1567
+ entry.from = saved;
1568
+ const supported = known ? known.models.has(saved) : !!entry && (saved === entry.to || (entry.from !== null && saved !== entry.from));
1569
+ if (supported || (!known && !entry)) {
1570
+ if (entry && entry.from !== saved)
1571
+ this.saveModelRecords(config.instanceDir, records);
1572
+ return null;
1573
+ }
1574
+ if (!entry || entry.from !== saved)
1575
+ entry = records.switched[resumed.id] = { from: saved, to: known?.defaultModel ?? entry?.to ?? KIRO_FALLBACK_MODEL, announced: entry?.announced ?? false };
1576
+ }
1577
+ else if (!entry) {
1578
+ return null;
1579
+ }
1580
+ if (!entry.announced) {
1581
+ this.addLaunchWarning(t("kiro.retired_model_switched", config.instanceName, entry.from ?? t("kiro.retired_model_unknown"), entry.to));
1582
+ entry.announced = true;
1583
+ }
1584
+ this.saveModelRecords(config.instanceDir, records);
1585
+ return entry.to;
1586
+ }
1587
+ saveModelRecords(instanceDir, records) {
1588
+ try {
1589
+ writeKiroModelRecords(instanceDir, records);
1590
+ }
1591
+ catch { /* the next launch decides again; at worst it says so again */ }
1592
+ }
1593
+ /** #2648 steps 2 and 4: what a model error on this launch should do (records it); null before any launch. */
1594
+ onModelError() {
1595
+ const m = this.launchModels;
1596
+ if (!m)
1597
+ return null;
1598
+ return decideKiroModelError(m.instanceDir, m, readKiroKnownModels(getAgendHome())?.defaultModel ?? KIRO_FALLBACK_MODEL);
1599
+ }
1293
1600
  addLaunchWarning(message) {
1294
1601
  this.launchWarning = this.launchWarning ? `${this.launchWarning}\n${message}` : message;
1295
1602
  }
@@ -1656,7 +1963,7 @@ export class KiroBackend {
1656
1963
  // picker. Its choices range across credit multipliers and capability
1657
1964
  // tiers, so this must be a human decision, never an automatic Enter or
1658
1965
  // Escape. The ordinary /model menu alone is intentionally not matched.
1659
- pattern: /^\s*Select model \(type to search\):\s*$/m,
1966
+ pattern: KIRO_MODEL_PICKER_HEADER,
1660
1967
  keys: [],
1661
1968
  description: "Kiro model unavailable — choose a replacement model in the instance pane",
1662
1969
  blocksDelivery: true,
@@ -1668,7 +1975,7 @@ export class KiroBackend {
1668
1975
  // Any active /model picker owns stdin, even if a future Kiro release
1669
1976
  // changes the exact warning, credit wording or row layout. Keep this
1670
1977
  // after the verified-outage case: it never labels /model as an outage.
1671
- pattern: /^\s*Select model \(type to search\):\s*$/m,
1978
+ pattern: KIRO_MODEL_PICKER_HEADER,
1672
1979
  keys: [],
1673
1980
  description: "Kiro model picker — manual selection required",
1674
1981
  blocksDelivery: true,
@@ -1676,6 +1983,30 @@ export class KiroBackend {
1676
1983
  inputBlocked: true,
1677
1984
  isActive: kiroModelPickerAtTail,
1678
1985
  },
1986
+ {
1987
+ // #2587: an MCP server's input request (kiro-cli 2.34.0 V3) owns stdin until the person answers or cancels it.
1988
+ pattern: KIRO_MCP_INPUT_HEADER,
1989
+ keys: [],
1990
+ description: "Kiro MCP server is asking for input — answer it in the instance pane",
1991
+ blocksDelivery: true,
1992
+ holdOnly: true,
1993
+ inputBlocked: true,
1994
+ isActive: kiroMcpInputRequestActive,
1995
+ requestIdentity: pane => readKiroMcpInputCard(pane)?.identity ?? null,
1996
+ remoteCard: readKiroMcpInputCard,
1997
+ },
1998
+ {
1999
+ // #2592: the agent's question to the user owns stdin until the person answers or cancels it.
2000
+ pattern: KIRO_QUESTION_HEADER,
2001
+ keys: [],
2002
+ description: "Kiro agent is asking a question — answer it in the instance pane",
2003
+ blocksDelivery: true,
2004
+ holdOnly: true,
2005
+ inputBlocked: true,
2006
+ isActive: kiroQuestionCardActive,
2007
+ requestIdentity: pane => readKiroQuestionCard(pane)?.identity ?? null,
2008
+ remoteCard: readKiroQuestionCard,
2009
+ },
1679
2010
  ...kiroTrustPromptDialogs(this.activeTrustAll),
1680
2011
  ...kiroWorkspaceTrustDialogs(this.activeTrustAll),
1681
2012
  ];
@@ -1684,6 +2015,16 @@ export class KiroBackend {
1684
2015
  return null;
1685
2016
  }
1686
2017
  logLaunch() { return this.builtLaunch ? { ...this.builtLaunch } : null; }
2018
+ /**
2019
+ * #2598 B1: the store is the launch's engine's (v1 = the classic sqlite store, v2 = the TUI's session files), and the
2020
+ * conversation is the one the launch resumed by id. A fresh or legacy-mode launch owns no id yet.
2021
+ */
2022
+ transcriptLaunch() {
2023
+ const built = this.builtLaunch, plan = this.activePlan;
2024
+ if (!built)
2025
+ return null;
2026
+ return { engine: built.engine, sessionId: plan?.mode === "resume" && plan.id ? plan.id : null };
2027
+ }
1687
2028
  getSessionId() {
1688
2029
  // Kiro manages sessions internally via SQLite keyed by working directory.
1689
2030
  // No external session ID needed — --resume handles it automatically.
@@ -1709,7 +2050,7 @@ export class KiroBackend {
1709
2050
  */
1710
2051
  readModelsPayload() {
1711
2052
  try {
1712
- const out = execFileSync(this.binaryPath, ["chat", "--list-models", "--format", "json"], { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"] });
2053
+ const out = execFileSync(this.binaryPath, ["chat", "--list-models", "--format", "json"], { encoding: "utf-8", timeout: 5000, stdio: ["ignore", "pipe", "ignore"], env: kiroChildEnv() });
1713
2054
  const parsed = JSON.parse(out);
1714
2055
  const arr = Array.isArray(parsed) ? parsed : (parsed?.models ?? []);
1715
2056
  const models = arr.map((m) => {