@gajae-code/coding-agent 0.15.6 → 0.16.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 (270) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/dist/types/async/job-manager.d.ts +32 -1
  3. package/dist/types/browser-backend.d.ts +12 -0
  4. package/dist/types/cli/args.d.ts +2 -0
  5. package/dist/types/cli/root-flags.d.ts +14 -0
  6. package/dist/types/cli/update-cli.d.ts +14 -0
  7. package/dist/types/cli.d.ts +7 -0
  8. package/dist/types/commands/launch.d.ts +10 -0
  9. package/dist/types/commands/sdk.d.ts +2 -0
  10. package/dist/types/config/autorouting-tier-map.d.ts +24 -0
  11. package/dist/types/config/billing-path.d.ts +25 -0
  12. package/dist/types/config/config-schema-version.d.ts +13 -1
  13. package/dist/types/config/file-lock.d.ts +5 -0
  14. package/dist/types/config/model-registry.d.ts +22 -0
  15. package/dist/types/config/settings-schema.d.ts +27 -8
  16. package/dist/types/config/settings.d.ts +2 -0
  17. package/dist/types/coordinator-mcp/projection-scan.d.ts +8 -0
  18. package/dist/types/crash/record-loader.d.ts +15 -0
  19. package/dist/types/extensibility/extensions/types.d.ts +8 -3
  20. package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
  21. package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
  22. package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
  23. package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
  24. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +66 -1
  25. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
  26. package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
  27. package/dist/types/main.d.ts +1 -0
  28. package/dist/types/master-mode/context.d.ts +21 -0
  29. package/dist/types/master-mode/first-request.d.ts +34 -0
  30. package/dist/types/master-mode/snapshot.d.ts +32 -0
  31. package/dist/types/modes/acp/acp-agent.d.ts +21 -21
  32. package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
  33. package/dist/types/modes/components/model-selector.d.ts +4 -0
  34. package/dist/types/modes/components/oauth-selector.d.ts +1 -0
  35. package/dist/types/modes/components/session-selector.d.ts +1 -1
  36. package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
  37. package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
  38. package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
  39. package/dist/types/modes/components/status-line/segments.d.ts +10 -0
  40. package/dist/types/modes/interactive-mode.d.ts +16 -2
  41. package/dist/types/modes/print-mode.d.ts +17 -0
  42. package/dist/types/modes/theme/theme.d.ts +2 -1
  43. package/dist/types/modes/types.d.ts +2 -0
  44. package/dist/types/modes/ui-language.d.ts +7 -1
  45. package/dist/types/sdk/broker/broker.d.ts +79 -2
  46. package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
  47. package/dist/types/sdk/broker/ensure.d.ts +3 -0
  48. package/dist/types/sdk/broker/index.d.ts +3 -0
  49. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
  50. package/dist/types/sdk/broker/lifecycle.d.ts +40 -0
  51. package/dist/types/sdk/broker/master-capability.d.ts +10 -0
  52. package/dist/types/sdk/broker/session-index.d.ts +40 -11
  53. package/dist/types/sdk/broker/session-scope.d.ts +72 -0
  54. package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
  55. package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
  56. package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
  57. package/dist/types/sdk/broker/state-version.d.ts +21 -1
  58. package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
  59. package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
  60. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
  61. package/dist/types/sdk/bus/index.d.ts +9 -1
  62. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
  63. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
  64. package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
  65. package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
  66. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
  67. package/dist/types/sdk/cli/index.d.ts +1 -0
  68. package/dist/types/sdk/cli/master-cli.d.ts +63 -0
  69. package/dist/types/sdk/cli/rows.d.ts +12 -5
  70. package/dist/types/sdk/cli/session-cli.d.ts +20 -3
  71. package/dist/types/sdk/client/client.d.ts +3 -2
  72. package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
  73. package/dist/types/sdk/host/control/index.d.ts +1 -1
  74. package/dist/types/sdk/host/control/operations.d.ts +2 -0
  75. package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
  76. package/dist/types/sdk/host/events.d.ts +1 -0
  77. package/dist/types/sdk/host/host.d.ts +10 -0
  78. package/dist/types/sdk/host/query/cursor.d.ts +4 -2
  79. package/dist/types/sdk/host/query/handlers.d.ts +1 -0
  80. package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
  81. package/dist/types/sdk/host/session-runtime.d.ts +70 -17
  82. package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
  83. package/dist/types/sdk/lifecycle/service.d.ts +15 -3
  84. package/dist/types/sdk/lifecycle-session.d.ts +3 -1
  85. package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
  86. package/dist/types/sdk/receipt-state.d.ts +2 -0
  87. package/dist/types/sdk/router/session-router.d.ts +6 -0
  88. package/dist/types/sdk/session.d.ts +16 -0
  89. package/dist/types/sdk/startup-capability.d.ts +2 -1
  90. package/dist/types/sdk/turn-result.d.ts +6 -0
  91. package/dist/types/session/agent-session.d.ts +52 -7
  92. package/dist/types/session/credential-store-errors.d.ts +1 -0
  93. package/dist/types/session/fold-coordinator.d.ts +29 -2
  94. package/dist/types/session/session-manager.d.ts +79 -2
  95. package/dist/types/session/yield-queue.d.ts +5 -4
  96. package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
  97. package/dist/types/tools/ask.d.ts +1 -0
  98. package/dist/types/tools/bash.d.ts +16 -0
  99. package/dist/types/tools/browser/launch.d.ts +10 -0
  100. package/dist/types/tools/browser/registry.d.ts +8 -0
  101. package/dist/types/tools/gh.d.ts +8 -0
  102. package/dist/types/tools/index.d.ts +10 -10
  103. package/dist/types/tools/job.d.ts +3 -1
  104. package/dist/types/utils/external-editor.d.ts +7 -0
  105. package/examples/extensions/README.md +1 -1
  106. package/examples/extensions/pirate.ts +7 -4
  107. package/package.json +7 -7
  108. package/scripts/generate-sdk-operation-inventory.ts +13 -1
  109. package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
  110. package/src/async/job-manager.ts +107 -9
  111. package/src/browser-backend.ts +29 -0
  112. package/src/cli/accounts-cli.ts +9 -5
  113. package/src/cli/args.ts +17 -0
  114. package/src/cli/fast-help.ts +1 -0
  115. package/src/cli/plugin-cli.ts +29 -0
  116. package/src/cli/root-flags.ts +5 -0
  117. package/src/cli/update-cli.ts +140 -23
  118. package/src/commands/launch.ts +86 -18
  119. package/src/commands/sdk.ts +135 -28
  120. package/src/config/autorouting-tier-map.ts +12 -0
  121. package/src/config/billing-path.ts +42 -0
  122. package/src/config/config-schema-version.ts +18 -1
  123. package/src/config/file-lock.ts +158 -83
  124. package/src/config/model-preset-registry.ts +178 -23
  125. package/src/config/model-profiles.ts +5 -5
  126. package/src/config/model-registry.ts +291 -42
  127. package/src/config/model-resolver.ts +5 -0
  128. package/src/config/settings-schema.ts +27 -8
  129. package/src/config/settings.ts +51 -15
  130. package/src/coordinator-mcp/projection-scan.ts +27 -6
  131. package/src/coordinator-mcp/question-state.ts +9 -11
  132. package/src/coordinator-mcp/server.ts +115 -21
  133. package/src/crash/index-store.ts +2 -0
  134. package/src/crash/record-loader.ts +10 -0
  135. package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
  136. package/src/extensibility/extensions/runner.ts +27 -0
  137. package/src/extensibility/extensions/types.ts +8 -3
  138. package/src/extensibility/gjc-plugins/installer.ts +51 -11
  139. package/src/extensibility/runtime-skill-discovery.ts +110 -3
  140. package/src/extensibility/skills.ts +22 -12
  141. package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
  142. package/src/gjc-runtime/launch-worktree.ts +285 -4
  143. package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
  144. package/src/gjc-runtime/session-state-lock.ts +213 -14
  145. package/src/gjc-runtime/session-state-sidecar.ts +938 -39
  146. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
  147. package/src/gjc-runtime/tmux-owner-isolation.ts +18 -3
  148. package/src/gjc-runtime/tmux-sessions.ts +252 -13
  149. package/src/hooks/skill-state.ts +6 -0
  150. package/src/internal-urls/docs-index.generated.ts +15 -15
  151. package/src/internal-urls/issue-pr-protocol.ts +4 -2
  152. package/src/main.ts +61 -3
  153. package/src/master-mode/context.ts +50 -0
  154. package/src/master-mode/first-request.ts +65 -0
  155. package/src/master-mode/snapshot.ts +205 -0
  156. package/src/modes/acp/acp-agent.ts +610 -161
  157. package/src/modes/components/footer.ts +4 -2
  158. package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
  159. package/src/modes/components/model-selector.ts +76 -8
  160. package/src/modes/components/oauth-selector.ts +6 -1
  161. package/src/modes/components/session-selector.ts +23 -15
  162. package/src/modes/components/sessions-dashboard.ts +8 -5
  163. package/src/modes/components/settings-selector.ts +1 -0
  164. package/src/modes/components/status-line/model-name.ts +53 -0
  165. package/src/modes/components/status-line/priority-row.ts +107 -0
  166. package/src/modes/components/status-line/segments.ts +23 -9
  167. package/src/modes/components/tool-status-header.ts +151 -15
  168. package/src/modes/controllers/command-controller.ts +1 -1
  169. package/src/modes/controllers/event-controller.ts +30 -4
  170. package/src/modes/controllers/extension-ui-controller.ts +13 -13
  171. package/src/modes/controllers/input-controller.ts +30 -2
  172. package/src/modes/controllers/selector-controller.ts +200 -98
  173. package/src/modes/interactive-mode.ts +36 -12
  174. package/src/modes/print-mode.ts +64 -0
  175. package/src/modes/prompt-action-autocomplete.ts +30 -4
  176. package/src/modes/runtime-init.ts +12 -9
  177. package/src/modes/theme/theme.ts +5 -0
  178. package/src/modes/types.ts +2 -0
  179. package/src/modes/ui-language.ts +49 -2
  180. package/src/modes/utils/ui-helpers.ts +22 -3
  181. package/src/prompts/agent-fragments/browser-aside.md +22 -0
  182. package/src/prompts/system/master-mode.md +28 -0
  183. package/src/prompts/tools/ask.md +1 -1
  184. package/src/prompts/tools/bash.md +1 -0
  185. package/src/prompts/tools/job.md +1 -1
  186. package/src/sdk/acp/adapter.ts +5 -0
  187. package/src/sdk/broker/broker.ts +1646 -27
  188. package/src/sdk/broker/endpoint-authority.ts +86 -0
  189. package/src/sdk/broker/ensure.ts +81 -3
  190. package/src/sdk/broker/index.ts +3 -0
  191. package/src/sdk/broker/lifecycle-ledger.ts +6 -0
  192. package/src/sdk/broker/lifecycle.ts +440 -75
  193. package/src/sdk/broker/master-capability.ts +232 -0
  194. package/src/sdk/broker/session-index.ts +121 -17
  195. package/src/sdk/broker/session-scope.ts +272 -0
  196. package/src/sdk/broker/spawn-authority.ts +1031 -0
  197. package/src/sdk/broker/spawn-substrate.ts +591 -0
  198. package/src/sdk/broker/startup-budget.ts +144 -2
  199. package/src/sdk/broker/state-version.ts +43 -14
  200. package/src/sdk/broker/transport.ts +1 -0
  201. package/src/sdk/broker/worktree-occupancy.ts +90 -0
  202. package/src/sdk/bus/chat-daemon-control.ts +5 -2
  203. package/src/sdk/bus/chat-daemon-runtime.ts +136 -18
  204. package/src/sdk/bus/discord-daemon.ts +50 -10
  205. package/src/sdk/bus/index.ts +84 -7
  206. package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
  207. package/src/sdk/bus/lifecycle-commands.ts +4 -0
  208. package/src/sdk/bus/notification-service.ts +22 -5
  209. package/src/sdk/bus/prompt-reconciliation.ts +7 -2
  210. package/src/sdk/bus/reconciliation-store.ts +55 -31
  211. package/src/sdk/bus/slack-conversation.ts +1 -1
  212. package/src/sdk/bus/slack-daemon.ts +72 -38
  213. package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
  214. package/src/sdk/cli/index.ts +1 -0
  215. package/src/sdk/cli/master-cli.ts +259 -0
  216. package/src/sdk/cli/rows.ts +41 -15
  217. package/src/sdk/cli/session-cli.ts +297 -26
  218. package/src/sdk/client/client.ts +14 -8
  219. package/src/sdk/host/control/dispatch.ts +101 -40
  220. package/src/sdk/host/control/index.ts +2 -0
  221. package/src/sdk/host/control/operations.ts +2 -0
  222. package/src/sdk/host/control/runtime-gate.ts +203 -11
  223. package/src/sdk/host/events.ts +1 -0
  224. package/src/sdk/host/host.ts +29 -2
  225. package/src/sdk/host/query/cursor.ts +52 -7
  226. package/src/sdk/host/query/handlers.ts +33 -12
  227. package/src/sdk/host/query/revision-store.ts +12 -0
  228. package/src/sdk/host/session-runtime.test.ts +673 -14
  229. package/src/sdk/host/session-runtime.ts +674 -103
  230. package/src/sdk/host/surface-policy.ts +3 -3
  231. package/src/sdk/lifecycle/broker-client.ts +28 -0
  232. package/src/sdk/lifecycle/service.ts +279 -20
  233. package/src/sdk/lifecycle-session.ts +17 -4
  234. package/src/sdk/prompt-deadline-manager.ts +2 -1
  235. package/src/sdk/protocol/operation-inventory.generated.json +105 -3
  236. package/src/sdk/protocol/operation-registry.ts +14 -4
  237. package/src/sdk/receipt-state.ts +10 -0
  238. package/src/sdk/router/session-router.ts +219 -118
  239. package/src/sdk/session.ts +416 -195
  240. package/src/sdk/startup-capability.ts +1 -0
  241. package/src/sdk/turn-result.ts +22 -0
  242. package/src/session/agent-session.ts +1628 -318
  243. package/src/session/blob-store.ts +15 -5
  244. package/src/session/credential-store-errors.ts +2 -0
  245. package/src/session/fold-coordinator.ts +40 -3
  246. package/src/session/internal/managed-session-storage.ts +153 -78
  247. package/src/session/session-manager.ts +850 -97
  248. package/src/session/yield-queue.ts +24 -10
  249. package/src/setup/frictionless-onboarding.ts +2 -0
  250. package/src/slash-commands/builtin-registry.ts +41 -0
  251. package/src/task/executor.ts +9 -4
  252. package/src/telemetry/events.ts +1138 -12
  253. package/src/tools/ask-contract.test.ts +64 -0
  254. package/src/tools/ask-contract.ts +36 -1
  255. package/src/tools/ask.ts +10 -8
  256. package/src/tools/bash-interactive.ts +11 -2
  257. package/src/tools/bash.ts +412 -75
  258. package/src/tools/browser/launch.ts +37 -0
  259. package/src/tools/browser/registry.ts +99 -9
  260. package/src/tools/cron.ts +6 -4
  261. package/src/tools/descriptors.test.ts +15 -0
  262. package/src/tools/descriptors.ts +5 -1
  263. package/src/tools/gh.ts +104 -48
  264. package/src/tools/github-cache.ts +2 -2
  265. package/src/tools/index.ts +10 -6
  266. package/src/tools/job.ts +4 -1
  267. package/src/tools/monitor.ts +12 -9
  268. package/src/tools/resolve.ts +30 -10
  269. package/src/tools/tool-catalog.generated.ts +2 -2
  270. package/src/utils/external-editor.ts +72 -1
