@sammorrowdrums/mcpi-ai 0.85.1 → 0.85.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/anthropic-messages.d.ts +3 -1
- package/dist/api/anthropic-messages.d.ts.map +1 -1
- package/dist/api/anthropic-messages.js +270 -59
- package/dist/api/anthropic-messages.js.map +1 -1
- package/dist/api/azure-openai-responses.d.ts.map +1 -1
- package/dist/api/azure-openai-responses.js +2 -0
- package/dist/api/azure-openai-responses.js.map +1 -1
- package/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/dist/api/bedrock-converse-stream.js +2 -0
- package/dist/api/bedrock-converse-stream.js.map +1 -1
- package/dist/api/google-generative-ai.d.ts.map +1 -1
- package/dist/api/google-generative-ai.js +2 -0
- package/dist/api/google-generative-ai.js.map +1 -1
- package/dist/api/google-vertex.d.ts.map +1 -1
- package/dist/api/google-vertex.js +2 -0
- package/dist/api/google-vertex.js.map +1 -1
- package/dist/api/mistral-conversations.d.ts.map +1 -1
- package/dist/api/mistral-conversations.js +2 -0
- package/dist/api/mistral-conversations.js.map +1 -1
- package/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/dist/api/openai-codex-responses.js +12 -2
- package/dist/api/openai-codex-responses.js.map +1 -1
- package/dist/api/openai-completions.d.ts +3 -1
- package/dist/api/openai-completions.d.ts.map +1 -1
- package/dist/api/openai-completions.js +23 -16
- package/dist/api/openai-completions.js.map +1 -1
- package/dist/api/openai-responses.d.ts.map +1 -1
- package/dist/api/openai-responses.js +12 -2
- package/dist/api/openai-responses.js.map +1 -1
- package/dist/api/provider-replay.d.ts +17 -0
- package/dist/api/provider-replay.d.ts.map +1 -0
- package/dist/api/provider-replay.js +2 -0
- package/dist/api/provider-replay.js.map +1 -0
- package/dist/api/transform-messages.d.ts.map +1 -1
- package/dist/api/transform-messages.js +5 -1
- package/dist/api/transform-messages.js.map +1 -1
- package/dist/image-models.generated.d.ts +30 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +30 -0
- package/dist/image-models.generated.js.map +1 -1
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/baseten.json +1 -1
- package/dist/providers/data/fireworks.json +1 -1
- package/dist/providers/data/github-copilot.json +1 -1
- package/dist/providers/data/huggingface.json +1 -1
- package/dist/providers/data/kimi-coding.json +1 -1
- package/dist/providers/data/nvidia.json +1 -1
- package/dist/providers/data/opencode-go.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/dist/providers/data/qwen-token-plan.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/deferred-tools.d.ts +63 -4
- package/dist/utils/deferred-tools.d.ts.map +1 -1
- package/dist/utils/deferred-tools.js +111 -12
- package/dist/utils/deferred-tools.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-tools.d.ts","sourceRoot":"","sources":["../../src/utils/deferred-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"deferred-tools.d.ts","sourceRoot":"","sources":["../../src/utils/deferred-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,KAAK,0BAA0B,EAAoC,MAAM,kBAAkB,CAAC;AAErG,KAAK,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAInD,+FAA+F;AAC/F,eAAO,MAAM,qCAAqC,+BAA+B,CAAC;AAElF,6FAA6F;AAC7F,MAAM,WAAW,qBAAqB;IACrC,4FAA4F;IAC5F,SAAS,EAAE,IAAI,EAAE,CAAC;IAClB,gGAAgG;IAChG,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5B,oFAAoF;IACpF,WAAW,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,0CAA0C;AAC1C,MAAM,WAAW,yBAAyB;IACzC,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iFAAiF;IACjF,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAWD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,GAAG,qBAAqB,CA4E9G;AAED;;;GAGG;AACH,wBAAgB,wCAAwC,CACvD,OAAO,EAAE;IAAE,WAAW,CAAC,EAAE,0BAA0B,EAAE,CAAA;CAAE,EACvD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,IAAI,GAAG,UAAU,CAAC,EAC1C,kBAAkB,EAAE,SAAS,MAAM,EAAE,GACnC,IAAI,CAYN","sourcesContent":["import type { Api, Context, Model, Tool } from \"../types.ts\";\nimport { type AssistantMessageDiagnostic, appendAssistantMessageDiagnostic } from \"./diagnostics.ts\";\n\ntype ToolNameNormalizer = (name: string) => string;\n\nconst identityToolName: ToolNameNormalizer = (name) => name;\n\n/** Diagnostic emitted when an API sends schemas up front that deferral would have withheld. */\nexport const DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC = \"deferred_tools_unsupported\";\n\n/** How the current tools split across the request tools array and transcript load points. */\nexport interface DeferredToolPlacement {\n\t/** Tools whose schemas are sent up front, registration order first, then promoted tools. */\n\timmediate: Tool[];\n\t/** Tools whose schemas are withheld until a transcript load point, keyed by normalized name. */\n\tdeferred: Map<string, Tool>;\n\t/** Names deferral would have withheld but that were sent as full schemas anyway. */\n\tunsupported: string[];\n}\n\n/** How a single API wants tools split. */\nexport interface SplitDeferredToolsOptions {\n\t/** False when the target model cannot withhold any schema. */\n\tenabled: boolean;\n\t/**\n\t * False when the API can only reveal a schema at a transcript marker, because it has no\n\t * turn-zero anchor and no server-side catalog the model could search. Deferring a\n\t * registered tool there would leave it in `Context.tools` but name it nowhere in the\n\t * request, so the model could never discover or call it. Those tools stay immediate for\n\t * the whole session instead, and are reported through `unsupported`.\n\t */\n\tregistrationDeferral?: boolean;\n\t/**\n\t * True when the caller adds its own discovery tool to the request, such as Anthropic's\n\t * server-side tool-search tool. The safety floor below exists only to guarantee the model\n\t * has something to start from; a discovery tool provides that, so the floor stands down and\n\t * a fully deferred tool set stays deferred.\n\t */\n\tprovidesToolSearch?: boolean;\n\t/** Applied to every tool name before comparison, for APIs that rewrite names. */\n\tnormalizeName?: ToolNameNormalizer;\n}\n\n/** Normalized names of the tools registered with `deferred: true`. */\nfunction listRegistrationDeferred(uniqueTools: ReadonlyMap<string, Tool>): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const [name, tool] of uniqueTools) {\n\t\tif (tool.deferred === true) names.add(name);\n\t}\n\treturn names;\n}\n\n/**\n * Split current tools into up-front definitions and definitions loaded from the transcript.\n *\n * A tool is deferred when it was registered with `deferred: true` or when a\n * `ToolResultMessage.addedToolNames` marker introduced it. Either way it stays in\n * `Context.tools`, so dispatch and grammar membership never change.\n *\n * A deferred tool is promoted back to immediate when the transcript shows the model calling\n * it before any load point covered it. That keeps replayed histories valid: a `tool_use`\n * block always has a schema behind it. Promotion is sticky because the call stays in the\n * transcript, and promoted tools are appended after the registration-immediate ones so the\n * cacheable prefix of the tools array does not shift.\n *\n * Deferring is withholding a schema, never withholding a name. An API that keeps deferred\n * entries in its own tools array, such as Anthropic's `defer_loading`, still names every tool\n * in the request, so a call the model does make resolves. `registrationDeferral: false` exists\n * for the APIs that would instead drop the tool from the request altogether.\n *\n * Deferral is resolved even for an API that cannot express it. Those names come back in\n * `unsupported` with their full schemas kept immediate, so the caller can report the\n * expansion instead of silently inlining every schema.\n */\nexport function splitDeferredTools(context: Context, options: SplitDeferredToolsOptions): DeferredToolPlacement {\n\tconst normalizeName = options.normalizeName ?? identityToolName;\n\tconst uniqueTools = new Map<string, Tool>();\n\tfor (const tool of context.tools ?? []) uniqueTools.set(normalizeName(tool.name), tool);\n\tconst registrationDeferred = listRegistrationDeferred(uniqueTools);\n\tconst honorRegistration = options.registrationDeferral !== false;\n\n\tconst deferredNames = new Set(registrationDeferred);\n\tconst loadedNames = new Set<string>();\n\tconst usedNames = new Set<string>();\n\tfor (const message of context.messages) {\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type !== \"toolCall\") continue;\n\t\t\t\tconst name = normalizeName(block.name);\n\t\t\t\tusedNames.add(name);\n\t\t\t\t// Called before any load point covered it, so its schema was already visible.\n\t\t\t\tif (!loadedNames.has(name)) deferredNames.delete(name);\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\") {\n\t\t\tfor (const rawName of message.addedToolNames ?? []) {\n\t\t\t\tconst name = normalizeName(rawName);\n\t\t\t\tif (usedNames.has(name)) continue;\n\t\t\t\tdeferredNames.add(name);\n\t\t\t\tloadedNames.add(name);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const name of deferredNames) {\n\t\tif (!uniqueTools.has(name)) deferredNames.delete(name);\n\t}\n\n\t// An API with no turn-zero anchor cannot withhold a registered tool's schema without also\n\t// withholding its name, which would make the tool undiscoverable. Those tools are sent up\n\t// front and stay that way: a later marker must not pull one back out of the tools array,\n\t// because the model has already seen it and removing it would churn the cached prefix.\n\tconst unanchored = new Set<string>();\n\tif (!honorRegistration) {\n\t\tfor (const name of registrationDeferred) {\n\t\t\tdeferredNames.delete(name);\n\t\t\tunanchored.add(name);\n\t\t}\n\t}\n\t// Registration order keeps the reported names stable across turns.\n\tconst orderNames = (names: ReadonlySet<string>): string[] => [...uniqueTools.keys()].filter((n) => names.has(n));\n\n\tif (!options.enabled) {\n\t\t// Registration order is preserved rather than reassembled from immediate plus deferred,\n\t\t// which would reorder the tools array and invalidate the provider's cached prefix.\n\t\treturn {\n\t\t\timmediate: [...uniqueTools.values()],\n\t\t\tdeferred: new Map(),\n\t\t\tunsupported: orderNames(new Set([...deferredNames, ...unanchored])),\n\t\t};\n\t}\n\n\tconst immediate: Tool[] = [];\n\tconst promoted: Tool[] = [];\n\tconst deferred = new Map<string, Tool>();\n\tfor (const [name, tool] of uniqueTools) {\n\t\tif (deferredNames.has(name)) deferred.set(name, tool);\n\t\telse if (honorRegistration && registrationDeferred.has(name)) promoted.push(tool);\n\t\telse immediate.push(tool);\n\t}\n\timmediate.push(...promoted);\n\n\t// Safety floor: never leave a request without an up-front tool while tools are registered,\n\t// unless the caller supplies a discovery tool that can reach the deferred ones.\n\tif (options.providesToolSearch !== true && immediate.length === 0 && deferred.size > 0) {\n\t\treturn {\n\t\t\timmediate: [...uniqueTools.values()],\n\t\t\tdeferred: new Map(),\n\t\t\tunsupported: orderNames(new Set([...unanchored, ...deferred.keys()])),\n\t\t};\n\t}\n\treturn { immediate, deferred, unsupported: orderNames(unanchored) };\n}\n\n/**\n * Record that schemas deferral would have withheld were sent up front instead, so a silent\n * loss of progressive disclosure is always visible. No-op when nothing was expanded.\n */\nexport function appendDeferredToolsUnsupportedDiagnostic(\n\tmessage: { diagnostics?: AssistantMessageDiagnostic[] },\n\tmodel: Pick<Model<Api>, \"id\" | \"provider\">,\n\tdeferredCandidates: readonly string[],\n): void {\n\tif (deferredCandidates.length === 0) return;\n\tappendAssistantMessageDiagnostic(message, {\n\t\ttype: DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC,\n\t\ttimestamp: Date.now(),\n\t\tdetails: {\n\t\t\treason: `${model.provider}/${model.id} does not support deferred tool loading; deferred tool schemas were sent up front`,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tdeferredCandidates: [...deferredCandidates],\n\t\t},\n\t});\n}\n"]}
|
|
@@ -1,36 +1,135 @@
|
|
|
1
|
+
import { appendAssistantMessageDiagnostic } from "./diagnostics.js";
|
|
1
2
|
const identityToolName = (name) => name;
|
|
2
|
-
/**
|
|
3
|
-
export
|
|
3
|
+
/** Diagnostic emitted when an API sends schemas up front that deferral would have withheld. */
|
|
4
|
+
export const DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC = "deferred_tools_unsupported";
|
|
5
|
+
/** Normalized names of the tools registered with `deferred: true`. */
|
|
6
|
+
function listRegistrationDeferred(uniqueTools) {
|
|
7
|
+
const names = new Set();
|
|
8
|
+
for (const [name, tool] of uniqueTools) {
|
|
9
|
+
if (tool.deferred === true)
|
|
10
|
+
names.add(name);
|
|
11
|
+
}
|
|
12
|
+
return names;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Split current tools into up-front definitions and definitions loaded from the transcript.
|
|
16
|
+
*
|
|
17
|
+
* A tool is deferred when it was registered with `deferred: true` or when a
|
|
18
|
+
* `ToolResultMessage.addedToolNames` marker introduced it. Either way it stays in
|
|
19
|
+
* `Context.tools`, so dispatch and grammar membership never change.
|
|
20
|
+
*
|
|
21
|
+
* A deferred tool is promoted back to immediate when the transcript shows the model calling
|
|
22
|
+
* it before any load point covered it. That keeps replayed histories valid: a `tool_use`
|
|
23
|
+
* block always has a schema behind it. Promotion is sticky because the call stays in the
|
|
24
|
+
* transcript, and promoted tools are appended after the registration-immediate ones so the
|
|
25
|
+
* cacheable prefix of the tools array does not shift.
|
|
26
|
+
*
|
|
27
|
+
* Deferring is withholding a schema, never withholding a name. An API that keeps deferred
|
|
28
|
+
* entries in its own tools array, such as Anthropic's `defer_loading`, still names every tool
|
|
29
|
+
* in the request, so a call the model does make resolves. `registrationDeferral: false` exists
|
|
30
|
+
* for the APIs that would instead drop the tool from the request altogether.
|
|
31
|
+
*
|
|
32
|
+
* Deferral is resolved even for an API that cannot express it. Those names come back in
|
|
33
|
+
* `unsupported` with their full schemas kept immediate, so the caller can report the
|
|
34
|
+
* expansion instead of silently inlining every schema.
|
|
35
|
+
*/
|
|
36
|
+
export function splitDeferredTools(context, options) {
|
|
37
|
+
const normalizeName = options.normalizeName ?? identityToolName;
|
|
4
38
|
const uniqueTools = new Map();
|
|
5
39
|
for (const tool of context.tools ?? [])
|
|
6
40
|
uniqueTools.set(normalizeName(tool.name), tool);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const deferredNames = new Set();
|
|
41
|
+
const registrationDeferred = listRegistrationDeferred(uniqueTools);
|
|
42
|
+
const honorRegistration = options.registrationDeferral !== false;
|
|
43
|
+
const deferredNames = new Set(registrationDeferred);
|
|
44
|
+
const loadedNames = new Set();
|
|
10
45
|
const usedNames = new Set();
|
|
11
46
|
for (const message of context.messages) {
|
|
12
47
|
if (message.role === "assistant") {
|
|
13
48
|
for (const block of message.content) {
|
|
14
|
-
if (block.type
|
|
15
|
-
|
|
49
|
+
if (block.type !== "toolCall")
|
|
50
|
+
continue;
|
|
51
|
+
const name = normalizeName(block.name);
|
|
52
|
+
usedNames.add(name);
|
|
53
|
+
// Called before any load point covered it, so its schema was already visible.
|
|
54
|
+
if (!loadedNames.has(name))
|
|
55
|
+
deferredNames.delete(name);
|
|
16
56
|
}
|
|
17
57
|
}
|
|
18
58
|
else if (message.role === "toolResult") {
|
|
19
|
-
for (const
|
|
20
|
-
const
|
|
21
|
-
if (
|
|
22
|
-
|
|
59
|
+
for (const rawName of message.addedToolNames ?? []) {
|
|
60
|
+
const name = normalizeName(rawName);
|
|
61
|
+
if (usedNames.has(name))
|
|
62
|
+
continue;
|
|
63
|
+
deferredNames.add(name);
|
|
64
|
+
loadedNames.add(name);
|
|
23
65
|
}
|
|
24
66
|
}
|
|
25
67
|
}
|
|
68
|
+
for (const name of deferredNames) {
|
|
69
|
+
if (!uniqueTools.has(name))
|
|
70
|
+
deferredNames.delete(name);
|
|
71
|
+
}
|
|
72
|
+
// An API with no turn-zero anchor cannot withhold a registered tool's schema without also
|
|
73
|
+
// withholding its name, which would make the tool undiscoverable. Those tools are sent up
|
|
74
|
+
// front and stay that way: a later marker must not pull one back out of the tools array,
|
|
75
|
+
// because the model has already seen it and removing it would churn the cached prefix.
|
|
76
|
+
const unanchored = new Set();
|
|
77
|
+
if (!honorRegistration) {
|
|
78
|
+
for (const name of registrationDeferred) {
|
|
79
|
+
deferredNames.delete(name);
|
|
80
|
+
unanchored.add(name);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Registration order keeps the reported names stable across turns.
|
|
84
|
+
const orderNames = (names) => [...uniqueTools.keys()].filter((n) => names.has(n));
|
|
85
|
+
if (!options.enabled) {
|
|
86
|
+
// Registration order is preserved rather than reassembled from immediate plus deferred,
|
|
87
|
+
// which would reorder the tools array and invalidate the provider's cached prefix.
|
|
88
|
+
return {
|
|
89
|
+
immediate: [...uniqueTools.values()],
|
|
90
|
+
deferred: new Map(),
|
|
91
|
+
unsupported: orderNames(new Set([...deferredNames, ...unanchored])),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
26
94
|
const immediate = [];
|
|
95
|
+
const promoted = [];
|
|
27
96
|
const deferred = new Map();
|
|
28
97
|
for (const [name, tool] of uniqueTools) {
|
|
29
98
|
if (deferredNames.has(name))
|
|
30
99
|
deferred.set(name, tool);
|
|
100
|
+
else if (honorRegistration && registrationDeferred.has(name))
|
|
101
|
+
promoted.push(tool);
|
|
31
102
|
else
|
|
32
103
|
immediate.push(tool);
|
|
33
104
|
}
|
|
34
|
-
|
|
105
|
+
immediate.push(...promoted);
|
|
106
|
+
// Safety floor: never leave a request without an up-front tool while tools are registered,
|
|
107
|
+
// unless the caller supplies a discovery tool that can reach the deferred ones.
|
|
108
|
+
if (options.providesToolSearch !== true && immediate.length === 0 && deferred.size > 0) {
|
|
109
|
+
return {
|
|
110
|
+
immediate: [...uniqueTools.values()],
|
|
111
|
+
deferred: new Map(),
|
|
112
|
+
unsupported: orderNames(new Set([...unanchored, ...deferred.keys()])),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return { immediate, deferred, unsupported: orderNames(unanchored) };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Record that schemas deferral would have withheld were sent up front instead, so a silent
|
|
119
|
+
* loss of progressive disclosure is always visible. No-op when nothing was expanded.
|
|
120
|
+
*/
|
|
121
|
+
export function appendDeferredToolsUnsupportedDiagnostic(message, model, deferredCandidates) {
|
|
122
|
+
if (deferredCandidates.length === 0)
|
|
123
|
+
return;
|
|
124
|
+
appendAssistantMessageDiagnostic(message, {
|
|
125
|
+
type: DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC,
|
|
126
|
+
timestamp: Date.now(),
|
|
127
|
+
details: {
|
|
128
|
+
reason: `${model.provider}/${model.id} does not support deferred tool loading; deferred tool schemas were sent up front`,
|
|
129
|
+
provider: model.provider,
|
|
130
|
+
model: model.id,
|
|
131
|
+
deferredCandidates: [...deferredCandidates],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
35
134
|
}
|
|
36
135
|
//# sourceMappingURL=deferred-tools.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deferred-tools.js","sourceRoot":"","sources":["../../src/utils/deferred-tools.ts"],"names":[],"mappings":"AAIA,MAAM,gBAAgB,GAAuB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC;AAE5D,yEAAyE;AACzE,MAAM,UAAU,kBAAkB,CACjC,OAAgB,EAChB,OAAgB,EAChB,aAAa,GAAuB,gBAAgB,EACC;IACrD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAgB,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE;QAAE,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACxF,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;IAEnF,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAClC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;oBAAE,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACzE,CAAC;QACF,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1C,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,cAAc,IAAI,EAAE,EAAE,CAAC;gBACjD,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC;oBAAE,aAAa,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACvE,CAAC;QACF,CAAC;IACF,CAAC;IAED,MAAM,SAAS,GAAW,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgB,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC;QACxC,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;;YACjD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAAA,CAC/B","sourcesContent":["import type { Context, Tool } from \"../types.ts\";\n\ntype ToolNameNormalizer = (name: string) => string;\n\nconst identityToolName: ToolNameNormalizer = (name) => name;\n\n/** Split current tools into prefix and transcript-loaded definitions. */\nexport function splitDeferredTools(\n\tcontext: Context,\n\tenabled: boolean,\n\tnormalizeName: ToolNameNormalizer = identityToolName,\n): { immediate: Tool[]; deferred: Map<string, Tool> } {\n\tconst uniqueTools = new Map<string, Tool>();\n\tfor (const tool of context.tools ?? []) uniqueTools.set(normalizeName(tool.name), tool);\n\tif (!enabled) return { immediate: [...uniqueTools.values()], deferred: new Map() };\n\n\tconst deferredNames = new Set<string>();\n\tconst usedNames = new Set<string>();\n\tfor (const message of context.messages) {\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type === \"toolCall\") usedNames.add(normalizeName(block.name));\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\") {\n\t\t\tfor (const name of message.addedToolNames ?? []) {\n\t\t\t\tconst normalizedName = normalizeName(name);\n\t\t\t\tif (!usedNames.has(normalizedName)) deferredNames.add(normalizedName);\n\t\t\t}\n\t\t}\n\t}\n\n\tconst immediate: Tool[] = [];\n\tconst deferred = new Map<string, Tool>();\n\tfor (const [name, tool] of uniqueTools) {\n\t\tif (deferredNames.has(name)) deferred.set(name, tool);\n\t\telse immediate.push(tool);\n\t}\n\treturn { immediate, deferred };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"deferred-tools.js","sourceRoot":"","sources":["../../src/utils/deferred-tools.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Context, Model, Tool } from \"../types.ts\";\nimport { type AssistantMessageDiagnostic, appendAssistantMessageDiagnostic } from \"./diagnostics.ts\";\n\ntype ToolNameNormalizer = (name: string) => string;\n\nconst identityToolName: ToolNameNormalizer = (name) => name;\n\n/** Diagnostic emitted when an API sends schemas up front that deferral would have withheld. */\nexport const DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC = \"deferred_tools_unsupported\";\n\n/** How the current tools split across the request tools array and transcript load points. */\nexport interface DeferredToolPlacement {\n\t/** Tools whose schemas are sent up front, registration order first, then promoted tools. */\n\timmediate: Tool[];\n\t/** Tools whose schemas are withheld until a transcript load point, keyed by normalized name. */\n\tdeferred: Map<string, Tool>;\n\t/** Names deferral would have withheld but that were sent as full schemas anyway. */\n\tunsupported: string[];\n}\n\n/** How a single API wants tools split. */\nexport interface SplitDeferredToolsOptions {\n\t/** False when the target model cannot withhold any schema. */\n\tenabled: boolean;\n\t/**\n\t * False when the API can only reveal a schema at a transcript marker, because it has no\n\t * turn-zero anchor and no server-side catalog the model could search. Deferring a\n\t * registered tool there would leave it in `Context.tools` but name it nowhere in the\n\t * request, so the model could never discover or call it. Those tools stay immediate for\n\t * the whole session instead, and are reported through `unsupported`.\n\t */\n\tregistrationDeferral?: boolean;\n\t/**\n\t * True when the caller adds its own discovery tool to the request, such as Anthropic's\n\t * server-side tool-search tool. The safety floor below exists only to guarantee the model\n\t * has something to start from; a discovery tool provides that, so the floor stands down and\n\t * a fully deferred tool set stays deferred.\n\t */\n\tprovidesToolSearch?: boolean;\n\t/** Applied to every tool name before comparison, for APIs that rewrite names. */\n\tnormalizeName?: ToolNameNormalizer;\n}\n\n/** Normalized names of the tools registered with `deferred: true`. */\nfunction listRegistrationDeferred(uniqueTools: ReadonlyMap<string, Tool>): Set<string> {\n\tconst names = new Set<string>();\n\tfor (const [name, tool] of uniqueTools) {\n\t\tif (tool.deferred === true) names.add(name);\n\t}\n\treturn names;\n}\n\n/**\n * Split current tools into up-front definitions and definitions loaded from the transcript.\n *\n * A tool is deferred when it was registered with `deferred: true` or when a\n * `ToolResultMessage.addedToolNames` marker introduced it. Either way it stays in\n * `Context.tools`, so dispatch and grammar membership never change.\n *\n * A deferred tool is promoted back to immediate when the transcript shows the model calling\n * it before any load point covered it. That keeps replayed histories valid: a `tool_use`\n * block always has a schema behind it. Promotion is sticky because the call stays in the\n * transcript, and promoted tools are appended after the registration-immediate ones so the\n * cacheable prefix of the tools array does not shift.\n *\n * Deferring is withholding a schema, never withholding a name. An API that keeps deferred\n * entries in its own tools array, such as Anthropic's `defer_loading`, still names every tool\n * in the request, so a call the model does make resolves. `registrationDeferral: false` exists\n * for the APIs that would instead drop the tool from the request altogether.\n *\n * Deferral is resolved even for an API that cannot express it. Those names come back in\n * `unsupported` with their full schemas kept immediate, so the caller can report the\n * expansion instead of silently inlining every schema.\n */\nexport function splitDeferredTools(context: Context, options: SplitDeferredToolsOptions): DeferredToolPlacement {\n\tconst normalizeName = options.normalizeName ?? identityToolName;\n\tconst uniqueTools = new Map<string, Tool>();\n\tfor (const tool of context.tools ?? []) uniqueTools.set(normalizeName(tool.name), tool);\n\tconst registrationDeferred = listRegistrationDeferred(uniqueTools);\n\tconst honorRegistration = options.registrationDeferral !== false;\n\n\tconst deferredNames = new Set(registrationDeferred);\n\tconst loadedNames = new Set<string>();\n\tconst usedNames = new Set<string>();\n\tfor (const message of context.messages) {\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (block.type !== \"toolCall\") continue;\n\t\t\t\tconst name = normalizeName(block.name);\n\t\t\t\tusedNames.add(name);\n\t\t\t\t// Called before any load point covered it, so its schema was already visible.\n\t\t\t\tif (!loadedNames.has(name)) deferredNames.delete(name);\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\") {\n\t\t\tfor (const rawName of message.addedToolNames ?? []) {\n\t\t\t\tconst name = normalizeName(rawName);\n\t\t\t\tif (usedNames.has(name)) continue;\n\t\t\t\tdeferredNames.add(name);\n\t\t\t\tloadedNames.add(name);\n\t\t\t}\n\t\t}\n\t}\n\tfor (const name of deferredNames) {\n\t\tif (!uniqueTools.has(name)) deferredNames.delete(name);\n\t}\n\n\t// An API with no turn-zero anchor cannot withhold a registered tool's schema without also\n\t// withholding its name, which would make the tool undiscoverable. Those tools are sent up\n\t// front and stay that way: a later marker must not pull one back out of the tools array,\n\t// because the model has already seen it and removing it would churn the cached prefix.\n\tconst unanchored = new Set<string>();\n\tif (!honorRegistration) {\n\t\tfor (const name of registrationDeferred) {\n\t\t\tdeferredNames.delete(name);\n\t\t\tunanchored.add(name);\n\t\t}\n\t}\n\t// Registration order keeps the reported names stable across turns.\n\tconst orderNames = (names: ReadonlySet<string>): string[] => [...uniqueTools.keys()].filter((n) => names.has(n));\n\n\tif (!options.enabled) {\n\t\t// Registration order is preserved rather than reassembled from immediate plus deferred,\n\t\t// which would reorder the tools array and invalidate the provider's cached prefix.\n\t\treturn {\n\t\t\timmediate: [...uniqueTools.values()],\n\t\t\tdeferred: new Map(),\n\t\t\tunsupported: orderNames(new Set([...deferredNames, ...unanchored])),\n\t\t};\n\t}\n\n\tconst immediate: Tool[] = [];\n\tconst promoted: Tool[] = [];\n\tconst deferred = new Map<string, Tool>();\n\tfor (const [name, tool] of uniqueTools) {\n\t\tif (deferredNames.has(name)) deferred.set(name, tool);\n\t\telse if (honorRegistration && registrationDeferred.has(name)) promoted.push(tool);\n\t\telse immediate.push(tool);\n\t}\n\timmediate.push(...promoted);\n\n\t// Safety floor: never leave a request without an up-front tool while tools are registered,\n\t// unless the caller supplies a discovery tool that can reach the deferred ones.\n\tif (options.providesToolSearch !== true && immediate.length === 0 && deferred.size > 0) {\n\t\treturn {\n\t\t\timmediate: [...uniqueTools.values()],\n\t\t\tdeferred: new Map(),\n\t\t\tunsupported: orderNames(new Set([...unanchored, ...deferred.keys()])),\n\t\t};\n\t}\n\treturn { immediate, deferred, unsupported: orderNames(unanchored) };\n}\n\n/**\n * Record that schemas deferral would have withheld were sent up front instead, so a silent\n * loss of progressive disclosure is always visible. No-op when nothing was expanded.\n */\nexport function appendDeferredToolsUnsupportedDiagnostic(\n\tmessage: { diagnostics?: AssistantMessageDiagnostic[] },\n\tmodel: Pick<Model<Api>, \"id\" | \"provider\">,\n\tdeferredCandidates: readonly string[],\n): void {\n\tif (deferredCandidates.length === 0) return;\n\tappendAssistantMessageDiagnostic(message, {\n\t\ttype: DEFERRED_TOOLS_UNSUPPORTED_DIAGNOSTIC,\n\t\ttimestamp: Date.now(),\n\t\tdetails: {\n\t\t\treason: `${model.provider}/${model.id} does not support deferred tool loading; deferred tool schemas were sent up front`,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tdeferredCandidates: [...deferredCandidates],\n\t\t},\n\t});\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sammorrowdrums/mcpi-ai",
|
|
3
|
-
"version": "0.85.
|
|
3
|
+
"version": "0.85.3",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@anthropic-ai/sdk": "0.91.1",
|
|
65
65
|
"@aws-sdk/client-bedrock-runtime": "3.1048.0",
|
|
66
|
-
"@sammorrowdrums/mcpi-telemetry": "^0.85.
|
|
66
|
+
"@sammorrowdrums/mcpi-telemetry": "^0.85.3",
|
|
67
67
|
"@google/genai": "1.52.0",
|
|
68
68
|
"@opentelemetry/api": "1.9.0",
|
|
69
69
|
"@smithy/node-http-handler": "4.7.3",
|