@inflexa-ai/harness 0.39.2 → 0.40.0

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 (184) hide show
  1. package/README.md +2 -3
  2. package/dist/agents/conversation-agent.d.ts +3 -0
  3. package/dist/agents/conversation-agent.js +13 -2
  4. package/dist/agents/report-session-agent.d.ts +3 -0
  5. package/dist/agents/report-session-agent.js +2 -0
  6. package/dist/agents/sandbox/cheminformatics-agent.js +0 -3
  7. package/dist/agents/sandbox/shared.d.ts +11 -2
  8. package/dist/agents/sandbox/shared.js +4 -0
  9. package/dist/app/chat-turn.d.ts +75 -12
  10. package/dist/app/chat-turn.js +149 -21
  11. package/dist/app/message-assembly.d.ts +16 -52
  12. package/dist/app/message-assembly.js +41 -68
  13. package/dist/app/run-activity.d.ts +4 -6
  14. package/dist/app/run-activity.js +7 -27
  15. package/dist/app/spawn-report-session.d.ts +6 -11
  16. package/dist/app/spawn-report-session.js +5 -6
  17. package/dist/app/synthesize-run.d.ts +3 -0
  18. package/dist/app/synthesize-run.js +1 -0
  19. package/dist/billing/usage-recorder.d.ts +3 -4
  20. package/dist/billing/usage-recorder.js +3 -4
  21. package/dist/contracts/chat-parts.d.ts +11 -1
  22. package/dist/contracts/chat-parts.js +1 -0
  23. package/dist/contracts/index.d.ts +1 -1
  24. package/dist/contracts/part-registry.js +1 -0
  25. package/dist/contracts/report-blocks.d.ts +8 -8
  26. package/dist/contracts/schemas/chat-parts.d.ts +32 -9
  27. package/dist/contracts/schemas/chat-parts.js +10 -0
  28. package/dist/execution/artifact-metadata.d.ts +26 -33
  29. package/dist/execution/artifact-metadata.js +44 -106
  30. package/dist/execution/post-step-pipeline.d.ts +23 -16
  31. package/dist/execution/post-step-pipeline.js +19 -21
  32. package/dist/execution/run-synthesis.d.ts +3 -0
  33. package/dist/execution/run-synthesis.js +11 -1
  34. package/dist/execution/step-summary.d.ts +21 -30
  35. package/dist/execution/step-summary.js +30 -57
  36. package/dist/index.d.ts +9 -8
  37. package/dist/index.js +15 -34
  38. package/dist/lib/zod-issues.js +2 -4
  39. package/dist/loop/compaction.d.ts +20 -0
  40. package/dist/loop/compaction.js +2 -0
  41. package/dist/loop/continue-agent.d.ts +40 -0
  42. package/dist/loop/continue-agent.js +38 -0
  43. package/dist/loop/metrics.d.ts +30 -22
  44. package/dist/loop/metrics.js +51 -22
  45. package/dist/loop/run-agent.d.ts +111 -13
  46. package/dist/loop/run-agent.js +467 -220
  47. package/dist/loop/run-to-terminal.d.ts +16 -17
  48. package/dist/loop/run-to-terminal.js +24 -35
  49. package/dist/loop/tool-mask.d.ts +23 -0
  50. package/dist/loop/tool-mask.js +46 -0
  51. package/dist/loop/tool-output.d.ts +48 -0
  52. package/dist/loop/tool-output.js +79 -0
  53. package/dist/loop/types.d.ts +6 -3
  54. package/dist/memory/ai-sdk-message-storage.d.ts +49 -2
  55. package/dist/memory/ai-sdk-message-storage.js +78 -2
  56. package/dist/memory/conversation-display-recorder.d.ts +6 -0
  57. package/dist/memory/conversation-display-recorder.js +64 -22
  58. package/dist/memory/conversation-display-replay.d.ts +5 -14
  59. package/dist/memory/conversation-display-replay.js +66 -32
  60. package/dist/memory/conversation-display-storage.d.ts +2 -1
  61. package/dist/memory/conversation-display-storage.js +2 -1
  62. package/dist/memory/conversation-view.d.ts +53 -0
  63. package/dist/memory/conversation-view.js +130 -0
  64. package/dist/memory/count-tokens.d.ts +5 -7
  65. package/dist/memory/count-tokens.js +7 -9
  66. package/dist/memory/thread-history.d.ts +74 -102
  67. package/dist/memory/thread-history.js +181 -246
  68. package/dist/memory/thread-store.d.ts +3 -3
  69. package/dist/memory/thread-store.js +7 -2
  70. package/dist/memory/tool-call-integrity.d.ts +15 -21
  71. package/dist/memory/tool-call-integrity.js +31 -38
  72. package/dist/memory/working-memory.d.ts +2 -5
  73. package/dist/memory/working-memory.js +2 -5
  74. package/dist/prompts/SOUL.d.ts +2 -2
  75. package/dist/prompts/SOUL.js +2 -7
  76. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  77. package/dist/prompts/analogical-reasoner.js +16 -32
  78. package/dist/prompts/briefing.js +1 -1
  79. package/dist/prompts/compaction.d.ts +4 -0
  80. package/dist/prompts/compaction.js +24 -0
  81. package/dist/prompts/conversation.d.ts +1 -1
  82. package/dist/prompts/conversation.js +26 -35
  83. package/dist/prompts/execute-analysis/step-summary.js +7 -5
  84. package/dist/prompts/literature-reviewer.d.ts +1 -1
  85. package/dist/prompts/literature-reviewer.js +29 -34
  86. package/dist/prompts/planner.js +15 -29
  87. package/dist/prompts/report-session.d.ts +1 -1
  88. package/dist/prompts/report-session.js +2 -33
  89. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  90. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +1 -2
  91. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/cheminformatics-agent.js +10 -29
  93. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/chromatin-agent.js +1 -2
  95. package/dist/prompts/sandbox/data-profiler.js +6 -3
  96. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  97. package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
  98. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  99. package/dist/prompts/sandbox/drug-repurposing-agent.js +20 -24
  100. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  101. package/dist/prompts/sandbox/enrichment-agent.js +3 -2
  102. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  103. package/dist/prompts/sandbox/genomic-variant-agent.js +0 -2
  104. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  105. package/dist/prompts/sandbox/immune-profiling-agent.js +7 -14
  106. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  107. package/dist/prompts/sandbox/metabolomics-agent.js +0 -2
  108. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  109. package/dist/prompts/sandbox/microbiome-agent.js +14 -21
  110. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  111. package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -19
  112. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  113. package/dist/prompts/sandbox/multimodal-sc-agent.js +4 -5
  114. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  115. package/dist/prompts/sandbox/network-agent.js +7 -8
  116. package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
  117. package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +19 -19
  118. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  119. package/dist/prompts/sandbox/proteomics-agent.js +3 -4
  120. package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
  121. package/dist/prompts/sandbox/scientific-executor.js +7 -10
  122. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  123. package/dist/prompts/sandbox/single-cell-agent.js +2 -4
  124. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  125. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -5
  126. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  127. package/dist/prompts/sandbox/statistical-modeling-agent.js +1 -1
  128. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  129. package/dist/prompts/sandbox/translational-safety-agent.js +10 -19
  130. package/dist/prompts/sandbox-standards.d.ts +7 -7
  131. package/dist/prompts/sandbox-standards.js +74 -76
  132. package/dist/prompts/synthesis-agent.d.ts +1 -1
  133. package/dist/prompts/synthesis-agent.js +16 -42
  134. package/dist/providers/ai-sdk.d.ts +39 -6
  135. package/dist/providers/ai-sdk.js +117 -18
  136. package/dist/providers/errors.d.ts +2 -0
  137. package/dist/providers/errors.js +5 -0
  138. package/dist/providers/prompt-cache.d.ts +43 -41
  139. package/dist/providers/prompt-cache.js +47 -41
  140. package/dist/providers/reasoning.d.ts +10 -21
  141. package/dist/providers/reasoning.js +10 -21
  142. package/dist/providers/types.d.ts +21 -5
  143. package/dist/report-model/authoring-grammar.d.ts +2 -2
  144. package/dist/runtime/assemble.d.ts +9 -7
  145. package/dist/runtime/assemble.js +7 -3
  146. package/dist/sandbox/create-sandbox.d.ts +2 -2
  147. package/dist/sandbox/submit-exec.d.ts +1 -1
  148. package/dist/schemas/artifact-manifest.d.ts +4 -4
  149. package/dist/state/init.js +39 -4
  150. package/dist/state/purge-analysis.js +4 -0
  151. package/dist/state/tool-outputs.d.ts +4 -0
  152. package/dist/state/tool-outputs.js +30 -0
  153. package/dist/tasks/data-profile.d.ts +3 -0
  154. package/dist/tasks/data-profile.js +2 -0
  155. package/dist/tools/ad-hoc-router.d.ts +8 -0
  156. package/dist/tools/ad-hoc-router.js +21 -2
  157. package/dist/tools/display/show-user.d.ts +1 -1
  158. package/dist/tools/execute-analysis.d.ts +3 -0
  159. package/dist/tools/execute-analysis.js +9 -1
  160. package/dist/tools/read-tool-output.d.ts +7 -0
  161. package/dist/tools/read-tool-output.js +130 -0
  162. package/dist/tools/research/generate-analogy-report.d.ts +10 -26
  163. package/dist/tools/research/generate-analogy-report.js +76 -163
  164. package/dist/tools/research/generate-plan.d.ts +3 -0
  165. package/dist/tools/research/generate-plan.js +4 -1
  166. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  167. package/dist/tools/research/literature-reviewer.js +3 -0
  168. package/dist/tools/sandbox/report-blocker.d.ts +18 -5
  169. package/dist/tools/sandbox/report-blocker.js +54 -6
  170. package/dist/tools/sandbox/submit-file-metadata.d.ts +30 -0
  171. package/dist/tools/sandbox/submit-file-metadata.js +67 -0
  172. package/dist/tools/start-report-session.d.ts +1 -3
  173. package/dist/tools/start-report-session.js +0 -2
  174. package/dist/tools/workspace/execute-command.js +7 -4
  175. package/dist/tools/workspace/result-bounds.d.ts +4 -4
  176. package/dist/tools/workspace/result-bounds.js +6 -5
  177. package/dist/tools/workspace/workspace-search.d.ts +1 -1
  178. package/dist/workflows/execute-analysis.d.ts +3 -0
  179. package/dist/workflows/execute-analysis.js +1 -0
  180. package/dist/workflows/sandbox-step.d.ts +30 -9
  181. package/dist/workflows/sandbox-step.js +37 -8
  182. package/package.json +9 -9
  183. package/dist/providers/anthropic.d.ts +0 -25
  184. package/dist/providers/anthropic.js +0 -28
