@cortexkit/opencode-magic-context 0.32.3 → 0.32.4

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.
package/dist/index.js CHANGED
@@ -16455,6 +16455,9 @@ function modelBodyField(spec) {
16455
16455
  }
16456
16456
 
16457
16457
  // src/shared/model-suggestion-retry.ts
16458
+ function copyPromptArgs(args, body) {
16459
+ return { ...args, body: { ...body } };
16460
+ }
16458
16461
  function extractMessage(error51) {
16459
16462
  if (typeof error51 === "string")
16460
16463
  return error51;
@@ -16568,40 +16571,41 @@ function shortErr(error51) {
16568
16571
  return extractMessage(error51);
16569
16572
  }
16570
16573
  async function attemptOnce(client, args, timeoutMs, signal, callContext, label) {
16574
+ const originalBody = { ...args.body };
16575
+ const attemptArgs = copyPromptArgs(args, originalBody);
16571
16576
  try {
16572
- await promptWithTimeout(client, args, timeoutMs, signal);
16577
+ await promptWithTimeout(client, attemptArgs, timeoutMs, signal);
16573
16578
  return;
16574
16579
  } catch (error51) {
16575
16580
  if (isNonRetryable(error51, signal))
16576
16581
  throw error51;
16577
16582
  const suggestion = parseModelSuggestion(error51);
16578
- if (!suggestion || !args.body.model) {
16583
+ if (!suggestion || !originalBody.model) {
16579
16584
  throw error51;
16580
16585
  }
16581
16586
  log(`[${callContext}] ${label}: model not found, retrying with suggestion`, {
16582
16587
  original: `${suggestion.providerID}/${suggestion.modelID}`,
16583
16588
  suggested: suggestion.suggestion
16584
16589
  });
16585
- await promptWithTimeout(client, {
16586
- ...args,
16587
- body: {
16588
- ...args.body,
16589
- model: {
16590
- providerID: suggestion.providerID,
16591
- modelID: suggestion.suggestion
16592
- }
16590
+ await promptWithTimeout(client, copyPromptArgs(args, {
16591
+ ...originalBody,
16592
+ model: {
16593
+ providerID: suggestion.providerID,
16594
+ modelID: suggestion.suggestion
16593
16595
  }
16594
- }, timeoutMs, signal);
16596
+ }), timeoutMs, signal);
16595
16597
  }
16596
16598
  }
16597
16599
  async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
16598
16600
  const timeoutMs = options.timeoutMs ?? 300000;
16599
16601
  const callContext = options.callContext ?? "subagent";
16600
16602
  const fallbacks = options.fallbackModels ?? [];
16601
- const explicitPrimaryLabel = args.body.model?.providerID && args.body.model.modelID ? `${args.body.model.providerID}/${args.body.model.modelID}` : "primary";
16603
+ const baseBody = { ...args.body };
16604
+ const baseArgs = copyPromptArgs(args, baseBody);
16605
+ const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
16602
16606
  let lastError = null;
16603
16607
  try {
16604
- await attemptOnce(client, args, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
16608
+ await attemptOnce(client, baseArgs, timeoutMs, options.signal, callContext, explicitPrimaryLabel);
16605
16609
  return;
16606
16610
  } catch (error51) {
16607
16611
  lastError = error51;
@@ -16619,10 +16623,10 @@ async function promptSyncWithModelSuggestionRetry(client, args, options = {}) {
16619
16623
  continue;
16620
16624
  }
16621
16625
  const label = `${parsed.providerID}/${parsed.modelID}`;
16622
- const attemptArgs = {
16623
- ...args,
16624
- body: { ...args.body, model: parsed }
16625
- };
16626
+ const attemptArgs = copyPromptArgs(baseArgs, {
16627
+ ...baseBody,
16628
+ model: parsed
16629
+ });
16626
16630
  try {
16627
16631
  await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
16628
16632
  log(`[${callContext}] fallback succeeded with ${label} (attempt ${i + 2}/${fallbacks.length + 1})`);
@@ -16650,17 +16654,19 @@ async function promptSyncWithValidatedOutputRetry(client, args, options) {
16650
16654
  const timeoutMs = options.timeoutMs ?? 300000;
16651
16655
  const callContext = options.callContext ?? "subagent";
16652
16656
  const fallbacks = options.fallbackModels ?? [];
16653
- const explicitPrimaryLabel = args.body.model?.providerID && args.body.model.modelID ? `${args.body.model.providerID}/${args.body.model.modelID}` : "primary";
16657
+ const baseBody = { ...args.body };
16658
+ const baseArgs = copyPromptArgs(args, baseBody);
16659
+ const explicitPrimaryLabel = baseBody.model?.providerID && baseBody.model.modelID ? `${baseBody.model.providerID}/${baseBody.model.modelID}` : "primary";
16654
16660
  const totalAttempts = fallbacks.length + 1;
16655
16661
  let firstError = null;
16656
16662
  let lastError = null;
16657
16663
  try {
16658
- return await attemptAndValidate(client, args, timeoutMs, options.signal, callContext, {
16664
+ return await attemptAndValidate(client, baseArgs, timeoutMs, options.signal, callContext, {
16659
16665
  label: explicitPrimaryLabel,
16660
16666
  attemptIndex: 0,
16661
16667
  isFallback: false,
16662
16668
  totalAttempts,
16663
- model: args.body.model
16669
+ model: baseBody.model
16664
16670
  }, options);
16665
16671
  } catch (error51) {
16666
16672
  firstError = error51;
@@ -16679,10 +16685,10 @@ async function promptSyncWithValidatedOutputRetry(client, args, options) {
16679
16685
  continue;
16680
16686
  }
16681
16687
  const label = `${parsed.providerID}/${parsed.modelID}`;
16682
- const attemptArgs = {
16683
- ...args,
16684
- body: { ...args.body, model: parsed }
16685
- };
16688
+ const attemptArgs = copyPromptArgs(baseArgs, {
16689
+ ...baseBody,
16690
+ model: parsed
16691
+ });
16686
16692
  const attempt = {
16687
16693
  label,
16688
16694
  attemptIndex: i + 1,
@@ -1 +1 @@
1
- {"version":3,"file":"model-suggestion-retry.d.ts","sourceRoot":"","sources":["../../src/shared/model-suggestion-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAM7D,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAOtD,MAAM,MAAM,UAAU,GAAG;IACrB,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAC9B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,WAAW,kBAAkB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAE,SAAQ,kBAAkB;IACxF;;;;OAIG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF;;;OAGG;IACH,cAAc,EAAE,CACZ,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,iBAAiB,KACzB,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B,CAAC,OAAO,EAAE,UAAU;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,iBAAiB,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACtB;AAiBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CA4C/E;AAqKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kCAAkC,CACpD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAgFf;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAsB,kCAAkC,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,EAClF,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAC,GAC1D,OAAO,CAAC,0BAA0B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAgG1D"}
1
+ {"version":3,"file":"model-suggestion-retry.d.ts","sourceRoot":"","sources":["../../src/shared/model-suggestion-retry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAM7D,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAOtD,MAAM,MAAM,UAAU,GAAG;IACrB,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1B,CAAC;AAWF,MAAM,WAAW,iBAAiB;IAC9B,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,UAAU,EAAE,OAAO,CAAC;IACpB,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,KAAK,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACnD;AAED,MAAM,WAAW,kBAAkB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wFAAwF;IACxF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAE,SAAQ,kBAAkB;IACxF;;;;OAIG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChF;;;OAGG;IACH,cAAc,EAAE,CACZ,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,iBAAiB,KACzB,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B,CAAC,OAAO,EAAE,UAAU;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,UAAU,CAAC;IACtB,OAAO,EAAE,iBAAiB,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACtB;AAiBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,GAAG,IAAI,CA4C/E;AAuKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kCAAkC,CACpD,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,IAAI,CAAC,CAqFf;AAiBD;;;;;;;;;;;GAWG;AACH,wBAAsB,kCAAkC,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,EAClF,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,2BAA2B,CAAC,OAAO,EAAE,UAAU,CAAC,GAC1D,OAAO,CAAC,0BAA0B,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAqG1D"}
@@ -156,6 +156,7 @@ export type SubagentProgressEvent = {
156
156
  * reported as `ok: false, reason: "no_assistant"` so callers can try fallback
157
157
  * models instead of accepting an unusable success.
158
158
  * - `reason`: failure category, one of:
159
+ * - `"invalid_prompt"`: a known zero-tool child was given no system prompt
159
160
  * - `"timeout"`: hit `timeoutMs` before the child finished
160
161
  * - `"abort"`: caller's `signal` was triggered
161
162
  * - `"model_failed"`: every configured model + fallback returned an error
@@ -188,9 +189,11 @@ export type SubagentRunResult = {
188
189
  meta?: Record<string, unknown>;
189
190
  } | {
190
191
  ok: false;
191
- reason: "timeout" | "abort" | "model_failed" | "truncated" | "spawn_failed" | "non_zero_exit" | "no_assistant" | "parse_failed";
192
+ reason: "invalid_prompt" | "timeout" | "abort" | "model_failed" | "truncated" | "spawn_failed" | "non_zero_exit" | "no_assistant" | "parse_failed";
192
193
  error: string;
193
194
  durationMs: number;
195
+ /** True when the caller should retry the task rather than advance its schedule. */
196
+ transient?: boolean;
194
197
  meta?: Record<string, unknown>;
195
198
  };
196
199
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-runner.d.ts","sourceRoot":"","sources":["../../src/shared/subagent-runner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subagent-runner.d.ts","sourceRoot":"","sources":["../../src/shared/subagent-runner.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/opencode-magic-context",
3
- "version": "0.32.3",
3
+ "version": "0.32.4",
4
4
  "type": "module",
5
5
  "description": "OpenCode plugin for Magic Context — cross-session memory and context management",
6
6
  "main": "dist/index.js",
@@ -6,7 +6,11 @@ import {
6
6
  } from "./model-suggestion-retry";
7
7
 
8
8
  type PromptCall = {
9
- body: { model?: { providerID: string; modelID: string } };
9
+ body: {
10
+ model?: { providerID: string; modelID: string };
11
+ system?: string;
12
+ [key: string]: unknown;
13
+ };
10
14
  signal?: AbortSignal;
11
15
  };
12
16
 
@@ -338,6 +342,42 @@ describe("promptSyncWithValidatedOutputRetry", () => {
338
342
  expect(messages).toHaveBeenCalledTimes(1);
339
343
  });
340
344
 
345
+ test("preserves body.system when a failed Pi-shaped attempt mutates its body", async () => {
346
+ const prompt = mock(async (args: PromptCall) => {
347
+ if (prompt.mock.calls.length === 1) {
348
+ // Reproduce a facade/SDK that consumes the request body before
349
+ // rejecting the primary model. The fallback must not inherit
350
+ // that mutation and spawn without its task prompt.
351
+ delete args.body.system;
352
+ throw new Error("primary failed");
353
+ }
354
+ return {};
355
+ });
356
+ const client = createClient(prompt);
357
+ const systemPrompt = "CLASSIFY_SYSTEM_PROMPT";
358
+
359
+ await promptSyncWithValidatedOutputRetry(
360
+ client,
361
+ {
362
+ path: { id: "ses-classify" },
363
+ body: {
364
+ agent: "dreamer-classifier",
365
+ system: systemPrompt,
366
+ parts: [{ type: "text", text: "classify" }],
367
+ },
368
+ },
369
+ {
370
+ fallbackModels: ["anthropic/claude-sonnet-4-6"],
371
+ fetchOutput: async () => "fallback-output",
372
+ validateOutput: (output: string) => output,
373
+ },
374
+ );
375
+
376
+ expect(prompt).toHaveBeenCalledTimes(2);
377
+ expect((prompt.mock.calls[0]?.[0] as PromptCall).body.system).toBeUndefined();
378
+ expect((prompt.mock.calls[1]?.[0] as PromptCall).body.system).toBe(systemPrompt);
379
+ });
380
+
341
381
  test("empty first model tries the next fallback", async () => {
342
382
  const prompt = mock(async () => ({}));
343
383
  const messages = mock(async () =>
@@ -23,6 +23,15 @@ export type PromptArgs = {
23
23
  [key: string]: unknown;
24
24
  };
25
25
 
26
+ /**
27
+ * Keep a prompt body independent from SDK/client mutation. Some prompt facades
28
+ * normalize or consume request bodies while handling a failed attempt; fallback
29
+ * attempts must start from the original system prompt and request body every time.
30
+ */
31
+ function copyPromptArgs(args: PromptArgs, body: PromptBody): PromptArgs {
32
+ return { ...args, body: { ...body } };
33
+ }
34
+
26
35
  export interface PromptAttemptInfo {
27
36
  /** Human-readable model label used in logs ("primary" or "provider/model"). */
28
37
  label: string;
@@ -280,8 +289,13 @@ async function attemptOnce(
280
289
  callContext: string,
281
290
  label: string,
282
291
  ): Promise<void> {
292
+ // Keep this snapshot separate from the object passed to the client. A
293
+ // failed prompt facade may rewrite its request body before rejecting; the
294
+ // model-suggestion retry still needs the original system prompt.
295
+ const originalBody = { ...args.body };
296
+ const attemptArgs = copyPromptArgs(args, originalBody);
283
297
  try {
284
- await promptWithTimeout(client, args, timeoutMs, signal);
298
+ await promptWithTimeout(client, attemptArgs, timeoutMs, signal);
285
299
  return;
286
300
  } catch (error) {
287
301
  // If non-retryable (abort, overflow, timeout), bubble up immediately.
@@ -289,7 +303,7 @@ async function attemptOnce(
289
303
  if (isNonRetryable(error, signal)) throw error;
290
304
 
291
305
  const suggestion = parseModelSuggestion(error);
292
- if (!suggestion || !args.body.model) {
306
+ if (!suggestion || !originalBody.model) {
293
307
  // No suggestion available — caller's fallback loop will decide
294
308
  // whether to try the next chain entry.
295
309
  throw error;
@@ -302,16 +316,13 @@ async function attemptOnce(
302
316
 
303
317
  await promptWithTimeout(
304
318
  client,
305
- {
306
- ...args,
307
- body: {
308
- ...args.body,
309
- model: {
310
- providerID: suggestion.providerID,
311
- modelID: suggestion.suggestion,
312
- },
319
+ copyPromptArgs(args, {
320
+ ...originalBody,
321
+ model: {
322
+ providerID: suggestion.providerID,
323
+ modelID: suggestion.suggestion,
313
324
  },
314
- },
325
+ }),
315
326
  timeoutMs,
316
327
  signal,
317
328
  );
@@ -340,12 +351,17 @@ export async function promptSyncWithModelSuggestionRetry(
340
351
  const timeoutMs = options.timeoutMs ?? 300_000;
341
352
  const callContext = options.callContext ?? "subagent";
342
353
  const fallbacks = options.fallbackModels ?? [];
354
+ // Snapshot the body before the first client call. The Pi facade and the
355
+ // OpenCode SDK both receive this same shape, and either may mutate it while
356
+ // handling a failed request. Fallbacks must never inherit that mutation.
357
+ const baseBody = { ...args.body };
358
+ const baseArgs = copyPromptArgs(args, baseBody);
343
359
 
344
360
  // Attempt 0 = whatever the agent or explicit body.model resolves to.
345
361
  // Subsequent attempts override body.model with each fallback in order.
346
362
  const explicitPrimaryLabel =
347
- args.body.model?.providerID && args.body.model.modelID
348
- ? `${args.body.model.providerID}/${args.body.model.modelID}`
363
+ baseBody.model?.providerID && baseBody.model.modelID
364
+ ? `${baseBody.model.providerID}/${baseBody.model.modelID}`
349
365
  : "primary";
350
366
 
351
367
  let lastError: unknown = null;
@@ -353,7 +369,7 @@ export async function promptSyncWithModelSuggestionRetry(
353
369
  try {
354
370
  await attemptOnce(
355
371
  client,
356
- args,
372
+ baseArgs,
357
373
  timeoutMs,
358
374
  options.signal,
359
375
  callContext,
@@ -385,10 +401,10 @@ export async function promptSyncWithModelSuggestionRetry(
385
401
  }
386
402
 
387
403
  const label = `${parsed.providerID}/${parsed.modelID}`;
388
- const attemptArgs: PromptArgs = {
389
- ...args,
390
- body: { ...args.body, model: parsed },
391
- };
404
+ const attemptArgs = copyPromptArgs(baseArgs, {
405
+ ...baseBody,
406
+ model: parsed,
407
+ });
392
408
 
393
409
  try {
394
410
  await attemptOnce(client, attemptArgs, timeoutMs, options.signal, callContext, label);
@@ -453,10 +469,15 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
453
469
  const timeoutMs = options.timeoutMs ?? 300_000;
454
470
  const callContext = options.callContext ?? "subagent";
455
471
  const fallbacks = options.fallbackModels ?? [];
472
+ // Snapshot the body before the first client call. The Pi facade and the
473
+ // OpenCode SDK both receive this same shape, and either may mutate it while
474
+ // handling a failed request. Fallbacks must never inherit that mutation.
475
+ const baseBody = { ...args.body };
476
+ const baseArgs = copyPromptArgs(args, baseBody);
456
477
 
457
478
  const explicitPrimaryLabel =
458
- args.body.model?.providerID && args.body.model.modelID
459
- ? `${args.body.model.providerID}/${args.body.model.modelID}`
479
+ baseBody.model?.providerID && baseBody.model.modelID
480
+ ? `${baseBody.model.providerID}/${baseBody.model.modelID}`
460
481
  : "primary";
461
482
  const totalAttempts = fallbacks.length + 1;
462
483
 
@@ -466,7 +487,7 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
466
487
  try {
467
488
  return await attemptAndValidate(
468
489
  client,
469
- args,
490
+ baseArgs,
470
491
  timeoutMs,
471
492
  options.signal,
472
493
  callContext,
@@ -475,7 +496,7 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
475
496
  attemptIndex: 0,
476
497
  isFallback: false,
477
498
  totalAttempts,
478
- model: args.body.model,
499
+ model: baseBody.model,
479
500
  },
480
501
  options,
481
502
  );
@@ -501,10 +522,10 @@ export async function promptSyncWithValidatedOutputRetry<TOutput, TValidated = T
501
522
  }
502
523
 
503
524
  const label = `${parsed.providerID}/${parsed.modelID}`;
504
- const attemptArgs: PromptArgs = {
505
- ...args,
506
- body: { ...args.body, model: parsed },
507
- };
525
+ const attemptArgs = copyPromptArgs(baseArgs, {
526
+ ...baseBody,
527
+ model: parsed,
528
+ });
508
529
  const attempt: PromptAttemptInfo = {
509
530
  label,
510
531
  attemptIndex: i + 1,
@@ -159,6 +159,7 @@ export type SubagentProgressEvent =
159
159
  * reported as `ok: false, reason: "no_assistant"` so callers can try fallback
160
160
  * models instead of accepting an unusable success.
161
161
  * - `reason`: failure category, one of:
162
+ * - `"invalid_prompt"`: a known zero-tool child was given no system prompt
162
163
  * - `"timeout"`: hit `timeoutMs` before the child finished
163
164
  * - `"abort"`: caller's `signal` was triggered
164
165
  * - `"model_failed"`: every configured model + fallback returned an error
@@ -194,6 +195,7 @@ export type SubagentRunResult =
194
195
  | {
195
196
  ok: false;
196
197
  reason:
198
+ | "invalid_prompt"
197
199
  | "timeout"
198
200
  | "abort"
199
201
  | "model_failed"
@@ -204,6 +206,8 @@ export type SubagentRunResult =
204
206
  | "parse_failed";
205
207
  error: string;
206
208
  durationMs: number;
209
+ /** True when the caller should retry the task rather than advance its schedule. */
210
+ transient?: boolean;
207
211
  meta?: Record<string, unknown>;
208
212
  };
209
213