@@ -0,0 +1,64 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { validRecommendedIndex } from "./ask";
3
+ import { askSchema, ordinaryAskSchema } from "./ask-contract";
4
+
5
+ const baseQuestion = {
6
+ id: "q",
7
+ question: "Choose one.",
8
+ options: [{ label: "Keep current behavior" }, { label: "Use repository default" }],
9
+ };
10
+
11
+ describe("ask option contract", () => {
12
+ test("rejects custom-input pseudo-options", () => {
13
+ for (const label of [
14
+ "Other (type your own)",
15
+ "Type your own",
16
+ "custom input",
17
+ "직접 입력",
18
+ "1. 직접 입력",
19
+ "직접 입력으로 추가",
20
+ " OTHER(SPECIFY) ",
21
+ "other…",
22
+ "write your own",
23
+ "enter manually",
24
+ "기타",
25
+ "기타 입력",
26
+ ]) {
27
+ const result = ordinaryAskSchema.safeParse({
28
+ questions: [{ ...baseQuestion, options: [{ label }, { label: "Cancel" }], recommended: 0 }],
29
+ });
30
+ expect(result.success, label).toBe(false);
31
+ }
32
+ });
33
+
34
+ test("accepts a genuine Other domain value", () => {
35
+ const result = ordinaryAskSchema.safeParse({
36
+ questions: [{ ...baseQuestion, options: [{ label: "Other" }, { label: "Cancel" }] }],
37
+ });
38
+ expect(result.success).toBe(true);
39
+ });
40
+
41
+ test("accepts descriptive labels that merely contain custom-input words", () => {
42
+ const result = ordinaryAskSchema.safeParse({
43
+ questions: [{ ...baseQuestion, options: [{ label: "Deploy Other (type your own)" }, { label: "Cancel" }] }],
44
+ });
45
+ expect(result.success).toBe(true);
46
+ });
47
+
48
+ test("accepts ordinary recommended options", () => {
49
+ const result = ordinaryAskSchema.safeParse({ questions: [{ ...baseQuestion, recommended: 0 }] });
50
+ expect(result.success).toBe(true);
51
+ });
52
+
53
+ test("applies pseudo-option rejection to deep-interview ask schema", () => {
54
+ const result = askSchema.safeParse({
55
+ questions: [{ ...baseQuestion, options: [{ label: "직접 입력으로 추가" }, { label: "취소" }] }],
56
+ });
57
+ expect(result.success).toBe(false);
58
+ });
59
+
60
+ test("does not recommend single-option questions", () => {
61
+ expect(validRecommendedIndex(0, 1)).toBeUndefined();
62
+ expect(validRecommendedIndex(0, 2)).toBe(0);
63
+ });
64
+ });
@@ -26,8 +26,43 @@ function deepInterviewBoundedString(maximum: number) {
26
26
  });
