@yanlinglabs/winter-agent-runtime 0.0.36 → 0.0.39
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-1nhzy5e2.js → index-b08mghw8.js} +2 -2
- package/dist/{index-c8j57y8v.js → index-dh2xed37.js} +640 -55
- package/dist/{index-83p1g9ap.js → index-es0skt5n.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/messaging/host-port.d.ts +19 -0
- package/dist/messaging/reference-adapter.d.ts +8 -1
- 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/impl/task-stop.d.ts +2 -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)
|
|
@@ -5057,7 +5155,25 @@ function createDefaultMessagingRuntime(opts = {}) {
|
|
|
5057
5155
|
subscribers,
|
|
5058
5156
|
now
|
|
5059
5157
|
});
|
|
5060
|
-
|
|
5158
|
+
const hostPorts = new Map;
|
|
5159
|
+
return {
|
|
5160
|
+
seam,
|
|
5161
|
+
adapter,
|
|
5162
|
+
notifications,
|
|
5163
|
+
loopGuard: createLoopGuard(),
|
|
5164
|
+
subscribers,
|
|
5165
|
+
now,
|
|
5166
|
+
peers,
|
|
5167
|
+
classifyDeliveryError,
|
|
5168
|
+
hostMessaging: (sessionId) => hostPorts.get(sessionId),
|
|
5169
|
+
registerHostMessagingPort(sessionId, port) {
|
|
5170
|
+
hostPorts.set(sessionId, port);
|
|
5171
|
+
return () => {
|
|
5172
|
+
if (hostPorts.get(sessionId) === port)
|
|
5173
|
+
hostPorts.delete(sessionId);
|
|
5174
|
+
};
|
|
5175
|
+
}
|
|
5176
|
+
};
|
|
5061
5177
|
}
|
|
5062
5178
|
function getDefaultMessagingRuntime() {
|
|
5063
5179
|
const runtime = getMessagingRuntime();
|
|
@@ -6470,6 +6586,7 @@ ${body}
|
|
|
6470
6586
|
// src/mcp/control.ts
|
|
6471
6587
|
var REPLACE_ELIGIBLE_ORIGINS = new Set(["explicit", "dynamic"]);
|
|
6472
6588
|
function createMcpControlSeam(internals, opts = {}) {
|
|
6589
|
+
const hostReserved = new Set(opts.hostReservedServerNames ?? []);
|
|
6473
6590
|
const reservedServerName = opts.reservedServerName ?? WINTER_SERVER_NAME;
|
|
6474
6591
|
return {
|
|
6475
6592
|
async reconnect(serverName) {
|
|
@@ -6498,6 +6615,10 @@ function createMcpControlSeam(internals, opts = {}) {
|
|
|
6498
6615
|
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
6616
|
continue;
|
|
6500
6617
|
}
|
|
6618
|
+
if (hostReserved.has(name) && raw?.type !== "sdk") {
|
|
6619
|
+
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`;
|
|
6620
|
+
continue;
|
|
6621
|
+
}
|
|
6501
6622
|
const result = validateServerConfig(raw);
|
|
6502
6623
|
if (!result.ok) {
|
|
6503
6624
|
errors[name] = result.reason;
|
|
@@ -6573,6 +6694,7 @@ function validateServerConfig(raw) {
|
|
|
6573
6694
|
}
|
|
6574
6695
|
function resolveMcpServerSources(sources, opts) {
|
|
6575
6696
|
const reservedServerName = opts.reservedServerName ?? WINTER_SERVER_NAME;
|
|
6697
|
+
const hostReserved = new Set(opts.hostReservedServerNames ?? []);
|
|
6576
6698
|
const resolved = [];
|
|
6577
6699
|
const shadowed = [];
|
|
6578
6700
|
const rejected = [];
|
|
@@ -6597,6 +6719,10 @@ function resolveMcpServerSources(sources, opts) {
|
|
|
6597
6719
|
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
6720
|
continue;
|
|
6599
6721
|
}
|
|
6722
|
+
if (hostReserved.has(name) && (origin !== "explicit" || raw?.type !== "sdk")) {
|
|
6723
|
+
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` });
|
|
6724
|
+
continue;
|
|
6725
|
+
}
|
|
6600
6726
|
const validated = validateServerConfig(raw);
|
|
6601
6727
|
if (!validated.ok) {
|
|
6602
6728
|
rejected.push({ name, origin, code: "invalid_config", reason: validated.reason });
|
|
@@ -7168,7 +7294,7 @@ function createMcpLifecycle(deps) {
|
|
|
7168
7294
|
};
|
|
7169
7295
|
return {
|
|
7170
7296
|
stateSource,
|
|
7171
|
-
controlSeam: createMcpControlSeam(internals, { ...deps.reservedServerName !== undefined ? { reservedServerName: deps.reservedServerName } : {} }),
|
|
7297
|
+
controlSeam: createMcpControlSeam(internals, { ...deps.reservedServerName !== undefined ? { reservedServerName: deps.reservedServerName } : {}, ...deps.hostReservedServerNames !== undefined ? { hostReservedServerNames: deps.hostReservedServerNames } : {} }),
|
|
7172
7298
|
start,
|
|
7173
7299
|
launch,
|
|
7174
7300
|
dispose,
|
|
@@ -7286,6 +7412,40 @@ function transformChildFrame(frame, correlation, forwardSubagentText) {
|
|
|
7286
7412
|
}
|
|
7287
7413
|
var SESSION_LEVEL_SYSTEM_SUBTYPES = new Set(["task_started", "task_progress", "task_notification", "task_updated", "background_tasks_changed"]);
|
|
7288
7414
|
|
|
7415
|
+
// src/messaging/host-port.ts
|
|
7416
|
+
import { HOST_MESSAGE_LIST_SUBTYPE, HOST_MESSAGE_SEND_SUBTYPE, HOST_SESSION_STOP_SUBTYPE } from "@yanlinglabs/winter-agent-sdk";
|
|
7417
|
+
import { isHostMessageSendAnswer, isHostSessionStopAnswer, normaliseHostMessageListAnswer } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
7418
|
+
var HOST_MESSAGE_SEND_TIMEOUT_MS = 180000;
|
|
7419
|
+
var HOST_MESSAGE_LIST_TIMEOUT_MS = 15000;
|
|
7420
|
+
var HOST_SESSION_STOP_TIMEOUT_MS = 30000;
|
|
7421
|
+
|
|
7422
|
+
class HostMessagingAnswerError extends Error {
|
|
7423
|
+
constructor(message) {
|
|
7424
|
+
super(message);
|
|
7425
|
+
this.name = "HostMessagingAnswerError";
|
|
7426
|
+
}
|
|
7427
|
+
}
|
|
7428
|
+
function createHostMessagingPort(sender) {
|
|
7429
|
+
return {
|
|
7430
|
+
async send(request, opts) {
|
|
7431
|
+
const answer = await sender.request(HOST_MESSAGE_SEND_SUBTYPE, request, { timeoutMs: HOST_MESSAGE_SEND_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7432
|
+
if (!isHostMessageSendAnswer(answer))
|
|
7433
|
+
throw new HostMessagingAnswerError("the host answered host_message_send with a malformed outcome");
|
|
7434
|
+
return answer;
|
|
7435
|
+
},
|
|
7436
|
+
async list(request, opts) {
|
|
7437
|
+
const answer = await sender.request(HOST_MESSAGE_LIST_SUBTYPE, request, { timeoutMs: HOST_MESSAGE_LIST_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7438
|
+
return normaliseHostMessageListAnswer(answer) ?? { sessions: [] };
|
|
7439
|
+
},
|
|
7440
|
+
async stop(request, opts) {
|
|
7441
|
+
const answer = await sender.request(HOST_SESSION_STOP_SUBTYPE, request, { timeoutMs: HOST_SESSION_STOP_TIMEOUT_MS, ...opts?.signal !== undefined ? { signal: opts.signal } : {} });
|
|
7442
|
+
if (!isHostSessionStopAnswer(answer))
|
|
7443
|
+
throw new HostMessagingAnswerError("the host answered host_session_stop with a malformed answer");
|
|
7444
|
+
return answer;
|
|
7445
|
+
}
|
|
7446
|
+
};
|
|
7447
|
+
}
|
|
7448
|
+
|
|
7289
7449
|
// src/permissions/ruleset.ts
|
|
7290
7450
|
import {
|
|
7291
7451
|
readFileSync as readFileSync6,
|
|
@@ -11012,13 +11172,15 @@ function capHookText(text, limit = MAX_HOOK_TEXT_CHARS) {
|
|
|
11012
11172
|
|
|
11013
11173
|
// src/hooks/runner.ts
|
|
11014
11174
|
function mcpToolProvenance(toolName) {
|
|
11015
|
-
const
|
|
11175
|
+
const registered = registeredNameForMcpSpelling(toolName);
|
|
11176
|
+
const server = mcpServerOwningTool(registered);
|
|
11016
11177
|
if (server === undefined)
|
|
11017
11178
|
return;
|
|
11179
|
+
const spelled = mcpSpellingOf(registered) ?? registered;
|
|
11018
11180
|
const prefix = `mcp__${server}__`;
|
|
11019
|
-
if (!
|
|
11181
|
+
if (!spelled.startsWith(prefix) || spelled.length === prefix.length)
|
|
11020
11182
|
return;
|
|
11021
|
-
return { server, tool:
|
|
11183
|
+
return { server, tool: spelled.slice(prefix.length) };
|
|
11022
11184
|
}
|
|
11023
11185
|
function mcpServerIdentity(toolName, renames) {
|
|
11024
11186
|
const name = mcpServerOwningTool(toolName);
|
|
@@ -14739,6 +14901,26 @@ stub({
|
|
|
14739
14901
|
disposition: "implement-now"
|
|
14740
14902
|
});
|
|
14741
14903
|
|
|
14904
|
+
// src/tools/descriptors/search.ts
|
|
14905
|
+
var SEARCH_CANONICAL_NAME = "Search";
|
|
14906
|
+
var SEARCH_ANSWER_CAPABILITY = "winter.search-answer";
|
|
14907
|
+
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.";
|
|
14908
|
+
var descriptor = {
|
|
14909
|
+
canonicalName: SEARCH_CANONICAL_NAME,
|
|
14910
|
+
advertisedName: SEARCH_CANONICAL_NAME,
|
|
14911
|
+
source: "builtin",
|
|
14912
|
+
inputSchema: { type: "object", properties: { query: { type: "string", minLength: 1, description: "The question to answer" } }, required: ["query"] },
|
|
14913
|
+
description: SEARCH_DESCRIPTION,
|
|
14914
|
+
searchHint: "answer a question from the web with sources",
|
|
14915
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
14916
|
+
exposure: "eager",
|
|
14917
|
+
permissionClass: "network",
|
|
14918
|
+
availability: { optIn: true },
|
|
14919
|
+
capabilityRequirements: [SEARCH_ANSWER_CAPABILITY],
|
|
14920
|
+
disposition: "implement-now"
|
|
14921
|
+
};
|
|
14922
|
+
stub(descriptor);
|
|
14923
|
+
|
|
14742
14924
|
// src/tools/descriptors/tool-search.ts
|
|
14743
14925
|
stub({
|
|
14744
14926
|
canonicalName: "ToolSearch",
|
|
@@ -14752,11 +14934,13 @@ stub({
|
|
|
14752
14934
|
},
|
|
14753
14935
|
required: ["query"]
|
|
14754
14936
|
},
|
|
14755
|
-
description:
|
|
14937
|
+
description: `Fetches full schema definitions for deferred tools so they can be called.
|
|
14938
|
+
|
|
14939
|
+
` + "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
14940
|
exposure: "eager",
|
|
14757
14941
|
permissionClass: "read",
|
|
14758
|
-
availability: { requiresToolSearchEnabled: true },
|
|
14759
|
-
capabilityRequirements: [
|
|
14942
|
+
availability: { requiresToolSearchEnabled: true, requiresAnyCapability: ["winter.mcp", DEFERRED_BUILTINS_CAPABILITY] },
|
|
14943
|
+
capabilityRequirements: [],
|
|
14760
14944
|
disposition: "implement-now"
|
|
14761
14945
|
});
|
|
14762
14946
|
|
|
@@ -14993,7 +15177,7 @@ var WEB_SEARCH_INPUT_SCHEMA_FIRST_PARTY = {
|
|
|
14993
15177
|
function webSearchInputSchemaFor(firstPartyAnthropic) {
|
|
14994
15178
|
return firstPartyAnthropic ? WEB_SEARCH_INPUT_SCHEMA_FIRST_PARTY : WEB_SEARCH_INPUT_SCHEMA_PORTABLE;
|
|
14995
15179
|
}
|
|
14996
|
-
var
|
|
15180
|
+
var descriptor2 = {
|
|
14997
15181
|
canonicalName: WEB_SEARCH_CANONICAL_NAME,
|
|
14998
15182
|
advertisedName: WEB_SEARCH_CANONICAL_NAME,
|
|
14999
15183
|
source: "builtin",
|
|
@@ -15007,14 +15191,14 @@ var descriptor = {
|
|
|
15007
15191
|
capabilityRequirements: ["winter.search-backend"],
|
|
15008
15192
|
disposition: "implement-now"
|
|
15009
15193
|
};
|
|
15010
|
-
Object.defineProperty(
|
|
15194
|
+
Object.defineProperty(descriptor2, "description", {
|
|
15011
15195
|
enumerable: true,
|
|
15012
15196
|
configurable: true,
|
|
15013
15197
|
get() {
|
|
15014
15198
|
return webSearchDescription(false);
|
|
15015
15199
|
}
|
|
15016
15200
|
});
|
|
15017
|
-
stub(
|
|
15201
|
+
stub(descriptor2);
|
|
15018
15202
|
|
|
15019
15203
|
// src/tools/descriptors/winter-list-agents.ts
|
|
15020
15204
|
import { WINTER_BRAND as WINTER_BRAND13, mcpToolName } from "@yanlinglabs/winter-agent-sdk";
|
|
@@ -18670,6 +18854,7 @@ replaceExecutor("TaskOutput", taskOutputExecutor);
|
|
|
18670
18854
|
|
|
18671
18855
|
// src/tools/impl/task-stop.ts
|
|
18672
18856
|
import { randomUUID as randomUUID15 } from "node:crypto";
|
|
18857
|
+
import { boundedHostNote } from "@yanlinglabs/winter-agent-sdk/messaging";
|
|
18673
18858
|
function parseTaskStopInput(input) {
|
|
18674
18859
|
if (typeof input !== "object" || input === null)
|
|
18675
18860
|
return { error: "TaskStop: input must be an object" };
|
|
@@ -18699,6 +18884,9 @@ var taskStopExecutor = {
|
|
|
18699
18884
|
const id = parsed.task_id ?? parsed.shell_id;
|
|
18700
18885
|
const task = getTask(id);
|
|
18701
18886
|
if (!task) {
|
|
18887
|
+
const host = getMessagingRuntime()?.hostMessaging?.(ctx.sessionId);
|
|
18888
|
+
if (host !== undefined)
|
|
18889
|
+
return stopHostSession(host, id, ctx.agentId, ctx.signal);
|
|
18702
18890
|
return { output: `Error: TaskStop: unknown task_id "${id}"`, isError: true };
|
|
18703
18891
|
}
|
|
18704
18892
|
if (task.status !== "running") {
|
|
@@ -18720,6 +18908,24 @@ var taskStopExecutor = {
|
|
|
18720
18908
|
return { output: formatResult(`stopped task ${id}`, id, task.kind, task.command) };
|
|
18721
18909
|
}
|
|
18722
18910
|
};
|
|
18911
|
+
var HOST_SESSION_TASK_TYPE = "session";
|
|
18912
|
+
async function stopHostSession(host, id, agentId, signal) {
|
|
18913
|
+
let answer;
|
|
18914
|
+
try {
|
|
18915
|
+
answer = await host.stop({ id, ...agentId !== undefined ? { fromAgentId: agentId } : {} }, signal !== undefined ? { signal } : {});
|
|
18916
|
+
} catch (err) {
|
|
18917
|
+
return { output: `Error: TaskStop: the host did not answer the stop for "${id}" (${err instanceof Error ? err.name : "error"}); it may or may not have stopped`, isError: true };
|
|
18918
|
+
}
|
|
18919
|
+
const note = boundedHostNote(answer.note);
|
|
18920
|
+
const withNote = (message) => note === undefined ? message : `${message}. ${note}`;
|
|
18921
|
+
if (answer.status === "stopped")
|
|
18922
|
+
return { output: JSON.stringify({ message: withNote(`stopped session ${id}: its running turn was interrupted`), task_id: id, task_type: HOST_SESSION_TASK_TYPE }) };
|
|
18923
|
+
if (answer.status === "not_running")
|
|
18924
|
+
return { output: JSON.stringify({ message: withNote(`session ${id} has no turn running -- nothing to stop`), task_id: id, task_type: HOST_SESSION_TASK_TYPE }) };
|
|
18925
|
+
if (answer.status === "not_found")
|
|
18926
|
+
return { output: `Error: TaskStop: unknown task_id "${id}" (${answer.reason ?? "not found"})`, isError: true };
|
|
18927
|
+
return { output: `Error: TaskStop: ${answer.reason ?? answer.status}`, isError: true };
|
|
18928
|
+
}
|
|
18723
18929
|
replaceExecutor("TaskStop", taskStopExecutor);
|
|
18724
18930
|
|
|
18725
18931
|
// src/tools/impl/todo-write.ts
|
|
@@ -19083,6 +19289,7 @@ function computeExposurePartition(query) {
|
|
|
19083
19289
|
...query.platform !== undefined ? { platform: query.platform } : {},
|
|
19084
19290
|
...query.capabilities !== undefined ? { capabilities: query.capabilities } : {},
|
|
19085
19291
|
...query.disallowedTools !== undefined ? { disallowedTools: query.disallowedTools } : {},
|
|
19292
|
+
...query.tools !== undefined ? { tools: query.tools } : {},
|
|
19086
19293
|
...query.insideSubagent !== undefined ? { insideSubagent: query.insideSubagent } : {},
|
|
19087
19294
|
...query.familyMetadata !== undefined ? { familyMetadata: query.familyMetadata } : {}
|
|
19088
19295
|
};
|
|
@@ -19142,6 +19349,7 @@ function exposureQuery(deps) {
|
|
|
19142
19349
|
...deps.platform !== undefined ? { platform: deps.platform } : {},
|
|
19143
19350
|
...deps.capabilities !== undefined ? { capabilities: deps.capabilities } : {},
|
|
19144
19351
|
...deps.disallowedTools !== undefined ? { disallowedTools: deps.disallowedTools } : {},
|
|
19352
|
+
...deps.tools !== undefined ? { tools: deps.tools } : {},
|
|
19145
19353
|
...deps.insideSubagent !== undefined ? { insideSubagent: deps.insideSubagent } : {},
|
|
19146
19354
|
...deps.familyMetadata !== undefined ? { familyMetadata: deps.familyMetadata } : {},
|
|
19147
19355
|
...deps.toolAliases !== undefined ? { toolAliases: deps.toolAliases } : {},
|
|
@@ -19175,7 +19383,9 @@ async function executeToolSearch(input, deps) {
|
|
|
19175
19383
|
const query = rec.query;
|
|
19176
19384
|
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
19385
|
const pendingWaitMs = deps.pendingWaitMs ?? DEFAULT_PENDING_WAIT_MS;
|
|
19178
|
-
const
|
|
19386
|
+
const resolveName = deps.resolveName ?? registeredNameForMcpSpelling;
|
|
19387
|
+
const parsedSelect = parseSelect(query);
|
|
19388
|
+
const selectNames = parsedSelect === undefined ? undefined : [...new Set(parsedSelect.map(resolveName))];
|
|
19179
19389
|
let attempt = runAttempt(query, selectNames, maxResults, deps);
|
|
19180
19390
|
const incomplete = selectNames !== undefined ? attempt.matches.length < selectNames.length : attempt.matches.length === 0;
|
|
19181
19391
|
if (incomplete && deps.stateSource !== undefined) {
|
|
@@ -19215,7 +19425,7 @@ function getToolSearchSessionRuntime(sessionId) {
|
|
|
19215
19425
|
}
|
|
19216
19426
|
|
|
19217
19427
|
// src/tools/impl/tool-search.ts
|
|
19218
|
-
var
|
|
19428
|
+
var TOOL_SEARCH_TOOL_NAME2 = "ToolSearch";
|
|
19219
19429
|
function notWiredResult() {
|
|
19220
19430
|
return {
|
|
19221
19431
|
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 +19443,7 @@ var toolSearchExecutor = {
|
|
|
19233
19443
|
return { output: JSON.stringify(outcome.result) };
|
|
19234
19444
|
}
|
|
19235
19445
|
};
|
|
19236
|
-
replaceExecutor(
|
|
19446
|
+
replaceExecutor(TOOL_SEARCH_TOOL_NAME2, toolSearchExecutor);
|
|
19237
19447
|
|
|
19238
19448
|
// src/tools/impl/wait-for-mcp-servers.ts
|
|
19239
19449
|
var WAIT_FOR_MCP_SERVERS_TOOL_NAME = "WaitForMcpServers";
|
|
@@ -19312,7 +19522,8 @@ function callerContextFrom(ctx) {
|
|
|
19312
19522
|
return {
|
|
19313
19523
|
sessionId: ctx.sessionId,
|
|
19314
19524
|
...ctx.agentId !== undefined ? { agentId: ctx.agentId } : {},
|
|
19315
|
-
...ctx.toolUseId !== undefined ? { toolUseId: ctx.toolUseId } : {}
|
|
19525
|
+
...ctx.toolUseId !== undefined ? { toolUseId: ctx.toolUseId } : {},
|
|
19526
|
+
...ctx.signal !== undefined ? { signal: ctx.signal } : {}
|
|
19316
19527
|
};
|
|
19317
19528
|
}
|
|
19318
19529
|
|
|
@@ -19845,6 +20056,9 @@ function inheritedWebSessionFacts(sessionId) {
|
|
|
19845
20056
|
function searchBackendUsable(runtime) {
|
|
19846
20057
|
return runtime.web.search.enabled;
|
|
19847
20058
|
}
|
|
20059
|
+
function searchAnswerUsable(runtime) {
|
|
20060
|
+
return runtime.web.search.enabled && runtime.web.search.authRef !== undefined && runtime.resolveToolSecret !== undefined;
|
|
20061
|
+
}
|
|
19848
20062
|
function digestModelResolves(runtime) {
|
|
19849
20063
|
const tag = runtime.web.fetch.digestModel;
|
|
19850
20064
|
if (tag === undefined)
|
|
@@ -20937,6 +21151,85 @@ async function runInnerModel(ctx, request, runtime) {
|
|
|
20937
21151
|
}
|
|
20938
21152
|
}
|
|
20939
21153
|
|
|
21154
|
+
// src/tools/impl/_site-icons.ts
|
|
21155
|
+
var SITE_ICONS_MAX = 10;
|
|
21156
|
+
var SITE_ICON_URL_MAX_LENGTH = 2048;
|
|
21157
|
+
var ICON_SCAN_CHARS = 256 * 1024;
|
|
21158
|
+
function siteIconHttpsUrl(value) {
|
|
21159
|
+
if (typeof value !== "string" || value.length === 0 || value.length > SITE_ICON_URL_MAX_LENGTH)
|
|
21160
|
+
return;
|
|
21161
|
+
let parsed;
|
|
21162
|
+
try {
|
|
21163
|
+
parsed = new URL(value.trim());
|
|
21164
|
+
} catch {
|
|
21165
|
+
return;
|
|
21166
|
+
}
|
|
21167
|
+
if (parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" || !isPublicName(parsed.hostname))
|
|
21168
|
+
return;
|
|
21169
|
+
const href = parsed.href;
|
|
21170
|
+
return href.length <= SITE_ICON_URL_MAX_LENGTH && /^https:\/\/[!-~]+$/.test(href) ? href : undefined;
|
|
21171
|
+
}
|
|
21172
|
+
var PRIVATE_NAME_SUFFIX = /\.(?:localhost|local|internal|lan|home|home\.arpa|corp|intranet|private)$/;
|
|
21173
|
+
function isPublicName(hostname) {
|
|
21174
|
+
const host = hostname.toLowerCase().replace(/\.$/, "");
|
|
21175
|
+
if (host.length === 0 || host.length > 253 || host.startsWith("[") || !host.includes("."))
|
|
21176
|
+
return false;
|
|
21177
|
+
if (host === "localhost" || PRIVATE_NAME_SUFFIX.test(host))
|
|
21178
|
+
return false;
|
|
21179
|
+
const labels = host.split(".");
|
|
21180
|
+
return /[a-z]/.test(labels[labels.length - 1]) && labels.every((l) => l.length > 0 && l.length <= 63);
|
|
21181
|
+
}
|
|
21182
|
+
function collectSiteIcons(entries) {
|
|
21183
|
+
const out = [];
|
|
21184
|
+
const seen = new Set;
|
|
21185
|
+
for (const e of entries) {
|
|
21186
|
+
if (out.length >= SITE_ICONS_MAX)
|
|
21187
|
+
break;
|
|
21188
|
+
const url = siteIconHttpsUrl(e.url);
|
|
21189
|
+
const iconUrl = siteIconHttpsUrl(e.iconUrl);
|
|
21190
|
+
if (url === undefined || iconUrl === undefined || seen.has(url))
|
|
21191
|
+
continue;
|
|
21192
|
+
seen.add(url);
|
|
21193
|
+
out.push({ url, iconUrl });
|
|
21194
|
+
}
|
|
21195
|
+
return out.length > 0 ? out : undefined;
|
|
21196
|
+
}
|
|
21197
|
+
var LINK_TAG = /<link\b[^>]*>/gi;
|
|
21198
|
+
var ATTRIBUTE = /([A-Za-z][\w:-]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'>]+))/g;
|
|
21199
|
+
function pageIconUrl(html, pageUrl) {
|
|
21200
|
+
const page = siteIconHttpsUrl(pageUrl);
|
|
21201
|
+
if (page === undefined)
|
|
21202
|
+
return;
|
|
21203
|
+
const head = html.length > ICON_SCAN_CHARS ? html.slice(0, ICON_SCAN_CHARS) : html;
|
|
21204
|
+
let best;
|
|
21205
|
+
for (const tag of head.matchAll(LINK_TAG)) {
|
|
21206
|
+
const attrs = {};
|
|
21207
|
+
for (const m of tag[0].matchAll(ATTRIBUTE))
|
|
21208
|
+
attrs[m[1].toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? "";
|
|
21209
|
+
const rel = (attrs["rel"] ?? "").toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
21210
|
+
const href = decodeHtmlEntities(attrs["href"] ?? "").trim();
|
|
21211
|
+
const lowered = href.toLowerCase();
|
|
21212
|
+
if (href.length === 0 || rel.includes("mask-icon") || lowered.startsWith("data:") || /\.svg(?:[?#]|$)/.test(lowered))
|
|
21213
|
+
continue;
|
|
21214
|
+
const rank = rel.includes("apple-touch-icon") || rel.includes("apple-touch-icon-precomposed") ? 3 : rel.includes("icon") ? 2 : 0;
|
|
21215
|
+
if (rank === 0)
|
|
21216
|
+
continue;
|
|
21217
|
+
if (best === undefined || rank > best.rank) {
|
|
21218
|
+
const resolved = resolveIcon(href, page);
|
|
21219
|
+
if (resolved !== undefined)
|
|
21220
|
+
best = { rank, href: resolved };
|
|
21221
|
+
}
|
|
21222
|
+
}
|
|
21223
|
+
return best?.href ?? siteIconHttpsUrl(new URL("/favicon.ico", page).href);
|
|
21224
|
+
}
|
|
21225
|
+
function resolveIcon(href, page) {
|
|
21226
|
+
try {
|
|
21227
|
+
return siteIconHttpsUrl(new URL(href, page).href);
|
|
21228
|
+
} catch {
|
|
21229
|
+
return;
|
|
21230
|
+
}
|
|
21231
|
+
}
|
|
21232
|
+
|
|
20940
21233
|
// src/tools/impl/web-fetch.ts
|
|
20941
21234
|
var DIGEST_CONTENT_CAP = 1e5;
|
|
20942
21235
|
var RESULT_CAP = 50000;
|
|
@@ -20993,6 +21286,10 @@ async function raceAgainstAbort(promise, signal) {
|
|
|
20993
21286
|
});
|
|
20994
21287
|
});
|
|
20995
21288
|
}
|
|
21289
|
+
function siteIconsFor(pageUrl, iconUrl) {
|
|
21290
|
+
const siteIcons = iconUrl !== undefined ? collectSiteIcons([{ url: pageUrl, iconUrl }]) : undefined;
|
|
21291
|
+
return siteIcons !== undefined ? { siteIcons } : {};
|
|
21292
|
+
}
|
|
20996
21293
|
function capResult(text) {
|
|
20997
21294
|
if (text.length <= RESULT_CAP)
|
|
20998
21295
|
return text;
|
|
@@ -21156,11 +21453,15 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21156
21453
|
const preapproved = isPreapprovedUrl(originalUrl);
|
|
21157
21454
|
let content;
|
|
21158
21455
|
let contentType;
|
|
21456
|
+
let pageUrl;
|
|
21457
|
+
let iconUrl;
|
|
21458
|
+
let addressPublic;
|
|
21159
21459
|
const cached = cache.get(ctx.sessionId, inputUrlString);
|
|
21160
21460
|
if (cached !== undefined) {
|
|
21161
21461
|
const addressVerdict = await raceAgainstAbort(classifyHostname(stripIpv6Brackets(originalUrl.hostname), resolveHost), ctx.signal);
|
|
21162
21462
|
if (addressVerdict === "aborted")
|
|
21163
21463
|
return { output: "WebFetch was interrupted.", isError: true };
|
|
21464
|
+
addressPublic = addressVerdict.class !== "private";
|
|
21164
21465
|
if (addressVerdict.class === "private") {
|
|
21165
21466
|
const refusal = privateAddressRefusal(originalUrl.hostname, policy, addressVerdict.reason);
|
|
21166
21467
|
if (refusal !== undefined)
|
|
@@ -21173,6 +21474,7 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21173
21474
|
const finalVerdict = await raceAgainstAbort(classifyHostname(stripIpv6Brackets(cachedHost), resolveHost), ctx.signal);
|
|
21174
21475
|
if (finalVerdict === "aborted")
|
|
21175
21476
|
return { output: "WebFetch was interrupted.", isError: true };
|
|
21477
|
+
addressPublic &&= finalVerdict.class !== "private";
|
|
21176
21478
|
if (finalVerdict.class === "private") {
|
|
21177
21479
|
const refusal = privateAddressRefusal(cachedHost, policy, finalVerdict.reason);
|
|
21178
21480
|
if (refusal !== undefined)
|
|
@@ -21181,6 +21483,8 @@ function createWebFetchExecutor(deps = {}) {
|
|
|
21181
21483
|
}
|
|
21182
21484
|
content = cached.content;
|
|
21183
21485
|
contentType = cached.contentType;
|
|
21486
|
+
pageUrl = cached.finalUrl;
|
|
21487
|
+
iconUrl = cached.iconUrl;
|
|
21184
21488
|
} else {
|
|
21185
21489
|
const outcome = await performWebFetch(inputUrlString, prompt, { blockedDomains, privateAddressPolicy: policy, userAgent: winterUserAgent(), ...ctx.signal !== undefined ? { signal: ctx.signal } : {} }, netDeps);
|
|
21186
21490
|
switch (outcome.kind) {
|
|
@@ -21226,9 +21530,12 @@ The response body was not retrieved. If this URL requires authentication, use an
|
|
|
21226
21530
|
}
|
|
21227
21531
|
const kind = classifyContentType(outcome.contentType);
|
|
21228
21532
|
if (kind === "html") {
|
|
21229
|
-
|
|
21533
|
+
const html = new TextDecoder("utf-8", { fatal: false }).decode(outcome.body);
|
|
21534
|
+
iconUrl = pageIconUrl(html, outcome.finalUrl);
|
|
21535
|
+
content = await convertFetchedHtml(html);
|
|
21230
21536
|
} else if (kind === "text") {
|
|
21231
21537
|
content = new TextDecoder("utf-8", { fatal: false }).decode(outcome.body);
|
|
21538
|
+
iconUrl = pageIconUrl("", outcome.finalUrl);
|
|
21232
21539
|
} else {
|
|
21233
21540
|
const savedPath = await saveBinaryToTemp(ctx, originalUrl, outcome.body);
|
|
21234
21541
|
const sizeLabel = `${outcome.body.byteLength.toLocaleString("en-US")} bytes`;
|
|
@@ -21236,14 +21543,18 @@ The response body was not retrieved. If this URL requires authentication, use an
|
|
|
21236
21543
|
return { output: note };
|
|
21237
21544
|
}
|
|
21238
21545
|
contentType = outcome.contentType;
|
|
21239
|
-
|
|
21546
|
+
pageUrl = outcome.finalUrl;
|
|
21547
|
+
addressPublic = policy !== "allow";
|
|
21548
|
+
const entry = { bytes: Buffer.byteLength(content, "utf8"), code: outcome.status, codeText: outcome.statusText, content, contentType, finalUrl: outcome.finalUrl, ...iconUrl !== undefined ? { iconUrl } : {} };
|
|
21240
21549
|
cache.set(ctx.sessionId, inputUrlString, entry);
|
|
21241
21550
|
}
|
|
21551
|
+
if (!addressPublic)
|
|
21552
|
+
iconUrl = undefined;
|
|
21242
21553
|
if (preapproved && contentType.toLowerCase().includes("text/markdown") && content.length < 1e5) {
|
|
21243
|
-
return { output: capResult(content) };
|
|
21554
|
+
return { output: capResult(content), ...siteIconsFor(pageUrl, iconUrl) };
|
|
21244
21555
|
}
|
|
21245
21556
|
const digest = await runDigest(ctx, runtime, content, prompt, preapproved);
|
|
21246
|
-
return { output: capResult(digest.output), ...digest.isError ? { isError: true } :
|
|
21557
|
+
return { output: capResult(digest.output), ...digest.isError ? { isError: true } : siteIconsFor(pageUrl, iconUrl) };
|
|
21247
21558
|
}
|
|
21248
21559
|
return {
|
|
21249
21560
|
async execute(rawInput, ctx) {
|
|
@@ -21387,7 +21698,8 @@ function hitFromRecord(record) {
|
|
|
21387
21698
|
const title = capText(typeof record["title"] === "string" && record["title"].trim().length > 0 ? record["title"].trim() : url, EXA_TITLE_MAX_CHARACTERS);
|
|
21388
21699
|
const highlight = highlightFrom(record["highlights"]) || highlightFrom(record["summary"]) || highlightFrom(record["text"]);
|
|
21389
21700
|
const published = typeof record["publishedDate"] === "string" && record["publishedDate"].length > 0 ? record["publishedDate"] : undefined;
|
|
21390
|
-
|
|
21701
|
+
const favicon = siteIconHttpsUrl(record["favicon"]);
|
|
21702
|
+
return { title, url, highlight: capText(highlight.trim(), EXA_HIGHLIGHTS_MAX_CHARACTERS), ...published !== undefined ? { publishedDate: published } : {}, ...favicon !== undefined ? { favicon } : {} };
|
|
21391
21703
|
}
|
|
21392
21704
|
var FIELD_LINE = /^(Title|URL|Published(?: Date)?|Author|Highlights|Text|Summary|Content):[ \t]*(.*)$/i;
|
|
21393
21705
|
function hitsFromPlainText(text) {
|
|
@@ -21946,6 +22258,7 @@ function createWebSearchExecutor(deps = {}) {
|
|
|
21946
22258
|
return { output: `Error: web search failed unexpectedly (${err instanceof Error ? err.name : "unknown error"}).`, isError: true };
|
|
21947
22259
|
}
|
|
21948
22260
|
const events = [];
|
|
22261
|
+
const iconEntries = [];
|
|
21949
22262
|
let successfulSearches = 0;
|
|
21950
22263
|
let attemptedSearches = 0;
|
|
21951
22264
|
let lastFailureMessage;
|
|
@@ -21970,6 +22283,9 @@ function createWebSearchExecutor(deps = {}) {
|
|
|
21970
22283
|
}
|
|
21971
22284
|
successfulSearches += 1;
|
|
21972
22285
|
events.push({ type: "search_result", hits: outcome.hits.map((h) => ({ title: h.title, url: h.url })) });
|
|
22286
|
+
for (const h of outcome.hits)
|
|
22287
|
+
if (h.favicon !== undefined)
|
|
22288
|
+
iconEntries.push({ url: h.url, iconUrl: h.favicon });
|
|
21973
22289
|
}
|
|
21974
22290
|
if (pass !== undefined && !pass.ok) {
|
|
21975
22291
|
events.push({ type: "text", text: `
|
|
@@ -21981,12 +22297,150 @@ ${innerFailureText(pass)}` });
|
|
|
21981
22297
|
|
|
21982
22298
|
${lastFailureMessage}` });
|
|
21983
22299
|
}
|
|
21984
|
-
|
|
22300
|
+
const siteIcons = collectSiteIcons(iconEntries);
|
|
22301
|
+
return { output: assembleWebSearchOutputCapped(query, events, resultCap), ...siteIcons !== undefined ? { siteIcons } : {} };
|
|
21985
22302
|
}
|
|
21986
22303
|
return { execute };
|
|
21987
22304
|
}
|
|
21988
22305
|
replaceExecutor(WEB_SEARCH_CANONICAL_NAME, createWebSearchExecutor());
|
|
21989
22306
|
|
|
22307
|
+
// src/tools/impl/search.ts
|
|
22308
|
+
var SEARCH_REQUEST_TIMEOUT_MS = 45000;
|
|
22309
|
+
var SEARCH_ANSWER_CHARS = 24000;
|
|
22310
|
+
var SEARCH_MAX_CITATIONS = 20;
|
|
22311
|
+
var SEARCH_TOTAL_OUTPUT_CHARS = 30000;
|
|
22312
|
+
var EXA_ANSWER_URL = "https://api.exa.ai/answer";
|
|
22313
|
+
function isValidCitations(value) {
|
|
22314
|
+
if (value === undefined)
|
|
22315
|
+
return true;
|
|
22316
|
+
if (!Array.isArray(value))
|
|
22317
|
+
return false;
|
|
22318
|
+
return value.every((item) => item !== null && typeof item === "object");
|
|
22319
|
+
}
|
|
22320
|
+
function searchStatusMessage(status) {
|
|
22321
|
+
switch (status) {
|
|
22322
|
+
case 401:
|
|
22323
|
+
case 403:
|
|
22324
|
+
return "search failed: the configured Exa API key was rejected — it needs to be replaced before Search can work";
|
|
22325
|
+
case 402:
|
|
22326
|
+
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";
|
|
22327
|
+
case 429:
|
|
22328
|
+
return "search failed: the search service is rate-limiting this key — wait a little before searching again, and do not retry in a loop";
|
|
22329
|
+
case 400:
|
|
22330
|
+
return "search failed: the search service rejected the request as malformed — try a plainer question";
|
|
22331
|
+
default:
|
|
22332
|
+
return `search failed: the search service is unavailable (HTTP ${status})`;
|
|
22333
|
+
}
|
|
22334
|
+
}
|
|
22335
|
+
var SEARCH_NO_KEY_MESSAGE = "Search needs an Exa API key (from exa.ai), and none is configured for this session";
|
|
22336
|
+
function errorResult6(output) {
|
|
22337
|
+
return { output, isError: true };
|
|
22338
|
+
}
|
|
22339
|
+
function createSearchExecutor(deps = {}) {
|
|
22340
|
+
const endpoint = deps.endpoint ?? EXA_ANSWER_URL;
|
|
22341
|
+
const timeoutMs = deps.timeoutMs ?? SEARCH_REQUEST_TIMEOUT_MS;
|
|
22342
|
+
async function execute(rawInput, ctx) {
|
|
22343
|
+
const record = typeof rawInput === "object" && rawInput !== null ? rawInput : {};
|
|
22344
|
+
const query = typeof record["query"] === "string" ? record["query"] : "";
|
|
22345
|
+
if (query.trim().length === 0)
|
|
22346
|
+
return errorResult6("Error: Missing query");
|
|
22347
|
+
const runtime = webSessionRuntimeFor(ctx);
|
|
22348
|
+
if (runtime === undefined)
|
|
22349
|
+
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).");
|
|
22350
|
+
if (!runtime.web.search.enabled)
|
|
22351
|
+
return { output: "Web search is turned off for this session." };
|
|
22352
|
+
const resolveKey = exaKeyResolverFor(runtime);
|
|
22353
|
+
if (resolveKey === undefined)
|
|
22354
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22355
|
+
let key;
|
|
22356
|
+
try {
|
|
22357
|
+
const secret = await resolveKey();
|
|
22358
|
+
if (secret.status === "missing")
|
|
22359
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22360
|
+
if (secret.status === "unreadable")
|
|
22361
|
+
return errorResult6(`search failed: an Exa API key is configured but could not be used: ${secret.message}`);
|
|
22362
|
+
key = secret.key;
|
|
22363
|
+
} catch (err) {
|
|
22364
|
+
return errorResult6(`search failed: the key resolver failed with ${err instanceof Error ? err.name : "an unknown error"}`);
|
|
22365
|
+
}
|
|
22366
|
+
if (key.length === 0)
|
|
22367
|
+
return errorResult6(SEARCH_NO_KEY_MESSAGE);
|
|
22368
|
+
const fetchFn = deps.fetchFn ?? fetch;
|
|
22369
|
+
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
22370
|
+
const signal = ctx.signal !== undefined ? AbortSignal.any([ctx.signal, timeoutSignal]) : timeoutSignal;
|
|
22371
|
+
let res;
|
|
22372
|
+
try {
|
|
22373
|
+
res = await fetchFn(endpoint, {
|
|
22374
|
+
method: "POST",
|
|
22375
|
+
headers: { "x-api-key": key, "content-type": "application/json" },
|
|
22376
|
+
body: JSON.stringify({ query }),
|
|
22377
|
+
redirect: "manual",
|
|
22378
|
+
signal
|
|
22379
|
+
});
|
|
22380
|
+
} catch (err) {
|
|
22381
|
+
const name = err instanceof Error ? err.name : "";
|
|
22382
|
+
if (name === "AbortError" || name === "TimeoutError") {
|
|
22383
|
+
return errorResult6(ctx.signal?.aborted === true ? "Search was interrupted." : `search timed out for ${query}`);
|
|
22384
|
+
}
|
|
22385
|
+
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
22386
|
+
console.error(`Search: network error (${name || "Error"}) — ${rawMessage.replaceAll(key, "<redacted>")}`);
|
|
22387
|
+
return errorResult6("search failed: could not reach the search service");
|
|
22388
|
+
}
|
|
22389
|
+
if (res.status !== 200) {
|
|
22390
|
+
try {
|
|
22391
|
+
await res.body?.cancel();
|
|
22392
|
+
} catch {}
|
|
22393
|
+
return errorResult6(searchStatusMessage(res.status));
|
|
22394
|
+
}
|
|
22395
|
+
let data;
|
|
22396
|
+
try {
|
|
22397
|
+
data = await res.json();
|
|
22398
|
+
} catch (err) {
|
|
22399
|
+
return errorResult6(`search failed: could not parse response (${err instanceof Error ? err.message : String(err)})`);
|
|
22400
|
+
}
|
|
22401
|
+
if (data === null || typeof data !== "object" || !isValidCitations(data.citations))
|
|
22402
|
+
return errorResult6("search failed: malformed response from search service");
|
|
22403
|
+
if (data.answer !== undefined && typeof data.answer !== "string")
|
|
22404
|
+
return errorResult6("search failed: malformed response from search service");
|
|
22405
|
+
const fullAnswer = (data.answer ?? "").trim();
|
|
22406
|
+
const answer = fullAnswer.length > SEARCH_ANSWER_CHARS ? fullAnswer.slice(0, SEARCH_ANSWER_CHARS) + `
|
|
22407
|
+
|
|
22408
|
+
[answer truncated]` : fullAnswer;
|
|
22409
|
+
const hostFloor = Array.isArray(record["blocked_domains"]) ? record["blocked_domains"].filter((d) => typeof d === "string" && d.trim().length > 0).slice(0, 1000) : [];
|
|
22410
|
+
const floor = hostFloor.length > 0 ? [...runtime.web.blockedDomains, ...hostFloor] : runtime.web.blockedDomains;
|
|
22411
|
+
const rawCitations = (data.citations ?? []).slice(0, SEARCH_MAX_CITATIONS);
|
|
22412
|
+
let withheld = 0;
|
|
22413
|
+
const citations = rawCitations.filter((c) => {
|
|
22414
|
+
if (typeof c.url !== "string" || c.url.length === 0 || !isDomainBlocked(c.url, floor))
|
|
22415
|
+
return true;
|
|
22416
|
+
withheld++;
|
|
22417
|
+
return false;
|
|
22418
|
+
});
|
|
22419
|
+
const withheldNote = withheld > 0 ? `
|
|
22420
|
+
|
|
22421
|
+
[${withheld} source${withheld === 1 ? "" : "s"} withheld — matched the dangerous-domain list]` : "";
|
|
22422
|
+
const siteIcons = collectSiteIcons(citations.map((c) => ({ url: c.url, iconUrl: c.favicon })));
|
|
22423
|
+
const withIcons = (output) => ({ output, ...siteIcons !== undefined ? { siteIcons } : {} });
|
|
22424
|
+
if (answer === "")
|
|
22425
|
+
return withIcons(`no answer for ${query}${withheldNote}`);
|
|
22426
|
+
const sources = citations.length === 0 ? `
|
|
22427
|
+
|
|
22428
|
+
[unsourced — ${withheld > 0 ? "every source was withheld by the dangerous-domain list" : "the search service returned no sources"}; say so if you repeat this]` : `
|
|
22429
|
+
|
|
22430
|
+
Sources:
|
|
22431
|
+
` + citations.map((c, i) => `${i + 1}. ${(typeof c.title === "string" ? c.title.trim() : "") || "-"}
|
|
22432
|
+
${typeof c.url === "string" && c.url.length > 0 ? c.url : "-"}`).join(`
|
|
22433
|
+
`);
|
|
22434
|
+
const rendered = answer + sources;
|
|
22435
|
+
const capped = rendered.length > SEARCH_TOTAL_OUTPUT_CHARS ? rendered.slice(0, SEARCH_TOTAL_OUTPUT_CHARS) + `
|
|
22436
|
+
|
|
22437
|
+
[truncated]` : rendered;
|
|
22438
|
+
return withIcons(capped + withheldNote);
|
|
22439
|
+
}
|
|
22440
|
+
return { execute };
|
|
22441
|
+
}
|
|
22442
|
+
replaceExecutor(SEARCH_CANONICAL_NAME, createSearchExecutor());
|
|
22443
|
+
|
|
21990
22444
|
// src/tools/read-state.ts
|
|
21991
22445
|
import { isAbsolute as isAbsolute6, join as join24, normalize as normalize3 } from "node:path";
|
|
21992
22446
|
function createSessionReadState(opts) {
|
|
@@ -22207,6 +22661,18 @@ function toolResultsForHost(content) {
|
|
|
22207
22661
|
};
|
|
22208
22662
|
return blank(content) ?? content;
|
|
22209
22663
|
}
|
|
22664
|
+
function withHostSiteIcons(content, icons) {
|
|
22665
|
+
if (icons.size === 0)
|
|
22666
|
+
return content;
|
|
22667
|
+
return content.map((block) => {
|
|
22668
|
+
if (block.type !== "tool_result")
|
|
22669
|
+
return block;
|
|
22670
|
+
const forCall = icons.get(block.tool_use_id);
|
|
22671
|
+
if (forCall === undefined || forCall.length === 0)
|
|
22672
|
+
return block;
|
|
22673
|
+
return { ...block, winter_site_icons: forCall.map((icon) => ({ url: icon.url, icon_url: icon.iconUrl })) };
|
|
22674
|
+
});
|
|
22675
|
+
}
|
|
22210
22676
|
function asRebuiltContent(content) {
|
|
22211
22677
|
return content.length === 1 && content[0].type === "text" ? content[0].text : content;
|
|
22212
22678
|
}
|
|
@@ -22349,11 +22815,48 @@ function toolOutputReplacement(value) {
|
|
|
22349
22815
|
return capHookText(String(value), cap);
|
|
22350
22816
|
}
|
|
22351
22817
|
}
|
|
22818
|
+
function withLegacyToolNameRules(opts) {
|
|
22819
|
+
const legacy = opts.config.legacyToolNames;
|
|
22820
|
+
if (legacy === undefined || Object.keys(legacy).length === 0)
|
|
22821
|
+
return opts;
|
|
22822
|
+
const expand = (rules) => {
|
|
22823
|
+
if (rules === undefined)
|
|
22824
|
+
return;
|
|
22825
|
+
const out = [...rules];
|
|
22826
|
+
for (const raw of rules) {
|
|
22827
|
+
const head = /^([^(]+)(.*)$/s.exec(raw.trim());
|
|
22828
|
+
if (head === null)
|
|
22829
|
+
continue;
|
|
22830
|
+
const target = Object.hasOwn(legacy, head[1]) ? legacy[head[1]] : undefined;
|
|
22831
|
+
if (typeof target === "string" && target.length > 0)
|
|
22832
|
+
out.push(`${target}${head[2]}`);
|
|
22833
|
+
}
|
|
22834
|
+
return [...new Set(out)];
|
|
22835
|
+
};
|
|
22836
|
+
const config = opts.config;
|
|
22837
|
+
const permissions = config.permissions;
|
|
22838
|
+
return {
|
|
22839
|
+
...opts,
|
|
22840
|
+
config: {
|
|
22841
|
+
...config,
|
|
22842
|
+
...config.allowedTools !== undefined ? { allowedTools: expand(config.allowedTools) } : {},
|
|
22843
|
+
...config.disallowedTools !== undefined ? { disallowedTools: expand(config.disallowedTools) } : {},
|
|
22844
|
+
...permissions !== undefined ? {
|
|
22845
|
+
permissions: {
|
|
22846
|
+
...permissions,
|
|
22847
|
+
...permissions.allow !== undefined ? { allow: expand(permissions.allow) } : {},
|
|
22848
|
+
...permissions.ask !== undefined ? { ask: expand(permissions.ask) } : {},
|
|
22849
|
+
...permissions.deny !== undefined ? { deny: expand(permissions.deny) } : {}
|
|
22850
|
+
}
|
|
22851
|
+
} : {}
|
|
22852
|
+
}
|
|
22853
|
+
};
|
|
22854
|
+
}
|
|
22352
22855
|
async function runEngine2(opts) {
|
|
22353
22856
|
const facetDisposers = [];
|
|
22354
22857
|
let sweepBackgroundShellTasksOnExit;
|
|
22355
22858
|
try {
|
|
22356
|
-
return await runEngineBody(opts, facetDisposers, (fn) => {
|
|
22859
|
+
return await runEngineBody(withLegacyToolNameRules(opts), facetDisposers, (fn) => {
|
|
22357
22860
|
sweepBackgroundShellTasksOnExit = fn;
|
|
22358
22861
|
});
|
|
22359
22862
|
} finally {
|
|
@@ -22434,6 +22937,16 @@ async function runEngineBody(opts, facetDisposers, registerBackgroundShellSweep)
|
|
|
22434
22937
|
resolveToolSecret: resolveToolSecretRoute,
|
|
22435
22938
|
onPricedGeneration
|
|
22436
22939
|
} = opts;
|
|
22940
|
+
const legacyToolNames = config.legacyToolNames ?? {};
|
|
22941
|
+
const currentToolName = (name) => {
|
|
22942
|
+
if (getRegisteredTool(name) !== undefined)
|
|
22943
|
+
return name;
|
|
22944
|
+
const legacy = Object.hasOwn(legacyToolNames, name) ? legacyToolNames[name] : undefined;
|
|
22945
|
+
if (typeof legacy === "string" && getRegisteredTool(legacy) !== undefined)
|
|
22946
|
+
return legacy;
|
|
22947
|
+
return name.startsWith("mcp__") ? registeredNameForMcpSpelling(name) : name;
|
|
22948
|
+
};
|
|
22949
|
+
const legacyNameOf = (name) => Object.entries(legacyToolNames).find(([old, current]) => current === name && getRegisteredTool(old) === undefined)?.[0];
|
|
22437
22950
|
registerBackgroundShellSweep(() => {
|
|
22438
22951
|
const swept = stopSessionShellTasks({ sessionId: config.sessionId, ...config.agentId !== undefined ? { agentId: config.agentId } : {} });
|
|
22439
22952
|
if (swept.length > 0) {
|
|
@@ -22795,6 +23308,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
22795
23308
|
opts.onChildRosterReady?.(() => childRoster);
|
|
22796
23309
|
opts.onForegroundChildrenReady?.(() => [...foregroundChildren]);
|
|
22797
23310
|
const removeChildRosterSource = ensureDefaultMessagingRuntimeRegistered().addChildRosterSource(() => childRoster);
|
|
23311
|
+
const removeHostMessagingPort = config.hostMessaging === true && config.agentId === undefined ? getDefaultMessagingRuntime()?.registerHostMessagingPort?.(config.sessionId, createHostMessagingPort(bridge)) : undefined;
|
|
22798
23312
|
const sandboxSettingsForSession = config.sandbox ?? DEFAULT_SANDBOX_SETTINGS;
|
|
22799
23313
|
const bashPostureFor = (call, dangerouslyDisableSandbox) => {
|
|
22800
23314
|
const command = call.input["command"];
|
|
@@ -23148,7 +23662,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23148
23662
|
});
|
|
23149
23663
|
return {
|
|
23150
23664
|
policy: policyResult,
|
|
23151
|
-
tools: req.definition?.tools !== undefined && !req.definition.tools.includes("*") ? req.definition.tools.filter((name) => currentAdvertisedCanonicalNames.includes(name)) : [...currentAdvertisedCanonicalNames],
|
|
23665
|
+
tools: req.definition?.tools !== undefined && !req.definition.tools.includes("*") ? [...new Set(req.definition.tools.map(currentToolName))].filter((name) => currentAdvertisedCanonicalNames.includes(name)) : [...currentAdvertisedCanonicalNames],
|
|
23152
23666
|
...childSlot,
|
|
23153
23667
|
effort: req.definition?.effort !== undefined ? String(req.definition.effort) : "inherit",
|
|
23154
23668
|
thinking: undefined,
|
|
@@ -23323,7 +23837,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23323
23837
|
const denyEntry = findAgentDenyRule(evalCtx.policy.rules, agentType, denyOpts);
|
|
23324
23838
|
if (denyEntry !== undefined)
|
|
23325
23839
|
return agentTypeDeniedMessage(agentType, denyEntry);
|
|
23326
|
-
if (isBuiltinAllToolsDenied(def, currentAdvertisedCanonicalNames)) {
|
|
23840
|
+
if (isBuiltinAllToolsDenied({ ...def.tools !== undefined ? { tools: def.tools.map(currentToolName) } : {} }, currentAdvertisedCanonicalNames)) {
|
|
23327
23841
|
return `Agent type '${agentType}' is unavailable because every tool it may use is denied by the current permission settings.`;
|
|
23328
23842
|
}
|
|
23329
23843
|
return;
|
|
@@ -23343,11 +23857,12 @@ ${escapeAttributionText(msg.body)}
|
|
|
23343
23857
|
if (serverCfg.type !== "sdk" || !serverCfg.tools || serverCfg.tools.length === 0)
|
|
23344
23858
|
continue;
|
|
23345
23859
|
const toolDefs = serverCfg.tools;
|
|
23346
|
-
|
|
23860
|
+
const toolNames = serverCfg.toolNames;
|
|
23861
|
+
registerMcpServerTools(serverName, toolDefs, { deferredDefault: true, ...toolNames !== undefined ? { toolNames } : {} });
|
|
23347
23862
|
sdkMcpServerNames.push(serverName);
|
|
23348
23863
|
const perServerTimeoutMs = serverCfg.timeout;
|
|
23349
23864
|
for (const tool of toolDefs) {
|
|
23350
|
-
const canonicalName = `mcp__${serverName}__${tool.name}`;
|
|
23865
|
+
const canonicalName = toolNames !== undefined && Object.hasOwn(toolNames, tool.name) ? toolNames[tool.name] : `mcp__${serverName}__${tool.name}`;
|
|
23351
23866
|
replaceExecutor(canonicalName, {
|
|
23352
23867
|
async execute(input) {
|
|
23353
23868
|
const timeoutMs = perServerTimeoutMs ?? mcpEnvConfig.toolTimeoutMs ?? 120000;
|
|
@@ -23379,13 +23894,15 @@ ${escapeAttributionText(msg.body)}
|
|
|
23379
23894
|
const resolvedSources = resolveMcpServerSources(sources, {
|
|
23380
23895
|
...config.strictMcpConfig !== undefined ? { strictMcpConfig: config.strictMcpConfig } : {},
|
|
23381
23896
|
trustedWorkspace,
|
|
23382
|
-
reservedServerName: sessionBrand.mcpServerName
|
|
23897
|
+
reservedServerName: sessionBrand.mcpServerName,
|
|
23898
|
+
...config.reservedMcpServerNames !== undefined ? { hostReservedServerNames: config.reservedMcpServerNames } : {}
|
|
23383
23899
|
});
|
|
23384
23900
|
mcpLifecycle = createMcpLifecycle({
|
|
23385
23901
|
servers: resolvedSources.resolved,
|
|
23386
23902
|
envConfig: mcpEnvConfig,
|
|
23387
23903
|
elicitationAsk: createElicitationAsker2(bridge),
|
|
23388
23904
|
reservedServerName: sessionBrand.mcpServerName,
|
|
23905
|
+
...config.reservedMcpServerNames !== undefined ? { hostReservedServerNames: config.reservedMcpServerNames } : {},
|
|
23389
23906
|
cwd: config.cwd,
|
|
23390
23907
|
...mcpOAuthStore !== undefined ? { oauth: createSessionMcpOAuth({ store: mcpOAuthStore, sender: bridge, brand: sessionBrand, ...config.hostCredentials === true ? { hostOwnsRefresh: true } : {} }) } : {}
|
|
23391
23908
|
});
|
|
@@ -23395,11 +23912,13 @@ ${escapeAttributionText(msg.body)}
|
|
|
23395
23912
|
const effectiveMcpStateSource = mcpServerStateSource ?? mcpLifecycle?.stateSource;
|
|
23396
23913
|
const effectiveMcpControlSeam = mcpControlSeam ?? mcpLifecycle?.controlSeam;
|
|
23397
23914
|
const sessionHasMcp = () => (effectiveMcpStateSource?.snapshot().length ?? 0) > 0;
|
|
23398
|
-
const
|
|
23915
|
+
const toolSearchExcludedByTools = config.tools !== undefined && !config.tools.includes(TOOL_SEARCH_TOOL_NAME);
|
|
23916
|
+
const enableToolSearch = toolSearchExcludedByTools ? "false" : config.toolSearchEnabled === true ? "true" : config.toolSearchEnabled === false ? "false" : mcpEnvConfig.enableToolSearch;
|
|
23399
23917
|
const deferralActivation = {
|
|
23400
23918
|
enableToolSearch,
|
|
23401
23919
|
providerSupportsToolSearch: providerSupportsToolSearch ?? true,
|
|
23402
|
-
deferrableContextShare: deferrableContextShare ?? 0
|
|
23920
|
+
deferrableContextShare: deferrableContextShare ?? 0,
|
|
23921
|
+
...config.deferTools !== undefined ? { deferTools: config.deferTools } : {}
|
|
23403
23922
|
};
|
|
23404
23923
|
const toolSearchEnabledDerived = isDeferralActive(deferralActivation);
|
|
23405
23924
|
const enableFileCheckpointing = config.enableFileCheckpointing === true;
|
|
@@ -23432,7 +23951,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23432
23951
|
return availableAgentNames(defs, {
|
|
23433
23952
|
isDenied: (agentType) => findAgentDenyRule(evalCtx.policy.rules, agentType, denyOpts) !== undefined,
|
|
23434
23953
|
...config.allowedAgentTypes !== undefined ? { allowedAgentTypes: config.allowedAgentTypes } : {},
|
|
23435
|
-
isAllToolsDenied: (def) => isBuiltinAllToolsDenied(def, currentAdvertisedCanonicalNames)
|
|
23954
|
+
isAllToolsDenied: (def) => isBuiltinAllToolsDenied({ ...def.tools !== undefined ? { tools: def.tools.map(currentToolName) } : {} }, currentAdvertisedCanonicalNames)
|
|
23436
23955
|
});
|
|
23437
23956
|
}
|
|
23438
23957
|
function sessionAvailableAgentDefinitions(at = {}) {
|
|
@@ -23503,11 +24022,14 @@ ${escapeAttributionText(msg.body)}
|
|
|
23503
24022
|
facetDisposers.push(forgetSessionWebState);
|
|
23504
24023
|
const WEB_DERIVED_CAPABILITIES = [
|
|
23505
24024
|
{ token: "winter.search-backend", probeTool: "WebSearch", fact: () => searchBackendUsable(webSessionRuntime) },
|
|
23506
|
-
{ token: "winter.fetch-extractor", probeTool: "WebFetch", fact: () => digestModelResolves(webSessionRuntime) }
|
|
24025
|
+
{ token: "winter.fetch-extractor", probeTool: "WebFetch", fact: () => digestModelResolves(webSessionRuntime) },
|
|
24026
|
+
{ token: SEARCH_ANSWER_CAPABILITY, probeTool: SEARCH_CANONICAL_NAME, fact: () => searchAnswerUsable(webSessionRuntime) }
|
|
23507
24027
|
];
|
|
23508
24028
|
const resolveLiveSessionCapabilities = () => {
|
|
23509
24029
|
const base = resolveSessionCapabilities(config.capabilities, { hasMcpServers: sessionHasMcp() });
|
|
23510
24030
|
const derived = reviewerResolves() ? [ADVISOR_CAPABILITY] : [];
|
|
24031
|
+
if ((config.deferTools?.length ?? 0) > 0)
|
|
24032
|
+
derived.push(DEFERRED_BUILTINS_CAPABILITY);
|
|
23511
24033
|
for (const cap of WEB_DERIVED_CAPABILITIES) {
|
|
23512
24034
|
if (getRegisteredTool(cap.probeTool)?.executor !== undefined && cap.fact())
|
|
23513
24035
|
derived.push(cap.token);
|
|
@@ -23521,6 +24043,7 @@ ${escapeAttributionText(msg.body)}
|
|
|
23521
24043
|
mode: policyStateStore.getState().mode,
|
|
23522
24044
|
platform: process.platform,
|
|
23523
24045
|
...config.disallowedTools !== undefined ? { disallowedTools: config.disallowedTools } : {},
|
|
24046
|
+
...config.tools !== undefined ? { tools: config.tools } : {},
|
|
23524
24047
|
capabilities: sessionCapabilities,
|
|
23525
24048
|
toolSearchEnabled: toolSearchEnabledDerived,
|
|
23526
24049
|
...config.insideSubagent !== undefined ? { insideSubagent: config.insideSubagent } : {},
|
|
@@ -23606,6 +24129,8 @@ ${escapeAttributionText(msg.body)}
|
|
|
23606
24129
|
return visibleServerToolFilter();
|
|
23607
24130
|
},
|
|
23608
24131
|
...config.disallowedTools !== undefined ? { disallowedTools: config.disallowedTools } : {},
|
|
24132
|
+
...config.tools !== undefined ? { tools: config.tools } : {},
|
|
24133
|
+
resolveName: currentToolName,
|
|
23609
24134
|
...config.insideSubagent !== undefined ? { insideSubagent: config.insideSubagent } : {},
|
|
23610
24135
|
...config.familyMetadata !== undefined ? { familyMetadata: config.familyMetadata } : {},
|
|
23611
24136
|
...config.toolAliases !== undefined ? { toolAliases: config.toolAliases } : {},
|
|
@@ -24638,6 +25163,12 @@ ${escapeAttributionText(msg.body)}
|
|
|
24638
25163
|
return { system: joinSystemBlocks(systemBlocks), systemBlocks };
|
|
24639
25164
|
};
|
|
24640
25165
|
const requestMessages = (context, effort, toolChanges) => buildRequestMessages(messages, context.userContextText, { systemReminders: systemRemindersOnWire(), ...effort !== undefined ? { effort } : {}, ...toolChanges !== undefined ? { toolChanges } : {} });
|
|
25166
|
+
const deferredToolNamesToAnnounce = () => {
|
|
25167
|
+
refreshAdvertisedPartition();
|
|
25168
|
+
const eagerNames = new Set(advertisedPartition.eager.map((d) => d.canonicalName));
|
|
25169
|
+
const twins = new Set(Object.entries(suppressionAliasTable).filter(([source]) => eagerNames.has(source)).map(([, target]) => target));
|
|
25170
|
+
return [...new Set(advertisedPartition.deferred.filter((d) => !twins.has(d.canonicalName)).map((d) => d.advertisedName))];
|
|
25171
|
+
};
|
|
24641
25172
|
const systemRemindersOnWire = () => currentModelDescription()?.wire?.midConversationSystem === true;
|
|
24642
25173
|
const currentModelDescription = () => {
|
|
24643
25174
|
const key = currentProviderIdentity?.modelKey ?? currentModel;
|
|
@@ -24793,6 +25324,13 @@ ${escapeAttributionText(msg.body)}
|
|
|
24793
25324
|
produced.push(needsAuth);
|
|
24794
25325
|
}
|
|
24795
25326
|
}
|
|
25327
|
+
if (exactRequestLayout === undefined) {
|
|
25328
|
+
const deferredNow = deferredToolNamesToAnnounce();
|
|
25329
|
+
const offeredNow = [...advertisedPartition.eager, ...advertisedPartition.deferred].map((d) => d.advertisedName);
|
|
25330
|
+
const deferredDelta = computeDeferredToolsDelta(deferredNow, offeredNow, messages);
|
|
25331
|
+
if (deferredDelta !== undefined)
|
|
25332
|
+
produced.push(deferredDelta);
|
|
25333
|
+
}
|
|
24796
25334
|
if (phase === "tool-round") {
|
|
24797
25335
|
const attachment = taskNotificationAttachment(notifications.drainFor(config.agentId, { maxPriority: "next" }), {
|
|
24798
25336
|
inHumanTurn: config.agentId === undefined && !turnStartedByNotification
|
|
@@ -25031,15 +25569,19 @@ ${escapeAttributionText(msg.body)}
|
|
|
25031
25569
|
return cached ? { name: identity.name, configName: identity.configName } : identity;
|
|
25032
25570
|
};
|
|
25033
25571
|
const declaredMcpIdentity = (toolName) => {
|
|
25034
|
-
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
|
|
25038
|
-
|
|
25039
|
-
|
|
25040
|
-
|
|
25572
|
+
const legacy = legacyNameOf(toolName);
|
|
25573
|
+
if (legacy !== undefined)
|
|
25574
|
+
return legacy;
|
|
25575
|
+
const spelled = mcpSpellingOf(toolName) ?? toolName;
|
|
25576
|
+
if (mcpServerRenames !== undefined) {
|
|
25577
|
+
for (const [actual, declared] of Object.entries(mcpServerRenames)) {
|
|
25578
|
+
if (spelled === `mcp__${actual}`)
|
|
25579
|
+
return `mcp__${declared}`;
|
|
25580
|
+
if (spelled.startsWith(`mcp__${actual}__`))
|
|
25581
|
+
return `mcp__${declared}__${spelled.slice(`mcp__${actual}__`.length)}`;
|
|
25582
|
+
}
|
|
25041
25583
|
}
|
|
25042
|
-
return;
|
|
25584
|
+
return spelled !== toolName ? spelled : undefined;
|
|
25043
25585
|
};
|
|
25044
25586
|
const postToolHookSubject = (event, toolName) => {
|
|
25045
25587
|
const declared = declaredMcpIdentity(toolName);
|
|
@@ -25612,10 +26154,13 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25612
26154
|
...generationEffort ?? {}
|
|
25613
26155
|
});
|
|
25614
26156
|
const resultBlocks = [];
|
|
26157
|
+
const hostSiteIcons = new Map;
|
|
25615
26158
|
let toolThrowText = null;
|
|
25616
26159
|
let structuredTerminated = false;
|
|
25617
26160
|
const truncatedByOutputLimit = turn.stopReason === "max_tokens";
|
|
25618
|
-
for (const
|
|
26161
|
+
for (const emitted of turn.calls) {
|
|
26162
|
+
const resolvedName = currentToolName(emitted.name);
|
|
26163
|
+
const call = resolvedName !== emitted.name ? { ...emitted, name: resolvedName } : emitted;
|
|
25619
26164
|
const stoppedBy = currentTurnStop();
|
|
25620
26165
|
if (stoppedBy !== undefined) {
|
|
25621
26166
|
resultBlocks.push({ type: "tool_result", tool_use_id: call.id, content: `[not executed: the ${stoppedBy.hookName} hook stopped the turn]`, error: true });
|
|
@@ -25847,6 +26392,8 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25847
26392
|
...loadedTools.length > 0 ? { loadedTools } : {},
|
|
25848
26393
|
...loadedToolDefinitions.length > 0 ? { loadedToolDefinitions } : {}
|
|
25849
26394
|
});
|
|
26395
|
+
if (raced.value.siteIcons !== undefined && raced.value.siteIcons.length > 0)
|
|
26396
|
+
hostSiteIcons.set(call.id, raced.value.siteIcons);
|
|
25850
26397
|
const postToolUseHookOutcome = raced.value.isError === true ? await fireObservationalHook("PostToolUseFailure", {
|
|
25851
26398
|
toolUseID: call.id,
|
|
25852
26399
|
toolName: postToolHookSubject("PostToolUseFailure", call.name),
|
|
@@ -25908,7 +26455,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25908
26455
|
}
|
|
25909
26456
|
}
|
|
25910
26457
|
}
|
|
25911
|
-
output.write({ type: "data", message: { type: "user", message: { content: toolResultsForHost(resultBlocks.map((b) => b.type === "tool_result" && b.loadedTools !== undefined ? withoutLoadedTools(b) : b)) } } });
|
|
26458
|
+
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
26459
|
messages.push({ role: "tool", content: resultBlocks });
|
|
25913
26460
|
await recordUser(resultBlocks);
|
|
25914
26461
|
await flushPendingHookAttachments();
|
|
@@ -25968,6 +26515,7 @@ ${reason}` : ""}` : `${promptStop.hookName} hook stopped continuation${reason.le
|
|
|
25968
26515
|
if (config.agentId === undefined)
|
|
25969
26516
|
clearNotificationQueue(config.sessionId);
|
|
25970
26517
|
removeChildRosterSource();
|
|
26518
|
+
removeHostMessagingPort?.();
|
|
25971
26519
|
facetSelfPeer?.();
|
|
25972
26520
|
facetNoticeForwarder?.();
|
|
25973
26521
|
disposeSessionMcpLifecycle?.();
|
|
@@ -26168,8 +26716,43 @@ var TEST_PROVIDER_NAMES = new Set([
|
|
|
26168
26716
|
"p5skill",
|
|
26169
26717
|
"p5checkpoint",
|
|
26170
26718
|
"p5workflow",
|
|
26171
|
-
"readimage"
|
|
26719
|
+
"readimage",
|
|
26720
|
+
"calls"
|
|
26172
26721
|
]);
|
|
26722
|
+
var CALL_LINE = /^CALL\s+(\S+)(?:\s+(.*))?$/;
|
|
26723
|
+
function promptedCallsProvider() {
|
|
26724
|
+
return {
|
|
26725
|
+
async generate({ messages }) {
|
|
26726
|
+
let at = -1;
|
|
26727
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
26728
|
+
const m = messages[i];
|
|
26729
|
+
if (m.role === "user" && userMessageText(m).split(`
|
|
26730
|
+
`).some((line) => CALL_LINE.test(line.trim()))) {
|
|
26731
|
+
at = i;
|
|
26732
|
+
break;
|
|
26733
|
+
}
|
|
26734
|
+
}
|
|
26735
|
+
if (at < 0)
|
|
26736
|
+
return { kind: "text", text: "[]" };
|
|
26737
|
+
const script = userMessageText(messages[at]).split(`
|
|
26738
|
+
`).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]) : {} }));
|
|
26739
|
+
const after = messages.slice(at + 1);
|
|
26740
|
+
const made = after.filter((m) => m.role === "assistant" && Array.isArray(m.content) && m.content.some((b) => b.type === "tool_use")).length;
|
|
26741
|
+
if (made < script.length) {
|
|
26742
|
+
const next = script[made];
|
|
26743
|
+
return { kind: "tool_use", calls: [{ id: `calls-${at + 1}-${made + 1}`, name: next.name, input: next.input }] };
|
|
26744
|
+
}
|
|
26745
|
+
const results = after.flatMap((m) => Array.isArray(m.content) ? m.content : []).filter((b) => b.type === "tool_result");
|
|
26746
|
+
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]] : []));
|
|
26747
|
+
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(`
|
|
26748
|
+
`) : String(content);
|
|
26749
|
+
return {
|
|
26750
|
+
kind: "text",
|
|
26751
|
+
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) })))
|
|
26752
|
+
};
|
|
26753
|
+
}
|
|
26754
|
+
};
|
|
26755
|
+
}
|
|
26173
26756
|
function isTestProviderName2(v) {
|
|
26174
26757
|
return TEST_PROVIDER_NAMES.has(v);
|
|
26175
26758
|
}
|
|
@@ -26334,6 +26917,8 @@ function rawTestProviderByName(name) {
|
|
|
26334
26917
|
}
|
|
26335
26918
|
};
|
|
26336
26919
|
}
|
|
26920
|
+
case "calls":
|
|
26921
|
+
return promptedCallsProvider();
|
|
26337
26922
|
case "mcpsdk":
|
|
26338
26923
|
return scriptedProvider2([
|
|
26339
26924
|
{ kind: "tool_use", calls: [{ id: "mcpsdk-call-1", name: MCP_SDK_TEST_TOOL_NAME2, input: { x: 1 } }] },
|
|
@@ -27090,7 +27675,7 @@ class TranscriptWriter2 {
|
|
|
27090
27675
|
return entries;
|
|
27091
27676
|
}
|
|
27092
27677
|
}
|
|
27093
|
-
var RUNTIME_ENGINE_VERSION2 = "0.0.
|
|
27678
|
+
var RUNTIME_ENGINE_VERSION2 = "0.0.39";
|
|
27094
27679
|
function withPermissionJournal(writer, location) {
|
|
27095
27680
|
return {
|
|
27096
27681
|
transcriptPath: join26(location.winterHome, "projects", location.projectKey, `${location.sessionId}.jsonl`),
|