@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 +52 -12
- package/dist/compaction/budget.d.mts +2 -1
- package/dist/compaction/budget.d.mts.map +1 -1
- package/dist/compaction/budget.mjs +3 -1
- package/dist/compaction/budget.mjs.map +1 -1
- package/dist/providers/anthropic/claude-provider.d.mts.map +1 -1
- package/dist/providers/anthropic/claude-provider.mjs +111 -42
- package/dist/providers/anthropic/claude-provider.mjs.map +1 -1
- package/dist/providers/openai/codex-provider.d.mts +7 -3
- package/dist/providers/openai/codex-provider.d.mts.map +1 -1
- package/dist/providers/openai/codex-provider.mjs +1 -1
- package/dist/providers/openai/codex-provider.mjs.map +1 -1
- package/dist/providers/provider-error.d.mts.map +1 -1
- package/dist/providers/provider-error.mjs +1 -1
- package/dist/providers/provider-error.mjs.map +1 -1
- package/dist/skillset/merge.d.mts +3 -3
- package/dist/skillset/name.d.mts +2 -2
- package/dist/skillset/name.d.mts.map +1 -1
- package/dist/tools/index.d.mts +2 -2
- package/dist/tools/index.mjs +2 -2
- package/dist/tools/subagent.d.mts +24 -2
- package/dist/tools/subagent.d.mts.map +1 -1
- package/dist/tools/subagent.mjs +126 -20
- package/dist/tools/subagent.mjs.map +1 -1
- package/package.json +1 -1
- package/src/compaction/budget.ts +11 -1
- package/src/providers/anthropic/claude-provider.ts +189 -73
- package/src/providers/openai/codex-provider.ts +8 -3
- package/src/providers/provider-error.ts +3 -0
- package/src/tools/index.ts +5 -0
- package/src/tools/subagent.ts +172 -10
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
|
|
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 |
|
|
116
|
-
| ---------------------------------- |
|
|
117
|
-
| `makeOpenAiProviderLayer` | `maxCompletionTokens`
|
|
118
|
-
| `makeOpenAiCodexProviderLayer` |
|
|
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
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
293
|
-
const
|
|
294
|
-
return isJsonObject(
|
|
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
|
-
|
|
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
|
-
...
|
|
327
|
-
...
|
|
328
|
-
|
|
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 {
|
|
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
|
|
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
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
-
|
|
368
|
-
if (
|
|
369
|
-
return
|
|
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
|
-
|
|
374
|
-
|
|
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:
|
|
441
|
+
properties: mergeUnionJsonSchemaObjects(variants.map(jsonSchemaProperties), variants),
|
|
379
442
|
required: mergeJsonSchemaRequired("oneOf", variants),
|
|
380
443
|
$defs: mergeJsonSchemaObjects(variants.map(jsonSchemaDefinitions)),
|
|
381
|
-
additionalProperties:
|
|
444
|
+
...additionalProperties === void 0 ? {} : { additionalProperties }
|
|
382
445
|
};
|
|
383
446
|
};
|
|
384
447
|
const normalizeJsonSchemaCombinator = (schema, combinator) => {
|
|
385
448
|
const base = normalizeJsonSchemaObjectFields(schema);
|
|
386
|
-
const
|
|
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
|
-
|
|
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:
|
|
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: [],
|