27
27
  }
28
28
 
29
+ function isReservedCustomInputOptionLabel(label: string): boolean {
30
+ const withoutNumbering = label
31
+ .normalize("NFKC")
32
+ .toLowerCase()
33
+ .trim()
34
+ .replace(/^\d+[.)]\s+/u, "");
35
+ if (/^other[\p{P}\p{S}\s]*$/u.test(withoutNumbering) && withoutNumbering !== "other") return true;
36
+ const wordsOnly = withoutNumbering
37
+ .replace(/[\p{P}\p{S}]+/gu, " ")
38
+ .replace(/\s+/g, " ")
39
+ .trim();
40
+ const englishPseudoOptions = new Set([
41
+ "other type your own",
42
+ "other specify",
43
+ "other please specify",
44
+ "type your own",
45
+ "write your own",
46
+ "enter manually",
47
+ "custom input",
48
+ ]);
49
+ if (englishPseudoOptions.has(wordsOnly)) return true;
50
+ const compact = wordsOnly.replace(/\s+/g, "");
51
+ return ["직접입력", "직접입력으로추가", "기타", "기타입력"].includes(compact);
52
+ }
53
+
29
54
  const OptionItem = z.object({
30
- label: z.string().describe("display label"),
55
+ label: z
56
+ .string()
57
+ .describe("display label")
58
+ .superRefine((value, context) => {
59
+ if (isReservedCustomInputOptionLabel(value))
60
+ context.addIssue({
61
+ code: "custom",
62
+ message:
63
+ "ask option labels must not imitate the reserved custom-input control; omit the pseudo-option and let the Ask UI add Other (type your own)",
64
+ });
65
+ }),
31
66
  });
