@yanlinglabs/winter-agent-runtime 0.0.36 → 0.0.38
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/context/attachments.d.ts +32 -0
- package/dist/embedded-worker.js +3 -3
- package/dist/embedded.js +3 -3
- package/dist/engine.d.ts +11 -1
- package/dist/{index-c8j57y8v.js → index-5xe59vkt.js} +561 -53
- package/dist/{index-1nhzy5e2.js → index-8x4yjdg7.js} +2 -2
- package/dist/{index-83p1g9ap.js → index-z3970wsj.js} +24 -9
- package/dist/index.js +1 -1
- package/dist/mcp/control.d.ts +1 -0
- package/dist/mcp/lifecycle.d.ts +3 -0
- package/dist/provider/mock.d.ts +1 -1
- package/dist/store/dialect.d.ts +1 -1
- package/dist/subagents/child-engine.d.ts +26 -0
- package/dist/testing.js +2 -2
- package/dist/tools/descriptors/index.d.ts +1 -0
- package/dist/tools/descriptors/search.d.ts +4 -0
- package/dist/tools/impl/_exa-client.d.ts +2 -0
- package/dist/tools/impl/_site-icons.d.ts +17 -0
- package/dist/tools/impl/_web-fetch-cache.d.ts +2 -0
- package/dist/tools/impl/index.d.ts +1 -0
- package/dist/tools/impl/search.d.ts +25 -0
- package/dist/tools/registry.d.ts +58 -0
- package/dist/toolsearch/exposure.d.ts +2 -0
- package/dist/toolsearch/search.d.ts +5 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/web/session-runtime.d.ts +8 -0
- package/package.json +4 -4
|
@@ -934,6 +934,7 @@ function validatePermissionRuleString(raw, direction) {
|
|
|
934
934
|
}
|
|
935
935
|
|
|
936
936
|
// src/tools/registry.ts
|
|
937
|
+
var DEFERRED_BUILTINS_CAPABILITY = "winter.deferred-builtins";
|
|
937
938
|
var registry = new Map;
|
|
938
939
|
function registerTool2(t) {
|
|
939
940
|
if (registry.has(t.descriptor.canonicalName)) {
|
|
@@ -996,8 +997,9 @@ function onRegistryChange(cb) {
|
|
|
996
997
|
registryChangeListeners.delete(cb);
|
|
997
998
|
};
|
|
998
999
|
}
|
|
999
|
-
function buildMcpToolDescriptor(server, tool, opts) {
|
|
1000
|
-
const
|
|
1000
|
+
function buildMcpToolDescriptor(server, tool, opts, plainName) {
|
|
1001
|
+
const mcpSpelling = `mcp__${server}__${tool.name}`;
|
|
1002
|
+
const canonicalName = plainName ?? mcpSpelling;
|
|
1001
1003
|
const requiresInteraction = tool._meta?.["anthropic/requiresUserInteraction"] === true;
|
|
1002
1004
|
const perToolAlwaysLoad = tool._meta?.["anthropic/alwaysLoad"] === true;
|
|
1003
1005
|
const alwaysLoadResolved = perToolAlwaysLoad || opts.alwaysLoad === true ? true : opts.alwaysLoad;
|
|
@@ -1017,9 +1019,23 @@ function buildMcpToolDescriptor(server, tool, opts) {
|
|
|
1017
1019
|
...alwaysLoadResolved !== undefined ? { alwaysLoad: alwaysLoadResolved } : {},
|
|
1018
1020
|
deferred: opts.deferredDefault,
|
|
1019
1021
|
...tool._meta !== undefined ? { _meta: tool._meta } : {},
|
|
1020
|
-
...requiresInteraction ? { interaction: "required" } : {}
|
|
1022
|
+
...requiresInteraction ? { interaction: "required" } : {},
|
|
1023
|
+
...plainName !== undefined ? { mcpName: mcpSpelling } : {}
|
|
1021
1024
|
};
|
|
1022
1025
|
}
|
|
1026
|
+
var TOOL_SEARCH_TOOL_NAME = "ToolSearch";
|
|
1027
|
+
var PLAIN_TOOL_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
1028
|
+
function mcpSpellingOf(name) {
|
|
1029
|
+
return registry.get(name)?.descriptor.mcpName;
|
|
1030
|
+
}
|
|
1031
|
+
function registeredNameForMcpSpelling(name) {
|
|
1032
|
+
if (registry.has(name))
|
|
1033
|
+
return name;
|
|
1034
|
+
for (const [registered, entry] of registry)
|
|
1035
|
+
if (entry.descriptor.mcpName === name)
|
|
1036
|
+
return registered;
|
|
1037
|
+
return name;
|
|
1038
|
+
}
|
|
1023
1039
|
var RESERVED_MCP_SERVER_NAMES = new Set([WINTER_BRAND.mcpServerName]);
|
|
1024
1040
|
var STANDING_REBRANDS = new Map;
|
|
1025
1041
|
function rebrandStandingServerTools(renames, serverName) {
|
|
@@ -1101,13 +1117,36 @@ function registerMcpServerTools(server, incomingTools, opts) {
|
|
|
1101
1117
|
throw new Error(`registerMcpServerTools: "${server}" is a RESERVED server name -- the standing Winter server ` + `(its name is defined in mcp/winter-server.ts) owns this identity and is registry-native; it is never installed through ` + `this live-mutation function, and nothing else may register live tools under it either.`);
|
|
1102
1118
|
}
|
|
1103
1119
|
const tools = dedupeSameBatch(server, incomingTools);
|
|
1104
|
-
const
|
|
1120
|
+
const plainNameFor = (tool) => opts.toolNames !== undefined && Object.hasOwn(opts.toolNames, tool.name) ? opts.toolNames[tool.name] : undefined;
|
|
1121
|
+
const registeredName = (tool) => plainNameFor(tool) ?? `mcp__${server}__${tool.name}`;
|
|
1122
|
+
const newNames = new Set(tools.map(registeredName));
|
|
1105
1123
|
const previouslyOwned = mcpServerOwnedNames.get(server);
|
|
1106
1124
|
if (tools.length === 0 && (previouslyOwned === undefined || previouslyOwned.size === 0))
|
|
1107
1125
|
return;
|
|
1126
|
+
const plainNamesSeen = new Set;
|
|
1108
1127
|
for (const tool of tools) {
|
|
1109
|
-
const
|
|
1128
|
+
const plain = plainNameFor(tool);
|
|
1129
|
+
if (plain !== undefined) {
|
|
1130
|
+
if (typeof plain !== "string" || !PLAIN_TOOL_NAME.test(plain) || plain.startsWith("mcp__")) {
|
|
1131
|
+
throw new Error(`registerMcpServerTools: server "${server}" names its tool "${tool.name}" ${JSON.stringify(plain)} -- a plain tool name must match ${String(PLAIN_TOOL_NAME)} and must not start with "mcp__"`);
|
|
1132
|
+
}
|
|
1133
|
+
if (plainNamesSeen.has(plain)) {
|
|
1134
|
+
throw new Error(`registerMcpServerTools: server "${server}" gives two of its tools the same plain name "${plain}"`);
|
|
1135
|
+
}
|
|
1136
|
+
plainNamesSeen.add(plain);
|
|
1137
|
+
}
|
|
1138
|
+
const canonicalName = registeredName(tool);
|
|
1110
1139
|
const existingOwner = mcpToolOwner.get(canonicalName);
|
|
1140
|
+
const spelling = plain !== undefined ? `mcp__${server}__${tool.name}` : undefined;
|
|
1141
|
+
for (const [name, entry] of registry) {
|
|
1142
|
+
const foreign = mcpToolOwner.get(name) !== server;
|
|
1143
|
+
if (foreign && entry.descriptor.mcpName !== undefined && (entry.descriptor.mcpName === canonicalName || entry.descriptor.mcpName === spelling)) {
|
|
1144
|
+
throw new Error(`registerMcpServerTools: "${canonicalName}" collides with the old spelling "${entry.descriptor.mcpName}" that "${name}" keeps -- refusing to register a second tool under it`);
|
|
1145
|
+
}
|
|
1146
|
+
if (foreign && spelling !== undefined && name === spelling) {
|
|
1147
|
+
throw new Error(`registerMcpServerTools: the old spelling "${spelling}" of server "${server}"'s renamed tool "${plain}" is already a registered tool -- refusing to register a second tool under it`);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1111
1150
|
if (registry.has(canonicalName) && existingOwner === undefined) {
|
|
1112
1151
|
throw new Error(`registerMcpServerTools: "${canonicalName}" is already registered by a non-live-MCP mechanism ` + `(e.g. a static WS-06 descriptor stub, or the standing winter server's own advisor identity) -- ` + `refusing to overwrite it. If this name is meant to be a live-connected MCP tool, its static ` + `registration must be removed first.`);
|
|
1113
1152
|
}
|
|
@@ -1125,9 +1164,9 @@ function registerMcpServerTools(server, incomingTools, opts) {
|
|
|
1125
1164
|
}
|
|
1126
1165
|
const nowOwned = new Set;
|
|
1127
1166
|
for (const tool of tools) {
|
|
1128
|
-
const canonicalName =
|
|
1167
|
+
const canonicalName = registeredName(tool);
|
|
1129
1168
|
const existingEntry = registry.get(canonicalName);
|
|
1130
|
-
const descriptor = buildMcpToolDescriptor(server, tool, opts);
|
|
1169
|
+
const descriptor = buildMcpToolDescriptor(server, tool, opts, plainNameFor(tool));
|
|
1131
1170
|
registry.set(canonicalName, existingEntry ? { ...existingEntry, descriptor } : { descriptor });
|
|
1132
1171
|
mcpToolOwner.set(canonicalName, server);
|
|
1133
1172
|
nowOwned.add(canonicalName);
|
|
@@ -1206,12 +1245,15 @@ function resolveDeferral(descriptor, mode, activation) {
|
|
|
1206
1245
|
const modes = descriptor.availability.modes;
|
|
1207
1246
|
if (modes !== undefined && !modes.includes(mode))
|
|
1208
1247
|
return "hidden";
|
|
1209
|
-
if (descriptor.
|
|
1248
|
+
if (descriptor.canonicalName === TOOL_SEARCH_TOOL_NAME)
|
|
1210
1249
|
return "eager";
|
|
1211
1250
|
if (descriptor.alwaysLoad === true)
|
|
1212
1251
|
return "eager";
|
|
1252
|
+
const hostDeferred = activation.deferTools?.includes(descriptor.canonicalName) === true;
|
|
1253
|
+
if (descriptor.source === "builtin" && !hostDeferred)
|
|
1254
|
+
return "eager";
|
|
1213
1255
|
const declared = descriptor.deferred;
|
|
1214
|
-
const eligible = declared === true ? true : Array.isArray(declared) ? declared.includes(mode) : false;
|
|
1256
|
+
const eligible = hostDeferred || (declared === true ? true : Array.isArray(declared) ? declared.includes(mode) : false);
|
|
1215
1257
|
if (!eligible)
|
|
1216
1258
|
return "eager";
|
|
1217
1259
|
return isDeferralActive(activation) ? "deferred" : "eager";
|
|
@@ -1250,6 +1292,8 @@ function isAvailable(descriptor, cfg) {
|
|
|
1250
1292
|
return false;
|
|
1251
1293
|
if (a.insideSubagent === false && cfg.insideSubagent === true)
|
|
1252
1294
|
return false;
|
|
1295
|
+
if (a.requiresAnyCapability !== undefined && !a.requiresAnyCapability.some((c) => cfg.capabilities?.includes(c) === true))
|
|
1296
|
+
return false;
|
|
1253
1297
|
if (!descriptor.capabilityRequirements.every((c) => cfg.capabilities?.includes(c) === true))
|
|
1254
1298
|
return false;
|
|
1255
1299
|
return true;
|
|
@@ -1276,8 +1320,17 @@ function isBareDenied(canonicalName, disallowedTools) {
|
|
|
1276
1320
|
});
|
|
1277
1321
|
}
|
|
1278
1322
|
function buildAdvertisedSet(cfg) {
|
|
1279
|
-
|
|
1280
|
-
|
|
1323
|
+
return listRegisteredTools().map((t) => t.descriptor).filter((d) => d.disposition !== "correctly-absent").filter((d) => d.exposure !== "hidden").filter((d) => isRequestedTool(d, cfg.tools)).filter((d) => isAvailable(d, cfg)).filter((d) => !isDescriptorBareDenied(d, cfg.disallowedTools));
|
|
1324
|
+
}
|
|
1325
|
+
function isRequestedTool(descriptor, tools) {
|
|
1326
|
+
if (descriptor.source !== "builtin")
|
|
1327
|
+
return true;
|
|
1328
|
+
if (tools === undefined)
|
|
1329
|
+
return descriptor.availability.optIn !== true;
|
|
1330
|
+
return tools.includes(descriptor.canonicalName);
|
|
1331
|
+
}
|
|
1332
|
+
function isDescriptorBareDenied(descriptor, disallowedTools) {
|
|
1333
|
+
return isBareDenied(descriptor.canonicalName, disallowedTools) || descriptor.mcpName !== undefined && isBareDenied(descriptor.mcpName, disallowedTools);
|
|
1281
1334
|
}
|
|
1282
1335
|
function unknownToolResult(name) {
|
|
1283
1336
|
return { output: `Error: unknown tool "${name}" -- no descriptor is registered under that name`, isError: true };
|
|
@@ -1295,7 +1348,12 @@ function notYetExecutableResult(name) {
|
|
|
1295
1348
|
};
|
|
1296
1349
|
}
|
|
1297
1350
|
function foldResult(result) {
|
|
1298
|
-
return {
|
|
1351
|
+
return {
|
|
1352
|
+
output: result.output,
|
|
1353
|
+
...result.blocks !== undefined ? { blocks: result.blocks } : {},
|
|
1354
|
+
...result.isError === true ? { isError: true } : {},
|
|
1355
|
+
...result.siteIcons !== undefined && result.siteIcons.length > 0 ? { siteIcons: result.siteIcons } : {}
|
|
1356
|
+
};
|
|
1299
1357
|
}
|
|
1300
1358
|
function unavailableResult(name) {
|
|
1301
1359
|
return {
|
|
@@ -2462,6 +2520,8 @@ function renderPlanModeBlock(input) {
|
|
|
2462
2520
|
}
|
|
2463
2521
|
|
|
2464
2522
|
// src/context/attachments.ts
|
|
2523
|
+
var DEFERRED_TOOLS_ADDED_HEADER = "The following deferred tools are now available via ToolSearch:";
|
|
2524
|
+
var DEFERRED_TOOLS_REMOVED_HEADER = "The following deferred tools are no longer available (their MCP server disconnected). Do not search for them — ToolSearch will return no match:";
|
|
2465
2525
|
var AGENT_LISTING_INITIAL_HEADER = "Available agent types for the Agent tool:";
|
|
2466
2526
|
var AGENT_LISTING_ADDED_HEADER = "New agent types are now available for the Agent tool:";
|
|
2467
2527
|
var AGENT_LISTING_REMOVED_HEADER = "The following agent types are no longer available:";
|
|
@@ -2511,6 +2571,44 @@ ${removedTypes.map((t) => `- ${t}`).join(`
|
|
|
2511
2571
|
|
|
2512
2572
|
`));
|
|
2513
2573
|
});
|
|
2574
|
+
registerAttachmentRenderer("deferred_tools_delta", (a) => {
|
|
2575
|
+
const addedLines = stringArray(a["addedLines"]);
|
|
2576
|
+
const removedNames = stringArray(a["removedNames"]);
|
|
2577
|
+
const parts = [];
|
|
2578
|
+
if (addedLines.length > 0)
|
|
2579
|
+
parts.push(`${DEFERRED_TOOLS_ADDED_HEADER}
|
|
2580
|
+
${addedLines.join(`
|
|
2581
|
+
`)}`);
|
|
2582
|
+
if (removedNames.length > 0)
|
|
2583
|
+
parts.push(`${DEFERRED_TOOLS_REMOVED_HEADER}
|
|
2584
|
+
${removedNames.join(`
|
|
2585
|
+
`)}`);
|
|
2586
|
+
return parts.length === 0 ? undefined : neutralizeReminderTags(parts.join(`
|
|
2587
|
+
|
|
2588
|
+
`));
|
|
2589
|
+
});
|
|
2590
|
+
function announcedDeferredTools(messages) {
|
|
2591
|
+
const announced = new Set;
|
|
2592
|
+
for (const a of attachmentsIn(messages)) {
|
|
2593
|
+
if (a.type !== "deferred_tools_delta")
|
|
2594
|
+
continue;
|
|
2595
|
+
if (Array.isArray(a["addedLines"]))
|
|
2596
|
+
for (const name of stringArray(a["addedNames"]))
|
|
2597
|
+
announced.add(name);
|
|
2598
|
+
for (const name of stringArray(a["removedNames"]))
|
|
2599
|
+
announced.delete(name);
|
|
2600
|
+
}
|
|
2601
|
+
return announced;
|
|
2602
|
+
}
|
|
2603
|
+
function computeDeferredToolsDelta(deferred, offered, history) {
|
|
2604
|
+
const announced = announcedDeferredTools(history);
|
|
2605
|
+
const offeredNow = new Set([...offered, ...deferred]);
|
|
2606
|
+
const added = [...new Set(deferred)].filter((name) => !announced.has(name)).sort();
|
|
2607
|
+
const removed = [...announced].filter((name) => !offeredNow.has(name)).sort();
|
|
2608
|
+
if (added.length === 0 && removed.length === 0)
|
|
2609
|
+
return;
|
|
2610
|
+
return { type: "deferred_tools_delta", addedNames: added, addedLines: added, removedNames: removed };
|
|
2611
|
+
}
|
|
2514
2612
|
registerAttachmentRenderer("skill_listing", (a) => {
|
|
2515
2613
|
const content = typeof a["content"] === "string" ? a["content"] : "";
|
|
2516
2614
|
if (content.length === 0)
|
|
@@ -6470,6 +6568,7 @@ ${body}
|
|
|
6470
6568
|
// src/mcp/control.ts
|
|
6471
6569
|
var REPLACE_ELIGIBLE_ORIGINS = new Set(["explicit", "dynamic"]);
|
|
6472
6570
|
function createMcpControlSeam(internals, opts = {}) {
|
|
6571
|
+
const hostReserved = new Set(opts.hostReservedServerNames ?? []);
|
|
6473
6572
|
const reservedServerName = opts.reservedServerName ?? WINTER_SERVER_NAME;
|
|
6474
6573
|
return {
|
|
6475
6574
|
async reconnect(serverName) {
|
|
@@ -6498,6 +6597,10 @@ function createMcpControlSeam(internals, opts = {}) {
|
|
|
6498
6597
|
errors[name] = `"${reservedServerName}" is a reserved server identity (RULING P4-B, the standing Winter server) -- no source may configure a live MCP server under this name`;
|
|
6499
6598
|
continue;
|
|
6500
6599
|
}
|
|
6600
|
+
if (hostReserved.has(name) && raw?.type !== "sdk") {
|
|
6601
|
+
errors[name] = `"${name}" is a server name this session's host reserves for its own in-process server -- only an in-process (type "sdk") server may be configured under it`;
|
|
6602
|
+
continue;
|
|
6603
|
+
}
|
|
6501
6604
|
const result = validateServerConfig(raw);
|
|
6502
6605
|
if (!result.ok) {
|
|
6503
6606
|
errors[name] = result.reason;
|
|
@@ -6573,6 +6676,7 @@ function validateServerConfig(raw) {
|
|
|
6573
6676
|
}
|
|
6574
6677
|
function resolveMcpServerSources(sources, opts) {
|
|
6575
6678
|
const reservedServerName = opts.reservedServerName ?? WINTER_SERVER_NAME;
|
|
6679
|
+
const hostReserved = new Set(opts.hostReservedServerNames ?? []);
|
|
6576
6680
|
const resolved = [];
|
|
6577
6681
|
const shadowed = [];
|
|
6578
6682
|
const rejected = [];
|
|
@@ -6597,6 +6701,10 @@ function resolveMcpServerSources(sources, opts) {
|
|
|
6597
6701
|
rejected.push({ name, origin, code: "reserved_name", reason: `"${reservedServerName}" is a reserved server identity (RULING P4-B, the standing Winter server) -- no source may configure a live MCP server under this name` });
|
|
6598
6702
|
continue;
|
|
6599
6703
|
}
|
|
6704
|
+
if (hostReserved.has(name) && (origin !== "explicit" || raw?.type !== "sdk")) {
|
|
6705
|
+
rejected.push({ name, origin, code: "reserved_name", reason: `"${name}" is a server name this session's host reserves for its own in-process server -- no ${origin} source may configure a server under this name` });
|
|
6706
|
+
continue;
|
|
6707
|
+
}
|
|
6600
6708
|
const validated = validateServerConfig(raw);
|
|
6601
6709
|
if (!validated.ok) {
|
|
6602
6710
|
rejected.push({ name, origin, code: "invalid_config", reason: validated.reason });
|
|
@@ -7168,7 +7276,7 @@ function createMcpLifecycle(deps) {
|
|
|
7168
7276
|
};
|
|
7169
7277
|
return {
|
|
7170
7278
|
stateSource,
|
|
7171
|
-
controlSeam: createMcpControlSeam(internals, { ...deps.reservedServerName !== undefined ? { reservedServerName: deps.reservedServerName } : {} }),
|
|
7279
|
+
controlSeam: createMcpControlSeam(internals, { ...deps.reservedServerName !== undefined ? { reservedServerName: deps.reservedServerName } : {}, ...deps.hostReservedServerNames !== undefined ? { hostReservedServerNames: deps.hostReservedServerNames } : {} }),
|
|
7172
7280
|
start,
|
|
7173
7281
|
launch,
|
|
7174
7282
|
dispose,
|
|
@@ -11012,13 +11120,15 @@ function capHookText(text, limit = MAX_HOOK_TEXT_CHARS) {
|
|
|
11012
11120
|
|
|
11013
11121
|
// src/hooks/runner.ts
|
|
11014
11122
|
function mcpToolProvenance(toolName) {
|
|
11015
|
-
const
|
|
11123
|
+
const registered = registeredNameForMcpSpelling(toolName);
|
|
11124
|
+
const server = mcpServerOwningTool(registered);
|
|
11016
11125
|
if (server === undefined)
|
|
11017
11126
|
return;
|
|
11127
|
+
const spelled = mcpSpellingOf(registered) ?? registered;
|
|
11018
11128
|
const prefix = `mcp__${server}__`;
|
|
11019
|
-
if (!
|
|
11129
|
+
if (!spelled.startsWith(prefix) || spelled.length === prefix.length)
|
|
11020
11130
|
return;
|
|
11021
|
-
return { server, tool:
|
|
11131
|
+
return { server, tool: spelled.slice(prefix.length) };
|
|
11022
11132
|
}
|
|
11023
11133
|
function mcpServerIdentity(toolName, renames) {
|
|
11024
11134
|
const name = mcpServerOwningTool(toolName);
|
|
@@ -14739,6 +14849,26 @@ stub({
|
|
|
14739
14849
|
disposition: "implement-now"
|
|
14740
14850
|
});
|
|
14741
14851
|
|
|
14852
|
+
// src/tools/descriptors/search.ts
|
|
14853
|
+
var SEARCH_CANONICAL_NAME = "Search";
|
|
14854
|
+
var SEARCH_ANSWER_CAPABILITY = "winter.search-answer";
|
|
14855
|
+
var SEARCH_DESCRIPTION = "Search the web and get back a written answer with its sources, in a single call. Ask a real question, not keywords — a search engine answers it and the answer comes back already synthesized, followed by the pages it came from. Use it freely whenever a fact might be newer than you are, or when the user asks about something current, and cite the URLs you used.";
|
|
14856
|
+
var descriptor = {
|
|
14857
|
+
canonicalName: SEARCH_CANONICAL_NAME,
|
|
14858
|
+
advertisedName: SEARCH_CANONICAL_NAME,
|
|
14859
|
+
source: "builtin",
|
|
14860
|
+
inputSchema: { type: "object", properties: { query: { type: "string", minLength: 1, description: "The question to answer" } }, required: ["query"] },
|
|
14861
|
+
description: SEARCH_DESCRIPTION,
|
|
14862
|
+
searchHint: "answer a question from the web with sources",
|
|
14863
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
14864
|
+
exposure: "eager",
|
|
14865
|
+
permissionClass: "network",
|
|
14866
|
+
availability: { optIn: true },
|
|
14867
|
+
capabilityRequirements: [SEARCH_ANSWER_CAPABILITY],
|
|
14868
|
+
disposition: "implement-now"
|
|
14869
|
+
};
|
|
14870
|
+
stub(descriptor);
|
|
14871
|
+
|
|
14742
14872
|
// src/tools/descriptors/tool-search.ts
|
|
14743
14873
|
stub({
|
|
14744
14874
|
canonicalName: "ToolSearch",
|
|
@@ -14752,11 +14882,13 @@ stub({
|
|
|
14752
14882
|
},
|
|
14753
14883
|
required: ["query"]
|
|
14754
14884
|
},
|
|
14755
|
-
description:
|
|
14885
|
+
description: `Fetches full schema definitions for deferred tools so they can be called.
|
|
14886
|
+
|
|
14887
|
+
` + "Deferred tools appear by name in <system-reminder> messages. Until fetched, only the name is known — there is no parameter schema, so the tool cannot be invoked. " + 'Query forms: "select:Read,Edit,Grep" fetches these exact tools by name; "notebook jupyter" is a keyword search over the deferred tools.',
|
|
14756
14888
|
exposure: "eager",
|
|
14757
14889
|
permissionClass: "read",
|
|
14758
|
-
availability: { requiresToolSearchEnabled: true },
|
|
14759
|
-
capabilityRequirements: [
|
|
14890
|
+
availability: { requiresToolSearchEnabled: true, requiresAnyCapability: ["winter.mcp", DEFERRED_BUILTINS_CAPABILITY] },
|
|
14891
|
+
capabilityRequirements: [],
|
|
14760
14892
|
disposition: "implement-now"
|
|
14761
14893
|
});
|
|
14762
14894
|
|
|
@@ -14993,7 +15125,7 @@ var WEB_SEARCH_INPUT_SCHEMA_FIRST_PARTY = {
|
|
|
14993
15125
|
function webSearchInputSchemaFor(firstPartyAnthropic) {
|
|
14994
15126
|
return firstPartyAnthropic ? WEB_SEARCH_INPUT_SCHEMA_FIRST_PARTY : WEB_SEARCH_INPUT_SCHEMA_PORTABLE;
|
|
14995
15127
|
}
|
|
14996
|
-
var
|
|
15128
|
+
var descriptor2 = {
|
|
14997
15129
|
canonicalName: WEB_SEARCH_CANONICAL_NAME,
|
|
14998
15130
|
advertisedName: WEB_SEARCH_CANONICAL_NAME,
|
|
14999
15131
|
source: "builtin",
|
|
@@ -15007,14 +15139,14 @@ var descriptor = {
|
|
|
15007
15139
|
capabilityRequirements: ["winter.search-backend"],
|
|
15008
15140
|
disposition: "implement-now"
|
|
15009
15141
|
};
|
|
15010
|
-
Object.defineProperty(
|
|
15142
|
+
Object.defineProperty(descriptor2, "description", {
|
|
15011
15143
|
enumerable: true,
|
|
15012
15144
|
configurable: true,
|
|
15013
15145
|
get() {
|
|
15014
15146
|
return webSearchDescription(false);
|
|
15015
15147
|
}
|
|
15016
15148
|
});
|
|
15017
|
-
stub(
|
|
15149
|
+
stub(descriptor2);
|
|
15018
15150
|
|
|
15019
15151
|
// src/tools/descriptors/winter-list-agents.ts
|
|
15020
15152
|
import { WINTER_BRAND as WINTER_BRAND13, mcpToolName } from "@yanlinglabs/winter-agent-sdk";
|
|
@@ -19083,6 +19215,7 @@ function computeExposurePartition(query) {
|
|
|
19083
19215
|
...query.platform !== undefined ? { platform: query.platform } : {},
|
|
19084
19216
|
...query.capabilities !== undefined ? { capabilities: query.capabilities } : {},
|
|
19085
19217
|
...query.disallowedTools !== undefined ? { disallowedTools: query.disallowedTools } : {},
|
|
19218
|
+
...query.tools !== undefined ? { tools: query.tools } : {},
|
|
19086
19219
|
...query.insideSubagent !== undefined ? { insideSubagent: query.insideSubagent } : {},
|
|
19087
19220
|
...query.familyMetadata !== undefined ? { familyMetadata: query.familyMetadata } : {}
|
|
19088
19221
|
};
|
|
@@ -19142,6 +19275,7 @@ function exposureQuery(deps) {
|
|
|
19142
19275
|
...deps.platform !== undefined ? { platform: deps.platform } : {},
|
|
19143
19276
|
...deps.capabilities !== undefined ? { capabilities: deps.capabilities } : {},
|
|
19144
19277
|
...deps.disallowedTools !== undefined ? { disallowedTools: deps.disallowedTools } : {},
|
|
19278
|
+
...deps.tools !== undefined ? { tools: deps.tools } : {},
|
|
19145
19279
|
...deps.insideSubagent !== undefined ? { insideSubagent: deps.insideSubagent } : {},
|
|
19146
19280
|
...deps.familyMetadata !== undefined ? { familyMetadata: deps.familyMetadata } : {},
|
|
19147
19281
|
...deps.toolAliases !== undefined ? { toolAliases: deps.toolAliases } : {},
|
|
@@ -19175,7 +19309,9 @@ async function executeToolSearch(input, deps) {
|
|
|
19175
19309
|
const query = rec.query;
|
|
19176
19310
|
const maxResults = typeof rec.max_results === "number" && Number.isFinite(rec.max_results) && rec.max_results > 0 ? Math.floor(rec.max_results) : DEFAULT_MAX_RESULTS;
|
|
19177
19311
|
const pendingWaitMs = deps.pendingWaitMs ?? DEFAULT_PENDING_WAIT_MS;
|
|
19178
|
-
const
|
|
19312
|
+
const resolveName = deps.resolveName ?? registeredNameForMcpSpelling;
|
|
19313
|
+
const parsedSelect = parseSelect(query);
|
|
19314
|
+
const selectNames = parsedSelect === undefined ? undefined : [...new Set(parsedSelect.map(resolveName))];
|
|
19179
19315
|
let attempt = runAttempt(query, selectNames, maxResults, deps);
|
|
19180
19316
|
const incomplete = selectNames !== undefined ? attempt.matches.length < selectNames.length : attempt.matches.length === 0;
|
|
19181
19317
|
if (incomplete && deps.stateSource !== undefined) {
|
|
@@ -19215,7 +19351,7 @@ function getToolSearchSessionRuntime(sessionId) {
|
|
|
19215
19351
|
}
|
|
19216
19352
|
|
|
19217
19353
|
// src/tools/impl/tool-search.ts
|
|
19218
|
-
var
|
|
19354
|
+
var TOOL_SEARCH_TOOL_NAME2 = "ToolSearch";
|
|
19219
19355
|
function notWiredResult() {
|
|
19220
19356
|
return {
|
|
19221
19357
|
output: "Error: ToolSearch has no session runtime registered for this session (WS-09 §8) -- engine.ts must call registerToolSearchSessionRuntime(sessionId, ...) once per run (toolsearch/search.ts); this is a host wiring gap, not a model input error.",
|
|
@@ -19233,7 +19369,7 @@ var toolSearchExecutor = {
|
|
|
19233
19369
|
return { output: JSON.stringify(outcome.result) };
|
|
19234
19370
|
}
|
|
19235
19371
|
};
|
|
19236
|
-
replaceExecutor(
|
|
19372
|
+
replaceExecutor(TOOL_SEARCH_TOOL_NAME2, toolSearchExecutor);
|
|
19237
19373
|
|
|
19238
19374
|
// src/tools/impl/wait-for-mcp-servers.ts
|
|
19239
19375
|
var WAIT_FOR_MCP_SERVERS_TOOL_NAME = "WaitForMcpServers";
|
|
@@ -19845,6 +19981,9 @@ function inheritedWebSessionFacts(sessionId) {
|
|
|
19845
19981
|
function searchBackendUsable(runtime) {
|
|
19846
19982
|
return runtime.web.search.enabled;
|
|
19847
19983
|
}
|
|
19984
|
+
function searchAnswerUsable(runtime) {
|
|
19985
|
+
return runtime.web.search.enabled && runtime.web.search.authRef !== undefined && runtime.resolveToolSecret !== undefined;
|
|
19986
|
+
}
|
|
19848
19987
|
function digestModelResolves(runtime) {
|
|
19849
19988
|
const tag = runtime.web.fetch.digestModel;
|
|
19850
19989
|
if (tag === undefined)
|
|
@@ -20937,6 +21076,85 @@ async function runInnerModel(ctx, request, runtime) {
|
|
|
20937
21076
|
}
|
|
20938
21077
|
}
|
|
20939
21078
|
|
|
21079
|
+
// src/tools/impl/_site-icons.ts
|
|
21080
|
+
var SITE_ICONS_MAX = 10;
|
|
21081
|
+
var SITE_ICON_URL_MAX_LENGTH = 2048;
|
|
21082
|
+
var ICON_SCAN_CHARS = 256 * 1024;
|
|
21083
|
+
function siteIconHttpsUrl(value) {
|
|
21084
|
+
if (typeof value !== "string" || value.length === 0 || value.length > SITE_ICON_URL_MAX_LENGTH)
|
|
21085
|
+
return;
|
|
21086
|
+
let parsed;
|
|
21087
|
+
try {
|
|
21088
|
+
parsed = new URL(value.trim());
|
|
21089
|
+
} catch {
|
|
21090
|
+
return;
|
|
21091
|
+
}
|
|
21092
|
+
if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || !isPublicName(parsed.hostname))
|
|
21093
|
+
return;
|
|
21094
|
+
const href = parsed.href;
|
|
21095
|
+
return href.length <= SITE_ICON_URL_MAX_LENGTH && /^https:\/\/[!-~]+$/.test(href) ? href : undefined;
|
|
21096
|
+
}
|
|
21097
|
+
var PRIVATE_NAME_SUFFIX = /\.(?:localhost|local|internal|lan|home|home\.arpa|corp|intranet|private)$/;
|
|
21098
|
+
function isPublicName(hostname) {
|
|
21099
|
+
const host = hostname.toLowerCase().replace(/\.$/, "");
|
|
21100
|
+
if (host.length === 0 || host.length > 253 || host.startsWith("[") || !host.includes("."))
|
|
21101
|
+
return false;
|
|
21102
|
+
if (host === "localhost" || PRIVATE_NAME_SUFFIX.test(host))
|
|
21103
|
+
return false;
|
|
21104
|
+
const labels = host.split(".");
|
|
21105
|
+
return /[a-z]/.test(labels[labels.length - 1]) && labels.every((l) => l.length > 0 && l.length <= 63);
|
|
21106
|
+
}
|
|
21107
|
+
function collectSiteIcons(entries) {
|
|
21108
|
+
const out = [];
|
|
21109
|
+
const seen = new Set;
|
|
21110
|
+
for (const e of entries) {
|
|
21111
|
+
if (out.length >= SITE_ICONS_MAX)
|
|
21112
|
+
break;
|
|
21113
|
+
const url = siteIconHttpsUrl(e.url);
|
|
21114
|
+
const iconUrl = siteIconHttpsUrl(e.iconUrl);
|
|
21115
|
+
if (url === undefined || iconUrl === undefined || seen.has(url))
|
|
21116
|
+
continue;
|
|
21117
|
+
seen.add(url);
|
|
21118
|
+
out.push({ url, iconUrl });
|
|
21119
|
+
}
|
|
21120
|
+
return out.length > 0 ? out : undefined;
|
|
21121
|
+
}
|
|
21122
|
+
var LINK_TAG = /<link\b[^>]*>/gi;
|
|
21123
|
+
var ATTRIBUTE = /([A-Za-z][\w:-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/g;
|
|
21124
|
+
function pageIconUrl(html, pageUrl) {
|
|
21125
|
+
const page = siteIconHttpsUrl(pageUrl);
|
|
21126
|
+
if (page === undefined)
|
|
21127
|
+
return;
|
|
21128
|
+
const head = html.length > ICON_SCAN_CHARS ? html.slice(0, ICON_SCAN_CHARS) : html;
|
|
21129
|
+
let best;
|
|
21130
|
+
for (const tag of head.matchAll(LINK_TAG)) {
|
|
21131
|
+
const attrs = {};
|
|
21132
|
+
for (const m of tag[0].matchAll(ATTRIBUTE))
|
|
21133
|
+
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
|
|
21134
|
+
const rel = (attrs["rel"] ?? "").toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
21135
|
+
const href = decodeHtmlEntities(attrs["href"] ?? "").trim();
|
|
21136
|
+
const lowered = href.toLowerCase();
|
|
21137
|
+
if (href.length === 0 || rel.includes("mask-icon") || lowered.startsWith("data:") || /\.svg(?:[?#]|$)/.test(lowered))
|
|
21138
|
+
continue;
|
|
21139
|
+
const rank = rel.includes("apple-touch-icon") || rel.includes("apple-touch-icon-precomposed") ? 3 : rel.includes("icon") ? 2 : 0;
|
|
21140
|
+
if (rank === 0)
|
|
21141
|
+
continue;
|
|
21142
|
+
if (best === undefined || rank > best.rank) {
|
|
21143
|
+
const resolved = resolveIcon(href, page);
|
|
21144
|
+
if (resolved !== undefined)
|
|
21145
|
+
best = { rank, href: resolved };
|
|
21146
|
+
}
|
|
21147
|
+
}
|
|
21148
|
+
return best?.href ?? siteIconHttpsUrl(new URL("/favicon.ico", page).href);
|
|
21149
|
+
}
|
|
21150
|
+
function resolveIcon(href, page) {
|
|
21151
|
+
try {
|
|
21152
|
+
return siteIconHttpsUrl(new URL(href, page).href);
|
|
21153
|
+
} catch {
|
|
21154
|
+
return;
|
|
21155
|
+
}
|
|
21156
|
+
}
|
|
21157
|
+
|
|
20940
21158
|
// src/tools/impl/web-fetch.ts
|
|
20941
21159
|
var DIGEST_CONTENT_CAP = 1e5;
|
|
20942
21160
|
var RESULT_CAP = 50000;
|
|
@@ -20993,6 +21211,10 @@ async function raceAgainstAbort(promise, signal) {
|
|
|
20993
21211
|
});
|
|
20994
21212
|
});
|
|
20995
21213
|
}
|
|
21214
|
+
function siteIconsFor(pageUrl, iconUrl) {
|
|
21215
|
+
const siteIcons = iconUrl !== undefined ? collectSiteIcons([{ url: pageUrl, iconUrl }]) : undefined;
|
|
21216
|
+
return siteIcons !== undefined ? { siteIcons } : {};
|
|
21217
|
+
}
|
|
20996
21218
|
function capResult(text) {
|
|
20997
21219
|
if (text.length <= RESULT_CAP)
|
|
20998
21220
|
return text;
|
|
@@ -21156,11 +21378,15 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21156
21378
|
const preapproved = isPreapprovedUrl(originalUrl);
|
|
21157
21379
|
let content;
|
|
21158
21380
|
let contentType;
|
|
21381
|
+
let pageUrl;
|
|
21382
|
+
let iconUrl;
|
|
21383
|
+
let addressPublic;
|
|
21159
21384
|
const cached = cache.get(ctx.sessionId, inputUrlString);
|
|
21160
21385
|
if (cached !== undefined) {
|
|
21161
21386
|
const addressVerdict = await raceAgainstAbort(classifyHostname(stripIpv6Brackets(originalUrl.hostname), resolveHost), ctx.signal);
|
|
21162
21387
|
if (addressVerdict === "aborted")
|
|
21163
21388
|
return { output: "WebFetch was interrupted.", isError: true };
|
|
21389
|
+
addressPublic = addressVerdict.class !== "private";
|
|
21164
21390
|
if (addressVerdict.class === "private") {
|
|
21165
21391
|
const refusal = privateAddressRefusal(originalUrl.hostname, policy, addressVerdict.reason);
|
|
21166
21392
|
if (refusal !== undefined)
|
|
@@ -21173,6 +21399,7 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21173
21399
|
const finalVerdict = await raceAgainstAbort(classifyHostname(stripIpv6Brackets(cachedHost), resolveHost), ctx.signal);
|
|
21174
21400
|
if (finalVerdict === "aborted")
|
|
21175
21401
|
return { output: "WebFetch was interrupted.", isError: true };
|
|
21402
|
+
addressPublic &&= finalVerdict.class !== "private";
|
|
21176
21403
|
if (finalVerdict.class === "private") {
|
|
21177
21404
|
const refusal = privateAddressRefusal(cachedHost, policy, finalVerdict.reason);
|
|
21178
21405
|
if (refusal !== undefined)
|
|
@@ -21181,6 +21408,8 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21181
21408
|
}
|
|
21182
21409
|
content = cached.content;
|
|
21183
21410
|
contentType = cached.contentType;
|
|
21411
|
+
pageUrl = cached.finalUrl;
|
|
21412
|
+
iconUrl = cached.iconUrl;
|
|
21184
21413
|
} else {
|
|
21185
21414
|
const outcome = await performWebFetch(inputUrlString, prompt, { blockedDomains, privateAddressPolicy: policy, userAgent: winterUserAgent(), ...ctx.signal !== undefined ? { signal: ctx.signal } : {} }, netDeps);
|
|
21186
21415
|
switch (outcome.kind) {
|
|
@@ -21226,9 +21455,12 @@ The response body was not retrieved. If this URL requires authentication, use an
|
|
|
21226
21455
|
}
|
|
21227
21456
|
const kind = classifyContentType(outcome.contentType);
|
|
21228
21457
|
if (kind === "html") {
|
|
21229
|
-
|
|
21458
|
+
const html = new TextDecoder("utf-8", { fatal: false }).decode(outcome.body);
|
|
21459
|
+
iconUrl = pageIconUrl(html, outcome.finalUrl);
|
|
21460
|
+
content = await convertFetchedHtml(html);
|
|
21230
21461
|
} else if (kind === "text") {
|
|
21231
21462
|
content = new TextDecoder("utf-8", { fatal: false }).decode(outcome.body);
|
|
21463
|
+
iconUrl = pageIconUrl("", outcome.finalUrl);
|
|
21232
21464
|
} else {
|
|
21233
21465
|
const savedPath = await saveBinaryToTemp(ctx, originalUrl, outcome.body);
|
|
21234
21466
|
const sizeLabel = `${outcome.body.byteLength.toLocaleString("en-US")} bytes`;
|
|
@@ -21236,14 +21468,18 @@ The response body was not retrieved. If this URL requires authentication, use an
|
|
|
21236
21468
|
return { output: note };
|
|
21237
21469
|
}
|
|
21238
21470
|
contentType = outcome.contentType;
|
|
21239
|
-
|
|
21471
|
+
pageUrl = outcome.finalUrl;
|
|
21472
|
+
addressPublic = policy !== "allow";
|
|
21473
|
+
const entry = { bytes: Buffer.byteLength(content, "utf8"), code: outcome.status, codeText: outcome.statusText, content, contentType, finalUrl: outcome.finalUrl, ...iconUrl !== undefined ? { iconUrl } : {} };
|
|
21240
21474
|
cache.set(ctx.sessionId, inputUrlString, entry);
|
|
21241
21475
|
}
|
|
21476
|
+
if (!addressPublic)
|
|
21477
|
+
iconUrl = undefined;
|
|
21242
21478
|
if (preapproved && contentType.toLowerCase().includes("text/markdown") && content.length < 1e5) {
|
|
21243
|
-
return { output: capResult(content) };
|
|
21479
|
+
return { output: capResult(content), ...siteIconsFor(pageUrl, iconUrl) };
|
|
21244
21480
|
}
|
|
21245
21481
|
const digest = await runDigest(ctx, runtime, content, prompt, preapproved);
|
|
21246
|
-
return { output: capResult(digest.output), ...digest.isError ? { isError: true } :
|
|
21482
|
+
return { output: capResult(digest.output), ...digest.isError ? { isError: true } : siteIconsFor(pageUrl, iconUrl) };
|
|
21247
21483
|
}
|
|
21248
21484
|
return {
|
|
21249
21485
|
async execute(rawInput, ctx) {
|
|
@@ -21387,7 +21623,8 @@ function hitFromRecord(record) {
|
|
|
21387
21623
|
const title = capText(typeof record["title"] === "string" && record["title"].trim().length > 0 ? record["title"].trim() : url, EXA_TITLE_MAX_CHARACTERS);
|
|
21388
21624
|
const highlight = highlightFrom(record["highlights"]) || highlightFrom(record["summary"]) || highlightFrom(record["text"]);
|
|
21389
21625
|
const published = typeof record["publishedDate"] === "string" && record["publishedDate"].length > 0 ? record["publishedDate"] : undefined;
|
|
21390
|
-
|
|
21626
|
+
const favicon = siteIconHttpsUrl(record["favicon"]);
|
|
21627
|
+
return { title, url, highlight: capText(highlight.trim(), EXA_HIGHLIGHTS_MAX_CHARACTERS), ...published !== undefined ? { publishedDate: published } : {}, ...favicon !== undefined ? { favicon } : {} };
|
|
21391
21628
|
}
|
|
21392
21629
|
var FIELD_LINE = /^(Title|URL|Published(?: Date)?|Author|Highlights|Text|Summary|Content):[ \t]*(.*)$/i;
|
|
21393
21630
|
function hitsFromPlainText(text) {
|
|
@@ -21946,6 +22183,7 @@ function createWebSearchExecutor(deps = {}) {
|
|
|
21946
22183
|
return { output: `Error: web search failed unexpectedly (${err instanceof Error ? err.name : "unknown error"}).`, isError: true };
|
|
21947
22184
|
}
|
|
21948
22185
|
const events = [];
|
|
22186
|
+
const iconEntries = [];
|
|
21949
22187
|
let successfulSearches = 0;
|
|
21950
22188
|
let attemptedSearches = 0;
|
|
21951
22189
|
let lastFailureMessage;
|
|
@@ -21970,6 +22208,9 @@ function createWebSearchExecutor(deps = {}) {
|
|
|
21970
22208
|
}
|
|
21971
22209
|
successfulSearches += 1;
|
|
21972
22210
|
events.push({ type: "search_result", hits: outcome.hits.map((h) => ({ title: h.title, url: h.url })) });
|
|
22211
|
+
for (const h of outcome.hits)
|
|
22212
|
+
if (h.favicon !== undefined)
|
|
22213
|
+
iconEntries.push({ url: h.url, iconUrl: h.favicon });
|
|
21973
22214
|
}
|
|
21974
22215
|
if (pass !== undefined && !pass.ok) {
|
|
21975
22216
|
events.push({ type: "text", text: `
|
|
@@ -21981,12 +22222,150 @@ ${innerFailureText(pass)}` });
|
|
|
21981
22222
|
|
|
21982
22223
|
${lastFailureMessage}` });
|
|
21983
22224
|
}
|
|
21984
|
-
|
|
22225
|
+
const siteIcons = collectSiteIcons(iconEntries);
|
|
22226
|
+
return { output: assembleWebSearchOutputCapped(query, events, resultCap), ...siteIcons !== undefined ? { siteIcons } : {} };
|
|
21985
22227
|
}
|
|
21986
22228
|
return { execute };
|
|
21987
22229
|
}
|
|
21988
22230
|
replaceExecutor(WEB_SEARCH_CANONICAL_NAME, createWebSearchExecutor());
|
|
21989
22231
|
|
|
22232
|
+
// src/tools/impl/search.ts
|
|
22233
|
+
var SEARCH_REQUEST_TIMEOUT_MS = 45000;
|
|
22234
|
+
var SEARCH_ANSWER_CHARS = 24000;
|
|
22235
|
+
var SEARCH_MAX_CITATIONS = 20;
|
|
22236
|
+
var SEARCH_TOTAL_OUTPUT_CHARS = 30000;
|
|
22237
|
+
var EXA_ANSWER_URL = "https://api.exa.ai/answer";
|
|
22238
|
+
function isValidCitations(value) {
|
|
22239
|
+
if (value === undefined)
|
|
22240
|
+
return true;
|
|
22241
|
+
if (!Array.isArray(value))
|
|
22242
|
+
return false;
|
|
22243
|
+
return value.every((item) => item !== null && typeof item === "object");
|
|
22244
|
+
}
|
|
22245
|
+
function searchStatusMessage(status) {
|
|
22246
|
+
switch (status) {
|
|
22247
|
+
case 401:
|
|
22248
|
+
case 403:
|
|
22249
|
+
return "search failed: the configured Exa API key was rejected — it needs to be replaced before Search can work";
|
|
22250
|
+
case 402:
|
|
22251
|
+
return "search failed: this Exa account is out of credits or over its budget — top it up at exa.ai, or answer from what you already know and say the search was unavailable";
|
|
22252
|
+
case 429:
|
|
22253
|
+
return "search failed: the search service is rate-limiting this key — wait a little before searching again, and do not retry in a loop";
|
|
22254
|
+
case 400:
|
|
22255
|
+
return "search failed: the search service rejected the request as malformed — try a plainer question";
|
|
22256
|
+
default:
|
|
22257
|
+
return `search failed: the search service is unavailable (HTTP ${status})`;
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
var SEARCH_NO_KEY_MESSAGE = "Search needs an Exa API key (from exa.ai), and none is configured for this session";
|
|
22261
|
+
function errorResult6(output) {
|
|
22262
|
+
return { output, isError: true };
|
|
22263
|
+
}
|
|
22264
|
+
function createSearchExecutor(deps = {}) {
|
|
22265
|
+
const endpoint = deps.endpoint ?? EXA_ANSWER_URL;
|
|
22266
|
+
const timeoutMs = deps.timeoutMs ?? SEARCH_REQUEST_TIMEOUT_MS;
|
|
22267
|
+
async function execute(rawInput, ctx) {
|
|
22268
|
+
const record = typeof rawInput === "object" && rawInput !== null ? rawInput : {};
|
|
22269
|
+
const query = typeof record["query"] === "string" ? record["query"] : "";
|
|
22270
|
+
if (query.trim().length === 0)
|
|
22271
|
+
return errorResult6("Error: Missing query");
|
|
22272
|
+
const runtime = webSessionRuntimeFor(ctx);
|
|
22273
|
+
if (runtime === undefined)
|
|
22274
|
+
return errorResult6("Error: Search is not available for this session (no search runtime is wired up for it -- a host wiring gap, not an input error).");
|
|
22275
|
+
if (!runtime.web.search.enabled)
|
|
22276
|
+
return { output: "Web search is turned off for this session." };
|
|
22277
|
+
const resolveKey = exaKeyResolverFor(runtime);
|
|
22278
|
+
if (resolveKey === undefined)
|
|
22279
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22280
|
+
let key;
|
|
22281
|
+
try {
|
|
22282
|
+
const secret = await resolveKey();
|
|
22283
|
+
if (secret.status === "missing")
|
|
22284
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22285
|
+
if (secret.status === "unreadable")
|
|
22286
|
+
return errorResult6(`search failed: an Exa API key is configured but could not be used: ${secret.message}`);
|
|
22287
|
+
key = secret.key;
|
|
22288
|
+
} catch (err) {
|
|
22289
|
+
return errorResult6(`search failed: the key resolver failed with ${err instanceof Error ? err.name : "an unknown error"}`);
|
|
22290
|
+
}
|
|
22291
|
+
if (key.length === 0)
|
|
22292
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22293
|
+
const fetchFn = deps.fetchFn ?? fetch;
|
|
22294
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
22295
|
+
const signal = ctx.signal !== undefined ? AbortSignal.any([ctx.signal, timeoutSignal]) : timeoutSignal;
|
|
22296
|
+
let res;
|
|
22297
|
+
try {
|
|
22298
|
+
res = await fetchFn(endpoint, {
|
|
22299
|
+
method: "POST",
|
|
22300
|
+
headers: { "x-api-key": key, "content-type": "application/json" },
|
|
22301
|
+
body: JSON.stringify({ query }),
|
|
22302
|
+
redirect: "manual",
|
|
22303
|
+
signal
|
|
22304
|
+
});
|
|
22305
|
+
} catch (err) {
|
|
22306
|
+
const name = err instanceof Error ? err.name : "";
|
|
22307
|
+
if (name === "AbortError" || name === "TimeoutError") {
|
|
22308
|
+
return errorResult6(ctx.signal?.aborted === true ? "Search was interrupted." : `search timed out for ${query}`);
|
|
22309
|
+
}
|
|
22310
|
+
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
22311
|
+
console.error(`Search: network error (${name || "Error"}) — ${rawMessage.replaceAll(key, "<redacted>")}`);
|
|
22312
|
+
return errorResult6("search failed: could not reach the search service");
|
|
22313
|
+
}
|
|
22314
|
+
if (res.status !== 200) {
|
|
22315
|
+
try {
|
|
22316
|
+
await res.body?.cancel();
|
|
22317
|
+
} catch {}
|
|
22318
|
+
return errorResult6(searchStatusMessage(res.status));
|
|
22319
|
+
}
|
|
22320
|
+
let data;
|
|
22321
|
+
try {
|
|
22322
|
+
data = await res.json();
|
|
22323
|
+
} catch (err) {
|
|
22324
|
+
return errorResult6(`search failed: could not parse response (${err instanceof Error ? err.message : String(err)})`);
|
|
22325
|
+
}
|
|
22326
|
+
if (data === null || typeof data !== "object" || !isValidCitations(data.citations))
|
|
22327
|
+
return errorResult6("search failed: malformed response from search service");
|
|
22328
|
+
if (data.answer !== undefined && typeof data.answer !== "string")
|
|
22329
|
+
return errorResult6("search failed: malformed response from search service");
|
|
22330
|
+
const fullAnswer = (data.answer ?? "").trim();
|
|
22331
|
+
const answer = fullAnswer.length > SEARCH_ANSWER_CHARS ? fullAnswer.slice(0, SEARCH_ANSWER_CHARS) + `
|
|
22332
|
+
|
|
22333
|
+
[answer truncated]` : fullAnswer;
|
|
22334
|
+
const hostFloor = Array.isArray(record["blocked_domains"]) ? record["blocked_domains"].filter((d) => typeof d === "string" && d.trim().length > 0).slice(0, 1000) : [];
|
|
22335
|
+
const floor = hostFloor.length > 0 ? [...runtime.web.blockedDomains, ...hostFloor] : runtime.web.blockedDomains;
|
|
22336
|
+
const rawCitations = (data.citations ?? []).slice(0, SEARCH_MAX_CITATIONS);
|
|
22337
|
+
let withheld = 0;
|
|
22338
|
+
const citations = rawCitations.filter((c) => {
|
|
22339
|
+
if (typeof c.url !== "string" || c.url.length === 0 || !isDomainBlocked(c.url, floor))
|
|
22340
|
+
return true;
|
|
22341
|
+
withheld++;
|
|
22342
|
+
return false;
|
|
22343
|
+
});
|
|
22344
|
+
const withheldNote = withheld > 0 ? `
|
|
22345
|
+
|
|
22346
|
+
[${withheld} source${withheld === 1 ? "" : "s"} withheld — matched the dangerous-domain list]` : "";
|
|
22347
|
+
const siteIcons = collectSiteIcons(citations.map((c) => ({ url: c.url, iconUrl: c.favicon })));
|
|
22348
|
+
const withIcons = (output) => ({ output, ...siteIcons !== undefined ? { siteIcons } : {} });
|
|
22349
|
+
if (answer === "")
|
|
22350
|
+
return withIcons(`no answer for ${query}${withheldNote}`);
|
|
22351
|
+
const sources = citations.length === 0 ? `
|
|
22352
|
+
|
|
22353
|
+
[unsourced — ${withheld > 0 ? "every source was withheld by the dangerous-domain list" : "the search service returned no sources"}; say so if you repeat this]` : `
|
|
22354
|
+
|
|
22355
|
+
Sources:
|
|
22356
|
+
` + citations.map((c, i) => `${i + 1}. ${(typeof c.title === "string" ? c.title.trim() : "") || "-"}
|
|
22357
|
+
${typeof c.url === "string" && c.url.length > 0 ? c.url : "-"}`).join(`
|
|
22358
|
+
`);
|
|
22359
|
+
const rendered = answer + sources;
|
|
22360
|
+
const capped = rendered.length > SEARCH_TOTAL_OUTPUT_CHARS ? rendered.slice(0, SEARCH_TOTAL_OUTPUT_CHARS) + `
|
|
22361
|
+
|
|
22362
|
+
[truncated]` : rendered;
|
|
22363
|
+
return withIcons(capped + withheldNote);
|
|
22364
|
+
}
|
|
22365
|
+
return { execute };
|
|
22366
|
+
}
|
|
22367
|
+
replaceExecutor(SEARCH_CANONICAL_NAME, createSearchExecutor());
|
|
22368
|
+
|
|
21990
22369
|
// src/tools/read-state.ts
|
|
21991
22370
|
import { isAbsolute as isAbsolute6, join as join24, normalize as normalize3 } from "node:path";
|
|
21992
22371
|
function createSessionReadState(opts) {
|
|
@@ -22207,6 +22586,18 @@ function toolResultsForHost(content) {
|
|
|
22207
22586
|
};
|
|
22208
22587
|
return blank(content) ?? content;
|
|
22209
22588
|
}
|
|
22589
|
+
function withHostSiteIcons(content, icons) {
|
|
22590
|
+
if (icons.size === 0)
|
|
22591
|
+
return content;
|
|
22592
|
+
return content.map((block) => {
|
|
22593
|
+
if (block.type !== "tool_result")
|
|
22594
|
+
return block;
|
|
22595
|
+
const forCall = icons.get(block.tool_use_id);
|
|
22596
|
+
if (forCall === undefined || forCall.length === 0)
|
|
22597
|
+
return block;
|
|
22598
|
+
return { ...block, winter_site_icons: forCall.map((icon) => ({ url: icon.url, icon_url: icon.iconUrl })) };
|
|
22599
|
+
});
|
|
22600
|
+
}
|
|
22210
22601
|
function asRebuiltContent(content) {
|
|
22211
22602
|
return content.length === 1 && content[0].type === "text" ? content[0].text : content;
|
|
22212
22603
|
}
|
|
@@ -22349,11 +22740,48 @@ function toolOutputReplacement(value) {
|
|
|
22349
22740
|
return capHookText(String(value), cap);
|
|
22350
22741
|
}
|
|
22351
22742
|
}
|
|
22743
|
+
function withLegacyToolNameRules(opts) {
|
|
22744
|
+
const legacy = opts.config.legacyToolNames;
|
|
22745
|
+
if (legacy === undefined || Object.keys(legacy).length === 0)
|
|
22746
|
+
return opts;
|
|
22747
|
+
const expand = (rules) => {
|
|
22748
|
+
if (rules === undefined)
|
|
22749
|
+
return;
|
|
22750
|
+
const out = [...rules];
|
|
22751
|
+
for (const raw of rules) {
|
|
22752
|
+
const head = /^([^(]+)(.*)$/s.exec(raw.trim());
|
|
22753
|
+
if (head === null)
|
|
22754
|
+
continue;
|
|
22755
|
+
const target = Object.hasOwn(legacy, head[1]) ? legacy[head[1]] : undefined;
|
|
22756
|
+
if (typeof target === "string" && target.length > 0)
|
|
22757
|
+
out.push(`${target}${head[2]}`);
|
|
22758
|
+
}
|
|
22759
|
+
return [...new Set(out)];
|
|
22760
|
+
};
|
|
22761
|
+
const config = opts.config;
|
|
22762
|
+
const permissions = config.permissions;
|
|
22763
|
+
return {
|
|
22764
|
+
...opts,
|
|
22765
|
+
config: {
|
|
22766
|
+
...config,
|
|
22767
|
+
...config.allowedTools !== undefined ? { allowedTools: expand(config.allowedTools) } : {},
|
|
22768
|
+
...config.disallowedTools !== undefined ? { disallowedTools: expand(config.disallowedTools) } : {},
|
|
22769
|
+
...permissions !== undefined ? {
|
|
22770
|
+
permissions: {
|
|
22771
|
+
...permissions,
|
|
22772
|
+
...permissions.allow !== undefined ? { allow: expand(permissions.allow) } : {},
|
|
22773
|
+
...permissions.ask !== undefined ? { ask: expand(permissions.ask) } : {},
|
|
22774
|
+
...permissions.deny !== undefined ? { deny: expand(permissions.deny) } : {}
|
|
22775
|
+
}
|
|
22776
|
+
} : {}
|
|
22777
|
+
}
|
|
22778
|
+
};
|
|
22779
|
+
}
|
|
22352
22780
|
async function runEngine2(opts) {
|
|
22353
22781
|
const facetDisposers = [];
|
|
22354
22782
|
let sweepBackgroundShellTasksOnExit;
|
|
22355
22783
|
try {
|
|
22356
|
-
return await runEngineBody(opts, facetDisposers, (fn) => {
|
|
22784
|
+
return await runEngineBody(withLegacyToolNameRules(opts), facetDisposers, (fn) => {
|
|
22357
22785
|
sweepBackgroundShellTasksOnExit = fn;
|
|
22358
22786
|
});
|
|
22359
22787
|
} finally {
|
|
@@ -22434,6 +22862,16 @@ async function runEngineBody(opts, facetDisposers, registerBackgroundShellSweep)
|
|
|
22434
22862
|
resolveToolSecret: resolveToolSecretRoute,
|
|
22435
22863
|
onPricedGeneration
|
|
22436
22864
|
} = opts;
|
|
22865
|
+
const legacyToolNames = config.legacyToolNames ?? {};
|
|
22866
|
+
const currentToolName = (name) => {
|
|
22867
|
+
if (getRegisteredTool(name) !== undefined)
|
|
22868
|
+
return name;
|
|
22869
|
+
const legacy = Object.hasOwn(legacyToolNames, name) ? legacyToolNames[name] : undefined;
|
|
22870
|
+
if (typeof legacy === "string" && getRegisteredTool(legacy) !== undefined)
|
|
22871
|
+
return legacy;
|
|
22872
|
+
return name.startsWith("mcp__") ? registeredNameForMcpSpelling(name) : name;
|
|
22873
|
+
};
|
|
22874
|
+
const legacyNameOf = (name) => Object.entries(legacyToolNames).find(([old, current]) => current === name && getRegisteredTool(old) === undefined)?.[0];
|
|
22437
22875
|
registerBackgroundShellSweep(() => {
|
|
22438
22876
|
const swept = stopSessionShellTasks({ sessionId: config.sessionId, ...config.agentId !== undefined ? { agentId: config.agentId } : {} });
|
|
22439
22877
|
if (swept.length > 0) {
|
|
@@ -23148,7 +23586,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23148
23586
|
});
|
|
23149
23587
|
return {
|
|
23150
23588
|
policy: policyResult,
|
|
23151
|
-
tools: req.definition?.tools !== undefined && !req.definition.tools.includes("*") ? req.definition.tools.filter((name) => currentAdvertisedCanonicalNames.includes(name)) : [...currentAdvertisedCanonicalNames],
|
|
23589
|
+
tools: req.definition?.tools !== undefined && !req.definition.tools.includes("*") ? [...new Set(req.definition.tools.map(currentToolName))].filter((name) => currentAdvertisedCanonicalNames.includes(name)) : [...currentAdvertisedCanonicalNames],
|
|
23152
23590
|
...childSlot,
|
|
23153
23591
|
effort: req.definition?.effort !== undefined ? String(req.definition.effort) : "inherit",
|
|
23154
23592
|
thinking: undefined,
|
|
@@ -23323,7 +23761,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23323
23761
|
const denyEntry = findAgentDenyRule(evalCtx.policy.rules, agentType, denyOpts);
|
|
23324
23762
|
if (denyEntry !== undefined)
|
|
23325
23763
|
return agentTypeDeniedMessage(agentType, denyEntry);
|
|
23326
|
-
if (isBuiltinAllToolsDenied(def, currentAdvertisedCanonicalNames)) {
|
|
23764
|
+
if (isBuiltinAllToolsDenied({ ...def.tools !== undefined ? { tools: def.tools.map(currentToolName) } : {} }, currentAdvertisedCanonicalNames)) {
|
|
23327
23765
|
return `Agent type '${agentType}' is unavailable because every tool it may use is denied by the current permission settings.`;
|
|
23328
23766
|
}
|
|
23329
23767
|
return;
|
|
@@ -23343,11 +23781,12 @@ ${escapeAttributionText(msg.body)}
|
|
|
23343
23781
|
if (serverCfg.type !== "sdk" || !serverCfg.tools || serverCfg.tools.length === 0)
|
|
23344
23782
|
continue;
|
|
23345
23783
|
const toolDefs = serverCfg.tools;
|
|
23346
|
-
|
|
23784
|
+
const toolNames = serverCfg.toolNames;
|
|
23785
|
+
registerMcpServerTools(serverName, toolDefs, { deferredDefault: true, ...toolNames !== undefined ? { toolNames } : {} });
|
|
23347
23786
|
sdkMcpServerNames.push(serverName);
|
|
23348
23787
|
const perServerTimeoutMs = serverCfg.timeout;
|
|
23349
23788
|
for (const tool of toolDefs) {
|
|
23350
|
-
const canonicalName = `mcp__${serverName}__${tool.name}`;
|
|
23789
|
+
const canonicalName = toolNames !== undefined && Object.hasOwn(toolNames, tool.name) ? toolNames[tool.name] : `mcp__${serverName}__${tool.name}`;
|
|
23351
23790
|
replaceExecutor(canonicalName, {
|
|
23352
23791
|
async execute(input) {
|
|
23353
23792
|
const timeoutMs = perServerTimeoutMs ?? mcpEnvConfig.toolTimeoutMs ?? 120000;
|
|
@@ -23379,13 +23818,15 @@ ${escapeAttributionText(msg.body)}
|
|
|
23379
23818
|
const resolvedSources = resolveMcpServerSources(sources, {
|
|
23380
23819
|
...config.strictMcpConfig !== undefined ? { strictMcpConfig: config.strictMcpConfig } : {},
|
|
23381
23820
|
trustedWorkspace,
|
|
23382
|
-
reservedServerName: sessionBrand.mcpServerName
|
|
23821
|
+
reservedServerName: sessionBrand.mcpServerName,
|
|
23822
|
+
...config.reservedMcpServerNames !== undefined ? { hostReservedServerNames: config.reservedMcpServerNames } : {}
|
|
23383
23823
|
});
|
|
23384
23824
|
mcpLifecycle = createMcpLifecycle({
|
|
23385
23825
|
servers: resolvedSources.resolved,
|
|
23386
23826
|
envConfig: mcpEnvConfig,
|
|
23387
23827
|
elicitationAsk: createElicitationAsker2(bridge),
|
|
23388
23828
|
reservedServerName: sessionBrand.mcpServerName,
|
|
23829
|
+
...config.reservedMcpServerNames !== undefined ? { hostReservedServerNames: config.reservedMcpServerNames } : {},
|
|
23389
23830
|
cwd: config.cwd,
|
|
23390
23831
|
...mcpOAuthStore !== undefined ? { oauth: createSessionMcpOAuth({ store: mcpOAuthStore, sender: bridge, brand: sessionBrand, ...config.hostCredentials === true ? { hostOwnsRefresh: true } : {} }) } : {}
|
|
23391
23832
|
});
|
|
@@ -23395,11 +23836,13 @@ ${escapeAttributionText(msg.body)}
|
|
|
23395
23836
|
const effectiveMcpStateSource = mcpServerStateSource ?? mcpLifecycle?.stateSource;
|
|
23396
23837
|
const effectiveMcpControlSeam = mcpControlSeam ?? mcpLifecycle?.controlSeam;
|
|
23397
23838
|
const sessionHasMcp = () => (effectiveMcpStateSource?.snapshot().length ?? 0) > 0;
|
|
23398
|
-
const
|
|
23839
|
+
const toolSearchExcludedByTools = config.tools !== undefined && !config.tools.includes(TOOL_SEARCH_TOOL_NAME);
|
|
23840
|
+
const enableToolSearch = toolSearchExcludedByTools ? "false" : config.toolSearchEnabled === true ? "true" : config.toolSearchEnabled === false ? "false" : mcpEnvConfig.enableToolSearch;
|
|
23399
23841
|
const deferralActivation = {
|
|
23400
23842
|
enableToolSearch,
|
|
23401
23843
|
providerSupportsToolSearch: providerSupportsToolSearch ?? true,
|
|
23402
|
-
deferrableContextShare: deferrableContextShare ?? 0
|
|
23844
|
+
deferrableContextShare: deferrableContextShare ?? 0,
|
|
23845
|
+
...config.deferTools !== undefined ? { deferTools: config.deferTools } : {}
|
|
23403
23846
|
};
|
|
23404
23847
|
const toolSearchEnabledDerived = isDeferralActive(deferralActivation);
|
|
23405
23848
|
const enableFileCheckpointing = config.enableFileCheckpointing === true;
|
|
@@ -23432,7 +23875,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23432
23875
|
return availableAgentNames(defs, {
|
|
23433
23876
|
isDenied: (agentType) => findAgentDenyRule(evalCtx.policy.rules, agentType, denyOpts) !== undefined,
|
|
23434
23877
|
...config.allowedAgentTypes !== undefined ? { allowedAgentTypes: config.allowedAgentTypes } : {},
|
|
23435
|
-
isAllToolsDenied: (def) => isBuiltinAllToolsDenied(def, currentAdvertisedCanonicalNames)
|
|
23878
|
+
isAllToolsDenied: (def) => isBuiltinAllToolsDenied({ ...def.tools !== undefined ? { tools: def.tools.map(currentToolName) } : {} }, currentAdvertisedCanonicalNames)
|
|
23436
23879
|
});
|
|
23437
23880
|
}
|
|
23438
23881
|
function sessionAvailableAgentDefinitions(at = {}) {
|
|
@@ -23503,11 +23946,14 @@ ${escapeAttributionText(msg.body)}
|
|
|
23503
23946
|
facetDisposers.push(forgetSessionWebState);
|
|
23504
23947
|
const WEB_DERIVED_CAPABILITIES = [
|
|
23505
23948
|
{ token: "winter.search-backend", probeTool: "WebSearch", fact: () => searchBackendUsable(webSessionRuntime) },
|
|
23506
|
-
{ token: "winter.fetch-extractor", probeTool: "WebFetch", fact: () => digestModelResolves(webSessionRuntime) }
|
|
23949
|
+
{ token: "winter.fetch-extractor", probeTool: "WebFetch", fact: () => digestModelResolves(webSessionRuntime) },
|
|
23950
|
+
{ token: SEARCH_ANSWER_CAPABILITY, probeTool: SEARCH_CANONICAL_NAME, fact: () => searchAnswerUsable(webSessionRuntime) }
|
|
23507
23951
|
];
|
|
23508
23952
|
const resolveLiveSessionCapabilities = () => {
|
|
23509
23953
|
const base = resolveSessionCapabilities(config.capabilities, { hasMcpServers: sessionHasMcp() });
|
|
23510
23954
|
const derived = reviewerResolves() ? [ADVISOR_CAPABILITY] : [];
|
|
23955
|
+
if ((config.deferTools?.length ?? 0) > 0)
|
|
23956
|
+
derived.push(DEFERRED_BUILTINS_CAPABILITY);
|
|
23511
23957
|
for (const cap of WEB_DERIVED_CAPABILITIES) {
|
|
23512
23958
|
if (getRegisteredTool(cap.probeTool)?.executor !== undefined && cap.fact())
|
|
23513
23959
|
derived.push(cap.token);
|
|
@@ -23521,6 +23967,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23521
23967
|
mode: policyStateStore.getState().mode,
|
|
23522
23968
|
platform: process.platform,
|
|
23523
23969
|
...config.disallowedTools !== undefined ? { disallowedTools: config.disallowedTools } : {},
|
|
23970
|
+
...config.tools !== undefined ? { tools: config.tools } : {},
|
|
23524
23971
|
capabilities: sessionCapabilities,
|
|
23525
23972
|
toolSearchEnabled: toolSearchEnabledDerived,
|
|
23526
23973
|
...config.insideSubagent !== undefined ? { insideSubagent: config.insideSubagent } : {},
|
|
@@ -23606,6 +24053,8 @@ ${escapeAttributionText(msg.body)}
|
|
|
23606
24053
|
return visibleServerToolFilter();
|
|
23607
24054
|
},
|
|
23608
24055
|
...config.disallowedTools !== undefined ? { disallowedTools: config.disallowedTools } : {},
|
|
24056
|
+
...config.tools !== undefined ? { tools: config.tools } : {},
|
|
24057
|
+
resolveName: currentToolName,
|
|
23609
24058
|
...config.insideSubagent !== undefined ? { insideSubagent: config.insideSubagent } : {},
|
|
23610
24059
|
...config.familyMetadata !== undefined ? { familyMetadata: config.familyMetadata } : {},
|
|
23611
24060
|
...config.toolAliases !== undefined ? { toolAliases: config.toolAliases } : {},
|
|
@@ -24638,6 +25087,12 @@ ${escapeAttributionText(msg.body)}
|
|
|
24638
25087
|
return { system: joinSystemBlocks(systemBlocks), systemBlocks };
|
|
24639
25088
|
};
|
|
24640
25089
|
const requestMessages = (context, effort, toolChanges) => buildRequestMessages(messages, context.userContextText, { systemReminders: systemRemindersOnWire(), ...effort !== undefined ? { effort } : {}, ...toolChanges !== undefined ? { toolChanges } : {} });
|
|
25090
|
+
const deferredToolNamesToAnnounce = () => {
|
|
25091
|
+
refreshAdvertisedPartition();
|
|
25092
|
+
const eagerNames = new Set(advertisedPartition.eager.map((d) => d.canonicalName));
|
|
25093
|
+
const twins = new Set(Object.entries(suppressionAliasTable).filter(([source]) => eagerNames.has(source)).map(([, target]) => target));
|
|
25094
|
+
return [...new Set(advertisedPartition.deferred.filter((d) => !twins.has(d.canonicalName)).map((d) => d.advertisedName))];
|
|
25095
|
+
};
|
|
24641
25096
|
const systemRemindersOnWire = () => currentModelDescription()?.wire?.midConversationSystem === true;
|
|
24642
25097
|
const currentModelDescription = () => {
|
|
24643
25098
|
const key = currentProviderIdentity?.modelKey ?? currentModel;
|
|
@@ -24793,6 +25248,13 @@ ${escapeAttributionText(msg.body)}
|
|
|
24793
25248
|
produced.push(needsAuth);
|
|
24794
25249
|
}
|
|
24795
25250
|
}
|
|
25251
|
+
if (exactRequestLayout === undefined) {
|
|
25252
|
+
const deferredNow = deferredToolNamesToAnnounce();
|
|
25253
|
+
const offeredNow = [...advertisedPartition.eager, ...advertisedPartition.deferred].map((d) => d.advertisedName);
|
|
25254
|
+
const deferredDelta = computeDeferredToolsDelta(deferredNow, offeredNow, messages);
|
|
25255
|
+
if (deferredDelta !== undefined)
|
|
25256
|
+
produced.push(deferredDelta);
|
|
25257
|
+
}
|
|
24796
25258
|
if (phase === "tool-round") {
|
|
24797
25259
|
const attachment = taskNotificationAttachment(notifications.drainFor(config.agentId, { maxPriority: "next" }), {
|
|
24798
25260
|
inHumanTurn: config.agentId === undefined && !turnStartedByNotification
|
|
@@ -25031,15 +25493,19 @@ ${escapeAttributionText(msg.body)}
|
|
|
25031
25493
|
return cached ? { name: identity.name, configName: identity.configName } : identity;
|
|
25032
25494
|
};
|
|
25033
25495
|
const declaredMcpIdentity = (toolName) => {
|
|
25034
|
-
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
|
|
25038
|
-
|
|
25039
|
-
|
|
25040
|
-
|
|
25496
|
+
const legacy = legacyNameOf(toolName);
|
|
25497
|
+
if (legacy !== undefined)
|
|
25498
|
+
return legacy;
|
|
25499
|
+
const spelled = mcpSpellingOf(toolName) ?? toolName;
|
|
25500
|
+
if (mcpServerRenames !== undefined) {
|
|
25501
|
+
for (const [actual, declared] of Object.entries(mcpServerRenames)) {
|
|
25502
|
+
if (spelled === `mcp__${actual}`)
|
|
25503
|
+
return `mcp__${declared}`;
|
|
25504
|
+
if (spelled.startsWith(`mcp__${actual}__`))
|
|
25505
|
+
return `mcp__${declared}__${spelled.slice(`mcp__${actual}__`.length)}`;
|
|
25506
|
+
}
|
|
25041
25507
|
}
|
|
25042
|
-
return;
|
|
25508
|
+
return spelled !== toolName ? spelled : undefined;
|
|
25043
25509
|
};
|
|
25044
25510
|
const postToolHookSubject = (event, toolName) => {
|
|
25045
25511
|
const declared = declaredMcpIdentity(toolName);
|
|
@@ -25612,10 +26078,13 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25612
26078
|
...generationEffort ?? {}
|
|
25613
26079
|
});
|
|
25614
26080
|
const resultBlocks = [];
|
|
26081
|
+
const hostSiteIcons = new Map;
|
|
25615
26082
|
let toolThrowText = null;
|
|
25616
26083
|
let structuredTerminated = false;
|
|
25617
26084
|
const truncatedByOutputLimit = turn.stopReason === "max_tokens";
|
|
25618
|
-
for (const
|
|
26085
|
+
for (const emitted of turn.calls) {
|
|
26086
|
+
const resolvedName = currentToolName(emitted.name);
|
|
26087
|
+
const call = resolvedName !== emitted.name ? { ...emitted, name: resolvedName } : emitted;
|
|
25619
26088
|
const stoppedBy = currentTurnStop();
|
|
25620
26089
|
if (stoppedBy !== undefined) {
|
|
25621
26090
|
resultBlocks.push({ type: "tool_result", tool_use_id: call.id, content: `[not executed: the ${stoppedBy.hookName} hook stopped the turn]`, error: true });
|
|
@@ -25847,6 +26316,8 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25847
26316
|
...loadedTools.length > 0 ? { loadedTools } : {},
|
|
25848
26317
|
...loadedToolDefinitions.length > 0 ? { loadedToolDefinitions } : {}
|
|
25849
26318
|
});
|
|
26319
|
+
if (raced.value.siteIcons !== undefined && raced.value.siteIcons.length > 0)
|
|
26320
|
+
hostSiteIcons.set(call.id, raced.value.siteIcons);
|
|
25850
26321
|
const postToolUseHookOutcome = raced.value.isError === true ? await fireObservationalHook("PostToolUseFailure", {
|
|
25851
26322
|
toolUseID: call.id,
|
|
25852
26323
|
toolName: postToolHookSubject("PostToolUseFailure", call.name),
|
|
@@ -25908,7 +26379,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25908
26379
|
}
|
|
25909
26380
|
}
|
|
25910
26381
|
}
|
|
25911
|
-
output.write({ type: "data", message: { type: "user", message: { content: toolResultsForHost(resultBlocks.map((b) => b.type === "tool_result" && b.loadedTools !== undefined ? withoutLoadedTools(b) : b)) } } });
|
|
26382
|
+
output.write({ type: "data", message: { type: "user", message: { content: withHostSiteIcons(toolResultsForHost(resultBlocks.map((b) => b.type === "tool_result" && b.loadedTools !== undefined ? withoutLoadedTools(b) : b)), hostSiteIcons) } } });
|
|
25912
26383
|
messages.push({ role: "tool", content: resultBlocks });
|
|
25913
26384
|
await recordUser(resultBlocks);
|
|
25914
26385
|
await flushPendingHookAttachments();
|
|
@@ -26168,8 +26639,43 @@ var TEST_PROVIDER_NAMES = new Set([
|
|
|
26168
26639
|
"p5skill",
|
|
26169
26640
|
"p5checkpoint",
|
|
26170
26641
|
"p5workflow",
|
|
26171
|
-
"readimage"
|
|
26642
|
+
"readimage",
|
|
26643
|
+
"calls"
|
|
26172
26644
|
]);
|
|
26645
|
+
var CALL_LINE = /^CALL\s+(\S+)(?:\s+(.*))?$/;
|
|
26646
|
+
function promptedCallsProvider() {
|
|
26647
|
+
return {
|
|
26648
|
+
async generate({ messages }) {
|
|
26649
|
+
let at = -1;
|
|
26650
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
26651
|
+
const m = messages[i];
|
|
26652
|
+
if (m.role === "user" && userMessageText(m).split(`
|
|
26653
|
+
`).some((line) => CALL_LINE.test(line.trim()))) {
|
|
26654
|
+
at = i;
|
|
26655
|
+
break;
|
|
26656
|
+
}
|
|
26657
|
+
}
|
|
26658
|
+
if (at < 0)
|
|
26659
|
+
return { kind: "text", text: "[]" };
|
|
26660
|
+
const script = userMessageText(messages[at]).split(`
|
|
26661
|
+
`).map((line) => CALL_LINE.exec(line.trim())).filter((m) => m !== null).map((m) => ({ name: m[1], input: m[2] !== undefined && m[2].trim() !== "" ? JSON.parse(m[2]) : {} }));
|
|
26662
|
+
const after = messages.slice(at + 1);
|
|
26663
|
+
const made = after.filter((m) => m.role === "assistant" && Array.isArray(m.content) && m.content.some((b) => b.type === "tool_use")).length;
|
|
26664
|
+
if (made < script.length) {
|
|
26665
|
+
const next = script[made];
|
|
26666
|
+
return { kind: "tool_use", calls: [{ id: `calls-${at + 1}-${made + 1}`, name: next.name, input: next.input }] };
|
|
26667
|
+
}
|
|
26668
|
+
const results = after.flatMap((m) => Array.isArray(m.content) ? m.content : []).filter((b) => b.type === "tool_result");
|
|
26669
|
+
const named = new Map(after.flatMap((m) => m.role === "assistant" && Array.isArray(m.content) ? m.content : []).flatMap((b) => b.type === "tool_use" ? [[b.id, b.name]] : []));
|
|
26670
|
+
const text = (content) => typeof content === "string" ? content : Array.isArray(content) ? content.map((b) => b !== null && typeof b === "object" && typeof b.text === "string" ? b.text : "").join(`
|
|
26671
|
+
`) : String(content);
|
|
26672
|
+
return {
|
|
26673
|
+
kind: "text",
|
|
26674
|
+
text: JSON.stringify(results.map((r) => ({ name: named.get(r.tool_use_id) ?? null, isError: r.is_error === true || r.error === true, content: text(r.content) })))
|
|
26675
|
+
};
|
|
26676
|
+
}
|
|
26677
|
+
};
|
|
26678
|
+
}
|
|
26173
26679
|
function isTestProviderName2(v) {
|
|
26174
26680
|
return TEST_PROVIDER_NAMES.has(v);
|
|
26175
26681
|
}
|
|
@@ -26334,6 +26840,8 @@ function rawTestProviderByName(name) {
|
|
|
26334
26840
|
}
|
|
26335
26841
|
};
|
|
26336
26842
|
}
|
|
26843
|
+
case "calls":
|
|
26844
|
+
return promptedCallsProvider();
|
|
26337
26845
|
case "mcpsdk":
|
|
26338
26846
|
return scriptedProvider2([
|
|
26339
26847
|
{ kind: "tool_use", calls: [{ id: "mcpsdk-call-1", name: MCP_SDK_TEST_TOOL_NAME2, input: { x: 1 } }] },
|
|
@@ -27090,7 +27598,7 @@ class TranscriptWriter2 {
|
|
|
27090
27598
|
return entries;
|
|
27091
27599
|
}
|
|
27092
27600
|
}
|
|
27093
|
-
var RUNTIME_ENGINE_VERSION2 = "0.0.
|
|
27601
|
+
var RUNTIME_ENGINE_VERSION2 = "0.0.38";
|
|
27094
27602
|
function withPermissionJournal(writer, location) {
|
|
27095
27603
|
return {
|
|
27096
27604
|
transcriptPath: join26(location.winterHome, "projects", location.projectKey, `${location.sessionId}.jsonl`),
|