@gajae-code/coding-agent 0.11.7 → 0.11.8

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 (180) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/dist/types/cli/args.d.ts +0 -2
  3. package/dist/types/config/keybindings.d.ts +33 -3
  4. package/dist/types/config/model-profile-activation.d.ts +4 -3
  5. package/dist/types/config/model-profile-contract.d.ts +38 -0
  6. package/dist/types/config/settings-schema.d.ts +15 -2
  7. package/dist/types/edit/modes/patch.d.ts +13 -0
  8. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  9. package/dist/types/eval/py/executor.d.ts +1 -1
  10. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  11. package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
  12. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +1 -0
  13. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +24 -0
  14. package/dist/types/main.d.ts +8 -0
  15. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  16. package/dist/types/modes/components/tool-status-header.d.ts +3 -2
  17. package/dist/types/modes/components/welcome.d.ts +2 -0
  18. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  19. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  20. package/dist/types/modes/interactive-mode.d.ts +5 -2
  21. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  22. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  23. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  24. package/dist/types/modes/types.d.ts +3 -0
  25. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  26. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  27. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  28. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  29. package/dist/types/sdk/broker/broker.d.ts +2 -0
  30. package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
  31. package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -1
  32. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  33. package/dist/types/sdk/bus/config.d.ts +2 -0
  34. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  35. package/dist/types/sdk/bus/index.d.ts +25 -0
  36. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  37. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
  38. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +13 -3
  39. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  40. package/dist/types/sdk/bus/telegram-daemon.d.ts +128 -1
  41. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  42. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  43. package/dist/types/sdk/host/host.d.ts +1 -1
  44. package/dist/types/sdk/host/query/handlers.d.ts +9 -0
  45. package/dist/types/sdk/index.d.ts +2 -0
  46. package/dist/types/sdk/prompt-status.d.ts +88 -0
  47. package/dist/types/sdk/session-directory.d.ts +1 -1
  48. package/dist/types/sdk/startup-capability.d.ts +3 -0
  49. package/dist/types/session/agent-session.d.ts +2 -0
  50. package/dist/types/session/internal/managed-session-scope.d.ts +11 -2
  51. package/dist/types/session/internal/managed-session-storage.d.ts +17 -2
  52. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  53. package/dist/types/session/session-storage.d.ts +18 -0
  54. package/dist/types/task/index.d.ts +3 -1
  55. package/dist/types/task/receipt.d.ts +2 -0
  56. package/dist/types/task/types.d.ts +94 -0
  57. package/dist/types/tools/ask.d.ts +454 -174
  58. package/dist/types/tools/image-gen.d.ts +47 -1
  59. package/dist/types/tools/index.d.ts +4 -0
  60. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  61. package/package.json +8 -8
  62. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  63. package/scripts/dogfood-repository-binding.ts +209 -0
  64. package/scripts/generate-sdk-operation-inventory.ts +2 -0
  65. package/scripts/run-test-manifest.ts +4 -4
  66. package/src/cli/args.ts +2 -92
  67. package/src/cli/notify-cli.ts +5 -38
  68. package/src/cli.ts +18 -0
  69. package/src/commands/sdk.ts +2 -2
  70. package/src/config/keybindings.ts +157 -24
  71. package/src/config/model-profile-activation.ts +7 -18
  72. package/src/config/model-profile-contract.ts +161 -0
  73. package/src/config/model-registry.ts +11 -1
  74. package/src/config/settings-schema.ts +17 -2
  75. package/src/coordinator-mcp/model-preset.ts +21 -74
  76. package/src/defaults/gjc/skills/deep-interview/SKILL.md +2 -2
  77. package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
  78. package/src/edit/index.ts +3 -6
  79. package/src/edit/modes/patch.ts +38 -1
  80. package/src/edit/modes/replace.ts +42 -0
  81. package/src/edit/path-mutation-lock.ts +112 -0
  82. package/src/eval/py/executor.ts +30 -11
  83. package/src/eval/py/prelude.py +2 -2
  84. package/src/eval/py/tool-bridge.ts +34 -18
  85. package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
  86. package/src/gjc-runtime/launch-tmux.ts +1 -1
  87. package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
  88. package/src/gjc-runtime/ralplan-runtime.ts +75 -3
  89. package/src/gjc-runtime/repository-binding.ts +267 -0
  90. package/src/gjc-runtime/session-state-sidecar.ts +25 -9
  91. package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
  92. package/src/gjc-runtime/tmux-sessions.ts +45 -6
  93. package/src/gjc-runtime/ultragoal-guard.ts +5 -5
  94. package/src/gjc-runtime/ultragoal-runtime.ts +181 -19
  95. package/src/internal-urls/docs-index.generated.ts +6 -5
  96. package/src/internal-urls/local-protocol.ts +33 -16
  97. package/src/internal-urls/mcp-protocol.ts +73 -11
  98. package/src/main.ts +25 -10
  99. package/src/modes/DESIGN.md +5 -0
  100. package/src/modes/acp/acp-agent.ts +50 -16
  101. package/src/modes/components/model-selector.ts +42 -8
  102. package/src/modes/components/plan-preview-overlay.ts +1 -1
  103. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  104. package/src/modes/components/tool-status-header.ts +20 -4
  105. package/src/modes/components/welcome.ts +22 -18
  106. package/src/modes/controllers/command-controller.ts +97 -54
  107. package/src/modes/controllers/event-controller.ts +10 -0
  108. package/src/modes/controllers/input-controller.ts +7 -2
  109. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  110. package/src/modes/controllers/selector-controller.ts +46 -54
  111. package/src/modes/interactive-mode.ts +41 -27
  112. package/src/modes/prompt-action-autocomplete.ts +32 -13
  113. package/src/modes/prompt-suggestion-controller.ts +96 -0
  114. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  115. package/src/modes/types.ts +3 -0
  116. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  117. package/src/modes/utils/ui-helpers.ts +5 -3
  118. package/src/prompts/agents/architect.md +0 -1
  119. package/src/prompts/agents/critic.md +0 -1
  120. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  121. package/src/prompts/system/system-prompt.md +6 -4
  122. package/src/prompts/tools/cron.md +3 -1
  123. package/src/runtime-mcp/manager.ts +51 -53
  124. package/src/runtime-mcp/oauth-flow.ts +0 -72
  125. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  126. package/src/runtime-mcp/transports/http.ts +13 -6
  127. package/src/sdk/broker/broker.ts +34 -20
  128. package/src/sdk/broker/lifecycle.ts +185 -36
  129. package/src/sdk/bus/chat-daemon-control.ts +18 -5
  130. package/src/sdk/bus/config-commands.ts +2 -2
  131. package/src/sdk/bus/config.ts +9 -1
  132. package/src/sdk/bus/conversation-store.ts +40 -6
  133. package/src/sdk/bus/index.ts +240 -65
  134. package/src/sdk/bus/lifecycle-commands.ts +47 -3
  135. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  136. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  137. package/src/sdk/bus/notification-orchestration.ts +12 -0
  138. package/src/sdk/bus/notification-service.ts +27 -2
  139. package/src/sdk/bus/prompt-reconciliation.ts +239 -0
  140. package/src/sdk/bus/recent-activity.ts +90 -26
  141. package/src/sdk/bus/telegram-daemon-contract.ts +13 -3
  142. package/src/sdk/bus/telegram-daemon-control.ts +145 -33
  143. package/src/sdk/bus/telegram-daemon.ts +835 -110
  144. package/src/sdk/bus/telegram-reference.ts +20 -2
  145. package/src/sdk/cli/session-cli.ts +15 -2
  146. package/src/sdk/host/control/dispatch.ts +1 -1
  147. package/src/sdk/host/control/operations.ts +1 -1
  148. package/src/sdk/host/host.ts +2 -6
  149. package/src/sdk/host/query/handlers.ts +70 -5
  150. package/src/sdk/index.ts +12 -0
  151. package/src/sdk/prompt-status.ts +97 -0
  152. package/src/sdk/protocol/operation-inventory.generated.json +47 -0
  153. package/src/sdk/protocol/operation-registry.ts +18 -4
  154. package/src/sdk/session-directory.ts +5 -1
  155. package/src/sdk/session.ts +2 -0
  156. package/src/sdk/startup-capability.ts +9 -1
  157. package/src/session/agent-session.ts +52 -5
  158. package/src/session/blob-store.ts +7 -2
  159. package/src/session/internal/managed-session-scope.ts +854 -84
  160. package/src/session/internal/managed-session-storage.ts +251 -199
  161. package/src/session/internal/native-publish-outcome.ts +266 -0
  162. package/src/session/session-manager.ts +310 -327
  163. package/src/session/session-storage.ts +128 -11
  164. package/src/task/gjc-command.ts +2 -2
  165. package/src/task/index.ts +121 -15
  166. package/src/task/receipt.ts +3 -0
  167. package/src/task/types.ts +34 -3
  168. package/src/tools/ask.ts +198 -88
  169. package/src/tools/ast-edit.ts +2 -2
  170. package/src/tools/bash-pty-selection.ts +2 -2
  171. package/src/tools/browser/screenshot-format.ts +6 -1
  172. package/src/tools/cron.ts +1 -1
  173. package/src/tools/image-gen.ts +178 -6
  174. package/src/tools/index.ts +4 -0
  175. package/src/tools/render-utils.ts +7 -1
  176. package/src/utils/edit-mode.ts +2 -2
  177. package/src/utils/prompt-suggestion.ts +265 -0
  178. package/src/utils/sixel.ts +3 -3
  179. package/src/web/insane/url-guard.ts +3 -1
  180. package/src/web/scrapers/docs-rs.ts +30 -5