32
67
 
33
68
  const DEEP_INTERVIEW_INTENT_ID_PATTERN = /^(artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}$/;
package/src/tools/ask.ts CHANGED
@@ -158,7 +158,8 @@ function getDoneOptionLabel(): string {
158
158
  return success ? `${success} Done selecting` : "Done selecting";
159
159
  }
160
160
 
161
- function validRecommendedIndex(recommended: number | undefined, optionCount: number): number | undefined {
161
+ export function validRecommendedIndex(recommended: number | undefined, optionCount: number): number | undefined {
162
+ if (optionCount <= 1) return undefined;
162
163
  return typeof recommended === "number" &&
163
164
  Number.isFinite(recommended) &&
164
165
  Number.isInteger(recommended) &&
@@ -350,7 +351,7 @@ async function askSingleQuestion(
350
351
  options: AskSingleQuestionOptions = {},
351
352
  ): Promise<SelectionResult> {
352
353
  const {
353
- recommended,
354
+ recommended: requestedRecommended,
354
355
  timeout,
355
356
  signal,
356
357
  initialSelection,
@@ -358,6 +359,7 @@ async function askSingleQuestion(
358
359
  scrollTitleRows,
359
360
  autoSelectOnTimeout = true,
360
361
  } = options;
362
+ const recommended = validRecommendedIndex(requestedRecommended, optionLabels.length);
361
363
  const doneLabel = getDoneOptionLabel();
362
364
  const otherOptionLabel = options.otherOptionLabel ?? OTHER_OPTION;
363
365
  const clarificationOptionLabel = options.clarificationOptionLabel;
@@ -1118,6 +1120,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1118
1120
  },
1119
1121
  ) => {
1120
1122
  const rawOptionLabels = q.options.map(o => o.label);
1123
+ const recommended = validRecommendedIndex(q.recommended, rawOptionLabels.length);
1121
1124
  const questionIndex = params.questions.indexOf(q);
1122
1125
  const emptyCustomAttempt = options?.emptyCustomAttempt ?? 0;
1123
1126
  // Route headless asks through the SDK workflow-gate emitter; a connected
@@ -1128,7 +1131,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1128
1131
  question: q.question,
1129
1132
  options: q.options,
1130
1133
  multi: q.multi,
1131
- recommended: q.recommended,
1134
+ recommended,
1132
1135
  deepInterview: q.deepInterview,
1133
1136
  workflowGate: q.workflowGate,
1134
1137
  allowEmpty: q.multi === true && params.questions.length > 1,
@@ -1177,12 +1180,11 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1177
1180
  }),
1178
1181
  }