@@ -1,5 +1,9 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { streamText, wrapLanguageModel, } from "ai";
2
3
  import { createAnthropic } from "@ai-sdk/anthropic";
4
+ // Any `providerOptions.anthropic.thinking` bypasses this table's thinking
5
+ // selection (see `thinkingWithBinding`).
6
+ import { getModelCapabilities } from "@ai-sdk/anthropic/internal";
3
7
  import { createOpenAI } from "@ai-sdk/openai";
4
8
  import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
5
9
  import { OpenTelemetry } from "@ai-sdk/otel";
@@ -11,6 +15,7 @@ import { scopeWorkloadId } from "../auth/types.js";
11
15
  import { createNoopLogger } from "../lib/console-logger.js";
12
16
  import { passThroughTracer } from "../lib/otel-spans.js";
13
17
  import { DEFAULT_SUSPEND_ON, classifyProviderError, RequestTimeoutError, toProviderError } from "./errors.js";
18
+ import { DEFAULT_REASONING } from "./reasoning.js";
14
19
  import { headersRefusalError, requestHeadersFor } from "./request-headers.js";
15
20
  /**
16
21
  * The harness-owned retry envelope. The AI SDK's built-in retry exposes only a
@@ -25,11 +30,10 @@ export const RETRY_BACKOFF_FACTOR = 2;
25
30
  export const RETRY_MAX_DELAY_MS = 30_000;
26
31
  /**
27
32
  * Output-token ceiling requested when a config names none — a floor against a
28
- * provider default, not a target. `@ai-sdk/anthropic` resolves an unset value
29
- * from a capability table that lags Anthropic's releases and falls back to 4096
30
- * for ids it does not know, so the newest models silently truncate mid
31
- * tool-call. The SDK clamps this down per known model (opus-4-x to 32k,
32
- * claude-3-haiku to 4096), so it cannot over-request one it recognizes.
33
+ * provider default, not a target. For an unset value, `@ai-sdk/anthropic`
34
+ * defaults to 128000 for an unknown `claude-*` id and 4096 for a non-Claude
35
+ * id. The anthropic arm always sends a value, so it never truncates mid
36
+ * tool-call.
33
37
  *
34
38
  * Lower it per-config for servers that validate `prompt + max_tokens <= context`
35
39
  * (vLLM and similar) against a small-context model.
@@ -63,6 +67,50 @@ function routeSdkWarningsTo(logger) {
63
67
  function workloadOf(session) {
64
68
  return `${session.scope.kind}:${scopeWorkloadId(session.scope)}`;
65
69
  }
70
+ /**
71
+ * Identifiers only, never a name or address. The run frame wins over the
72
+ * thread so parallel steps of one run keep separate gateway cache accounts.
73
+ */
74
+ export function sessionKeyOf(session) {
75
+ const { scope, runFrame } = session;
76
+ if (runFrame !== undefined) {
77
+ return runFrame.stepId === undefined ? `${scope.analysisId}:${runFrame.runId}` : `${scope.analysisId}:${runFrame.runId}:${runFrame.stepId}`;
78
+ }
79
+ return scope.threadId === undefined ? scope.analysisId : `${scope.analysisId}:${scope.threadId}`;
80
+ }
81
+ /**
82
+ * Base64url SHA-256 digest of {@link sessionKeyOf}, always 43 characters.
83
+ * OpenAI and Azure reject a `prompt_cache_key` over 64 characters with a
84
+ * non-retryable 400.
85
+ */
86
+ export function sessionKeyDigestOf(session) {
87
+ return createHash("sha256").update(sessionKeyOf(session)).digest("base64url");
88
+ }
89
+ /**
90
+ * CAUTION: setting `providerOptions.anthropic.thinking` without a `type`
91
+ * turns thinking off. Call only for a model whose capability row says
92
+ * `rejectsThinkingDisabled`.
93
+ */
94
+ function thinkingWithBinding(reasoning, mode) {
95
+ const blockBinding = { prefixMismatchBehavior: mode };
96
+ if (reasoning === "none" || reasoning === "provider-default")
97
+ return { blockBinding };
98
+ return { type: "adaptive", display: "summarized", blockBinding };
99
+ }
100
+ /** Reads `providerMetadata.anthropic.inputTransformations`; empty for another vendor. */
101
+ function inputTransformationsOf(metadata) {
102
+ const entries = metadata?.["anthropic"]?.["inputTransformations"];
103
+ if (!Array.isArray(entries))
104
+ return [];
105
+ return entries.map((entry) => {
106
+ const fields = typeof entry === "object" && entry !== null ? entry : {};
107
+ const text = (key) => {
108
+ const value = fields[key];
109
+ return typeof value === "string" ? value : undefined;
110
+ };
111
+ return { type: text("type"), path: text("path"), reason: text("reason") };
112
+ });
113
+ }
66
114
  function isAbortError(value) {
67
115
  if (value instanceof DOMException && value.name === "AbortError")
68
116
  return true;
@@ -252,6 +300,10 @@ function toChatUsage(usage) {
252
300
  function requestedModelIdOf(model) {
253
301
  return typeof model === "string" ? model : model.modelId;
254
302
  }
303
+ /** The provider id of the model bound into this provider, as the AI SDK names it — a bare id string names no provider. */
304
+ function providerIdOf(model) {
305
+ return typeof model === "string" ? undefined : model.provider;
306
+ }
255
307
  /**
256
308
  * Capture the served model id at the raw model boundary, because that is the
257
309
  * only place it is still distinguishable from the requested one: `streamText`
@@ -294,13 +346,13 @@ function captureServedModelId(model) {
294
346
  return { model: wrapped, servedModelId: () => served };
295
347
  }
296
348
  function responseFromMessages(input) {
297
- const { messages, fallbackText, finishReason, rawFinishReason, requestedModelId, servedModelId } = input;
349
+ const { messages, fallbackText, finishReason, rawFinishReason, requestedModelId, servedModelId, provider } = input;
298
350
  const usage = toChatUsage(input.usage);
299
351
  const message = [...messages].reverse().find((m) => m.role === "assistant");
300
352
  if (message === undefined) {
301
- return { message: { role: "assistant", content: fallbackText }, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
353
+ return { message: { role: "assistant", content: fallbackText }, finishReason, rawFinishReason, usage, requestedModelId, servedModelId, provider };
302
354
  }
303
- return { message, finishReason, rawFinishReason, usage, requestedModelId, servedModelId };
355
+ return { message, finishReason, rawFinishReason, usage, requestedModelId, servedModelId, provider };
304
356
  }
305
357
  /**
306
358
  * Drop empty text parts — and messages left with no content at all — before
@@ -435,6 +487,8 @@ export function createAiSdkProvider(deps) {
435
487
  const suspendOn = deps.suspendOn ?? DEFAULT_SUSPEND_ON;
436
488
  const requestTimeoutMs = deps.requestTimeoutMs;
437
489
  const requestedModelId = requestedModelIdOf(deps.model);
490
+ const providerId = providerIdOf(deps.model);
491
+ const effortOf = (req) => req.reasoning ?? deps.reasoning ?? DEFAULT_REASONING;
438
492
  // Each model call emits OpenTelemetry GenAI spans through the AI SDK, with no
439
493
  // prompt or completion text on them. The integration rides on each call
440
494
  // instead of the global registry, so the harness never traces the embedder's
@@ -461,7 +515,18 @@ export function createAiSdkProvider(deps) {
461
515
  ...logger.errorFields(thrown),
462
516
  });
463
517
  }
518
+ /**
519
+ * Logs each dropped thinking block; the drop does not fail the call.
520
+ * `reason` is `prefix_binding_mismatch` or `model_binding_mismatch`.
521
+ */
522
+ function logDroppedThinkingBlocks(session, metadata) {
523
+ for (const drop of inputTransformationsOf(metadata)) {
524
+ logger.warn("thinking block dropped", { workload: workloadOf(session), type: drop.type, path: drop.path, reason: drop.reason });
525
+ }
526
+ }
464
527
  function chat(req, session, signal) {
528
+ const reasoning = effortOf(req);
529
+ const providerOptions = deps.providerOptionsFor?.({ session, reasoning });
465
530
  const run = async () => {
466
531
  const hookCall = { unsettled: false };
467
532
  const retry = createRetry(signal, logger, maxRetries, suspendOn, hookCall);
@@ -504,8 +569,8 @@ export function createAiSdkProvider(deps) {
504
569
  // carries the one configured bound, and a turn that writes
505
570
  // steadily runs as long as it needs.
506
571
  ...(requestTimeoutMs !== undefined ? { timeout: { firstChunkMs: requestTimeoutMs, chunkMs: requestTimeoutMs } } : {}),
507
- providerOptions: req.providerOptions,
508
- reasoning: req.reasoning,
572
+ providerOptions,
573
+ reasoning,
509
574
  });
510
575
  // The drain sits inside the retried closure, thus a failure at any
511
576
  // point of the stream re-runs the whole attempt and the envelope
@@ -530,14 +595,20 @@ export function createAiSdkProvider(deps) {
530
595
  finishReason: await result.finishReason,
531
596
  rawFinishReason: await result.rawFinishReason,
532
597
  usage: await result.usage,
598
+ providerMetadata: (await result.finalStep).providerMetadata,
533
599
  };
534
600
  });
601
+ // Logged past the envelope, thus only the attempt that succeeded
602
+ // reports its drops.
603
+ const { providerMetadata: metadata, ...reply } = collected;
604
+ logDroppedThinkingBlocks(session, metadata);
535
605
  return ok(responseFromMessages({
536
- ...collected,
606
+ ...reply,
537
607
  requestedModelId,
538
608
  // Read after the drain: the metadata chunk that carries the
539
609
  // served id reaches the capture only as the stream is consumed.
540
610
  servedModelId: capture.servedModelId(),
611
+ provider: providerId,
541
612
  }));
542
613
  }