@@ -156,6 +156,18 @@ export function createAliasTable(): AliasTable {
156
156
  },
157
157
  };
158
158
  }
159
+ /** Copy labels for display and annotate one valid recommended option only. */
160
+ function withRecommendedOptionLabel(options: readonly string[], recommendedIndex: unknown): string[] {
161
+ if (
162
+ typeof recommendedIndex !== "number" ||
163
+ !Number.isFinite(recommendedIndex) ||
164
+ !Number.isInteger(recommendedIndex) ||
165
+ recommendedIndex < 0 ||
166
+ recommendedIndex >= options.length
167
+ )
168
+ return [...options];
169
+ return options.map((label, index) => (index === recommendedIndex ? `${label} (Recommended)` : label));
170
+ }
159
171
 
160
172
  /** Render an `action_needed` payload into a Telegram message. */
161
173
  export function buildActionMessage(action: {
@@ -163,6 +175,7 @@ export function buildActionMessage(action: {
163
175
  id: string;
164
176
  question?: string;
165
177
  options?: string[];
178
+ recommendedIndex?: unknown;
166
179
  controls?: readonly TelegramActionControl[];
167
180
  summary?: string;
168
181
  }): RenderedMessage {
@@ -172,9 +185,10 @@ export function buildActionMessage(action: {
172
185
  }
173
186
  const text = `❓ ${bold(action.question ?? "Question")}`;
174
187
  const options = action.options ?? [];
188
+ const displayOptions = withRecommendedOptionLabel(options, action.recommendedIndex);
175
189
  const controls = (action.controls ?? []).filter(control => control.enabled);
176
190
  if (options.length === 0 && controls.length === 0) return { text: `${text}\n\n(reply with text)` };
177
- const body = options.length ? `${text}\n\n${numberedOptionList(options)}` : text;
191
+ const body = options.length ? `${text}\n\n${numberedOptionList(displayOptions)}` : text;
178
192
  const inline_keyboard = [
179
193
  ...(options.length ? buildCompactChoiceGrid(options, i => encodeCallbackData(action.id, i)) : []),
180
194
  ...controls.map(control => [
@@ -189,6 +203,7 @@ export function buildActionMarkdown(action: {
189
203
  kind: "ask" | "idle";
190
204
  question?: string;
191
205
  options?: string[];
206
+ recommendedIndex?: unknown;
192
207
  summary?: string;
193
208
  }): string {
194
209
  if (action.kind === "idle") {
@@ -196,8 +211,9 @@ export function buildActionMarkdown(action: {
196
211
  }
197
212
  const heading = `❓ **${action.question ?? "Question"}**`;
198
213
  const options = action.options ?? [];
214
+ const displayOptions = withRecommendedOptionLabel(options, action.recommendedIndex);
199
215
  if (options.length === 0) return `${heading}\n\n(reply with text)`;
200
- const list = options.map((label, i) => `${i + 1}. ${label.replace(/^\s*\d+[.)]\s+/, "")}`).join("\n");
216
+ const list = displayOptions.map((label, i) => `${i + 1}. ${label.replace(/^\s*\d+[.)]\s+/, "")}`).join("\n");
201
217
  return `${heading}\n\n${list}`;
202
218
  }
203
219
 
@@ -362,6 +378,7 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
362
378
  id?: string;
363
379
  question?: string;
364
380
  options?: string[];
381
+ recommendedIndex?: unknown;
365
382
  controls?: TelegramActionControl[];
366
383
  summary?: string;
367
384
  reason?: string;
@@ -374,6 +391,7 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
374
391
  id: msg.id,
375
392
  question: msg.question,
376
393
  options: msg.options,
394
+ recommendedIndex: msg.recommendedIndex,
377
395
  controls: msg.controls,
378
396
  summary: msg.summary,
379
397
  });
@@ -40,6 +40,7 @@ class SdkSessionCliError extends Error {
40
40
  readonly code: string,
41
41
  message: string,
42
42
  readonly exitCode: 1 | 2,
43
+ readonly details?: unknown,
43
44
  ) {
44
45
  super(message);
45
46
  }
@@ -261,7 +262,12 @@ export async function runSdkSessionCli(
261
262
  error instanceof SdkSessionCliError
262
263
  ? error
263
264
  : error instanceof SdkClientError
264
- ? new SdkSessionCliError(error.code, error.message, 1)
265
+ ? new SdkSessionCliError(
266
+ error.code,
267
+ error.message,
268
+ 1,
269
+ (error.details as { details?: unknown } | undefined)?.details,
270
+ )
265
271
  : error instanceof SdkDiscoveryError
266
272
  ? new SdkSessionCliError(error.code, error.message, 1)
267
273
  : new SdkSessionCliError(
@@ -269,7 +275,14 @@ export async function runSdkSessionCli(
269
275
  error instanceof Error ? error.message : "SDK operation failed.",
270
276
  1,
271
277
  );
272
- writeOutput({ ok: false, error: { code: cliError.code, message: cliError.message } });
278
+ writeOutput({
279
+ ok: false,
280
+ error: {
281
+ code: cliError.code,
282
+ message: cliError.message,
283
+ ...(cliError.details ? { details: cliError.details } : {}),
284
+ },
285
+ });
273
286
  setExitCode(cliError.exitCode);
274
287
  }
275
288
  }
@@ -137,7 +137,7 @@ function invoke(
137
137
  ): Promise<ControlValue> | ControlValue {
138
138
  switch (operation) {
139
139
  case "turn.prompt":
140
- return surface.prompt(text(input), input.images);
140
+ return surface.prompt(text(input), input.images, input.clientRef as string | undefined);
141
141
  case "turn.steer":
142
142
  return surface.steer(text(input));
143
143
  case "turn.follow_up":
@@ -6,7 +6,7 @@ export type ControlInput = Record<string, unknown>;
6
6
  * AgentSession and its controllers without exposing those concrete types here.
7
7
  */
8
8
  export interface ControlSurface {
9
- prompt(text: string, images?: ControlValue): Promise<ControlValue> | ControlValue;
9
+ prompt(text: string, images?: ControlValue, clientRef?: string): Promise<ControlValue> | ControlValue;
10
10
  steer(text: string): Promise<ControlValue> | ControlValue;
11
11
  followUp(text: string): Promise<ControlValue> | ControlValue;
12
12
  abort(): Promise<ControlValue> | ControlValue;
@@ -15,7 +15,7 @@ export interface SessionSdkHostOptions extends HostEndpointAdapters {
15
15
  onProviderDefinitionsRemoved?: (capability: string) => void;
16
16
  onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void;
17
17
  /** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */
18
- connectionCapabilities?: (connectionId: string) => ReadonlySet<string>;
18
+ connectionCapabilities?: (connectionId: string) => ReadonlySet<string> | undefined;
19
19
  }
20
20
 
21
21
  const TOOL_ACTIVITY_V1 = "tool_activity_v1";
@@ -231,11 +231,7 @@ export class SessionSdkHost {
231
231
  const sinceGeneration = rawGeneration;
232
232
  const sinceSeq = rawSeq;
233
233
  const replay = this.events.replay(sinceSeq, sinceGeneration);
234
- const capabilities = Array.isArray(frame.capabilities)
235
- ? new Set(
236
- frame.capabilities.filter((capability): capability is string => typeof capability === "string"),
237
- )
238
- : (this.#options.connectionCapabilities?.(connectionId) ?? EMPTY_CAPABILITIES);
234
+ const capabilities = this.#options.connectionCapabilities?.(connectionId) ?? EMPTY_CAPABILITIES;
239
235
  const events = replay.events.filter(
240
236
  event => !CAP_GATED_FRAME_KINDS.has(String(event.kind)) || capabilities.has(TOOL_ACTIVITY_V1),
241
237
  );
@@ -1,3 +1,4 @@
1
+ import { PROMPT_CLIENT_REF_MAX_LENGTH } from "../../prompt-status.js";
1
2
  import {
2
3
  assertCursorSelector,
3
4
  type CursorEnvelope,
@@ -43,6 +44,10 @@ export interface SessionSurface {
43
44
  | Promise<{ bytes: Uint8Array; totalBytes: number } | undefined>;
44
45
 
45
46
  getJobs(): unknown | Promise<unknown>;
47
+ /** Q26 keyed lookup of a submitted prompt's authoritative reconciliation status. */
48
+ getPromptStatus?(selector: { commandId?: string; turnId?: string; clientRef?: string }): unknown | Promise<unknown>;
49
+ /** Q27 effective model-profile catalog from the live session registry. */
50
+ getModelProfiles?(): unknown[] | Promise<unknown[]>;
46
51
  /** Query rows backed by the session's installed binding map. */
47
52
  installedQueries?: ReadonlySet<string>;
48
53
  }
@@ -66,7 +71,7 @@ export interface QueryResponse {
66
71
  ok: boolean;
67
72
  page?: QueryPage;
68
73
  result?: unknown;
69
- error?: { code: string; message: string; restartQuery?: boolean };
74
+ error?: { code: string; message: string; restartQuery?: boolean; details?: unknown };
70
75
  }
71
76
 
72
77
  const sources: Record<string, { resource: string; method: keyof SessionSurface; mvcc: boolean }> = {
@@ -92,6 +97,7 @@ const sources: Record<string, { resource: string; method: keyof SessionSurface;
92
97
  Q21: { resource: "queue", method: "getQueueMessages", mvcc: true },
93
98
  Q22: { resource: "extensions", method: "getExtensions", mvcc: true },
94
99
  Q25: { resource: "jobs", method: "getJobs", mvcc: false },
100
+ Q27: { resource: "modelProfiles", method: "getModelProfiles", mvcc: true },
95
101
  };
96
102
  const names = [
97
103
  "transcript.list",
@@ -119,6 +125,8 @@ const names = [
119
125
  "resource.body",
120
126
  "artifact.read",
121
127
  "runtime.jobs.list",
128
+ "turn.prompt_status",
129
+ "models.profiles.list",
122
130
  ];
123
131
 
124
132
  export class QueryHandlers {
@@ -147,13 +155,18 @@ export class QueryHandlers {
147
155
  );
148
156
  if (query === "Q02") return await this.#transcriptBody(request);
149
157
  if (query === "Q23") return await this.#resourceBody(request);
158
+ if (query === "Q26") return await this.#promptStatus(request);
150
159
  if (query === "Q24") return await this.#artifact(request);
160
+ if (query === "Q27" && request.input && Object.keys(request.input).length > 0)
161
+ return this.#error(request, "invalid_request", false, "models.profiles.list does not accept input fields.");
162
+ if (query === "Q27" && typeof this.surface.getModelProfiles !== "function")
163
+ return this.#error(request, "unavailable", false, "models.profiles.list is unavailable for this session.");
151
164
  const source = sources[query];
152
165
  if (!source) return this.#error(request, "invalid_request");
153
166
  return await this.#pageSource(request, query, source);
154
167
  } catch (error) {
155
168
  if (error instanceof CursorError) return this.#error(request, error.code, error.restartQuery, error.message);
156
- if (isTypedError(error)) return this.#error(request, error.code, false, error.message);
169
+ if (isTypedError(error)) return this.#error(request, error.code, false, error.message, error.details);
157
170
  return this.#error(request, "internal", false, error instanceof Error ? error.message : String(error));
158
171
  }
159
172
  }
@@ -504,8 +517,60 @@ export class QueryHandlers {
504
517
  return { id: request.id, ok: true, page };
505
518
  }
506
519
 
507
- #error(request: QueryRequest, code: string, restartQuery = false, message = code): QueryResponse {
508
- return { id: request.id, ok: false, error: { code, message, ...(restartQuery ? { restartQuery: true } : {}) } };
520
+ async #promptStatus(request: QueryRequest): Promise<QueryResponse> {
521
+ if (request.cursor)
522
+ return this.#error(request, "invalid_request", false, "turn.prompt_status does not support cursors.");
523
+ const input = request.input ?? {};
524
+ for (const key of Object.keys(input))
525
+ if (key !== "commandId" && key !== "turnId" && key !== "clientRef")
526
+ return this.#error(
527
+ request,
528
+ "invalid_request",
529
+ false,
530
+ `turn.prompt_status does not accept selector field "${key}".`,
531
+ );
532
+ const commandId = typeof input.commandId === "string" && input.commandId ? input.commandId : undefined;
533
+ const turnId = typeof input.turnId === "string" && input.turnId ? input.turnId : undefined;
534
+ const rawClientRef = typeof input.clientRef === "string" ? input.clientRef : undefined;
535
+ const trimmedClientRef = rawClientRef?.trim();
536
+ if (rawClientRef !== undefined && (!trimmedClientRef || trimmedClientRef.length > PROMPT_CLIENT_REF_MAX_LENGTH))
537
+ return this.#error(
538
+ request,
539
+ "invalid_request",
540
+ false,
541
+ "clientRef must be a non-empty string of at most 128 characters.",
542
+ );
543
+ const clientRef = trimmedClientRef || undefined;
544
+ if ((commandId === undefined) !== (turnId === undefined))
545
+ return this.#error(request, "invalid_request", false, "commandId and turnId must be provided together.");
546
+ if (commandId !== undefined && clientRef !== undefined)
547
+ return this.#error(
548
+ request,
549
+ "invalid_request",
550
+ false,
551
+ "Provide exactly one selector: a commandId/turnId pair or a clientRef.",
552
+ );
553
+ if (commandId === undefined && clientRef === undefined)
554
+ return this.#error(
555
+ request,
556
+ "invalid_request",
557
+ false,
558
+ "turn.prompt_status requires a commandId/turnId pair or a clientRef.",
559
+ );
560
+ if (typeof this.surface.getPromptStatus !== "function")
561
+ return this.#error(request, "unavailable", false, "turn.prompt_status is unavailable for this session.");
562
+ const result = await this.surface.getPromptStatus(
563
+ clientRef !== undefined ? { clientRef } : { commandId: commandId!, turnId: turnId! },
564
+ );
565
+ return { id: request.id, ok: true, result };
566
+ }
567
+
568
+ #error(request: QueryRequest, code: string, restartQuery = false, message = code, details?: unknown): QueryResponse {
569
+ return {
570
+ id: request.id,
571
+ ok: false,
572
+ error: { code, message, ...(restartQuery ? { restartQuery: true } : {}), ...(details ? { details } : {}) },
573
+ };
509
574
  }
510
575
  }
511
576
  function selectorFor(queryId: string, input: Record<string, unknown> | undefined): CursorSelector {
@@ -521,7 +586,7 @@ function idOf(value: unknown): string | undefined {
521
586
  function lastId(value: unknown): string | undefined {
522
587
  return Array.isArray(value) ? idOf(value.at(-1)) : undefined;
523
588
  }
524
- function isTypedError(error: unknown): error is { code: string; message: string } {
589
+ function isTypedError(error: unknown): error is { code: string; message: string; details?: unknown } {
525
590
  return Boolean(
526
591
  error &&
527
592
  typeof error === "object" &&
package/src/sdk/index.ts CHANGED
@@ -1,3 +1,14 @@
1
+ export {
2
+ MODEL_PROFILE_DISCOVERY_QUERY,
3
+ MODEL_PROFILE_ERROR_DETAIL_MAX_BYTES,
4
+ type ModelProfileCatalogItem,
5
+ type ModelProfileErrorCode,
6
+ type ModelProfileErrorDetails,
7
+ ModelProfileRegistryError,
8
+ type ModelProfileRegistryErrorDetails,
9
+ type UnknownModelProfileDetails,
10
+ UnknownModelProfileError,
11
+ } from "../config/model-profile-contract";
1
12
  export * as bus from "./bus";
2
13
  export * from "./client";
3
14
  export * as host from "./host";
@@ -10,5 +21,6 @@ export type {
10
21
  Q10ThinkingEffort,
11
22
  Q10ThinkingMode,
12
23
  } from "./models";
24
+ export * from "./prompt-status";
13
25
  export * from "./session";
14
26
  export * from "./session-directory";
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Public DTO contract for Q26 `turn.prompt_status` and the `clientRef`
3
+ * correlation field on `turn.prompt`.
4
+ *
5
+ * Semantics:
6
+ * - `turn.prompt` remains ordered and non-idempotent. An envelope
7
+ * `idempotencyKey` is ignored for replay/conflict purposes on ordered
8
+ * operations (no replay cache, no `idempotency_conflict`).
9
+ * - `clientRef` is a caller-chosen correlation key (trimmed, non-empty,
10
+ * 1..PROMPT_CLIENT_REF_MAX_LENGTH chars; one fresh value per logical prompt)
11
+ * mapped to the generated commandId/turnId at preflight acceptance so a
12
+ * caller can reconcile after a lost acknowledgement frame.
13
+ * - The clientRef index is scoped to one live session runtime: identical refs
14
+ * may coexist in different sessions, lookups never cross sessions, a ref
15
+ * conflicts only while its record is retained (`client_ref_conflict`), and
16
+ * after terminal TTL/capacity eviction a ref may be admitted again with the
17
+ * prior outcome unknown — callers MUST NOT reuse a clientRef as a retry
18
+ * mechanism and MUST treat `unknown` as uncertainty, not proof of
19
+ * non-execution.
20
+ * - The durability floor is the live session process: after restart or
21
+ * retained-record eviction, a lookup honestly reports `unknown`.
22
+ * - Q26 tracks prompts accepted through the SDK control surface (which always
23
+ * carries a requesting connection); submissions without a delivery owner are
24
+ * outside the reconciliation contract and hold no reservation.
25
+ */
26
+
27
+ export const PROMPT_CLIENT_REF_MAX_LENGTH = 128;
28
+
29
+ export interface TurnPromptImageInput {
30
+ data: string;
31
+ mimeType?: string;
32
+ }
33
+
34
+ /** Public input contract for ordered `turn.prompt`. */
35
+ export interface TurnPromptInput {
36
+ text: string;
37
+ images?: TurnPromptImageInput[];
38
+ clientRef?: string;
39
+ }
40
+
41
+ /** Terminal outcome is preserved exactly; active records never age into terminal. */
42
+ export type PromptReconciliationStatus = "accepted" | "in_flight" | "terminal_ok" | "failed";
43
+
44
+ /** Exactly one selector per lookup. */
45
+ export type TurnPromptStatusSelector = { clientRef: string } | { commandId: string; turnId: string };
46
+
47
+ interface TurnPromptReconciliationIdentity {
48
+ commandId: string;
49
+ turnId: string;
50
+ clientRef?: string;
51
+ /** Epoch milliseconds of preflight acceptance. */
52
+ acceptedAt: number;
53
+ }
54
+
55
+ export interface TurnPromptReconciliationAccepted extends TurnPromptReconciliationIdentity {
56
+ status: "accepted";
57
+ }
58
+
59
+ export interface TurnPromptReconciliationInFlight extends TurnPromptReconciliationIdentity {
60
+ status: "in_flight";
61
+ /** Epoch milliseconds of the agent_start transition. */
62
+ startedAt: number;
63
+ }
64
+
65
+ export interface TurnPromptReconciliationTerminalOk extends TurnPromptReconciliationIdentity {
66
+ status: "terminal_ok";
67
+ startedAt?: number;
68
+ /** Epoch milliseconds of the terminal transition. */
69
+ terminalAt: number;
70
+ }
71
+
72
+ export interface TurnPromptReconciliationFailed extends TurnPromptReconciliationIdentity {
73
+ status: "failed";
74
+ startedAt?: number;
75
+ terminalAt: number;
76
+ /** Bounded, sanitized failure detail (code safe-token ≤64, message ≤512). */
77
+ error: { code: string; message: string };
78
+ }
79
+
80
+ export interface TurnPromptReconciliationUnknown {
81
+ status: "unknown";
82
+ }
83
+
84
+ export type TurnPromptReconciliation =
85
+ | TurnPromptReconciliationAccepted
86
+ | TurnPromptReconciliationInFlight
87
+ | TurnPromptReconciliationTerminalOk
88
+ | TurnPromptReconciliationFailed
89
+ | TurnPromptReconciliationUnknown;
90
+
91
+ /** Result of a successful `turn.prompt` preflight acknowledgement. */
92
+ export interface TurnPromptAcceptedResult {
93
+ commandId: string;
94
+ turnId: string;
95
+ accepted: true;
96
+ clientRef?: string;
97
+ }
@@ -1511,6 +1511,42 @@
1511
1511
  "packages/coding-agent/test/sdk-operation-inventory.test.ts"
1512
1512
  ]
1513
1513
  },
1514
+ {
1515
+ "sourceId": "registry:Q26",
1516
+ "sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
1517
+ "sourceKind": "registry",
1518
+ "decision": "include",
1519
+ "sdkId": "turn.prompt_status",
1520
+ "adapterMappings": {
1521
+ "telegram": "prohibited",
1522
+ "discord": "prohibited",
1523
+ "slack": "prohibited",
1524
+ "mcp": "generic_safe",
1525
+ "acp": "generic_safe",
1526
+ "daemonCli": "generic_safe"
1527
+ },
1528
+ "testIds": [
1529
+ "packages/coding-agent/test/sdk-operation-inventory.test.ts"
1530
+ ]
1531
+ },
1532
+ {
1533
+ "sourceId": "registry:Q27",
1534
+ "sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
1535
+ "sourceKind": "registry",
1536
+ "decision": "include",
1537
+ "sdkId": "models.profiles.list",
1538
+ "adapterMappings": {
1539
+ "telegram": "prohibited",
1540
+ "discord": "prohibited",
1541
+ "slack": "prohibited",
1542
+ "mcp": "generic_safe",
1543
+ "acp": "generic_safe",
1544
+ "daemonCli": "generic_safe"
1545
+ },
1546
+ "testIds": [
1547
+ "packages/coding-agent/test/sdk-operation-inventory.test.ts"
1548
+ ]
1549
+ },
1514
1550
  {
1515
1551
  "sourceId": "registry:R01",
1516
1552
  "sourceFile": "packages/coding-agent/src/sdk/protocol/operation-registry.ts",
@@ -2294,6 +2330,17 @@
2294
2330
  "testIds": "not_applicable"
2295
2331
  }
2296
2332
  },
2333
+ {
2334
+ "sourceId": "agent_session:getDeepInterviewAskStage",
2335
+ "sourceFile": "packages/coding-agent/src/session/agent-session.ts",
2336
+ "sourceKind": "agent_session",
2337
+ "decision": "exclude",
2338
+ "rationale": "internal AskTool schema-selection accessor, not a user-facing SDK control seam",
2339
+ "exclusionMetadata": {
2340
+ "adapterMappings": "not_applicable",
2341
+ "testIds": "not_applicable"
2342
+ }
2343
+ },
2297
2344
  {
2298
2345
  "sourceId": "agent_session:peekQueueInvoker",
2299
2346
  "sourceFile": "packages/coding-agent/src/session/agent-session.ts",
@@ -154,6 +154,11 @@ const queries = [
154
154
  ["resource.body", "Read a bounded resource continuation."],
155
155
  ["artifact.read", "Read a bounded artifact range."],
156
156
  ["runtime.jobs.list", "List managed jobs."],
157
+ [
158
+ "turn.prompt_status",
159
+ "Read the authoritative reconciliation status of a submitted prompt by command/turn IDs or clientRef.",
160
+ ],
161
+ ["models.profiles.list", "List the effective built-in and configured model profiles for this session."],
157
162
  ] as const;
158
163
 
159
164
  const reverse = [
@@ -191,6 +196,7 @@ function controlDisposition(id: string): Record<Adapter, AdapterDisposition> {
191
196
 
192
197
  function controlErrors(id: string): string[] {
193
198
  const errors: Record<string, string[]> = {
199
+ C01: ["client_ref_conflict", "reconciliation_capacity"],
194
200
  C06: ["action_claimed"],
195
201
  C07: ["action_claimed", "terminal_uncertain"],
196
202
  C08: ["action_claimed", "terminal_uncertain"],
@@ -224,14 +230,14 @@ function revision(id: string): string | undefined {
224
230
  }
225
231
  function queryContinuityClass(id: string): QueryContinuityClass {
226
232
  if (["Q01", "Q02"].includes(id)) return "stable_prefix";
227
- if (["Q04", "Q05", "Q06", "Q07", "Q08", "Q11", "Q12", "Q13", "Q20", "Q21", "Q22", "Q23"].includes(id))
233
+ if (["Q04", "Q05", "Q06", "Q07", "Q08", "Q11", "Q12", "Q13", "Q20", "Q21", "Q22", "Q23", "Q27"].includes(id))
228
234
  return "retained_revision";
229
235
  if (id === "Q24") return "content_addressed";
230
236
  return "scalar_snapshot";
231
237
  }
232
238
 
233
239
  function queryDisposition(id: string): Record<Adapter, AdapterDisposition> {
234
- if (["Q23", "Q24", "Q25"].includes(id))
240
+ if (["Q23", "Q24", "Q25", "Q26", "Q27"].includes(id))
235
241
  return dispositions({ telegram: "prohibited", discord: "prohibited", slack: "prohibited" });
236
242
  return dispositions();
237
243
  }
@@ -263,7 +269,12 @@ export const OPERATIONS: readonly Operation[] = [
263
269
  kind: "global" as const,
264
270
  description,
265
271
  idempotency: "idempotent" as const,
266
- errorCodes: id === "G02" ? ["endpoint_credential_forbidden"] : ["invalid_request"],
272
+ errorCodes:
273
+ id === "G02"
274
+ ? ["endpoint_credential_forbidden"]
275
+ : ["G03", "G04", "G05"].includes(id)
276
+ ? ["invalid_request", "unknown_model_profile", "model_profile_registry_error"]
277
+ : ["invalid_request"],
267
278
  adapterDispositions:
268
279
  id === "G02"
269
280
  ? dispositions({
@@ -286,7 +297,10 @@ export const OPERATIONS: readonly Operation[] = [
286
297
  kind: "query" as const,
287
298
  description,
288
299
  idempotency: "idempotent" as const,
289
- errorCodes: ["invalid_request", "resource_gone"],
300
+ errorCodes:
301
+ id === "Q27"
302
+ ? ["invalid_request", "resource_gone", "model_profile_registry_error"]
303
+ : ["invalid_request", "resource_gone"],
290
304
  continuityClass: queryContinuityClass(id),
291
305
  adapterDispositions: queryDisposition(id),
292
306
  testIds: ["packages/coding-agent/test/sdk-operation-inventory.test.ts"],
@@ -38,7 +38,11 @@ export type ResolveManagedSessionScopeResult =
38
38
  | "network_unsupported"
39
39
  | "sessions_root_unavailable"
40
40
  | "binding_conflict"
41
- | "binding_invalid";
41
+ | "binding_invalid"
42
+ | "atomic_unavailable"
43
+ | "durability_not_provable"
44
+ | "durability_failed"
45
+ | "invalid_request";
42
46
  message: string;
43
47
  };
44
48
 
@@ -1170,6 +1170,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1170
1170
  imageProvider === "gemini" ||
1171
1171
  imageProvider === "openrouter" ||
1172
1172
  imageProvider === "antigravity" ||
1173
+ imageProvider === "alibaba" ||
1173
1174
  imageProvider === "custom"
1174
1175
  ) {
1175
1176
  setPreferredImageProvider(imageProvider === "custom" ? "auto" : imageProvider);
@@ -1523,6 +1524,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1523
1524
  isManagedSessionDestination: () => sessionManager.isManagedDestination(),
1524
1525
  getActiveSkillState: () => session?.getActiveSkillState(),
1525
1526
  getActiveSkillPhase: () => session?.getActiveSkillPhase(),
1527
+ getDeepInterviewAskStage: () => session?.getDeepInterviewAskStage(),
1526
1528
  getHindsightSessionState: () => session?.getHindsightSessionState(),
1527
1529
  get model() {
1528
1530
  return agent?.state.model ?? model;
@@ -1,3 +1,8 @@
1
+ import {
2
+ isModelProfileError,
3
+ type ModelProfileErrorCode,
4
+ type ModelProfileErrorDetails,
5
+ } from "../config/model-profile-contract";
1
6
  export type SdkStartupPhase = "registration" | "startup";
2
7
 
3
8
  export type SdkStartupReason = "disabled" | "ineligible" | "factory_absent" | "runner_absent" | "pending" | "failed";
@@ -6,6 +11,8 @@ export interface SdkStartupFailure {
6
11
  phase: SdkStartupPhase;
7
12
  reason: SdkStartupReason;
8
13
  message: string;
14
+ code?: ModelProfileErrorCode;
15
+ details?: ModelProfileErrorDetails;
9
16
  }
10
17
 
11
18
  export type SdkStartupResult = { status: "started" } | { status: "failed"; failure: SdkStartupFailure };
@@ -134,7 +141,8 @@ export function normalizeSdkStartupFailure(
134
141
  ? "SDK startup did not complete before readiness cutoff."
135
142
  : FALLBACK_MESSAGE;
136
143
  const message = sanitizeSdkStartupMessage(error, knownSecrets);
137
- return { phase, reason, message: message === FALLBACK_MESSAGE ? fallback : message };
144
+ const profileError = isModelProfileError(error) ? { code: error.code, details: error.details } : {};
145
+ return { phase, reason, message: message === FALLBACK_MESSAGE ? fallback : message, ...profileError };
138
146
  }
139
147
 
140
148
  /** Collect process-scoped credentials without exposing a raw-secret API. */