@yolk-sdk/agent 0.1.0-canary.55 → 0.1.0-canary.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,6 +13,7 @@ pnpm add @yolk-sdk/agent@canary effect
13
13
  Add `react` if you use `@yolk-sdk/agent/react`.
14
14
 
15
15
  Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
16
+ Published package metadata requires Node.js 22+.
16
17
 
17
18
  ## Subpaths
18
19
 
@@ -107,21 +108,40 @@ const program = run({
107
108
  // Provide LLM provider, loop config, context transformer, and tool executor layers in the host app.
108
109
  ```
109
110
 
111
+ ## OAuth credentials
112
+
113
+ `@yolk-sdk/agent/oauth` defines provider-neutral access-token, broker, freshness, and credential-source
114
+ contracts. Provider subpaths add vendor request/response conversion without owning persistence.
115
+
116
+ ```ts
117
+ import { credentialSourceFromBroker, type TokenBrokerClient } from '@yolk-sdk/agent/oauth'
118
+
119
+ const makeTokenProgram = (hostBroker: TokenBrokerClient) =>
120
+ credentialSourceFromBroker(hostBroker, {
121
+ provider: 'openai-codex',
122
+ subjectId: 'host-user-id'
123
+ }).getAccessToken({ minTtlSeconds: 300 })
124
+ ```
125
+
126
+ `hostBroker` is a host implementation of `TokenBrokerClient`. The host stores, refreshes, revokes,
127
+ and authorizes credentials; the package receives short-lived access tokens and never persists
128
+ secrets.
129
+
110
130
  ## Provider configuration
111
131
 
112
- Provider output limits are required and host-owned. Yolk does not infer model limits or apply hidden
113
- fallbacks.
132
+ Provider output limits are host-owned when the endpoint supports them. Yolk does not infer model
133
+ limits or apply hidden fallbacks.
114
134
 
115
- | Provider factory | Required output-limit field |
116
- | ---------------------------------- | --------------------------- |
117
- | `makeOpenAiProviderLayer` | `maxCompletionTokens` |
118
- | `makeOpenAiCodexProviderLayer` | `maxOutputTokens` |
119
- | `makeAnthropicClaudeProviderLayer` | `maxTokens` |
135
+ | Provider factory | Output-limit field |
136
+ | ---------------------------------- | --------------------- |
137
+ | `makeOpenAiProviderLayer` | `maxCompletionTokens` |
138
+ | `makeOpenAiCodexProviderLayer` | none |
139
+ | `makeAnthropicClaudeProviderLayer` | `maxTokens` |
120
140
 
121
- The public `toOpenAiRequestBody`, `toOpenAiCodexRequestBody`, and
122
- `toAnthropicClaudeRequestBody` helpers require the same limit configuration. ChatGPT subscription
123
- Codex rejects vendor `max_output_tokens`, so the Codex adapter retains the required host config but
124
- omits that field from requests. `OpenAiProviderLayer` reads both `OPENAI_API_KEY` and integer
141
+ The public `toOpenAiRequestBody` and `toAnthropicClaudeRequestBody` helpers require the matching
142
+ limit configuration. ChatGPT subscription Codex rejects vendor `max_output_tokens`, so
143
+ `makeOpenAiCodexProviderLayer` and `toOpenAiCodexRequestBody` ignore the optional deprecated
144
+ `maxOutputTokens` compatibility field. `OpenAiProviderLayer` reads both `OPENAI_API_KEY` and integer
125
145
  `OPENAI_MAX_COMPLETION_TOKENS` through Effect Config.
126
146
 
127
147
  Set optional `reasoningEffort` on `run` or `runRuntime`; provider adapters lower it to vendor
@@ -129,6 +149,15 @@ configuration. Anthropic Claude forwards `low`, `medium`, `high`, and `xhigh` th
129
149
  `output_config.effort`. It omits `minimal`, which Anthropic does not support. Hosts remain
130
150
  responsible for choosing an effort accepted by the selected provider and model.
131
151
 
152
+ ### Anthropic tool schemas
153
+
154
+ Claude subscription OAuth rejects some valid JSON Schema constructs that Effect Schema can emit
155
+ for unions, refinements, and tuples. The Claude adapter therefore projects tool parameters to a
156
+ provider-compatible object schema without `anyOf`, `oneOf`, `allOf`, or tuple-only `prefixItems`.
157
+ When a constraint cannot be represented faithfully, the projection widens the model-facing schema
158
+ rather than excluding a valid call. Tool execution remains safe because `makeTool` validates the
159
+ returned arguments against the original Effect Schema before invoking the executor.
160
+
132
161
  ## Provider failures and retries
133
162
 
134
163
  Provider adapters classify safe failure metadata at the boundary. The loop owns bounded retry
@@ -194,6 +223,14 @@ transformers. Exact provider-request accounting must also include system prompts
194
223
  vendor framing, and a safety margin. Reuse one estimator for warnings, planning, and before/after
195
224
  checks; tokenizer dependencies remain host-owned.
196
225
 
226
+ ## Skillsets
227
+
228
+ `@yolk-sdk/agent/skillset` parses skill Markdown and slash-command Markdown into portable
229
+ `SkillInfo`, `CommandInfo`, and `SkillsetManifest` data. Use `parseSkillMarkdown` /
230
+ `parseCommandMarkdown` at file or database boundaries, `renderCommand` for command invocation, and
231
+ `mergeSkillsets` to combine host sources. Earlier sources win name conflicts; duplicate names
232
+ inside one source fail validation. Hosts own file discovery, storage, enablement, and source order.
233
+
197
234
  ## Protocol content
198
235
 
199
236
  `Content` is either plain text or ordered parts:
@@ -403,7 +440,8 @@ Recommended setup:
403
440
  - use `makeSubagentRunId(call.id)` for protocol-aligned run ids
404
441
  - optionally configure model and reasoning-effort choices so the parent can select child runtime settings
405
442
  - treat omitted `model` and `reasoning_effort` parameters as inheritance of host runtime settings
406
- - return `makeSubagentToolResult(...)` so UI can show subagent id, type, status, model, reasoning effort, and timing
443
+ - return `makeSubagentToolResult(...)` so UI can show subagent id, type, status, model, reasoning effort, timing, optional usage/turns, and typed failure metadata
444
+ - use `subagentUsageFromToolResult(...)` when a host must add child usage to cumulative workflow usage
407
445
 
408
446
  Keep host-owned subagent execution wiring outside this package; pass only the package subagent contract across the boundary.
409
447
 
@@ -422,7 +460,9 @@ aborts, and implementation bugs outside typed tool execution.
422
460
  ## Host responsibilities
423
461
 
424
462
  - Choose models/providers and provide an LLM provider layer, using SDK provider subpaths or host adapters.
463
+ - Store, refresh, revoke, and authorize OAuth credentials; expose only runtime access tokens.
425
464
  - Configure model-specific provider output-token limits.
465
+ - Build UI components and styling, own auth, and wire headless React hooks to host transports.
426
466
  - Persist sessions, transcripts, and append logs.
427
467
  - Persist/return one `ToolResultMessage` for every host tool call, including `isError` failures.
428
468
  - Persist terminal provider failures and clear active run ids where applicable.
@@ -8,7 +8,8 @@ type ContextBudget = {
8
8
  };
9
9
  type ContextBudgetInput = {
10
10
  readonly contextWindowTokens: number;
11
- readonly reservedOutputTokens: number;
11
+ readonly reservedOutputTokens: number; /** Optional provider- or endpoint-specific ceiling below the model's total context window. */
12
+ readonly maxInputTokens?: number;
12
13
  readonly warningRatio: number;
13
14
  readonly compactionRatio: number;
14
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"budget.d.mts","names":[],"sources":["../../src/compaction/budget.ts"],"mappings":";KAAY,aAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,iBAAA;EAAA,SACA,kBAAA;EAAA,SACA,qBAAA;AAAA;AAAA,KAGC,kBAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,YAAA;EAAA,SACA,eAAA;AAAA;AAAA,cAGE,iBAAA,GAAqB,KAAA,EAAO,kBAAA,KAAqB,aAU7D;AAAA,cAEY,uBAAA,GAA2B,MAAA,UAAgB,MAAA,EAAQ,aAAa;AAAA,KAGjE,mBAAA;AAAA,cAEC,mBAAA,GAAuB,MAAA,UAAgB,MAAA,EAAQ,aAAA,KAAgB,mBAU3E"}
1
+ {"version":3,"file":"budget.d.mts","names":[],"sources":["../../src/compaction/budget.ts"],"mappings":";KAAY,aAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,iBAAA;EAAA,SACA,kBAAA;EAAA,SACA,qBAAA;AAAA;AAAA,KAGC,kBAAA;EAAA,SACD,mBAAA;EAAA,SACA,oBAAA,UALqB;EAAA,SAOrB,cAAA;EAAA,SACA,YAAA;EAAA,SACA,eAAA;AAAA;AAAA,cAGE,iBAAA,GAAqB,KAAA,EAAO,kBAAA,KAAqB,aAkB7D;AAAA,cAEY,uBAAA,GAA2B,MAAA,UAAgB,MAAA,EAAQ,aAAa;AAAA,KAGjE,mBAAA;AAAA,cAEC,mBAAA,GAAuB,MAAA,UAAgB,MAAA,EAAQ,aAAA,KAAgB,mBAU3E"}
@@ -1,6 +1,8 @@
1
1
  //#region src/compaction/budget.ts
2
2
  const makeContextBudget = (input) => {
3
- const usableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens);
3
+ const contextUsableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens);
4
+ const maxInputTokens = input.maxInputTokens === void 0 || !Number.isFinite(input.maxInputTokens) ? contextUsableInputTokens : Math.max(0, input.maxInputTokens);
5
+ const usableInputTokens = Math.min(contextUsableInputTokens, maxInputTokens);
4
6
  return {
5
7
  contextWindowTokens: input.contextWindowTokens,
6
8
  reservedOutputTokens: input.reservedOutputTokens,
@@ -1 +1 @@
1
- {"version":3,"file":"budget.mjs","names":[],"sources":["../../src/compaction/budget.ts"],"sourcesContent":["export type ContextBudget = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n readonly usableInputTokens: number\n readonly warningInputTokens: number\n readonly compactionInputTokens: number\n}\n\nexport type ContextBudgetInput = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n readonly warningRatio: number\n readonly compactionRatio: number\n}\n\nexport const makeContextBudget = (input: ContextBudgetInput): ContextBudget => {\n const usableInputTokens = Math.max(0, input.contextWindowTokens - input.reservedOutputTokens)\n\n return {\n contextWindowTokens: input.contextWindowTokens,\n reservedOutputTokens: input.reservedOutputTokens,\n usableInputTokens,\n warningInputTokens: Math.floor(usableInputTokens * input.warningRatio),\n compactionInputTokens: Math.floor(usableInputTokens * input.compactionRatio)\n }\n}\n\nexport const contextBudgetUsageRatio = (tokens: number, budget: ContextBudget) =>\n budget.usableInputTokens === 0 ? 0 : tokens / budget.usableInputTokens\n\nexport type ContextBudgetStatus = 'idle' | 'normal' | 'warning' | 'compact'\n\nexport const contextBudgetStatus = (tokens: number, budget: ContextBudget): ContextBudgetStatus => {\n if (tokens <= 0) {\n return 'idle'\n }\n\n if (tokens >= budget.compactionInputTokens) {\n return 'compact'\n }\n\n return tokens >= budget.warningInputTokens ? 'warning' : 'normal'\n}\n"],"mappings":";AAeA,MAAa,qBAAqB,UAA6C;CAC7E,MAAM,oBAAoB,KAAK,IAAI,GAAG,MAAM,sBAAsB,MAAM,oBAAoB;CAE5F,OAAO;EACL,qBAAqB,MAAM;EAC3B,sBAAsB,MAAM;EAC5B;EACA,oBAAoB,KAAK,MAAM,oBAAoB,MAAM,YAAY;EACrE,uBAAuB,KAAK,MAAM,oBAAoB,MAAM,eAAe;CAC7E;AACF;AAEA,MAAa,2BAA2B,QAAgB,WACtD,OAAO,sBAAsB,IAAI,IAAI,SAAS,OAAO;AAIvD,MAAa,uBAAuB,QAAgB,WAA+C;CACjG,IAAI,UAAU,GACZ,OAAO;CAGT,IAAI,UAAU,OAAO,uBACnB,OAAO;CAGT,OAAO,UAAU,OAAO,qBAAqB,YAAY;AAC3D"}
1
+ {"version":3,"file":"budget.mjs","names":[],"sources":["../../src/compaction/budget.ts"],"sourcesContent":["export type ContextBudget = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n readonly usableInputTokens: number\n readonly warningInputTokens: number\n readonly compactionInputTokens: number\n}\n\nexport type ContextBudgetInput = {\n readonly contextWindowTokens: number\n readonly reservedOutputTokens: number\n /** Optional provider- or endpoint-specific ceiling below the model's total context window. */\n readonly maxInputTokens?: number\n readonly warningRatio: number\n readonly compactionRatio: number\n}\n\nexport const makeContextBudget = (input: ContextBudgetInput): ContextBudget => {\n const contextUsableInputTokens = Math.max(\n 0,\n input.contextWindowTokens - input.reservedOutputTokens\n )\n const maxInputTokens =\n input.maxInputTokens === undefined || !Number.isFinite(input.maxInputTokens)\n ? contextUsableInputTokens\n : Math.max(0, input.maxInputTokens)\n const usableInputTokens = Math.min(contextUsableInputTokens, maxInputTokens)\n\n return {\n contextWindowTokens: input.contextWindowTokens,\n reservedOutputTokens: input.reservedOutputTokens,\n usableInputTokens,\n warningInputTokens: Math.floor(usableInputTokens * input.warningRatio),\n compactionInputTokens: Math.floor(usableInputTokens * input.compactionRatio)\n }\n}\n\nexport const contextBudgetUsageRatio = (tokens: number, budget: ContextBudget) =>\n budget.usableInputTokens === 0 ? 0 : tokens / budget.usableInputTokens\n\nexport type ContextBudgetStatus = 'idle' | 'normal' | 'warning' | 'compact'\n\nexport const contextBudgetStatus = (tokens: number, budget: ContextBudget): ContextBudgetStatus => {\n if (tokens <= 0) {\n return 'idle'\n }\n\n if (tokens >= budget.compactionInputTokens) {\n return 'compact'\n }\n\n return tokens >= budget.warningInputTokens ? 'warning' : 'normal'\n}\n"],"mappings":";AAiBA,MAAa,qBAAqB,UAA6C;CAC7E,MAAM,2BAA2B,KAAK,IACpC,GACA,MAAM,sBAAsB,MAAM,oBACpC;CACA,MAAM,iBACJ,MAAM,mBAAmB,KAAA,KAAa,CAAC,OAAO,SAAS,MAAM,cAAc,IACvE,2BACA,KAAK,IAAI,GAAG,MAAM,cAAc;CACtC,MAAM,oBAAoB,KAAK,IAAI,0BAA0B,cAAc;CAE3E,OAAO;EACL,qBAAqB,MAAM;EAC3B,sBAAsB,MAAM;EAC5B;EACA,oBAAoB,KAAK,MAAM,oBAAoB,MAAM,YAAY;EACrE,uBAAuB,KAAK,MAAM,oBAAoB,MAAM,eAAe;CAC7E;AACF;AAEA,MAAa,2BAA2B,QAAgB,WACtD,OAAO,sBAAsB,IAAI,IAAI,SAAS,OAAO;AAIvD,MAAa,uBAAuB,QAAgB,WAA+C;CACjG,IAAI,UAAU,GACZ,OAAO;CAGT,IAAI,UAAU,OAAO,uBACnB,OAAO;CAGT,OAAO,UAAU,OAAO,qBAAqB,YAAY;AAC3D"}
@@ -1 +1 @@
1
- {"version":3,"file":"claude-provider.d.mts","names":[],"sources":["../../../src/providers/anthropic/claude-provider.ts"],"mappings":";;;;;;KAoDY,6BAAA;EAAA,SACD,KAAA,EAAO,gBAAA;EAAA,SACP,WAAA;EAAA,SACA,SAAA;EAAA,SACA,YAAA,GAAe,QAAA,CAAS,MAAA;AAAA;AAAA,KAG9B,kBAAA;EAAA,SACM,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGV,oBAAA,GAAuB,kBAAkB;AAAA,KAEzC,mBAAA;EAAA,SACM,IAAA;EAAA,SACA,MAAA;IAAA,SAEM,IAAA;IAAA,SACA,UAAA;IAAA,SACA,IAAA;EAAA;IAAA,SAGA,IAAA;IAAA,SACA,GAAA;EAAA;AAAA;AAAA,KAIZ,sBAAA;EAAA,SACM,IAAA;EAAA,SACA,MAAA;IAAA,SAEM,IAAA;IAAA,SACA,UAAA;IAAA,SACA,IAAA;EAAA;IAAA,SAGA,IAAA;IAAA,SACA,GAAA;EAAA;EAAA,SAEN,KAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACM,IAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGN,+BAAA,GACD,kBAAA,GACA,mBAAA,GACA,sBAAA;AAAA,KAEC,wBAAA;EAAA,SACM,IAAA;EAAA,SACA,WAAA;EAAA,SACA,OAAA,WAAkB,aAAa,CAAC,+BAAA;EAAA,SAChC,QAAA;AAAA;AAAA,KAGN,kBAAA,GACD,kBAAA,GACA,mBAAA,GACA,sBAAA,GACA,wBAAA;AAAA,KACC,uBAAA,GAA0B,kBAAA,GAAqB,qBAAqB;AAAA,KAEpE,gBAAA;EAAA,SACU,IAAA;EAAA,SAAuB,OAAA,WAAkB,aAAA,CAAc,kBAAA;AAAA;EAAA,SACvD,IAAA;EAAA,SAA4B,OAAA,EAAS,aAAA,CAAc,uBAAA;AAAA;AAAA,KAS7D,aAAA;EAAA,SACM,IAAA;EAAA,SACA,WAAA;EAAA,SACA,YAAA;AAAA;AAAA,KAGN,oBAAA;EAAA,SACM,KAAA;EAAA,SACA,MAAA,EAAQ,aAAA,CAAc,oBAAA;EAAA,SACtB,QAAA,EAAU,aAAA,CAAc,gBAAA;EAAA,SACxB,UAAA;EAAA,SACA,aAAA;IAAA,SACE,MAAA;EAAA;EAAA,SAEF,MAAA;EAAA,SACA,KAAA,GAAQ,aAAA,CAAc,aAAA;AAAA;AAAA,cA4zBpB,4BAAA,GACX,OAAA,EAAS,UAAA,EACT,MAAA;EAAA,SAAmB,SAAA;EAAA,SAA4B,MAAA;AAAA,MAC9C,MAAA,CAAO,MAAA,CAAO,oBAAA,EAAsB,QAAA;AAAA,cAmmB1B,6BAAA,GACX,QAAA,EAAU,kBAAA,CAAmB,kBAAA,KAC5B,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,QAAA;AAAA,cAoGd,gCAAA,GAAoC,MAAA,EAAQ,6BAAA,KAA6B,KAAA,CAAA,KAAA,CAAA,WAAA,SAAA,UAAA,CAAA,UAAA"}
1
+ {"version":3,"file":"claude-provider.d.mts","names":[],"sources":["../../../src/providers/anthropic/claude-provider.ts"],"mappings":";;;;;;KAoDY,6BAAA;EAAA,SACD,KAAA,EAAO,gBAAA;EAAA,SACP,WAAA;EAAA,SACA,SAAA;EAAA,SACA,YAAA,GAAe,QAAA,CAAS,MAAA;AAAA;AAAA,KAG9B,kBAAA;EAAA,SACM,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGV,oBAAA,GAAuB,kBAAkB;AAAA,KAEzC,mBAAA;EAAA,SACM,IAAA;EAAA,SACA,MAAA;IAAA,SAEM,IAAA;IAAA,SACA,UAAA;IAAA,SACA,IAAA;EAAA;IAAA,SAGA,IAAA;IAAA,SACA,GAAA;EAAA;AAAA;AAAA,KAIZ,sBAAA;EAAA,SACM,IAAA;EAAA,SACA,MAAA;IAAA,SAEM,IAAA;IAAA,SACA,UAAA;IAAA,SACA,IAAA;EAAA;IAAA,SAGA,IAAA;IAAA,SACA,GAAA;EAAA;EAAA,SAEN,KAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACM,IAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGN,+BAAA,GACD,kBAAA,GACA,mBAAA,GACA,sBAAA;AAAA,KAEC,wBAAA;EAAA,SACM,IAAA;EAAA,SACA,WAAA;EAAA,SACA,OAAA,WAAkB,aAAa,CAAC,+BAAA;EAAA,SAChC,QAAA;AAAA;AAAA,KAGN,kBAAA,GACD,kBAAA,GACA,mBAAA,GACA,sBAAA,GACA,wBAAA;AAAA,KACC,uBAAA,GAA0B,kBAAA,GAAqB,qBAAqB;AAAA,KAEpE,gBAAA;EAAA,SACU,IAAA;EAAA,SAAuB,OAAA,WAAkB,aAAA,CAAc,kBAAA;AAAA;EAAA,SACvD,IAAA;EAAA,SAA4B,OAAA,EAAS,aAAA,CAAc,uBAAA;AAAA;AAAA,KAS7D,aAAA;EAAA,SACM,IAAA;EAAA,SACA,WAAA;EAAA,SACA,YAAA;AAAA;AAAA,KAGN,oBAAA;EAAA,SACM,KAAA;EAAA,SACA,MAAA,EAAQ,aAAA,CAAc,oBAAA;EAAA,SACtB,QAAA,EAAU,aAAA,CAAc,gBAAA;EAAA,SACxB,UAAA;EAAA,SACA,aAAA;IAAA,SACE,MAAA;EAAA;EAAA,SAEF,MAAA;EAAA,SACA,KAAA,GAAQ,aAAA,CAAc,aAAA;AAAA;AAAA,cAg7BpB,4BAAA,GACX,OAAA,EAAS,UAAA,EACT,MAAA;EAAA,SAAmB,SAAA;EAAA,SAA4B,MAAA;AAAA,MAC9C,MAAA,CAAO,MAAA,CAAO,oBAAA,EAAsB,QAAA;AAAA,cAmmB1B,6BAAA,GACX,QAAA,EAAU,kBAAA,CAAmB,kBAAA,KAC5B,MAAA,CAAO,MAAA,CAAO,QAAA,EAAU,QAAA;AAAA,cAoGd,gCAAA,GAAoC,MAAA,EAAQ,6BAAA,KAA6B,KAAA,CAAA,KAAA,CAAA,WAAA,SAAA,UAAA,CAAA,UAAA"}
@@ -288,11 +288,12 @@ const topLevelJsonSchemaCombinator = (schema) => {
288
288
  }
289
289
  };
290
290
  const withoutTopLevelJsonSchemaCombinators = (schema) => Object.fromEntries(Object.entries(schema).filter(([key]) => !isTopLevelJsonSchemaCombinatorKey(key)));
291
- const withoutJsonSchemaCombinatorsAndTupleHints = (schema) => Object.fromEntries(Object.entries(schema).filter(([key]) => !isTopLevelJsonSchemaCombinatorKey(key) && key !== "prefixItems"));
292
- const jsonSchemaProperties = (schema) => {
293
- const properties = jsonObjectField(schema, "properties");
294
- return isJsonObject(properties) ? properties : {};
291
+ const withoutJsonSchemaCombinatorsAndTupleHints = (schema) => Object.fromEntries(Object.entries(schema).filter(([key]) => !isTopLevelJsonSchemaCombinatorKey(key) && key !== "prefixItems" && key !== "not" && key !== "if" && key !== "then" && key !== "else"));
292
+ const jsonSchemaMap = (schema, key) => {
293
+ const value = jsonObjectField(schema, key);
294
+ return isJsonObject(value) ? value : {};
295
295
  };
296
+ const jsonSchemaProperties = (schema) => jsonSchemaMap(schema, "properties");
296
297
  const jsonSchemaDefinitions = (schema) => {
297
298
  const definitions = jsonObjectField(schema, "$defs");
298
299
  return isJsonObject(definitions) ? definitions : {};
@@ -321,24 +322,21 @@ const mergeEnumPropertySchemas = (left, right) => {
321
322
  const leftEnum = jsonObjectField(left, "enum");
322
323
  const rightEnum = jsonObjectField(right, "enum");
323
324
  if (!Array.isArray(leftEnum) || !Array.isArray(rightEnum)) return;
324
- if (jsonObjectField(left, "type") !== jsonObjectField(right, "type")) return;
325
+ const leftType = jsonObjectField(left, "type");
326
+ if (leftType !== jsonObjectField(right, "type")) return;
327
+ const description = [jsonObjectField(left, "description"), jsonObjectField(right, "description")].find((value) => typeof value === "string");
325
328
  return {
326
- ...left,
327
- ...right,
328
- enum: uniqueUnknownArray([...leftEnum, ...rightEnum])
329
+ ...leftType === void 0 ? {} : { type: leftType },
330
+ enum: uniqueUnknownArray([...leftEnum, ...rightEnum]),
331
+ ...description === void 0 ? {} : { description }
329
332
  };
330
333
  };
331
- const jsonSchemaAnyOfItems = (schema) => {
332
- if (!isJsonObject(schema)) return [schema];
333
- const anyOf = jsonObjectField(schema, "anyOf");
334
- return Array.isArray(anyOf) ? anyOf : [schema];
335
- };
336
334
  const mergePropertySchemas = (left, right) => {
337
335
  const leftKey = jsonValueKey(left);
338
336
  if (leftKey !== void 0 && leftKey === jsonValueKey(right)) return left;
339
337
  const mergedEnum = mergeEnumPropertySchemas(left, right);
340
338
  if (mergedEnum !== void 0) return mergedEnum;
341
- return { anyOf: uniqueUnknownArray([...jsonSchemaAnyOfItems(left), ...jsonSchemaAnyOfItems(right)]) };
339
+ return {};
342
340
  };
343
341
  const mergeJsonSchemaObjects = (objects) => {
344
342
  const merged = /* @__PURE__ */ new Map();
@@ -346,48 +344,115 @@ const mergeJsonSchemaObjects = (objects) => {
346
344
  else merged.set(key, value);
347
345
  return Object.fromEntries(merged);
348
346
  };
349
- const normalizeJsonSchemaObjectFields = (schema) => Object.fromEntries(Object.entries(withoutJsonSchemaCombinatorsAndTupleHints(schema)).map(([key, value]) => [key, normalizeAnthropicToolSchema(value)]));
347
+ const mergeUnionJsonSchemaObjects = (objects, owners) => {
348
+ const keys = new Set(objects.flatMap((object) => Object.keys(object)));
349
+ return Object.fromEntries(Array.from(keys, (key) => {
350
+ const [first, ...rest] = objects.flatMap((object, index) => {
351
+ if (Object.hasOwn(object, key)) return [jsonObjectField(object, key)];
352
+ const additionalProperties = owners?.[index];
353
+ if (additionalProperties === void 0) return [];
354
+ const additionalPropertySchema = jsonObjectField(additionalProperties, "additionalProperties");
355
+ if (additionalPropertySchema === false) return [];
356
+ return [isJsonObject(additionalPropertySchema) ? additionalPropertySchema : {}];
357
+ });
358
+ return [key, rest.reduce(mergePropertySchemas, first)];
359
+ }));
360
+ };
361
+ const mergeRightBiasedJsonSchemaObjects = (objects) => Object.assign({}, ...objects);
362
+ const mergeAllOfSchemaObjects = (objects) => {
363
+ const merged = mergeRightBiasedJsonSchemaObjects(objects);
364
+ const properties = mergeRightBiasedJsonSchemaObjects(objects.map(jsonSchemaProperties));
365
+ const required = mergeJsonSchemaRequired("allOf", objects);
366
+ const definitions = mergeJsonSchemaObjects(objects.map(jsonSchemaDefinitions));
367
+ return {
368
+ ...merged,
369
+ ...Object.keys(properties).length === 0 ? {} : { properties },
370
+ ...required.length === 0 ? {} : { required },
371
+ ...Object.keys(definitions).length === 0 ? {} : { $defs: definitions }
372
+ };
373
+ };
374
+ const jsonSchemaMapKeywords = new Set([
375
+ "properties",
376
+ "patternProperties",
377
+ "$defs",
378
+ "definitions",
379
+ "dependentSchemas"
380
+ ]);
381
+ const jsonSchemaDataKeywords = new Set([
382
+ "const",
383
+ "default",
384
+ "enum",
385
+ "examples"
386
+ ]);
387
+ const normalizeJsonSchemaObjectFields = (schema) => {
388
+ const normalized = Object.fromEntries(Object.entries(withoutJsonSchemaCombinatorsAndTupleHints(schema)).map(([key, value]) => {
389
+ if (jsonSchemaDataKeywords.has(key)) return [key, value];
390
+ if (key === "dependencies" && isJsonObject(value)) return [key, Object.fromEntries(Object.entries(value).map(([name, dependency]) => [name, Array.isArray(dependency) ? dependency : normalizeAnthropicToolSchema(dependency)]))];
391
+ if (jsonSchemaMapKeywords.has(key) && isJsonObject(value)) return [key, Object.fromEntries(Object.entries(value).map(([name, childSchema]) => [name, normalizeAnthropicToolSchema(childSchema)]))];
392
+ return [key, normalizeAnthropicToolSchema(value)];
393
+ }));
394
+ if (Array.isArray(jsonObjectField(schema, "prefixItems"))) return {
395
+ ...normalized,
396
+ items: {}
397
+ };
398
+ return normalized;
399
+ };
350
400
  const jsonSchemaType = (schema) => {
351
401
  const type = jsonObjectField(schema, "type");
352
402
  return typeof type === "string" ? type : void 0;
353
403
  };
354
- const isNullJsonSchema = (schema) => jsonSchemaType(schema) === "null";
355
404
  const firstJsonObject = (items) => items[0];
356
- const mergeCompatibleVariants = (variants) => {
357
- const first = firstJsonObject(variants);
358
- if (first === void 0) return void 0;
359
- let merged = first;
360
- for (const variant of variants.slice(1)) merged = mergePropertySchemas(merged, variant);
361
- if (!isJsonObject(merged)) return void 0;
362
- if (Array.isArray(jsonObjectField(merged, "anyOf"))) return void 0;
363
- return merged;
364
- };
365
- const samePrimitiveTypeVariant = (variants) => {
405
+ const independentCommonVariantKeys = new Set([
406
+ "type",
407
+ "enum",
408
+ "const",
409
+ "format",
410
+ "pattern",
411
+ "minLength",
412
+ "maxLength",
413
+ "minimum",
414
+ "maximum",
415
+ "exclusiveMinimum",
416
+ "exclusiveMaximum",
417
+ "multipleOf",
418
+ "minItems",
419
+ "maxItems",
420
+ "uniqueItems",
421
+ "minProperties",
422
+ "maxProperties",
423
+ "description"
424
+ ]);
425
+ const commonVariantFields = (variants) => {
366
426
  const first = firstJsonObject(variants);
367
- const type = first === void 0 ? void 0 : jsonSchemaType(first);
368
- if (type === void 0 || type === "object" || type === "array" || type === "null") return;
369
- return variants.every((variant) => jsonSchemaType(variant) === type) ? first : void 0;
427
+ if (first === void 0) return {};
428
+ if (variants.length === 1) return first;
429
+ return Object.fromEntries(Object.entries(first).filter(([key, value]) => {
430
+ if (!independentCommonVariantKeys.has(key)) return false;
431
+ const valueKey = jsonValueKey(value);
432
+ return valueKey !== void 0 && variants.every((variant) => valueKey === jsonValueKey(jsonObjectField(variant, key)));
433
+ }));
370
434
  };
371
435
  const objectVariant = (variants) => {
372
- if (variants.length === 0 || variants.some((variant) => {
373
- const type = jsonSchemaType(variant);
374
- return type !== void 0 && type !== "object";
375
- })) return;
436
+ if (variants.length === 0 || variants.some((variant) => jsonSchemaType(variant) !== "object")) return;
437
+ if (variants.some((variant) => Object.keys(jsonSchemaMap(variant, "patternProperties")).length > 0)) return { type: "object" };
438
+ const additionalProperties = mergeAdditionalProperties(variants);
376
439
  return {
377
440
  type: "object",
378
- properties: mergeJsonSchemaObjects(variants.map(jsonSchemaProperties)),
441
+ properties: mergeUnionJsonSchemaObjects(variants.map(jsonSchemaProperties), variants),
379
442
  required: mergeJsonSchemaRequired("oneOf", variants),
380
443
  $defs: mergeJsonSchemaObjects(variants.map(jsonSchemaDefinitions)),
381
- additionalProperties: mergeAdditionalProperties(variants) ?? false
444
+ ...additionalProperties === void 0 ? {} : { additionalProperties }
382
445
  };
383
446
  };
384
447
  const normalizeJsonSchemaCombinator = (schema, combinator) => {
385
448
  const base = normalizeJsonSchemaObjectFields(schema);
386
- const variants = combinator.items.map(normalizeAnthropicToolSchema).filter(isJsonObject).filter((variant) => !isNullJsonSchema(variant));
449
+ const normalizedItems = combinator.items.map(normalizeAnthropicToolSchema);
450
+ const variants = normalizedItems.filter(isJsonObject);
387
451
  const first = firstJsonObject(variants);
452
+ if (combinator.key === "allOf") return mergeAllOfSchemaObjects([base, ...variants]);
453
+ if (normalizedItems.includes(true)) return base;
388
454
  if (first === void 0) return base;
389
- if (combinator.key === "allOf") return mergeJsonSchemaObjects([base, ...variants]);
390
- return mergeJsonSchemaObjects([base, mergeCompatibleVariants(variants) ?? samePrimitiveTypeVariant(variants) ?? objectVariant(variants) ?? first]);
455
+ return mergeAllOfSchemaObjects([objectVariant(variants) ?? commonVariantFields(variants), base]);
391
456
  };
392
457
  function normalizeAnthropicToolSchema(value) {
393
458
  if (Array.isArray(value)) return value.map(normalizeAnthropicToolSchema);
@@ -410,22 +475,26 @@ const mergeAdditionalProperties = (objects) => {
410
475
  const flattenTopLevelCombinatorToolSchema = (schema, combinator) => {
411
476
  const base = withoutTopLevelJsonSchemaCombinators(schema);
412
477
  const objectVariants = combinator.items.filter(isJsonObject);
478
+ const hasUnconstrainedUnion = combinator.key !== "allOf" && combinator.items.some((item) => item === true || !isJsonObject(item));
413
479
  const additionalProperties = mergeAdditionalProperties(objectVariants);
414
480
  const flattened = {
415
481
  ...base,
416
482
  type: "object",
417
- properties: mergeJsonSchemaObjects(objectVariants.map(jsonSchemaProperties)),
418
- required: mergeJsonSchemaRequired(combinator.key, objectVariants),
483
+ properties: hasUnconstrainedUnion ? {} : combinator.key === "allOf" ? mergeRightBiasedJsonSchemaObjects(objectVariants.map(jsonSchemaProperties)) : mergeUnionJsonSchemaObjects(objectVariants.map(jsonSchemaProperties), objectVariants),
484
+ required: hasUnconstrainedUnion ? [] : mergeJsonSchemaRequired(combinator.key, objectVariants),
419
485
  $defs: mergeJsonSchemaObjects([jsonSchemaDefinitions(schema), ...objectVariants.map(jsonSchemaDefinitions)])
420
486
  };
421
- if (additionalProperties === void 0) return flattened;
487
+ if (hasUnconstrainedUnion || additionalProperties === void 0) return flattened;
422
488
  return {
423
489
  ...flattened,
424
490
  additionalProperties
425
491
  };
426
492
  };
427
493
  const anthropicToolInputSchema = (schema) => {
428
- if (!isJsonObject(schema)) return {
494
+ if (!isJsonObject(schema)) return schema === true ? {
495
+ type: "object",
496
+ properties: {}
497
+ } : {
429
498
  type: "object",
430
499
  properties: {},
431
500
  required: [],