1179
1182
  : options?.previous;
1180
- const recommendedIndex = validRecommendedIndex(q.recommended, rawOptionLabels.length);
1181
1183
  activeRemoteRequest = {
1182
1184
  question: displayQuestion,
1183
1185
  options: remoteSelectorOptions,
1184
1186
  interaction: "selector",
1185
- ...(recommendedIndex === undefined ? {} : { recommendedIndex }),
1187
+ ...(recommended === undefined ? {} : { recommendedIndex: recommended }),
1186
1188
  ...(timeout === undefined || timeout === null ? {} : { timeoutMs: timeout }),
1187
1189
  ...(clarificationOptionLabel ? { transitionCount: 2 } : { transitionCount: 1 }),
1188
1190
  multi: q.multi === true,
@@ -1204,7 +1206,7 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1204
1206
  cancelled,
1205
1207
  timedOut,
1206
1208
  } = await askSingleQuestion(ui, displayQuestion, optionLabels, q.multi ?? false, {
1207
- recommended: q.recommended,
1209
+ recommended,
1208
1210
  timeout: timeout ?? undefined,
1209
1211
  signal,
1210
1212
  initialSelection,
@@ -1221,8 +1223,8 @@ export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
1221
1223
  question: displayQuestion,
1222
1224
  options: state.interaction === "selector" ? remoteSelectorOptions : [],
1223
1225
  interaction: state.interaction,
1224
- ...(state.interaction === "selector" && recommendedIndex !== undefined
1225
- ? { recommendedIndex }
1226
+ ...(state.interaction === "selector" && recommended !== undefined
1227
+ ? { recommendedIndex: recommended }
1226
1228
  : {}),
1227
1229
  ...(state.interaction === "selector" && timeout !== undefined && timeout !== null
1228
1230
  ? { timeoutMs: timeout }
@@ -377,6 +377,7 @@ export async function runInteractiveBashPty(
377
377
  let observer: BashInteractiveOverlayComponent | undefined;
378
378
  let settleForeground: ((result: BashInteractiveResult) => void) | undefined;
379
379
  let settled = false;
380
+ let inputAttached = true;
380
381
  const foreground = Promise.withResolvers<BashInteractiveResult>();
381
382
  const terminal = Promise.withResolvers<BashInteractiveResult>();
382
383
 
@@ -449,6 +450,10 @@ export async function runInteractiveBashPty(
449
450
  options.signal?.removeEventListener("abort", forwardForegroundAbort);
450
451
  },
451
452
  detachObserver: (foldResult: BashInteractiveResult) => {
453
+ // Stop accepting bytes synchronously at the ownership-transfer boundary.
454
+ // Overlay teardown may be deferred by the host, so disposal alone is not
455
+ // a sufficient input fence for a folded, output-only PTY.
456
+ inputAttached = false;
452
457
  const outcome = settle(foldResult);
453
458
  if (outcome === "resolved") observer = undefined;
454
459
  return outcome;
@@ -476,15 +481,19 @@ export async function runInteractiveBashPty(
476
481
  // Escape explicitly force-kills, while disposal never kills the work.
477
482
  component.setHandlers(
478
483
  data => {
484
+ if (!inputAttached) return;
479
485
  try {
480
486
  session.write(data);
481
487
  } catch {
482
488
  // ignore writes after the command exits
483
489
  }
484
490
  },
485
- options.onDismiss ?? (() => session.kill()),
491
+ () => {
492
+ if (!inputAttached) return;
493
+ (options.onDismiss ?? (() => session.kill()))();
494
+ },
486
495
  () => {},
487
- options.onFoldKey ?? (() => false),
496
+ () => inputAttached && (options.onFoldKey?.() ?? false),
488
497
  );
489
498
  observer = component;
490
499
  return component;