@yolk-sdk/agent 0.1.0-canary.106 → 0.1.0-canary.108

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 (155) hide show
  1. package/README.md +112 -45
  2. package/dist/compaction/window.d.mts.map +1 -1
  3. package/dist/compaction/window.mjs +8 -2
  4. package/dist/compaction/window.mjs.map +1 -1
  5. package/dist/loop/index.d.mts +2 -1
  6. package/dist/loop/index.mjs +2 -1
  7. package/dist/loop/instructions.d.mts +39 -0
  8. package/dist/loop/instructions.d.mts.map +1 -0
  9. package/dist/loop/instructions.mjs +42 -0
  10. package/dist/loop/instructions.mjs.map +1 -0
  11. package/dist/loop/run.d.mts +15 -2
  12. package/dist/loop/run.d.mts.map +1 -1
  13. package/dist/loop/run.mjs +35 -17
  14. package/dist/loop/run.mjs.map +1 -1
  15. package/dist/loop/services/llm-provider.d.mts +23 -2
  16. package/dist/loop/services/llm-provider.d.mts.map +1 -1
  17. package/dist/loop/services/llm-provider.mjs.map +1 -1
  18. package/dist/protocol/index.d.mts +4 -3
  19. package/dist/protocol/index.d.mts.map +1 -1
  20. package/dist/protocol/index.mjs +4 -3
  21. package/dist/protocol/message.d.mts +34 -1
  22. package/dist/protocol/message.d.mts.map +1 -1
  23. package/dist/protocol/message.mjs +39 -2
  24. package/dist/protocol/message.mjs.map +1 -1
  25. package/dist/protocol/tool-search.d.mts +31 -0
  26. package/dist/protocol/tool-search.d.mts.map +1 -0
  27. package/dist/protocol/tool-search.mjs +42 -0
  28. package/dist/protocol/tool-search.mjs.map +1 -0
  29. package/dist/protocol/tool.d.mts +19 -6
  30. package/dist/protocol/tool.d.mts.map +1 -1
  31. package/dist/protocol/tool.mjs +17 -4
  32. package/dist/protocol/tool.mjs.map +1 -1
  33. package/dist/providers/anthropic/claude-provider.d.mts +22 -2
  34. package/dist/providers/anthropic/claude-provider.d.mts.map +1 -1
  35. package/dist/providers/anthropic/claude-provider.mjs.map +1 -1
  36. package/dist/providers/anthropic/claude.d.mts +8 -2
  37. package/dist/providers/anthropic/claude.d.mts.map +1 -1
  38. package/dist/providers/anthropic/claude.mjs +7 -1
  39. package/dist/providers/anthropic/claude.mjs.map +1 -1
  40. package/dist/providers/anthropic/conformance/cases.d.mts.map +1 -1
  41. package/dist/providers/anthropic/conformance/cases.mjs +3 -2
  42. package/dist/providers/anthropic/conformance/cases.mjs.map +1 -1
  43. package/dist/providers/anthropic/conformance/error-envelope.d.mts.map +1 -1
  44. package/dist/providers/anthropic/conformance/error-envelope.mjs +7 -2
  45. package/dist/providers/anthropic/conformance/error-envelope.mjs.map +1 -1
  46. package/dist/providers/anthropic/conformance/max-tokens.d.mts.map +1 -1
  47. package/dist/providers/anthropic/conformance/max-tokens.mjs +7 -2
  48. package/dist/providers/anthropic/conformance/max-tokens.mjs.map +1 -1
  49. package/dist/providers/anthropic/conformance/plain-text.d.mts.map +1 -1
  50. package/dist/providers/anthropic/conformance/plain-text.mjs +7 -2
  51. package/dist/providers/anthropic/conformance/plain-text.mjs.map +1 -1
  52. package/dist/providers/anthropic/conformance/thinking-before-text.d.mts.map +1 -1
  53. package/dist/providers/anthropic/conformance/thinking-before-text.mjs +7 -2
  54. package/dist/providers/anthropic/conformance/thinking-before-text.mjs.map +1 -1
  55. package/dist/providers/anthropic/conformance/tool-use-input-deltas.d.mts.map +1 -1
  56. package/dist/providers/anthropic/conformance/tool-use-input-deltas.mjs +9 -3
  57. package/dist/providers/anthropic/conformance/tool-use-input-deltas.mjs.map +1 -1
  58. package/dist/providers/anthropic-messages-provider-internal.d.mts +6 -3
  59. package/dist/providers/anthropic-messages-provider-internal.d.mts.map +1 -1
  60. package/dist/providers/anthropic-messages-provider-internal.mjs +6 -1
  61. package/dist/providers/anthropic-messages-provider-internal.mjs.map +1 -1
  62. package/dist/providers/anthropic-provider-internal.d.mts +28 -2
  63. package/dist/providers/anthropic-provider-internal.d.mts.map +1 -1
  64. package/dist/providers/anthropic-provider-internal.mjs +104 -7
  65. package/dist/providers/anthropic-provider-internal.mjs.map +1 -1
  66. package/dist/providers/cache-key-headers.d.mts +7 -0
  67. package/dist/providers/cache-key-headers.d.mts.map +1 -0
  68. package/dist/providers/cache-key-headers.mjs +10 -0
  69. package/dist/providers/cache-key-headers.mjs.map +1 -0
  70. package/dist/providers/openai/codex-provider.d.mts +2 -0
  71. package/dist/providers/openai/codex-provider.d.mts.map +1 -1
  72. package/dist/providers/openai/codex-provider.mjs +6 -0
  73. package/dist/providers/openai/codex-provider.mjs.map +1 -1
  74. package/dist/providers/openai/provider.d.mts +6 -0
  75. package/dist/providers/openai/provider.d.mts.map +1 -1
  76. package/dist/providers/openai/provider.mjs +18 -5
  77. package/dist/providers/openai/provider.mjs.map +1 -1
  78. package/dist/providers/openai-responses-provider-internal.d.mts +8 -1
  79. package/dist/providers/openai-responses-provider-internal.d.mts.map +1 -1
  80. package/dist/providers/openai-responses-provider-internal.mjs +19 -3
  81. package/dist/providers/openai-responses-provider-internal.mjs.map +1 -1
  82. package/dist/providers/opencode/conformance/messages-plain-text.d.mts.map +1 -1
  83. package/dist/providers/opencode/conformance/messages-plain-text.mjs +7 -2
  84. package/dist/providers/opencode/conformance/messages-plain-text.mjs.map +1 -1
  85. package/dist/providers/opencode/go-provider.d.mts.map +1 -1
  86. package/dist/providers/opencode/go-provider.mjs +6 -2
  87. package/dist/providers/opencode/go-provider.mjs.map +1 -1
  88. package/dist/providers/vercel/ai-gateway-provider.d.mts.map +1 -1
  89. package/dist/providers/vercel/ai-gateway-provider.mjs +8 -8
  90. package/dist/providers/vercel/ai-gateway-provider.mjs.map +1 -1
  91. package/dist/providers/vercel/conformance/cases.d.mts.map +1 -1
  92. package/dist/providers/vercel/conformance/cases.mjs.map +1 -1
  93. package/dist/providers/xai/grok-provider.d.mts +2 -0
  94. package/dist/providers/xai/grok-provider.d.mts.map +1 -1
  95. package/dist/providers/xai/grok-provider.mjs +6 -0
  96. package/dist/providers/xai/grok-provider.mjs.map +1 -1
  97. package/dist/react/chat-actions.d.mts +20 -20
  98. package/dist/react/chat-actions.d.mts.map +1 -1
  99. package/dist/react/chat-messages.d.mts +6 -0
  100. package/dist/react/chat-messages.d.mts.map +1 -1
  101. package/dist/react/chat-messages.mjs +18 -4
  102. package/dist/react/chat-messages.mjs.map +1 -1
  103. package/dist/runtime/run-runtime.d.mts +7 -2
  104. package/dist/runtime/run-runtime.d.mts.map +1 -1
  105. package/dist/runtime/run-runtime.mjs +18 -13
  106. package/dist/runtime/run-runtime.mjs.map +1 -1
  107. package/dist/skillset/merge.d.mts +3 -3
  108. package/dist/tools/index.d.mts +3 -2
  109. package/dist/tools/index.mjs +2 -1
  110. package/dist/tools/plan-runtime.d.mts.map +1 -1
  111. package/dist/tools/plan-runtime.mjs +20 -5
  112. package/dist/tools/plan-runtime.mjs.map +1 -1
  113. package/dist/tools/registry.d.mts +26 -5
  114. package/dist/tools/registry.d.mts.map +1 -1
  115. package/dist/tools/registry.mjs +58 -8
  116. package/dist/tools/registry.mjs.map +1 -1
  117. package/dist/tools/tool-search.d.mts +33 -0
  118. package/dist/tools/tool-search.d.mts.map +1 -0
  119. package/dist/tools/tool-search.mjs +119 -0
  120. package/dist/tools/tool-search.mjs.map +1 -0
  121. package/package.json +2 -2
  122. package/src/compaction/window.ts +16 -1
  123. package/src/loop/index.ts +2 -0
  124. package/src/loop/instructions.ts +76 -0
  125. package/src/loop/run.ts +65 -17
  126. package/src/loop/services/llm-provider.ts +23 -2
  127. package/src/protocol/index.ts +13 -0
  128. package/src/protocol/message.ts +61 -1
  129. package/src/protocol/tool-search.ts +72 -0
  130. package/src/protocol/tool.ts +30 -6
  131. package/src/providers/anthropic/claude-provider.ts +25 -1
  132. package/src/providers/anthropic/claude.ts +7 -1
  133. package/src/providers/anthropic/conformance/cases.ts +3 -2
  134. package/src/providers/anthropic/conformance/error-envelope.ts +13 -2
  135. package/src/providers/anthropic/conformance/max-tokens.ts +13 -2
  136. package/src/providers/anthropic/conformance/plain-text.ts +13 -2
  137. package/src/providers/anthropic/conformance/thinking-before-text.ts +13 -2
  138. package/src/providers/anthropic/conformance/tool-use-input-deltas.ts +16 -2
  139. package/src/providers/anthropic-messages-provider-internal.ts +11 -1
  140. package/src/providers/anthropic-provider-internal.ts +177 -13
  141. package/src/providers/cache-key-headers.ts +13 -0
  142. package/src/providers/openai/codex-provider.ts +11 -0
  143. package/src/providers/openai/provider.ts +40 -5
  144. package/src/providers/openai-responses-provider-internal.ts +31 -3
  145. package/src/providers/opencode/conformance/messages-plain-text.ts +13 -2
  146. package/src/providers/opencode/go-provider.ts +8 -2
  147. package/src/providers/vercel/ai-gateway-provider.ts +18 -14
  148. package/src/providers/vercel/conformance/cases.ts +2 -1
  149. package/src/providers/xai/grok-provider.ts +10 -0
  150. package/src/react/chat-messages.ts +42 -3
  151. package/src/runtime/run-runtime.ts +38 -13
  152. package/src/tools/index.ts +10 -0
  153. package/src/tools/plan-runtime.ts +27 -9
  154. package/src/tools/registry.ts +184 -14
  155. package/src/tools/tool-search.ts +183 -0