543
614
  catch (e) {
@@ -551,6 +622,8 @@ export function createAiSdkProvider(deps) {
551
622
  return new ResultAsync(run());
552
623
  }
553
624
  async function* chatStream(req, session, signal) {
625
+ const reasoning = effortOf(req);
626
+ const providerOptions = deps.providerOptionsFor?.({ session, reasoning });
554
627
  const hookCall = { unsettled: false };
555
628
  const retry = createRetry(signal, logger, maxRetries, suspendOn, hookCall);
556
629
  const capture = captureServedModelId(deps.model);
@@ -583,8 +656,8 @@ export function createAiSdkProvider(deps) {
583
656
  // no content, for example a keep-alive comment, produces no part,
584
657
  // thus it feeds neither bound.
585
658
  ...(requestTimeoutMs !== undefined ? { timeout: { firstChunkMs: requestTimeoutMs, chunkMs: requestTimeoutMs } } : {}),
586
- providerOptions: req.providerOptions,
587
- reasoning: req.reasoning,
659
+ providerOptions,
660
+ reasoning,
588
661
  });
589
662
  const iterator = result.fullStream[Symbol.asyncIterator]();
590
663
  const first = await pullNextDelta(iterator);
@@ -612,11 +685,13 @@ export function createAiSdkProvider(deps) {
612
685
  finishReason: await result.finishReason,
613
686
  rawFinishReason: await result.rawFinishReason,
614
687
  usage: await result.usage,
688
+ providerMetadata: (await result.finalStep).providerMetadata,
615
689
  };
616
690
  }