package/README.md CHANGED
@@ -20,47 +20,47 @@ Published package metadata requires Node.js 22+.
20
20
 
21
21
  ## Subpaths
22
22
 
23
- | Subpath | Purpose |
24
- | -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
25
- | `@yolk-sdk/agent/protocol` | Wire messages, events, content, usage, tool schemas |
26
- | `@yolk-sdk/agent/loop` | Stateless LLM/tool loop |
27
- | `@yolk-sdk/agent/loop/testing` | Faux provider and tool executor test helpers |
28
- | `@yolk-sdk/agent/runtime` | Transcript or append-backed runtime orchestration |
29
- | `@yolk-sdk/agent/client` | HTTP/NDJSON transport, HITL resume, retry/error state helpers |
30
- | `@yolk-sdk/agent/compaction` | Host-owned compaction budgets, checkpoints, formatting, retry |
31
- | `@yolk-sdk/agent/classification` | Classifier model contract: typed questions, answers, usage, errors |
32
- | `@yolk-sdk/agent/tools` | Tool registry, typed inputs, interactions, subagents/questions, durable tool ledger, staged tool plans, tool change previews |
33
- | `@yolk-sdk/agent/react` | Headless React chat hook, reducer, selectors, and render model |
34
- | `@yolk-sdk/agent/oauth` | Provider-neutral OAuth token and broker contracts |
35
- | `@yolk-sdk/agent/providers/openai` | OpenAI/Codex OAuth and broker helpers |
36
- | `@yolk-sdk/agent/providers/openai/codex` | OpenAI Codex request and auth helpers |
37
- | `@yolk-sdk/agent/providers/openai/codex-usage` | Codex subscription-allowance snapshots |
38
- | `@yolk-sdk/agent/providers/openai/codex-provider` | Codex LLM provider factory |
39
- | `@yolk-sdk/agent/providers/openai/provider` | OpenAI-compatible LLM provider factory |
40
- | `@yolk-sdk/agent/providers/openai/conformance` | Synthetic OpenAI chat, Codex Responses, and Codex usage fixtures and conformance cases |
41
- | `@yolk-sdk/agent/providers/openai/realtime` | OpenAI Realtime session config and event codecs |
42
- | `@yolk-sdk/agent/providers/openai/speech` | OpenAI text-to-speech and transcription adapters |
43
- | `@yolk-sdk/agent/providers/vercel/ai-gateway-provider` | Vercel AI Gateway Chat Completions provider factory |
44
- | `@yolk-sdk/agent/providers/vercel/ai-gateway-classifier` | Vercel AI Gateway classifier (`POST /v1/evaluate`) layer |
45
- | `@yolk-sdk/agent/providers/vercel/conformance` | Verified Gateway chat fixtures, synthetic classifier fixtures, and conformance cases |
46
- | `@yolk-sdk/agent/providers/opencode/go-provider` | OpenCode Go Chat Completions, Messages, and Responses provider |
47
- | `@yolk-sdk/agent/providers/opencode/usage` | OpenCode Go subscription-allowance snapshots |
48
- | `@yolk-sdk/agent/providers/opencode/conformance` | Synthetic OpenCode Go (per protocol, commentary replay, usage) fixtures and conformance cases |
49
- | `@yolk-sdk/agent/providers/anthropic` | Anthropic/Claude OAuth and broker helpers |
50
- | `@yolk-sdk/agent/providers/anthropic/claude` | Claude request and auth helpers |
51
- | `@yolk-sdk/agent/providers/anthropic/usage` | Claude subscription-allowance snapshots |
52
- | `@yolk-sdk/agent/providers/anthropic/claude-provider` | Claude LLM provider factory |
53
- | `@yolk-sdk/agent/providers/anthropic/conformance` | Synthetic Anthropic Messages and Claude usage fixtures and conformance cases |
54
- | `@yolk-sdk/agent/providers/xai` | Grok subscription OAuth and token broker helpers |
55
- | `@yolk-sdk/agent/providers/xai/grok` | Grok subscription request and auth helpers |
56
- | `@yolk-sdk/agent/providers/xai/grok-provider` | Grok subscription LLM provider factory |
57
- | `@yolk-sdk/agent/providers/xai/usage` | Grok subscription-allowance snapshots |
58
- | `@yolk-sdk/agent/providers/xai/conformance` | Synthetic Grok Responses and Grok usage fixtures and conformance cases |
59
- | `@yolk-sdk/agent/providers/subscription-usage` | Shared allowance snapshot and safe error schemas |
60
- | `@yolk-sdk/agent/skillset` | Portable skill and slash-command parsing/catalogs |
61
- | `@yolk-sdk/agent/voice` | Voice protocol, controller, tool handler, projection, speech |
62
- | `@yolk-sdk/agent/voice/browser` | Browser WebRTC voice transport |
63
- | `@yolk-sdk/agent/voice/react` | Headless browser voice React hook |
23
+ | Subpath | Purpose |
24
+ | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
25
+ | `@yolk-sdk/agent/protocol` | Wire messages, events, content, usage, tool schemas |
26
+ | `@yolk-sdk/agent/loop` | Stateless LLM/tool loop, host instructions |
27
+ | `@yolk-sdk/agent/loop/testing` | Faux provider and tool executor test helpers |
28
+ | `@yolk-sdk/agent/runtime` | Transcript or append-backed runtime orchestration |
29
+ | `@yolk-sdk/agent/client` | HTTP/NDJSON transport, HITL resume, retry/error state helpers |
30
+ | `@yolk-sdk/agent/compaction` | Host-owned compaction budgets, checkpoints, formatting, retry |
31
+ | `@yolk-sdk/agent/classification` | Classifier model contract: typed questions, answers, usage, errors |
32
+ | `@yolk-sdk/agent/tools` | Tool registry, deferred loading (`tool_search`), typed inputs, interactions, subagents/questions, durable tool ledger, staged tool plans, tool change previews |
33
+ | `@yolk-sdk/agent/react` | Headless React chat hook, reducer, selectors, and render model |
34
+ | `@yolk-sdk/agent/oauth` | Provider-neutral OAuth token and broker contracts |
35
+ | `@yolk-sdk/agent/providers/openai` | OpenAI/Codex OAuth and broker helpers |
36
+ | `@yolk-sdk/agent/providers/openai/codex` | OpenAI Codex request and auth helpers |
37
+ | `@yolk-sdk/agent/providers/openai/codex-usage` | Codex subscription-allowance snapshots |
38
+ | `@yolk-sdk/agent/providers/openai/codex-provider` | Codex LLM provider factory |
39
+ | `@yolk-sdk/agent/providers/openai/provider` | OpenAI-compatible LLM provider factory |
40
+ | `@yolk-sdk/agent/providers/openai/conformance` | Synthetic OpenAI chat, Codex Responses, and Codex usage fixtures and conformance cases |
41
+ | `@yolk-sdk/agent/providers/openai/realtime` | OpenAI Realtime session config and event codecs |
42
+ | `@yolk-sdk/agent/providers/openai/speech` | OpenAI text-to-speech and transcription adapters |
43
+ | `@yolk-sdk/agent/providers/vercel/ai-gateway-provider` | Vercel AI Gateway Chat Completions provider factory |
44
+ | `@yolk-sdk/agent/providers/vercel/ai-gateway-classifier` | Vercel AI Gateway classifier (`POST /v1/evaluate`) layer |
45
+ | `@yolk-sdk/agent/providers/vercel/conformance` | Verified Gateway chat fixtures, synthetic classifier fixtures, and conformance cases |
46
+ | `@yolk-sdk/agent/providers/opencode/go-provider` | OpenCode Go Chat Completions, Messages, and Responses provider |
47
+ | `@yolk-sdk/agent/providers/opencode/usage` | OpenCode Go subscription-allowance snapshots |
48
+ | `@yolk-sdk/agent/providers/opencode/conformance` | Synthetic OpenCode Go (per protocol, commentary replay, usage) fixtures and conformance cases |
49
+ | `@yolk-sdk/agent/providers/anthropic` | Anthropic/Claude OAuth and broker helpers |
50
+ | `@yolk-sdk/agent/providers/anthropic/claude` | Claude request and auth helpers |
51
+ | `@yolk-sdk/agent/providers/anthropic/usage` | Claude subscription-allowance snapshots |
52
+ | `@yolk-sdk/agent/providers/anthropic/claude-provider` | Claude LLM provider factory |
53
+ | `@yolk-sdk/agent/providers/anthropic/conformance` | Synthetic Anthropic Messages and Claude usage fixtures and conformance cases |
54
+ | `@yolk-sdk/agent/providers/xai` | Grok subscription OAuth and token broker helpers |
55
+ | `@yolk-sdk/agent/providers/xai/grok` | Grok subscription request and auth helpers |
56
+ | `@yolk-sdk/agent/providers/xai/grok-provider` | Grok subscription LLM provider factory |
57
+ | `@yolk-sdk/agent/providers/xai/usage` | Grok subscription-allowance snapshots |
58
+ | `@yolk-sdk/agent/providers/xai/conformance` | Synthetic Grok Responses and Grok usage fixtures and conformance cases |
59
+ | `@yolk-sdk/agent/providers/subscription-usage` | Shared allowance snapshot and safe error schemas |
60
+ | `@yolk-sdk/agent/skillset` | Portable skill and slash-command parsing/catalogs |
61
+ | `@yolk-sdk/agent/voice` | Voice protocol, controller, tool handler, projection, speech |
62
+ | `@yolk-sdk/agent/voice/browser` | Browser WebRTC voice transport |
63
+ | `@yolk-sdk/agent/voice/react` | Headless browser voice React hook |
64
64
 