617
691
  return { kind: "streaming", result, iterator, firstDelta: first.text };
618
692
  });
619
693
  if (opened.kind === "completed") {
694
+ logDroppedThinkingBlocks(session, opened.providerMetadata);
620
695
  const response = responseFromMessages({
621
696
  messages: opened.messages,
622
697
  fallbackText: "",
@@ -625,6 +700,7 @@ export function createAiSdkProvider(deps) {
625
700
  usage: opened.usage,
626
701
  requestedModelId,
627
702
  servedModelId: capture.servedModelId(),
703
+ provider: providerId,
628
704
  });
629
705
  yield { type: "done", response };
630
706
  return;
@@ -655,7 +731,9 @@ export function createAiSdkProvider(deps) {
655
731
  // Read after the drain: the metadata chunk carrying the served id
656
732
  // reaches the capture only as the stream is consumed.
657
733
  servedModelId: capture.servedModelId(),
734
+ provider: providerId,
658
735
  });
736
+ logDroppedThinkingBlocks(session, (await result.finalStep).providerMetadata);
659
737
  yield { type: "done", response };
660
738
  }
661
739
  catch (e) {
@@ -729,11 +807,10 @@ export function wrapFetchWithRequestTimeout(fetchImpl, requestTimeoutMs) {
729
807
  *
730
808
  * The middleware merges the value into the `openai` namespace of
731
809
  * `providerOptions`. It keeps each other namespace, and it keeps each other key
732
- * of the `openai` namespace, thus a cache directive of a different vendor rides
733
- * through untouched. The arm value wins over a request-level `store`, because
734
- * the retention mode belongs to the connection and not to one turn. A thread
735
- * that mixes the two modes replays a reference onto an item that the server
736
- * never stored.
810
+ * of the `openai` namespace, thus the session key of the call
811
+ * (`promptCacheKey`) rides through untouched. The retention mode belongs to the
812
+ * connection and not to one turn: a thread that mixes the two modes replays a
813
+ * reference onto an item that the server never stored.
737
814
  */
738
815
  function withStoreDirective(model, store) {
739
816
  return wrapLanguageModel({
@@ -783,6 +860,9 @@ export function createConfiguredAiSdkProvider(deps) {
783
860
  // must declare the capability. A config value overrides this default in
784
861
  // both directions.
785
862
  const pictureDefault = config.baseURL === undefined ? { imageToolResults: true } : {};
863
+ // A model that can run without thinking gets no binding (see `thinkingWithBinding`).
864
+ const bindingMode = config.thinkingBinding ?? "drop_block";
865
+ const binding = bindingMode !== "off" && getModelCapabilities(config.model).rejectsThinkingDisabled ? bindingMode : undefined;
786
866
  return createAiSdkProvider({
787
867
  model: provider.chat(config.model),
788
868
  ...hostPolicyOf(deps),
@@ -791,6 +871,15 @@ export function createConfiguredAiSdkProvider(deps) {
791
871
  ...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
792
872
  ...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
793
873
  ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
874
+ ...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
875
+ // The package sends the key as `metadata.user_id`, and the binding as
876
+ // `thinking.block_binding`.
877
+ providerOptionsFor: ({ session, reasoning }) => ({
878
+ anthropic: {
879
+ metadata: { userId: sessionKeyDigestOf(session) },
880
+ ...(binding !== undefined ? { thinking: thinkingWithBinding(reasoning, binding) } : {}),
881
+ },
882
+ }),
794
883
  });
795
884
  }
796
885
  if (config.kind === "openai") {
@@ -823,6 +912,10 @@ export function createConfiguredAiSdkProvider(deps) {
823
912
  maxOutputTokens: config.maxOutputTokens ?? "provider-maximum",
824
913
  ...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
825
914
  ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
915
+ ...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
916
+ // The package sends the key as `prompt_cache_key`. The store
917
+ // directive merges into the same namespace, and it keeps this key.
918
+ providerOptionsFor: ({ session }) => ({ openai: { promptCacheKey: sessionKeyDigestOf(session) } }),
826
919
  });
827
920
  }
828
921
  const provider = createOpenAICompatible({
@@ -830,7 +923,12 @@ export function createConfiguredAiSdkProvider(deps) {
830
923
  baseURL: config.baseURL,
831
924
  apiKey: config.apiKey,
832
925
  fetch: effectiveFetch,
926
+ // Each call of the provider streams, `chat` included. Without this flag
927
+ // the package asks for no usage on a stream, and the call reports none.
928
+ includeUsage: true,
833
929
  });
930
+ // The compatible wire has no standard field for a session key, thus this arm
931
+ // gives no provider options of its own.
834
932
  return createAiSdkProvider({
835
933
  // The compatible package reads the neutral `reasoning` of the call, thus
836
934
  // the seam needs no middleware to carry the depth into its namespace.
@@ -841,5 +939,6 @@ export function createConfiguredAiSdkProvider(deps) {
841
939
  ...(config.maxOutputTokens !== undefined ? { maxOutputTokens: config.maxOutputTokens } : {}),
842
940
  ...(config.maxRetries !== undefined ? { maxRetries: config.maxRetries } : {}),
843
941
  ...(requestTimeoutMs !== undefined ? { requestTimeoutMs } : {}),
942
+ ...(config.reasoning !== undefined ? { reasoning: config.reasoning } : {}),
844
943
  });
845
944
  }
@@ -89,6 +89,8 @@ export type SuspendError = Extract<ProviderError, {
89
89
  * and the non-enumerable `.cause` does not. Thus the walk also reads `.value`.
90
90
  */
91
91
  export declare function findSuspendError(err: unknown): SuspendError | undefined;
92
+ /** Find a `ProviderError` of any kind, by the walk of {@link findSuspendError}. */
93
+ export declare function findProviderError(err: unknown): ProviderError | undefined;
92
94
  /**
93
95
  * Turn a caught SDK throwable into a `ProviderError` value. Routes through
94
96
  * `classifyProviderError` with the `suspendOn` map of the provider, so the
@@ -63,6 +63,11 @@ export function findSuspendError(err) {
63
63
  const asSuspend = (value) => (isProviderError(value) && value.type === "suspend" ? value : undefined);
64
64
  return findInCauseChain(err, (link) => asSuspend(link) ?? (typeof link === "object" && link !== null ? asSuspend(link.value) : undefined));
65
65
  }
66
+ /** Find a `ProviderError` of any kind, by the walk of {@link findSuspendError}. */
67
+ export function findProviderError(err) {
68
+ const asProviderError = (value) => (isProviderError(value) ? value : undefined);
69
+ return findInCauseChain(err, (link) => asProviderError(link) ?? (typeof link === "object" && link !== null ? asProviderError(link.value) : undefined));
70
+ }
66
71
  /**
67
72
  * Turn a caught SDK throwable into a `ProviderError` value. Routes through
68
73
  * `classifyProviderError` with the `suspendOn` map of the provider, so the
@@ -15,29 +15,17 @@
15
15
  * error — on an OpenAI-compatible model, which is exactly what we want: that
16
16
  * family does automatic server-side prefix caching and needs no directive.
17
17
  *
18
- * ## What the Anthropic namespace does, and where the marker must go
19
- *
20
- * The options go on the LAST MESSAGE of the request, never on the request
21
- * itself. Both placements cache the same bytes — one breakpoint at the end of
22
- * the messages covers the whole prefix (tools → system → history), because the
23
- * cache keys on a prefix — but only the message placement survives an
24
- * intermediary.
25
- *
26
- * A REQUEST-level `cacheControl` makes the provider emit a top-level
27
- * `cache_control` field instead of a per-block marker, and the server then
28
- * places the breakpoint itself. That is one fewer thing for the harness to get
29
- * right, and it is why this module used to do it. It is also invisible: an
30
- * intermediary that counts breakpoints to stay under Anthropic's cap of four
31
- * counts BLOCKS, so a top-level field is a breakpoint nothing on the way
32
- * upstream can see. CLIProxyAPI — what the OSS CLI routes through on the Claude
33
- * OAuth path — injects up to four block markers of its own and trims to four by
34
- * that blind count, so the harness's invisible fifth breakpoint made every
35
- * request past a thread's first turn fail with `A maximum of 4 blocks with
36
- * cache_control may be provided. Found 5.` (HTTP 400, non-retryable — the thread
37
- * is wedged, because the next turn rebuilds the same shape).
38
- *
39
- * A marker on a message block is counted, trimmed, and reasoned about by every
40
- * hop. Keep it there. {@link withPromptCacheBreakpoint} is the only writer.
18
+ * ## What the Anthropic namespace does, and where the markers must go
19
+ *
20
+ * A request carries two markers, each on a block, never on the request itself:
21
+ * {@link withSystemPromptBreakpoint} at the end of the system prompt, and
22
+ * {@link withPromptCacheBreakpoint} at the last eligible message.
23
+ *
24
+ * A REQUEST-level `cacheControl` emits an invisible top-level field instead.
25
+ * CLIProxyAPI (the Claude OAuth path) counts BLOCKS toward Anthropic's cap of
26
+ * four, not this field, so the uncounted marker overflows it and fails with
27
+ * `A maximum of 4 blocks with cache_control may be provided. Found 5.`
28
+ * (HTTP 400, non-retryable — the thread stays wedged).
41
29
  *
42
30
  * ## Which vendors need a marker at all
43
31
  *
@@ -54,9 +42,9 @@
54
42
  * cache RESOURCE created out of band and referenced by name, which is not a
55
43
  * marker and does not fit a ttl policy.
56
44
  *
57
- * So both markers ride together on the one chosen message. A provider reads only
58
- * its own namespace, thus the pair is free to whichever vendor is not serving
59
- * the call, and the placement never learns which one that is.
45
+ * So both vendor markers ride together on each chosen message. A provider reads
46
+ * only its own namespace, thus the pair is free to whichever vendor is not
47
+ * serving the call, and the placement never learns which one that is.
60
48
  *
61
49
  * The asymmetry worth remembering: under-marking silently costs money, while
62
50
  * over-marking hard-fails. Anthropic answers 400 past four breakpoints, and the
@@ -65,20 +53,18 @@
65
53
  *
66
54
  * ## Cache defeaters — what silently kills the hit rate
67
55
  *
68
- * The cache keys on an *exact prefix*. Anything that perturbs the head of the
69
- * request invalidates everything after it. One known defeater remains in this
70
- * codebase, flagged at its own site as a separate change:
56
+ * The cache keys on an *exact prefix*, so anything that perturbs the head of the
57
+ * request invalidates everything after it. The wrap-up of `runAgent`, the
58
+ * salvage of `runToTerminal`, and a step's summary and file-metadata
59
+ * continuations extend one conversation under a tool mask, thus they keep its
60
+ * prefix (`loop/run-agent.ts`, `loop/continue-agent.ts`). A tool choice that
61
+ * forbids a call would defeat the cache — see the CAUTION on `ChatRequest.toolChoice`.
71
62
  *
72
- * 1. `runAgent`'s forced wrap-up swaps the tool set to `{}` — tools sit at the
73
- * very front of the prefix, so that one call reads nothing back and rewrites
74
- * the cache from scratch (`loop/run-agent.ts`).
63
+ * `loadRecent` gives the view of the latest compaction marker
64
+ * (`memory/conversation-view.ts`). A compaction moves the start of the view one
65
+ * time, and the message prefix holds still between two compactions.
75
66
  *
76
- * `loadRecent`'s history eviction used to be a second defeater — it advanced the
77
- * window one turn per turn, shifting the message prefix every request. It now
78
- * evicts in whole blocks so the prefix holds still between block boundaries
79
- * (`memory/thread-history.ts`).
80
- *
81
- * A sandbox agent's system prompt is NOT one of them: it is a pure function of
67
+ * A sandbox agent's system prompt is NOT a defeater: it is a pure function of
82
68
  * its agent type, byte-identical across every step of every run, and the per-step
83
69
  * paths ride in the step's briefing instead (`agents/sandbox/shared.ts`,
84
70
  * `prompts/briefing.ts`). Keep it that way — one interpolated id or path there
@@ -106,6 +92,7 @@
106
92
  * flat-zero read counter beside a non-zero write counter is still the signature
107
93
  * of an endpoint billing for writes and serving no reads.
108
94
  */
95
+ import type { SystemModelMessage } from "ai";
109
96
  import type { ModelMessage, PromptCachePolicy, ProviderOptions } from "./types.js";
110
97
  /**
111
98
  * The default policy: cache with the 5-minute TTL.
@@ -120,6 +107,12 @@ import type { ModelMessage, PromptCachePolicy, ProviderOptions } from "./types.j
120
107
  * premium for a cache nothing ever reads.
121
108
  */
122
109
  export declare const DEFAULT_PROMPT_CACHE: PromptCachePolicy;
110
+ /**
111
+ * The policy of the root loop of a chat turn only: a person can reply 5 to 60 minutes later, and a
112
+ * 5-minute entry does not last that gap. Each other loop keeps {@link DEFAULT_PROMPT_CACHE}, because
113
+ * its requests start less than 5 minutes apart.
114
+ */
115
+ export declare const CONVERSATION_PROMPT_CACHE: PromptCachePolicy;
123
116
  /**
124
117
  * Translate a neutral cache policy into provider wire options.
125
118
  *
@@ -141,11 +134,20 @@ export declare const DEFAULT_PROMPT_CACHE: PromptCachePolicy;
141
134
  *
142
135
  * CAUTION: this is the marker's VALUE, not its placement. Attaching it to a
143
136
  * `ChatRequest` emits the invisible top-level field the module header warns
144
- * about. Place it with {@link withPromptCacheBreakpoint}.
137
+ * about. Place it with {@link withSystemPromptBreakpoint} and
138
+ * {@link withPromptCacheBreakpoint}.
145
139
  */
146
140
  export declare function promptCacheProviderOptions(policy: PromptCachePolicy): ProviderOptions | undefined;
147
141
  /**
148
- * Place the request's ONE cache breakpoint, on the last message that can host it.
142
+ * Marks the end of the system prompt, the sibling marker of {@link withPromptCacheBreakpoint}.
143
+ *
144
+ * Skips an empty prompt — the Anthropic package renders it as an empty text
145
+ * block, which cannot carry a marker.
146
+ */
147
+ export declare function withSystemPromptBreakpoint(system: string, policy: PromptCachePolicy): string | SystemModelMessage;
148
+ /**
149
+ * Places the message-level breakpoint on the last eligible message — the sibling
150
+ * marker sits at the end of the system prompt ({@link withSystemPromptBreakpoint}).
149
151
  *
150
152
  * Returns a copy — the caller's array is the transcript the loop keeps pushing
151
153
  * onto and the host later persists, and a marker written into it would ride into
@@ -160,7 +162,7 @@ export declare function promptCacheProviderOptions(policy: PromptCachePolicy): P
160
162
  * what an agent loop needs.
161
163
  *
162
164
  * A marker already on any other message is REMOVED — the invariant is exactly one
163
- * breakpoint per request, and a stored message that carries one from an older
165
+ * breakpoint among the messages, and a stored message that carries one from an older
164
166
  * build (`memory/ai-sdk-message-storage.ts` reads `cache_control` back off a
165
167
  * stored block) would otherwise spend a slot that the harness never budgeted.
166
168
  * `"off"` strips and places nothing, so turning caching off really does send no
@@ -15,29 +15,17 @@
15
15
  * error — on an OpenAI-compatible model, which is exactly what we want: that
16
16
  * family does automatic server-side prefix caching and needs no directive.
17
17
  *
18
- * ## What the Anthropic namespace does, and where the marker must go
19
- *
20
- * The options go on the LAST MESSAGE of the request, never on the request
21
- * itself. Both placements cache the same bytes — one breakpoint at the end of
22
- * the messages covers the whole prefix (tools → system → history), because the
23
- * cache keys on a prefix — but only the message placement survives an
24
- * intermediary.
25
- *
26
- * A REQUEST-level `cacheControl` makes the provider emit a top-level
27
- * `cache_control` field instead of a per-block marker, and the server then
28
- * places the breakpoint itself. That is one fewer thing for the harness to get
29
- * right, and it is why this module used to do it. It is also invisible: an
30
- * intermediary that counts breakpoints to stay under Anthropic's cap of four
31
- * counts BLOCKS, so a top-level field is a breakpoint nothing on the way
32
- * upstream can see. CLIProxyAPI — what the OSS CLI routes through on the Claude
33
- * OAuth path — injects up to four block markers of its own and trims to four by
34
- * that blind count, so the harness's invisible fifth breakpoint made every
35
- * request past a thread's first turn fail with `A maximum of 4 blocks with
36
- * cache_control may be provided. Found 5.` (HTTP 400, non-retryable — the thread
37
- * is wedged, because the next turn rebuilds the same shape).
38
- *
39
- * A marker on a message block is counted, trimmed, and reasoned about by every
40
- * hop. Keep it there. {@link withPromptCacheBreakpoint} is the only writer.
18
+ * ## What the Anthropic namespace does, and where the markers must go
19
+ *
20
+ * A request carries two markers, each on a block, never on the request itself:
21
+ * {@link withSystemPromptBreakpoint} at the end of the system prompt, and
22
+ * {@link withPromptCacheBreakpoint} at the last eligible message.
23
+ *
24
+ * A REQUEST-level `cacheControl` emits an invisible top-level field instead.
25
+ * CLIProxyAPI (the Claude OAuth path) counts BLOCKS toward Anthropic's cap of
26
+ * four, not this field, so the uncounted marker overflows it and fails with
27
+ * `A maximum of 4 blocks with cache_control may be provided. Found 5.`
28
+ * (HTTP 400, non-retryable — the thread stays wedged).
41
29
  *
42
30
  * ## Which vendors need a marker at all
43
31
  *
@@ -54,9 +42,9 @@
54
42
  * cache RESOURCE created out of band and referenced by name, which is not a
55
43
  * marker and does not fit a ttl policy.
56
44
  *
57
- * So both markers ride together on the one chosen message. A provider reads only
58
- * its own namespace, thus the pair is free to whichever vendor is not serving
59
- * the call, and the placement never learns which one that is.
45
+ * So both vendor markers ride together on each chosen message. A provider reads
46
+ * only its own namespace, thus the pair is free to whichever vendor is not
47
+ * serving the call, and the placement never learns which one that is.
60
48
  *
61
49
  * The asymmetry worth remembering: under-marking silently costs money, while
62
50
  * over-marking hard-fails. Anthropic answers 400 past four breakpoints, and the
@@ -65,20 +53,18 @@
65
53
  *
66
54
  * ## Cache defeaters — what silently kills the hit rate
67
55
  *
68
- * The cache keys on an *exact prefix*. Anything that perturbs the head of the
69
- * request invalidates everything after it. One known defeater remains in this
70
- * codebase, flagged at its own site as a separate change:
71
- *
72
- * 1. `runAgent`'s forced wrap-up swaps the tool set to `{}` — tools sit at the
73
- * very front of the prefix, so that one call reads nothing back and rewrites
74
- * the cache from scratch (`loop/run-agent.ts`).
56
+ * The cache keys on an *exact prefix*, so anything that perturbs the head of the
57
+ * request invalidates everything after it. The wrap-up of `runAgent`, the
58
+ * salvage of `runToTerminal`, and a step's summary and file-metadata
59
+ * continuations extend one conversation under a tool mask, thus they keep its
60
+ * prefix (`loop/run-agent.ts`, `loop/continue-agent.ts`). A tool choice that
61
+ * forbids a call would defeat the cache — see the CAUTION on `ChatRequest.toolChoice`.
75
62
  *
76
- * `loadRecent`'s history eviction used to be a second defeater — it advanced the
77
- * window one turn per turn, shifting the message prefix every request. It now
78
- * evicts in whole blocks so the prefix holds still between block boundaries
79
- * (`memory/thread-history.ts`).
63
+ * `loadRecent` gives the view of the latest compaction marker
64
+ * (`memory/conversation-view.ts`). A compaction moves the start of the view one
65
+ * time, and the message prefix holds still between two compactions.
80
66
  *
81
- * A sandbox agent's system prompt is NOT one of them: it is a pure function of
67
+ * A sandbox agent's system prompt is NOT a defeater: it is a pure function of
82
68
  * its agent type, byte-identical across every step of every run, and the per-step
83
69
  * paths ride in the step's briefing instead (`agents/sandbox/shared.ts`,
84
70
  * `prompts/briefing.ts`). Keep it that way — one interpolated id or path there
@@ -119,6 +105,12 @@
119
105
  * premium for a cache nothing ever reads.
120
106
  */
121
107
  export const DEFAULT_PROMPT_CACHE = { ttl: "5m" };
108
+ /**
109
+ * The policy of the root loop of a chat turn only: a person can reply 5 to 60 minutes later, and a
110
+ * 5-minute entry does not last that gap. Each other loop keeps {@link DEFAULT_PROMPT_CACHE}, because
111
+ * its requests start less than 5 minutes apart.
112
+ */
113
+ export const CONVERSATION_PROMPT_CACHE = { ttl: "1h" };
122
114
  /**
123
115
  * Translate a neutral cache policy into provider wire options.
124
116
  *
@@ -140,7 +132,8 @@ export const DEFAULT_PROMPT_CACHE = { ttl: "5m" };
140
132
  *
141
133
  * CAUTION: this is the marker's VALUE, not its placement. Attaching it to a
142
134
  * `ChatRequest` emits the invisible top-level field the module header warns
143
- * about. Place it with {@link withPromptCacheBreakpoint}.
135
+ * about. Place it with {@link withSystemPromptBreakpoint} and
136
+ * {@link withPromptCacheBreakpoint}.
144
137
  */
145
138
  export function promptCacheProviderOptions(policy) {
146
139
  if (policy === "off")
@@ -149,6 +142,18 @@ export function promptCacheProviderOptions(policy) {
149
142
  const bedrock = { cachePoint: { type: "default", ttl: policy.ttl } };
150
143
  return { anthropic, bedrock };
151
144
  }
145
+ /**
146
+ * Marks the end of the system prompt, the sibling marker of {@link withPromptCacheBreakpoint}.
147
+ *
148
+ * Skips an empty prompt — the Anthropic package renders it as an empty text
149
+ * block, which cannot carry a marker.
150
+ */
151
+ export function withSystemPromptBreakpoint(system, policy) {
152
+ const providerOptions = promptCacheProviderOptions(policy);
153
+ if (providerOptions === undefined || system === "")
154
+ return system;
155
+ return { role: "system", content: system, providerOptions };
156
+ }
152
157
  /**
153
158
  * Every namespace that can carry a breakpoint, and the key it carries it under.
154
159
  *
@@ -229,7 +234,8 @@ function withoutBreakpoint(message) {
229
234
  return { ...message, providerOptions: remaining };
230
235
  }
231
236
  /**
232
- * Place the request's ONE cache breakpoint, on the last message that can host it.
237
+ * Places the message-level breakpoint on the last eligible message — the sibling
238
+ * marker sits at the end of the system prompt ({@link withSystemPromptBreakpoint}).
233
239
  *
234
240
  * Returns a copy — the caller's array is the transcript the loop keeps pushing
235
241
  * onto and the host later persists, and a marker written into it would ride into
@@ -244,7 +250,7 @@ function withoutBreakpoint(message) {
244
250
  * what an agent loop needs.
245
251
  *
246
252
  * A marker already on any other message is REMOVED — the invariant is exactly one
247
- * breakpoint per request, and a stored message that carries one from an older
253
+ * breakpoint among the messages, and a stored message that carries one from an older
248
254
  * build (`memory/ai-sdk-message-storage.ts` reads `cache_control` back off a
249
255
  * stored block) would otherwise spend a slot that the harness never budgeted.
250
256
  * `"off"` strips and places nothing, so turning caching off really does send no
@@ -1,35 +1,24 @@
1
1
  /**
2
- * The reasoning depth of a run.
2
+ * The reasoning depth of a call.
3
3
  *
4
- * The harness states the depth one time, on `ChatRequest.reasoning`, with the
5
- * neutral names of the AI SDK. It names no vendor key. Each provider package
6
- * holds the table of what its own models accept, and it maps the neutral name
7
- * onto the wire. The Anthropic package also selects adaptive thinking there.
4
+ * The effort resolves in order: `ChatRequest.reasoning` (set only through
5
+ * `RunAgentOptions.reasoning`), the provider configuration's `reasoning`, then
6
+ * {@link DEFAULT_REASONING}. Anthropic discards its message cache when the
7
+ * top-level effort changes between calls.
8
8
  *
9
- * ## Why the harness names no vendor key
10
- *
11
- * The harness wrote `providerOptions.anthropic.effort` before. A value on that
12
- * key turns the per-model table of the Anthropic package off, thus the raw name
13
- * reached the wire. A model that accepts `high` but not `xhigh` answered 400.
14
- * The neutral field has no such hazard, because the package resolves the name
15
- * for the model that it is bound to.
16
- *
17
- * ## Where the policy belongs
18
- *
19
- * The policy rides on the run, not on the provider, for the same reason that the
20
- * cache policy does. A one-shot LLM call elsewhere in the harness has its own
21
- * depth needs, and it must not inherit the depth of an agent loop.
9
+ * Do not set `providerOptions.anthropic.effort` directly. It bypasses the
10
+ * per-model table, and a model that rejects the raw name answers 400.
22
11
  */
23
12
  import type { ReasoningPolicy } from "./types.js";
24
13
  /**
25
- * The default policy: the deepest name of the neutral ladder.
14
+ * The last source of the effort: the deepest rung of the neutral ladder.
26
15
  *
27
16
  * Each agent of the harness drives tools over many iterations. A shallow turn
28
17
  * there wastes more calls than a deeper turn costs in tokens. The Anthropic
29
18
  * package sends `xhigh` to a model that accepts it, and `max` to a model that
30
19
  * does not. The OpenAI-compatible package sends the name as it is.
31
20
  *
32
- * A host that wants a cheaper loop passes a lower name. A host on a model with
33
- * no reasoning support passes `"provider-default"`.
21
+ * A host that wants a cheaper role sets a lower name on that role's provider
22
+ * configuration. With no reasoning support, a host sets `"provider-default"`.
34
23
  */
35
24
  export declare const DEFAULT_REASONING: ReasoningPolicy;