65
65
  ## Imports
66
66
 
@@ -297,6 +297,21 @@ configuration. Anthropic Claude forwards `low`, `medium`, `high`, and `xhigh` th
297
297
  `output_config.effort`. It omits `minimal`, which Anthropic does not support. Hosts remain
298
298
  responsible for choosing an effort accepted by the selected provider and model.
299
299
 
300
+ ### Prompt caching
301
+
302
+ Provider prompt caching is always on (ADR 0008). Set `cacheKey` (for example the session id) on
303
+ `run` or `runRuntime` so providers route a conversation to its cached prefix: Anthropic gets
304
+ `cache_control` breakpoints and a Claude session header derived from the key, Codex and Grok `prompt_cache_key` plus their session headers, Vercel AI
305
+ Gateway `caching: 'auto'` and `x-session-affinity`, and OpenCode Go `x-opencode-session`. The
306
+ generic OpenAI-compatible chat provider sends nothing unless its config sets `cacheKeyHeaders`.
307
+ Claude entries last five minutes unless the provider sets `promptCacheTtl: '1h'`.
308
+
309
+ Keep the prefix byte-identical: per-request facts (fresh signed URLs, the current time) belong in
310
+ `LLMRequest.turnContext`, which adapters append after the conversation (Anthropic after every
311
+ breakpoint; Gateway's automatic markers still read the prefix before it). Loop hosts set it by
312
+ decorating `LLMProvider`. `LLMRequest.toolChoice: 'none'` forbids tool calls but keeps the tool
313
+ definitions; on Anthropic a changed `tool_choice` invalidates the cached messages.
314
+
300
315
  ### Anthropic tool schemas
301
316
 
302
317
  Claude subscription OAuth rejects some valid JSON Schema constructs that Effect Schema can emit
@@ -416,6 +431,8 @@ policy and emits protocol-visible retry/error state:
416
431
  [stream diagnostics guide](https://github.com/magoz/yolk-sdk/blob/main/apps/docs/content/docs/troubleshooting.mdx#diagnose-chat-stream-failures-with-stream-diagnostics).
417
432
  - Chat Completions and Responses HTTP failures copy the envelope string `code` (falling back to
418
433
  `type`) into `provider.providerCode`; free-text upstream bodies stay out of `LLMError`.
434
+ - Claude subscription (`anthropic_claude`) HTTP and stream errors use `error.details.error_code`
435
+ (for example `claude_code_version_too_old`), then `error.type`, then `error.code`.
419
436
  - `AgentRetry.provider` exposes current retry metadata and chosen `delayMs`.
420
437
  - `AgentError.provider` preserves final terminal metadata.
421
438
  - `AgentErrorCode` includes `rate_limit`, `overloaded`, `context_overflow`, and generic
@@ -925,10 +942,15 @@ This package declares them; it does not run scripts.
925
942
 
926
943
  - `makeTool({ output })` adds declaration-only `ToolDef.outputSchema`, lowered like `parameters`.
927
944
  - `callableBy: 'all' | 'model' | 'codemode'` (default `all`) decides who may call a tool. For
928
- `codemode` only, `discovery: 'listed' | 'search'` (default `listed`) decides how scripts find it.
945
+ `codemode` only, `discovery: 'listed' | 'search' | 'pinned'` (default `listed`; `pinned` is
946
+ always listed, outside the catalog budget) decides how scripts find it.
929
947
  Codemode-only tools never reach providers. Approval, input, interaction, activated background,
930
948
  `question`, `subagent`, and nested-access tools never run from code mode (stageable approval tools
931
- are only staged; see Staged tool plans); `resolveTools` rejects `callableBy: 'codemode'` on them.
949
+ are only staged; see Staged tool plans); `resolveTools` rejects `callableBy: 'codemode'` on them,
950
+ except a stageable approval tool, which may be stage-only: never declared, only `stage()`d from
951
+ scripts (see [ADR 0007](https://github.com/magoz/yolk-sdk/blob/main/docs/adr/0007-tool-exposure-and-catalog.md)).
952
+ - `instruct: ({ tools }) => RunInstruction` on a nested-access registration contributes a host
953
+ instruction (see Host instructions), collected in `ResolvedToolSet.instructions`.
932
954
  - `nestedToolAccess: true` gives a registration's `execute` a `nested` executor over the other
933
955
  code-mode-callable tools of the same resolution and host context. Nested calls run through the
934
956
  resolved execute path and fail closed with model-visible error results. Assign nested call ids as
@@ -942,6 +964,48 @@ This package declares them; it does not run scripts.
942
964
  Size the record with `makeNestedToolCallRecorder({ maxCalls })`; per-status `counts` cover
943
965
  every call, dropped ones included.
944
966
 
967
+ ## Deferred tools
968
+
969
+ `loading: 'deferred'` (on `makeTool`, `ToolDef`, or the MCP/connector `exposure` resolvers) keeps a
970
+ model-callable tool out of provider requests until the model loads it, so hosts can connect many
971
+ tools without spending context on them ([ADR 0007](https://github.com/magoz/yolk-sdk/blob/main/docs/adr/0007-tool-exposure-and-catalog.md)).
972
+
973
+ - When any tool is deferred, `resolveTools` adds the package `tool_search` tool (read access,
974
+ model-only; `makeToolSearchTool`). Its result names the tools it loaded; opt out with
975
+ `resolveTools(modules, context, { toolSearch: false })`, or register your own `tool_search`.
976
+ - Activation is derived from the transcript, never stored: each request declares
977
+ `declaredToolDefs(tools, messages)`, the eager tools plus deferred tools a `tool_search` result
978
+ activated, so replayed and resumed steps declare the same tools. Loading a tool changes the
979
+ declared tools, which start the request, so that request misses the prompt cache once; later
980
+ requests reuse the new prefix. Prefer code mode scripts for exploratory use.
981
+ - Deferral saves context; it is not authority. Approval-gated tools keep their approval when
982
+ deferred. `loading` on a `callableBy: 'codemode'` tool is a type error (`invalid_tool_exposure` at
983
+ resolution for raw definitions); `tool_search`, `question`, `subagent`, input, interaction, plan
984
+ review, background, and nested-access tools cannot be deferred (`deferred_unsupported_tool`).
985
+
986
+ ## Host instructions
987
+
988
+ A `RunInstruction` (`{ key, state, render }`) is host context kept current in the conversation,
989
+ for example the code mode catalog (`ResolvedToolSet.instructions`). Pass it as
990
+ `RunConfig.instructions` or `RuntimeConfig.instructions`.
991
+
992
+ - Before each model request the loop compares `state` with the latest instruction message of the
993
+ same `key` and, only when it changed, appends a user-role message with `render(state)` and the
994
+ non-model-visible `instruction: { key, state }` envelope. Earlier messages never change, so a
995
+ changing instruction never invalidates the cached request prefix. `state: undefined` appends a
996
+ removal notice once.
997
+ - `render` gets only the current state: compaction keeps the latest message of each key
998
+ (`latestInstructionMessages`), so each one must stand alone.
999
+ - The appended messages join the run's created messages (persist them with the turn) but emit no
1000
+ `UserMessage` event. Durable hosts driving `runModelTurn` append and persist
1001
+ `instructionMessages(transcript, instructions)` before each step themselves; `runModelTurn` does
1002
+ not accept `instructions`.
1003
+ - Window compaction keeps instruction messages after its summary; custom compaction must keep
1004
+ `latestInstructionMessages(...)` too. Chat projections hide them (`isInstructionMessage`) and
1005
+ `toAgentMessages` round-trips them. Duplicate keys fail resolution (`duplicate_instruction`).
1006
+ - Hosts that cannot append messages to their conversation may put `render(state)` into the system
1007
+ prompt instead, only while `state` is defined (`undefined` renders the removal notice).
1008
+
945
1009
  ## Durable tool ledger
946
1010
 
947
1011
  Steps that hosts re-execute (Vercel Workflow's queue is at-least-once) must not repeat writes.
@@ -1009,8 +1073,11 @@ aborts, and implementation bugs outside typed tool execution.
1009
1073
  or step when call ids can repeat), keep its operations interruptible, and reserve about 15 s of
1010
1074
  the step budget after `deadline` for recording outcomes.
1011
1075
  - If you send `ToolDef`s to providers yourself (outside `run`/`runModelTurn`), filter them with
1012
- protocol `providerToolDefs`; codemode-only tools must never reach providers. Executor decorators
1013
- outside `ResolvedToolSet.execute` do not see nested code mode calls.
1076
+ protocol `declaredToolDefs(tools, messages)`: codemode-only tools must never reach providers, and
1077
+ deferred tools only once a `tool_search` result activated them.
1078
+ - Durable hosts driving `runModelTurn` append and persist `instructionMessages(...)` before each
1079
+ model step (see Host instructions).
1080
+ - Executor decorators outside `ResolvedToolSet.execute` do not see nested code mode calls.
1014
1081
  - Compact context and decide memory/search policy.
1015
1082
 
1016
1083
  ## Boundaries
@@ -1 +1 @@
1
- {"version":3,"file":"window.d.mts","names":[],"sources":["../../src/compaction/window.ts"],"mappings":"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"}
1
+ {"version":3,"file":"window.d.mts","names":[],"sources":["../../src/compaction/window.ts"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { estimateAgentMessagesTokens } from "./estimator.mjs";
2
2
  import { Data, Predicate } from "effect";
3
- import { CompactionEnd, CompactionStart } from "@yolk-sdk/agent/protocol";
3
+ import { CompactionEnd, CompactionStart, isInstructionMessage, latestInstructionMessages } from "@yolk-sdk/agent/protocol";
4
4
  //#region src/compaction/window.ts
5
5
  const defaultCompactionTailMessageCount = 16;
6
6
  const defaultMinimumCompactionMessages = 2;
@@ -51,7 +51,13 @@ const planWindowCompaction = (messages, options) => {
51
51
  const applyCompactionPlan = (plan, options) => {
52
52
  if (Predicate.isTagged(plan, "Skip")) return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens);
53
53
  const estimateTokens = estimator(options.estimateTokens);
54
- const messages = [options.summaryMessage, ...plan.recentMessages];
54
+ const recentKeys = new Set(plan.recentMessages.flatMap((message) => isInstructionMessage(message) ? [message.instruction.key] : []));
55
+ const keptInstructions = latestInstructionMessages(plan.compactedMessages).filter((message) => !recentKeys.has(message.instruction.key));
56
+ const messages = [
57
+ options.summaryMessage,
58
+ ...keptInstructions,
59
+ ...plan.recentMessages
60
+ ];
55
61
  const afterTokens = estimateTokens(messages);
56
62
  if (afterTokens >= plan.beforeTokens) return skippedCompactionResult("not_smaller", plan.messages, plan.beforeTokens);
57
63
  return CompactionResult.Compacted({
@@ -1 +1 @@
1
- {"version":3,"file":"window.mjs","names":[],"sources":["../../src/compaction/window.ts"],"sourcesContent":["import {\n CompactionEnd,\n CompactionStart,\n type AgentEvent,\n type AgentMessage\n} from '@yolk-sdk/agent/protocol'\nimport { estimateAgentMessagesTokens, type TranscriptTokenEstimator } from './estimator.ts'\nimport { Data, Predicate } from 'effect'\n\nexport const defaultCompactionTailMessageCount = 16\n\nexport const defaultMinimumCompactionMessages = 2\n\nexport type CompactionSkipReason =\n | 'too_few_messages'\n | 'below_threshold'\n | 'empty_window'\n | 'not_smaller'\n\nexport type WindowCompactionPlanOptions = {\n readonly thresholdTokens: number\n readonly tailMessageCount?: number\n readonly minMessages?: number\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nexport type WindowCompactionPlan = Data.TaggedEnum<{\n Skip: {\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly beforeTokens: number\n }\n Compact: {\n readonly messages: ReadonlyArray<AgentMessage>\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly tailStartIndex: number\n readonly beforeTokens: number\n }\n}>\n\nexport type SkippedCompactionPlan = Extract<WindowCompactionPlan, { readonly _tag: 'Skip' }>\n\nexport type CompactCompactionPlan = Extract<WindowCompactionPlan, { readonly _tag: 'Compact' }>\n\nconst WindowCompactionPlan = Data.taggedEnum<WindowCompactionPlan>()\n\nexport type CompactionResult = Data.TaggedEnum<{\n Skipped: {\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n }\n Compacted: {\n readonly strategy: string\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n readonly afterTokens: number\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly summaryMessage: AgentMessage\n }\n}>\n\nexport type SkippedCompactionResult = Extract<CompactionResult, { readonly _tag: 'Skipped' }>\n\nexport type CompletedCompactionResult = Extract<CompactionResult, { readonly _tag: 'Compacted' }>\n\nexport const CompactionResult = Data.taggedEnum<CompactionResult>()\n\nexport type ApplyCompactionPlanOptions = {\n readonly strategy: string\n readonly summaryMessage: AgentMessage\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nconst estimator = (estimateTokens?: TranscriptTokenEstimator) =>\n estimateTokens ?? estimateAgentMessagesTokens\n\nconst skippedCompactionResult = (\n reason: CompactionSkipReason,\n messages: ReadonlyArray<AgentMessage>,\n beforeTokens: number\n): SkippedCompactionResult =>\n CompactionResult.Skipped({\n reason,\n messages,\n events: [],\n beforeTokens\n })\n\nexport const windowTailStartIndex = (\n messages: ReadonlyArray<AgentMessage>,\n tailMessageCount: number\n) => {\n const tailCount = Math.min(Math.max(0, tailMessageCount), Math.max(0, messages.length - 1))\n const initialIndex = Math.max(1, messages.length - tailCount)\n let index = initialIndex\n\n while (index > 1 && messages[index]?._tag === 'ToolResult') {\n index -= 1\n }\n\n return index\n}\n\nexport const planWindowCompaction = (\n messages: ReadonlyArray<AgentMessage>,\n options: WindowCompactionPlanOptions\n): WindowCompactionPlan => {\n const estimateTokens = estimator(options.estimateTokens)\n const beforeTokens = estimateTokens(messages)\n const minMessages = options.minMessages ?? defaultMinimumCompactionMessages\n\n if (messages.length <= minMessages) {\n return WindowCompactionPlan.Skip({\n reason: 'too_few_messages',\n messages,\n beforeTokens\n })\n }\n\n if (beforeTokens < options.thresholdTokens) {\n return WindowCompactionPlan.Skip({\n reason: 'below_threshold',\n messages,\n beforeTokens\n })\n }\n\n const tailStart = windowTailStartIndex(\n messages,\n options.tailMessageCount ?? defaultCompactionTailMessageCount\n )\n\n const compactedMessages = messages.slice(0, tailStart)\n const recentMessages = messages.slice(tailStart)\n\n if (compactedMessages.length === 0 || recentMessages.length === 0) {\n return WindowCompactionPlan.Skip({\n reason: 'empty_window',\n messages,\n beforeTokens\n })\n }\n\n return WindowCompactionPlan.Compact({\n messages,\n compactedMessages,\n recentMessages,\n tailStartIndex: tailStart,\n beforeTokens\n })\n}\n\nexport const applyCompactionPlan = (\n plan: WindowCompactionPlan,\n options: ApplyCompactionPlanOptions\n): CompactionResult => {\n if (Predicate.isTagged(plan, 'Skip')) {\n return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens)\n }\n\n const estimateTokens = estimator(options.estimateTokens)\n const messages = [options.summaryMessage, ...plan.recentMessages]\n const afterTokens = estimateTokens(messages)\n\n if (afterTokens >= plan.beforeTokens) {\n return skippedCompactionResult('not_smaller', plan.messages, plan.beforeTokens)\n }\n\n return CompactionResult.Compacted({\n strategy: options.strategy,\n messages,\n events: [\n CompactionStart.make({ strategy: options.strategy }),\n CompactionEnd.make({\n strategy: options.strategy,\n beforeTokens: plan.beforeTokens,\n afterTokens\n })\n ],\n beforeTokens: plan.beforeTokens,\n afterTokens,\n compactedMessages: plan.compactedMessages,\n recentMessages: plan.recentMessages,\n summaryMessage: options.summaryMessage\n })\n}\n"],"mappings":";;;;AASA,MAAa,oCAAoC;AAEjD,MAAa,mCAAmC;AAkChD,MAAM,uBAAuB,KAAK,WAAiC;AAyBnE,MAAa,mBAAmB,KAAK,WAA6B;AAQlE,MAAM,aAAa,mBACjB,kBAAkB;AAEpB,MAAM,2BACJ,QACA,UACA,iBAEA,iBAAiB,QAAQ;CACvB;CACA;CACA,QAAQ,CAAC;CACT;AACF,CAAC;AAEH,MAAa,wBACX,UACA,qBACG;CACH,MAAM,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,gBAAgB,GAAG,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC;CAE1F,IAAI,QADiB,KAAK,IAAI,GAAG,SAAS,SAAS,SAC5B;CAEvB,OAAO,QAAQ,KAAK,SAAS,QAAQ,SAAS,cAC5C,SAAS;CAGX,OAAO;AACT;AAEA,MAAa,wBACX,UACA,YACyB;CAEzB,MAAM,eADiB,UAAU,QAAQ,cACP,EAAE,QAAQ;CAC5C,MAAM,cAAc,QAAQ,eAAA;CAE5B,IAAI,SAAS,UAAU,aACrB,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,IAAI,eAAe,QAAQ,iBACzB,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,MAAM,YAAY,qBAChB,UACA,QAAQ,oBAAA,EACV;CAEA,MAAM,oBAAoB,SAAS,MAAM,GAAG,SAAS;CACrD,MAAM,iBAAiB,SAAS,MAAM,SAAS;CAE/C,IAAI,kBAAkB,WAAW,KAAK,eAAe,WAAW,GAC9D,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,OAAO,qBAAqB,QAAQ;EAClC;EACA;EACA;EACA,gBAAgB;EAChB;CACF,CAAC;AACH;AAEA,MAAa,uBACX,MACA,YACqB;CACrB,IAAI,UAAU,SAAS,MAAM,MAAM,GACjC,OAAO,wBAAwB,KAAK,QAAQ,KAAK,UAAU,KAAK,YAAY;CAG9E,MAAM,iBAAiB,UAAU,QAAQ,cAAc;CACvD,MAAM,WAAW,CAAC,QAAQ,gBAAgB,GAAG,KAAK,cAAc;CAChE,MAAM,cAAc,eAAe,QAAQ;CAE3C,IAAI,eAAe,KAAK,cACtB,OAAO,wBAAwB,eAAe,KAAK,UAAU,KAAK,YAAY;CAGhF,OAAO,iBAAiB,UAAU;EAChC,UAAU,QAAQ;EAClB;EACA,QAAQ,CACN,gBAAgB,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC,GACnD,cAAc,KAAK;GACjB,UAAU,QAAQ;GAClB,cAAc,KAAK;GACnB;EACF,CAAC,CACH;EACA,cAAc,KAAK;EACnB;EACA,mBAAmB,KAAK;EACxB,gBAAgB,KAAK;EACrB,gBAAgB,QAAQ;CAC1B,CAAC;AACH"}
1
+ {"version":3,"file":"window.mjs","names":[],"sources":["../../src/compaction/window.ts"],"sourcesContent":["import {\n CompactionEnd,\n CompactionStart,\n isInstructionMessage,\n latestInstructionMessages,\n type AgentEvent,\n type AgentMessage\n} from '@yolk-sdk/agent/protocol'\nimport { estimateAgentMessagesTokens, type TranscriptTokenEstimator } from './estimator.ts'\nimport { Data, Predicate } from 'effect'\n\nexport const defaultCompactionTailMessageCount = 16\n\nexport const defaultMinimumCompactionMessages = 2\n\nexport type CompactionSkipReason =\n | 'too_few_messages'\n | 'below_threshold'\n | 'empty_window'\n | 'not_smaller'\n\nexport type WindowCompactionPlanOptions = {\n readonly thresholdTokens: number\n readonly tailMessageCount?: number\n readonly minMessages?: number\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nexport type WindowCompactionPlan = Data.TaggedEnum<{\n Skip: {\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly beforeTokens: number\n }\n Compact: {\n readonly messages: ReadonlyArray<AgentMessage>\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly tailStartIndex: number\n readonly beforeTokens: number\n }\n}>\n\nexport type SkippedCompactionPlan = Extract<WindowCompactionPlan, { readonly _tag: 'Skip' }>\n\nexport type CompactCompactionPlan = Extract<WindowCompactionPlan, { readonly _tag: 'Compact' }>\n\nconst WindowCompactionPlan = Data.taggedEnum<WindowCompactionPlan>()\n\nexport type CompactionResult = Data.TaggedEnum<{\n Skipped: {\n readonly reason: CompactionSkipReason\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n }\n Compacted: {\n readonly strategy: string\n readonly messages: ReadonlyArray<AgentMessage>\n readonly events: ReadonlyArray<AgentEvent>\n readonly beforeTokens: number\n readonly afterTokens: number\n readonly compactedMessages: ReadonlyArray<AgentMessage>\n readonly recentMessages: ReadonlyArray<AgentMessage>\n readonly summaryMessage: AgentMessage\n }\n}>\n\nexport type SkippedCompactionResult = Extract<CompactionResult, { readonly _tag: 'Skipped' }>\n\nexport type CompletedCompactionResult = Extract<CompactionResult, { readonly _tag: 'Compacted' }>\n\nexport const CompactionResult = Data.taggedEnum<CompactionResult>()\n\nexport type ApplyCompactionPlanOptions = {\n readonly strategy: string\n readonly summaryMessage: AgentMessage\n readonly estimateTokens?: TranscriptTokenEstimator\n}\n\nconst estimator = (estimateTokens?: TranscriptTokenEstimator) =>\n estimateTokens ?? estimateAgentMessagesTokens\n\nconst skippedCompactionResult = (\n reason: CompactionSkipReason,\n messages: ReadonlyArray<AgentMessage>,\n beforeTokens: number\n): SkippedCompactionResult =>\n CompactionResult.Skipped({\n reason,\n messages,\n events: [],\n beforeTokens\n })\n\nexport const windowTailStartIndex = (\n messages: ReadonlyArray<AgentMessage>,\n tailMessageCount: number\n) => {\n const tailCount = Math.min(Math.max(0, tailMessageCount), Math.max(0, messages.length - 1))\n const initialIndex = Math.max(1, messages.length - tailCount)\n let index = initialIndex\n\n while (index > 1 && messages[index]?._tag === 'ToolResult') {\n index -= 1\n }\n\n return index\n}\n\nexport const planWindowCompaction = (\n messages: ReadonlyArray<AgentMessage>,\n options: WindowCompactionPlanOptions\n): WindowCompactionPlan => {\n const estimateTokens = estimator(options.estimateTokens)\n const beforeTokens = estimateTokens(messages)\n const minMessages = options.minMessages ?? defaultMinimumCompactionMessages\n\n if (messages.length <= minMessages) {\n return WindowCompactionPlan.Skip({\n reason: 'too_few_messages',\n messages,\n beforeTokens\n })\n }\n\n if (beforeTokens < options.thresholdTokens) {\n return WindowCompactionPlan.Skip({\n reason: 'below_threshold',\n messages,\n beforeTokens\n })\n }\n\n const tailStart = windowTailStartIndex(\n messages,\n options.tailMessageCount ?? defaultCompactionTailMessageCount\n )\n\n const compactedMessages = messages.slice(0, tailStart)\n const recentMessages = messages.slice(tailStart)\n\n if (compactedMessages.length === 0 || recentMessages.length === 0) {\n return WindowCompactionPlan.Skip({\n reason: 'empty_window',\n messages,\n beforeTokens\n })\n }\n\n return WindowCompactionPlan.Compact({\n messages,\n compactedMessages,\n recentMessages,\n tailStartIndex: tailStart,\n beforeTokens\n })\n}\n\nexport const applyCompactionPlan = (\n plan: WindowCompactionPlan,\n options: ApplyCompactionPlanOptions\n): CompactionResult => {\n if (Predicate.isTagged(plan, 'Skip')) {\n return skippedCompactionResult(plan.reason, plan.messages, plan.beforeTokens)\n }\n\n const estimateTokens = estimator(options.estimateTokens)\n\n // Host instructions (ADR 0007) the summary would fold away stay verbatim after it: the loop\n // compares instruction state with the uncompacted transcript and would never re-append them.\n const recentKeys = new Set(\n plan.recentMessages.flatMap(message =>\n isInstructionMessage(message) ? [message.instruction.key] : []\n )\n )\n\n const keptInstructions = latestInstructionMessages(plan.compactedMessages).filter(\n message => !recentKeys.has(message.instruction.key)\n )\n\n const messages = [options.summaryMessage, ...keptInstructions, ...plan.recentMessages]\n const afterTokens = estimateTokens(messages)\n\n if (afterTokens >= plan.beforeTokens) {\n return skippedCompactionResult('not_smaller', plan.messages, plan.beforeTokens)\n }\n\n return CompactionResult.Compacted({\n strategy: options.strategy,\n messages,\n events: [\n CompactionStart.make({ strategy: options.strategy }),\n CompactionEnd.make({\n strategy: options.strategy,\n beforeTokens: plan.beforeTokens,\n afterTokens\n })\n ],\n beforeTokens: plan.beforeTokens,\n afterTokens,\n compactedMessages: plan.compactedMessages,\n recentMessages: plan.recentMessages,\n summaryMessage: options.summaryMessage\n })\n}\n"],"mappings":";;;;AAWA,MAAa,oCAAoC;AAEjD,MAAa,mCAAmC;AAkChD,MAAM,uBAAuB,KAAK,WAAiC;AAyBnE,MAAa,mBAAmB,KAAK,WAA6B;AAQlE,MAAM,aAAa,mBACjB,kBAAkB;AAEpB,MAAM,2BACJ,QACA,UACA,iBAEA,iBAAiB,QAAQ;CACvB;CACA;CACA,QAAQ,CAAC;CACT;AACF,CAAC;AAEH,MAAa,wBACX,UACA,qBACG;CACH,MAAM,YAAY,KAAK,IAAI,KAAK,IAAI,GAAG,gBAAgB,GAAG,KAAK,IAAI,GAAG,SAAS,SAAS,CAAC,CAAC;CAE1F,IAAI,QADiB,KAAK,IAAI,GAAG,SAAS,SAAS,SAC5B;CAEvB,OAAO,QAAQ,KAAK,SAAS,QAAQ,SAAS,cAC5C,SAAS;CAGX,OAAO;AACT;AAEA,MAAa,wBACX,UACA,YACyB;CAEzB,MAAM,eADiB,UAAU,QAAQ,cACP,EAAE,QAAQ;CAC5C,MAAM,cAAc,QAAQ,eAAA;CAE5B,IAAI,SAAS,UAAU,aACrB,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,IAAI,eAAe,QAAQ,iBACzB,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,MAAM,YAAY,qBAChB,UACA,QAAQ,oBAAA,EACV;CAEA,MAAM,oBAAoB,SAAS,MAAM,GAAG,SAAS;CACrD,MAAM,iBAAiB,SAAS,MAAM,SAAS;CAE/C,IAAI,kBAAkB,WAAW,KAAK,eAAe,WAAW,GAC9D,OAAO,qBAAqB,KAAK;EAC/B,QAAQ;EACR;EACA;CACF,CAAC;CAGH,OAAO,qBAAqB,QAAQ;EAClC;EACA;EACA;EACA,gBAAgB;EAChB;CACF,CAAC;AACH;AAEA,MAAa,uBACX,MACA,YACqB;CACrB,IAAI,UAAU,SAAS,MAAM,MAAM,GACjC,OAAO,wBAAwB,KAAK,QAAQ,KAAK,UAAU,KAAK,YAAY;CAG9E,MAAM,iBAAiB,UAAU,QAAQ,cAAc;CAIvD,MAAM,aAAa,IAAI,IACrB,KAAK,eAAe,SAAQ,YAC1B,qBAAqB,OAAO,IAAI,CAAC,QAAQ,YAAY,GAAG,IAAI,CAAC,CAC/D,CACF;CAEA,MAAM,mBAAmB,0BAA0B,KAAK,iBAAiB,EAAE,QACzE,YAAW,CAAC,WAAW,IAAI,QAAQ,YAAY,GAAG,CACpD;CAEA,MAAM,WAAW;EAAC,QAAQ;EAAgB,GAAG;EAAkB,GAAG,KAAK;CAAc;CACrF,MAAM,cAAc,eAAe,QAAQ;CAE3C,IAAI,eAAe,KAAK,cACtB,OAAO,wBAAwB,eAAe,KAAK,UAAU,KAAK,YAAY;CAGhF,OAAO,iBAAiB,UAAU;EAChC,UAAU,QAAQ;EAClB;EACA,QAAQ,CACN,gBAAgB,KAAK,EAAE,UAAU,QAAQ,SAAS,CAAC,GACnD,cAAc,KAAK;GACjB,UAAU,QAAQ;GAClB,cAAc,KAAK;GACnB;EACF,CAAC,CACH;EACA,cAAc,KAAK;EACnB;EACA,mBAAmB,KAAK;EACxB,gBAAgB,KAAK;EACrB,gBAAgB,QAAQ;CAC1B,CAAC;AACH"}
@@ -7,5 +7,6 @@ import { LLMProvider, LLMRequest } from "./services/llm-provider.mjs";
7
7
  import { LoopConfig, LoopConfigSettings } from "./services/loop-config.mjs";
8
8
  import { ToolExecutionOptions, ToolExecutor } from "./services/tool-executor.mjs";
9
9
  import { decorateLLMProvider, makeAgentLoopLayer } from "./layer.mjs";
10
+ import { RunInstruction, instructionMessages } from "./instructions.mjs";
10
11
  import { AgentLoopRunId, ModelTurnConfig, PreparedToolBatch, RunConfig, ToolBatchConfig, loadInteractionReceipts, prepareToolBatch, run, runModelTurn, runToolBatch } from "./run.mjs";
11
- export { AbortError, type AgentLoopError, type AgentLoopRunId, type CollectModelTurnAttemptResult, ContextTransformError, type ContextTransformResult, ContextTransformer, FauxExhaustedError, LLMDone, LLMError, LLMEvent, LLMProvider, type LLMProviderError, LLMProviderToolResult, LLMReasoningDelta, type LLMRequest, LLMResponseIssue, LLMTextDelta, LLMToolCall, LLMToolInputDelta, LLMToolInputStart, LLMUsage, LoopConfig, type LoopConfigSettings, type ModelTurnCollection, type ModelTurnConfig, type ModelTurnResult, type PreparedToolBatch, type RunConfig, type ToolBatchConfig, ToolError, type ToolExecutionOptions, ToolExecutor, accumulateAssistantMessage, agentLoopErrorToAgentError, collectModelTurn, collectModelTurnAttempt, collectReasoning, collectText, collectToolCalls, decorateLLMProvider, loadInteractionReceipts, makeAgentLoopLayer, prepareToolBatch, run, runModelTurn, runToolBatch };
12
+ export { AbortError, type AgentLoopError, type AgentLoopRunId, type CollectModelTurnAttemptResult, ContextTransformError, type ContextTransformResult, ContextTransformer, FauxExhaustedError, LLMDone, LLMError, LLMEvent, LLMProvider, type LLMProviderError, LLMProviderToolResult, LLMReasoningDelta, type LLMRequest, LLMResponseIssue, LLMTextDelta, LLMToolCall, LLMToolInputDelta, LLMToolInputStart, LLMUsage, LoopConfig, type LoopConfigSettings, type ModelTurnCollection, type ModelTurnConfig, type ModelTurnResult, type PreparedToolBatch, type RunConfig, type RunInstruction, type ToolBatchConfig, ToolError, type ToolExecutionOptions, ToolExecutor, accumulateAssistantMessage, agentLoopErrorToAgentError, collectModelTurn, collectModelTurnAttempt, collectReasoning, collectText, collectToolCalls, decorateLLMProvider, instructionMessages, loadInteractionReceipts, makeAgentLoopLayer, prepareToolBatch, run, runModelTurn, runToolBatch };
@@ -7,5 +7,6 @@ import { LLMProvider } from "./services/llm-provider.mjs";
7
7
  import { LoopConfig } from "./services/loop-config.mjs";
8
8
  import { ToolExecutor } from "./services/tool-executor.mjs";
9
9
  import { decorateLLMProvider, makeAgentLoopLayer } from "./layer.mjs";
10
+ import { instructionMessages } from "./instructions.mjs";
10
11
  import { loadInteractionReceipts, prepareToolBatch, run, runModelTurn, runToolBatch } from "./run.mjs";
11
- export { AbortError, ContextTransformError, ContextTransformer, FauxExhaustedError, LLMDone, LLMError, LLMEvent, LLMProvider, LLMProviderToolResult, LLMReasoningDelta, LLMResponseIssue, LLMTextDelta, LLMToolCall, LLMToolInputDelta, LLMToolInputStart, LLMUsage, LoopConfig, ToolError, ToolExecutor, accumulateAssistantMessage, agentLoopErrorToAgentError, collectModelTurn, collectModelTurnAttempt, collectReasoning, collectText, collectToolCalls, decorateLLMProvider, loadInteractionReceipts, makeAgentLoopLayer, prepareToolBatch, run, runModelTurn, runToolBatch };
12
+ export { AbortError, ContextTransformError, ContextTransformer, FauxExhaustedError, LLMDone, LLMError, LLMEvent, LLMProvider, LLMProviderToolResult, LLMReasoningDelta, LLMResponseIssue, LLMTextDelta, LLMToolCall, LLMToolInputDelta, LLMToolInputStart, LLMUsage, LoopConfig, ToolError, ToolExecutor, accumulateAssistantMessage, agentLoopErrorToAgentError, collectModelTurn, collectModelTurnAttempt, collectReasoning, collectText, collectToolCalls, decorateLLMProvider, instructionMessages, loadInteractionReceipts, makeAgentLoopLayer, prepareToolBatch, run, runModelTurn, runToolBatch };
@@ -0,0 +1,39 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { AgentMessage, UserMessage } from "@yolk-sdk/agent/protocol";
3
+
4
+ //#region src/loop/instructions.d.ts
5
+ /**
6
+ * A host instruction kept current in the conversation (ADR 0007), for example the code mode tool
7
+ * catalog. Before each model request the loop compares `state` with the state of the latest
8
+ * instruction message with the same `key` in the transcript; only when they differ it appends one
9
+ * user-role instruction message whose content is `render(current)`. Earlier messages never change,
10
+ * so a changing instruction never rewrites the cached request prefix, and a redelivered step
11
+ * decides the same from the same transcript.
12
+ */
13
+ type RunInstruction = {
14
+ readonly key: string; /** The current state; `undefined` when the instruction no longer applies. */
15
+ readonly state: Schema.Json | undefined;
16
+ /**
17
+ * The model-visible text of a state, or a removal notice (`undefined`). Self-contained: it never
18
+ * sees the previous state, because compaction keeps only the latest message of each key
19
+ * (`latestInstructionMessages`), so that message alone must carry the whole instruction.
20
+ */
21
+ readonly render: (current: Schema.Json | undefined) => string;
22
+ };
23
+ /**
24
+ * The instruction messages a model request needs before it runs, in `instructions` order. States
25
+ * compare as JSON values, independent of object key order. A removal is recorded once as an
26
+ * instruction message without a state.
27
+ */
28
+ declare const instructionMessages: (messages: ReadonlyArray<AgentMessage>, instructions: ReadonlyArray<RunInstruction>) => ReadonlyArray<UserMessage>;
29
+ /**
30
+ * The instruction messages a request needs, appended. They join the run's created messages (so
31
+ * hosts persist them with the turn) but emit no `UserMessage` event: they are not user input.
32
+ */
33
+ declare const withInstructionMessages: (messages: ReadonlyArray<AgentMessage>, instructions: ReadonlyArray<RunInstruction> | undefined) => {
34
+ messages: readonly (UserMessage | import("@yolk-sdk/agent/protocol").AssistantAgentMessage | import("@yolk-sdk/agent/protocol").ToolResultMessage)[];
35
+ appended: readonly UserMessage[];
36
+ };
37
+ //#endregion
38
+ export { RunInstruction, instructionMessages, withInstructionMessages };
39
+ //# sourceMappingURL=instructions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.d.mts","names":[],"sources":["../../src/loop/instructions.ts"],"mappings":";;;;;;AAgBA;;;;;;KAAY,cAAA;EAAA,SACD,GAAA,UAQA;EAAA,SANA,KAAA,EAAO,MAAA,CAAO,IAAA;EAMW;;;AAAgB;AAQpD;EARoC,SAAzB,MAAA,GAAS,OAAA,EAAS,MAAA,CAAO,IAAI;AAAA;;;;;;cAQ3B,mBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,YAAA,EAAc,aAAA,CAAc,cAAA,MAC3B,aAAA,CAAc,WAAA;;;;;cA6BJ,uBAAA,GACX,QAAA,EAAU,aAAA,CAAc,YAAA,GACxB,YAAA,EAAc,aAAA,CAAc,cAAA"}
@@ -0,0 +1,42 @@
1
+ import { UserMessage, interactionJsonEquals, latestInstructionState } from "@yolk-sdk/agent/protocol";
2
+ //#region src/loop/instructions.ts
3
+ /**
4
+ * The instruction messages a model request needs before it runs, in `instructions` order. States
5
+ * compare as JSON values, independent of object key order. A removal is recorded once as an
6
+ * instruction message without a state.
7
+ */
8
+ const instructionMessages = (messages, instructions) => instructions.flatMap((instruction) => {
9
+ const previous = latestInstructionState(messages, instruction.key);
10
+ const current = instruction.state;
11
+ if (current === void 0) {
12
+ if (previous === void 0) return [];
13
+ return [UserMessage.make({
14
+ content: instruction.render(void 0),
15
+ instruction: { key: instruction.key }
16
+ })];
17
+ }
18
+ if (previous !== void 0 && interactionJsonEquals(previous, current)) return [];
19
+ const content = instruction.render(current);
20
+ return content.trim().length === 0 ? [] : [UserMessage.make({
21
+ content,
22
+ instruction: {
23
+ key: instruction.key,
24
+ state: current
25
+ }
26
+ })];
27
+ });
28
+ /**
29
+ * The instruction messages a request needs, appended. They join the run's created messages (so
30
+ * hosts persist them with the turn) but emit no `UserMessage` event: they are not user input.
31
+ */
32
+ const withInstructionMessages = (messages, instructions) => {
33
+ const appended = instructions === void 0 || instructions.length === 0 ? [] : instructionMessages(messages, instructions);
34
+ return {
35
+ messages: appended.length === 0 ? messages : [...messages, ...appended],
36
+ appended
37
+ };
38
+ };
39
+ //#endregion
40
+ export { instructionMessages, withInstructionMessages };
41
+
42
+ //# sourceMappingURL=instructions.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions.mjs","names":[],"sources":["../../src/loop/instructions.ts"],"sourcesContent":["import type * as Schema from 'effect/Schema'\nimport {\n interactionJsonEquals,\n latestInstructionState,\n UserMessage,\n type AgentMessage\n} from '@yolk-sdk/agent/protocol'\n\n/**\n * A host instruction kept current in the conversation (ADR 0007), for example the code mode tool\n * catalog. Before each model request the loop compares `state` with the state of the latest\n * instruction message with the same `key` in the transcript; only when they differ it appends one\n * user-role instruction message whose content is `render(current)`. Earlier messages never change,\n * so a changing instruction never rewrites the cached request prefix, and a redelivered step\n * decides the same from the same transcript.\n */\nexport type RunInstruction = {\n readonly key: string\n /** The current state; `undefined` when the instruction no longer applies. */\n readonly state: Schema.Json | undefined\n /**\n * The model-visible text of a state, or a removal notice (`undefined`). Self-contained: it never\n * sees the previous state, because compaction keeps only the latest message of each key\n * (`latestInstructionMessages`), so that message alone must carry the whole instruction.\n */\n readonly render: (current: Schema.Json | undefined) => string\n}\n\n/**\n * The instruction messages a model request needs before it runs, in `instructions` order. States\n * compare as JSON values, independent of object key order. A removal is recorded once as an\n * instruction message without a state.\n */\nexport const instructionMessages = (\n messages: ReadonlyArray<AgentMessage>,\n instructions: ReadonlyArray<RunInstruction>\n): ReadonlyArray<UserMessage> =>\n instructions.flatMap(instruction => {\n const previous = latestInstructionState(messages, instruction.key)\n const current = instruction.state\n\n if (current === undefined) {\n if (previous === undefined) return []\n\n return [\n UserMessage.make({\n content: instruction.render(undefined),\n instruction: { key: instruction.key }\n })\n ]\n }\n\n if (previous !== undefined && interactionJsonEquals(previous, current)) return []\n\n const content = instruction.render(current)\n\n return content.trim().length === 0\n ? []\n : [UserMessage.make({ content, instruction: { key: instruction.key, state: current } })]\n })\n\n/**\n * The instruction messages a request needs, appended. They join the run's created messages (so\n * hosts persist them with the turn) but emit no `UserMessage` event: they are not user input.\n */\nexport const withInstructionMessages = (\n messages: ReadonlyArray<AgentMessage>,\n instructions: ReadonlyArray<RunInstruction> | undefined\n) => {\n const appended =\n instructions === undefined || instructions.length === 0\n ? []\n : instructionMessages(messages, instructions)\n\n return { messages: appended.length === 0 ? messages : [...messages, ...appended], appended }\n}\n"],"mappings":";;;;;;;AAiCA,MAAa,uBACX,UACA,iBAEA,aAAa,SAAQ,gBAAe;CAClC,MAAM,WAAW,uBAAuB,UAAU,YAAY,GAAG;CACjE,MAAM,UAAU,YAAY;CAE5B,IAAI,YAAY,KAAA,GAAW;EACzB,IAAI,aAAa,KAAA,GAAW,OAAO,CAAC;EAEpC,OAAO,CACL,YAAY,KAAK;GACf,SAAS,YAAY,OAAO,KAAA,CAAS;GACrC,aAAa,EAAE,KAAK,YAAY,IAAI;EACtC,CAAC,CACH;CACF;CAEA,IAAI,aAAa,KAAA,KAAa,sBAAsB,UAAU,OAAO,GAAG,OAAO,CAAC;CAEhF,MAAM,UAAU,YAAY,OAAO,OAAO;CAE1C,OAAO,QAAQ,KAAK,EAAE,WAAW,IAC7B,CAAC,IACD,CAAC,YAAY,KAAK;EAAE;EAAS,aAAa;GAAE,KAAK,YAAY;GAAK,OAAO;EAAQ;CAAE,CAAC,CAAC;AAC3F,CAAC;;;;;AAMH,MAAa,2BACX,UACA,iBACG;CACH,MAAM,WACJ,iBAAiB,KAAA,KAAa,aAAa,WAAW,IAClD,CAAC,IACD,oBAAoB,UAAU,YAAY;CAEhD,OAAO;EAAE,UAAU,SAAS,WAAW,IAAI,WAAW,CAAC,GAAG,UAAU,GAAG,QAAQ;EAAG;CAAS;AAC7F"}
@@ -3,6 +3,7 @@ import { ContextTransformer } from "./services/context-transformer.mjs";
3
3
  import { LLMProvider } from "./services/llm-provider.mjs";
4
4
  import { LoopConfig } from "./services/loop-config.mjs";
5
5
  import { ToolExecutor } from "./services/tool-executor.mjs";
6
+ import { RunInstruction } from "./instructions.mjs";
6
7
  import { Effect, Stream } from "effect";
7
8
  import { AgentEvent, AgentMessage, AgentModelCapabilities, AgentReasoningEffort, AgentUsage, HitlRequest, HitlResponse, InputToolHandler, InteractionHost, InteractionPreflight, InteractionReceipt, InteractionRef, ToolApprovalPreviewer, ToolCall, ToolDef } from "@yolk-sdk/agent/protocol";
8
9
 
@@ -12,6 +13,11 @@ type RunConfig = {
12
13
  readonly messages: ReadonlyArray<AgentMessage>;
13
14
  readonly systemPrompt: string;
14
15
  readonly tools: ReadonlyArray<ToolDef>;
16
+ /** Host instructions kept current in the conversation (ADR 0007), for example
17
+ * `ResolvedToolSet.instructions`: before each model request a changed one is appended as a
18
+ * user-role instruction message, never rewriting earlier messages.
19
+ */
20
+ readonly instructions?: ReadonlyArray<RunInstruction>;
15
21
  readonly hitlResponses?: ReadonlyArray<HitlResponse>; /** Server-side input validators by tool name (from ResolvedToolSet.inputs). */
16
22
  readonly inputs?: Readonly<Record<string, InputToolHandler>>;
17
23
  /** Server-side interaction preflight by tool name (from ResolvedToolSet.interactions).
@@ -35,9 +41,16 @@ type RunConfig = {
35
41
  readonly resume?: boolean;
36
42
  readonly model: string;
37
43
  readonly reasoningEffort?: AgentReasoningEffort;
38
- readonly capabilities?: AgentModelCapabilities;
44
+ readonly capabilities?: AgentModelCapabilities; /** Forwarded as `LLMRequest.cacheKey` on every model request of this run. */
45
+ readonly cacheKey?: string;
39
46
  };
40
- type ModelTurnConfig = RunConfig & {
47
+ /**
48
+ * One model step for durable hosts. They append and persist `instructionMessages(...)` before
49
+ * the step themselves (ADR 0007), so `instructions` is not accepted here: a step cannot report
50
+ * messages it appended, and they would be re-appended, unpersisted, on every step.
51
+ */
52
+ type ModelTurnConfig = Omit<RunConfig, 'instructions'> & {
53
+ readonly instructions?: never;
41
54
  readonly turn: number;
42
55
  };
43
56
  type ToolBatchConfig = {
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.mts","names":[],"sources":["../../src/loop/run.ts"],"mappings":"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"}
1
+ {"version":3,"file":"run.d.mts","names":[],"sources":["../../src/loop/run.ts"],"mappings":"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"}
package/dist/loop/run.mjs CHANGED
@@ -4,13 +4,14 @@ import { ContextTransformer } from "./services/context-transformer.mjs";
4
4
  import { LLMProvider } from "./services/llm-provider.mjs";
5
5
  import { LoopConfig } from "./services/loop-config.mjs";
6
6
  import { ToolExecutor, unavailableToolExecutor } from "./services/tool-executor.mjs";
7
+ import { withInstructionMessages } from "./instructions.mjs";
7
8
  import "../protocol/change-preview.mjs";
8
9
  import { truncateCodePoints } from "../protocol/bounded-text.mjs";
9
10
  import { validInteractionReceipt as validInteractionReceipt$1 } from "../protocol/tool.mjs";
10
11
  import { withToolArgumentsErrorHint } from "../protocol/tool-argument-hints.mjs";
11
12
  import { Array as Array$1, Clock, Data, Effect, Match, Option, Predicate, Ref, Stream, Struct } from "effect";
12
13
  import * as Schema$1 from "effect/Schema";
13
- import { AgentAwaitingInput, AgentEnd, AgentRetry, AgentStart, AssistantMessageEvent, InputRequest, InputRequested, InteractionRequest, InteractionRequested, InteractionResponse, LLMReasoningDelta, LLMStreamEnd, LLMStreamStart, LLMTextDelta, ProviderToolResult, QuestionRequest, QuestionRequested, QuestionToolParams, SubagentCompleted, SubagentStarted, ToolApprovalRequest, ToolApprovalRequested, ToolExecutionAccepted, ToolExecutionCompleted, ToolExecutionError, ToolExecutionStarted, ToolInputDelta, ToolInputEnd, ToolInputStart, ToolResult, TurnEnd, TurnStart, UsageUpdate, addAgentUsage, assistantHostToolCalls, contentParts, contentPreview, decodeBackgroundToolInput, formatInputResponseContent, formatQuestionResponseContent, hitlResponseEvent, inputRequestId, inputResponseStructuredContent, interactionRequestId, makeSubagentRunId, providerToolDefs, questionResponseStructuredContent, toolResultMessageFromResult, zeroAgentUsage } from "@yolk-sdk/agent/protocol";
14
+ import { AgentAwaitingInput, AgentEnd, AgentRetry, AgentStart, AssistantMessageEvent, InputRequest, InputRequested, InteractionRequest, InteractionRequested, InteractionResponse, LLMReasoningDelta, LLMStreamEnd, LLMStreamStart, LLMTextDelta, ProviderToolResult, QuestionRequest, QuestionRequested, QuestionToolParams, SubagentCompleted, SubagentStarted, ToolApprovalRequest, ToolApprovalRequested, ToolExecutionAccepted, ToolExecutionCompleted, ToolExecutionError, ToolExecutionStarted, ToolInputDelta, ToolInputEnd, ToolInputStart, ToolResult, TurnEnd, TurnStart, UsageUpdate, addAgentUsage, assistantHostToolCalls, contentParts, contentPreview, declaredToolDefs, decodeBackgroundToolInput, formatInputResponseContent, formatQuestionResponseContent, hitlResponseEvent, inputRequestId, inputResponseStructuredContent, interactionRequestId, makeSubagentRunId, providerToolDefs, questionResponseStructuredContent, toolResultMessageFromResult, zeroAgentUsage } from "@yolk-sdk/agent/protocol";
14
15
  //#region src/loop/run.ts
15
16
  const objectField = (input, key) => {
16
17
  if (!Predicate.isObjectOrArray(input)) return;
@@ -783,14 +784,23 @@ const makeModelOnlyAfterLlmStream = (input, llmEventsRef) => Stream.unwrap(Effec
783
784
  })
784
785
  ]);
785
786
  }));
787
+ /**
788
+ * One provider request. `transcript` is the full conversation before any context transform:
789
+ * deferred tools activated by `tool_search` results in it are declared (ADR 0007).
790
+ */
791
+ const providerRequest = (config, messages, transcript) => {
792
+ const request = {
793
+ messages,
794
+ tools: declaredToolDefs(config.tools, transcript),
795
+ model: config.model,
796
+ reasoningEffort: config.reasoningEffort,
797
+ systemPrompt: config.systemPrompt
798
+ };
799
+ if (config.cacheKey !== void 0) request.cacheKey = config.cacheKey;
800
+ return request;
801
+ };
786
802
  const makeLlmStream = (input, llmEvents, result) => {
787
- const makeStream = () => input.provider.stream({
788
- messages: result.messages,
789
- tools: providerToolDefs(input.config.tools),
790
- model: input.config.model,
791
- reasoningEffort: input.config.reasoningEffort,
792
- systemPrompt: input.config.systemPrompt
793
- });
803
+ const makeStream = () => input.provider.stream(providerRequest(input.config, result.messages, input.currentMessages));
794
804
  return Stream.fromIterable(result.events).pipe(Stream.concat(withProviderRetries(makeStream(), input.loopConfig, makeStream, 1))).pipe(Stream.tap((event) => {
795
805
  if (!isLlmEvent(event)) return Effect.void;
796
806
  const appendEvent = Ref.update(llmEvents, (events) => [...events, event]);
@@ -799,13 +809,7 @@ const makeLlmStream = (input, llmEvents, result) => {
799
809
  }), Stream.flatMap((event) => Predicate.isTagged(event, "AgentRetry") ? Stream.make(event) : isLlmEvent(event) ? Stream.fromIterable(toLlmEvent(event)) : Stream.make(event)), Stream.concat(makeAfterLlmStream(input, llmEvents)));
800
810
  };
801
811
  const makeModelOnlyLlmStream = (input, llmEvents, result) => {
802
- const makeStream = () => input.provider.stream({
803
- messages: result.messages,
804
- tools: providerToolDefs(input.config.tools),
805
- model: input.config.model,
806
- reasoningEffort: input.config.reasoningEffort,
807
- systemPrompt: input.config.systemPrompt
808
- });
812
+ const makeStream = () => input.provider.stream(providerRequest(input.config, result.messages, input.currentMessages));
809
813
  return Stream.fromIterable(result.events).pipe(Stream.concat(withProviderRetries(makeStream(), input.loopConfig, makeStream, 1))).pipe(Stream.tap((event) => {
810
814
  if (!isLlmEvent(event)) return Effect.void;
811
815
  const appendEvent = Ref.update(llmEvents, (events) => [...events, event]);
@@ -822,13 +826,27 @@ const withoutConsumedAnswers = (config) => Struct.omit(config, [
822
826
  "awaitingRequests",
823
827
  "resume"
824
828
  ]);
829
+ /**
830
+ * Appends the instruction messages this model request needs (ADR 0007) to the turn's messages and
831
+ * to the run's created messages, so later turns and the host's persisted transcript see them.
832
+ */
833
+ const withTurnInstructions = (input) => Effect.gen(function* () {
834
+ const { messages, appended } = withInstructionMessages(input.currentMessages, input.config.instructions);
835
+ if (appended.length === 0) return input;
836
+ yield* Ref.update(input.createdMessages, (created) => [...created, ...appended]);
837
+ return {
838
+ ...input,
839
+ currentMessages: messages
840
+ };
841
+ });
825
842
  const makeTurnStream = (input) => Stream.suspend(() => {
826
843
  if (input.turn > input.loopConfig.maxTurns) return Stream.fail(new AbortError({ reason: "max_turns" }));
827
844
  const llmStream = Stream.unwrap(Effect.gen(function* () {
828
845
  const llmEvents = yield* Ref.make([]);
829
- const result = yield* input.contextTransformer.transform(input.currentMessages);
846
+ const instructed = yield* withTurnInstructions(input);
847
+ const result = yield* input.contextTransformer.transform(instructed.currentMessages);
830
848
  yield* validateCapabilities(input.config, result.messages);
831
- return makeLlmStream(input, llmEvents, result);
849
+ return makeLlmStream(instructed, llmEvents, result);
832
850
  }));
833
851
  return Stream.fromIterable([TurnStart.make({ turn: input.turn }), LLMStreamStart.make({ turn: input.turn })]).pipe(Stream.concat(llmStream));
834
852
  });