@yanlinglabs/winter-agent-runtime 0.0.35 → 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 +30 -0
- package/dist/{index-q5jpx2mz.js → index-5xe59vkt.js} +3001 -2065
- package/dist/{index-91rvsndj.js → index-8x4yjdg7.js} +2 -2
- package/dist/{index-838n35xx.js → index-z3970wsj.js} +46 -18
- package/dist/index.js +2 -2
- package/dist/mcp/control.d.ts +1 -0
- package/dist/mcp/lifecycle.d.ts +5 -0
- package/dist/provider/mock.d.ts +1 -1
- package/dist/sandbox/profile.d.ts +37 -0
- 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/image-prep.d.ts +208 -0
- package/dist/tools/image-test-fixtures.d.ts +7 -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/monitor.d.ts +2 -0
- package/dist/tools/impl/read.d.ts +1 -42
- package/dist/tools/impl/search.d.ts +25 -0
- package/dist/tools/registry.d.ts +95 -0
- package/dist/tools/sandbox-run-inputs.d.ts +41 -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
|
@@ -9,14 +9,14 @@ import {
|
|
|
9
9
|
resolveEngineSession2,
|
|
10
10
|
resolveProductionWinterHome2,
|
|
11
11
|
resolveProductionStoreHome
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-5xe59vkt.js";
|
|
13
13
|
import {
|
|
14
14
|
registerDefaultChildEngineFactory,
|
|
15
15
|
loadResumedChain,
|
|
16
16
|
buildProductionWiring,
|
|
17
17
|
withAutoSkillPermissions,
|
|
18
18
|
restoreChildRoster
|
|
19
|
-
} from "./index-
|
|
19
|
+
} from "./index-z3970wsj.js";
|
|
20
20
|
|
|
21
21
|
// src/embedded.ts
|
|
22
22
|
import { splitFrames, encodeFrame } from "@yanlinglabs/winter-agent-sdk";
|
|
@@ -15,6 +15,10 @@ import {
|
|
|
15
15
|
mcpServerHasRegistrations,
|
|
16
16
|
STRUCTURED_OUTPUT_TOOL_NAME,
|
|
17
17
|
looksLikeCommand,
|
|
18
|
+
matchFileRule,
|
|
19
|
+
resolveFileRuleAbsolutePath,
|
|
20
|
+
resolveFileRuleAbsoluteGlobText,
|
|
21
|
+
resolvesWithinPluginRoot,
|
|
18
22
|
capBytes,
|
|
19
23
|
neutralizeReminderTags,
|
|
20
24
|
readWhole,
|
|
@@ -22,12 +26,8 @@ import {
|
|
|
22
26
|
registerAttachmentRenderer,
|
|
23
27
|
attachmentsIn,
|
|
24
28
|
registerChildEngineFactory,
|
|
25
|
-
matchFileRule,
|
|
26
29
|
rawToRuleValue,
|
|
27
30
|
sourceRule,
|
|
28
|
-
resolveFileRuleAbsolutePath,
|
|
29
|
-
resolveFileRuleAbsoluteGlobText,
|
|
30
|
-
resolvesWithinPluginRoot,
|
|
31
31
|
parseFrontmatter,
|
|
32
32
|
parseAgentDefinitionFile,
|
|
33
33
|
allowedAgentTypesFromTools,
|
|
@@ -85,7 +85,7 @@ import {
|
|
|
85
85
|
providerCredentialRef2,
|
|
86
86
|
createModelClassifier2,
|
|
87
87
|
selectClassifierRoute2
|
|
88
|
-
} from "./index-
|
|
88
|
+
} from "./index-5xe59vkt.js";
|
|
89
89
|
import {
|
|
90
90
|
createKeychainCredentialStore,
|
|
91
91
|
createKeychainSecretReader,
|
|
@@ -311,7 +311,7 @@ function applySubagentToolPool(tools, opts) {
|
|
|
311
311
|
return true;
|
|
312
312
|
if (policyName === BACKGROUND_AGENT_ALWAYS_ALLOWED_TOOL)
|
|
313
313
|
return true;
|
|
314
|
-
if (name.startsWith("mcp__")
|
|
314
|
+
if (!twins.has(name) && (name.startsWith("mcp__") || getRegisteredTool(name)?.descriptor.source === "mcp"))
|
|
315
315
|
return true;
|
|
316
316
|
return allow.has(policyName);
|
|
317
317
|
});
|
|
@@ -492,12 +492,14 @@ function renameMcpReference(entry, renames) {
|
|
|
492
492
|
}
|
|
493
493
|
return;
|
|
494
494
|
}
|
|
495
|
-
function allocateChildScopedServers(declared, parentVisible, prefer, parentDeclared) {
|
|
495
|
+
function allocateChildScopedServers(declared, parentVisible, prefer, parentDeclared, reserved) {
|
|
496
496
|
const claimed = [];
|
|
497
|
-
const
|
|
497
|
+
const reservedSet = new Set(reserved ?? []);
|
|
498
|
+
const inUse = (name) => liveChildScopedServerNames.has(name) || parentVisible.has(name) || mcpServerHasRegistrations(name) || reservedSet.has(name);
|
|
498
499
|
const servers = {};
|
|
499
500
|
const actual = new Map;
|
|
500
501
|
const notes = [];
|
|
502
|
+
const reservedRenames = new Set;
|
|
501
503
|
const claim = (name) => {
|
|
502
504
|
liveChildScopedServerNames.add(name);
|
|
503
505
|
claimed.push(name);
|
|
@@ -505,7 +507,7 @@ function allocateChildScopedServers(declared, parentVisible, prefer, parentDecla
|
|
|
505
507
|
for (const [name, cfg] of Object.entries(declared)) {
|
|
506
508
|
if (cfg.type === "sdk") {
|
|
507
509
|
if (inUse(name)) {
|
|
508
|
-
notes.push(parentVisible.has(name) && parentDeclared?.[name]?.type === "sdk" ? `mcpServers declares the in-process server "${name}", which this session already has -- the session's "${name}" is used` : parentVisible.has(name) ? `mcpServers declares the in-process server "${name}", but this session's "${name}" is a different server -- this agent's own in-process "${name}" is not connected, and the session's is what this agent sees` : `mcpServers declares the in-process server "${name}", which another agent in this session is using -- this agent's own in-process "${name}" is not connected`);
|
|
510
|
+
notes.push(parentVisible.has(name) && parentDeclared?.[name]?.type === "sdk" ? `mcpServers declares the in-process server "${name}", which this session already has -- the session's "${name}" is used` : parentVisible.has(name) ? `mcpServers declares the in-process server "${name}", but this session's "${name}" is a different server -- this agent's own in-process "${name}" is not connected, and the session's is what this agent sees` : reservedSet.has(name) && !liveChildScopedServerNames.has(name) && !mcpServerHasRegistrations(name) ? `mcpServers declares the in-process server "${name}", a name this session's host reserves -- this agent's own in-process "${name}" is not connected` : `mcpServers declares the in-process server "${name}", which another agent in this session is using -- this agent's own in-process "${name}" is not connected`);
|
|
509
511
|
continue;
|
|
510
512
|
}
|
|
511
513
|
claim(name);
|
|
@@ -523,6 +525,8 @@ function allocateChildScopedServers(declared, parentVisible, prefer, parentDecla
|
|
|
523
525
|
claim(pick);
|
|
524
526
|
servers[pick] = cfg;
|
|
525
527
|
actual.set(name, pick);
|
|
528
|
+
if (pick !== name && reservedSet.has(name))
|
|
529
|
+
reservedRenames.add(name);
|
|
526
530
|
if (pick !== name)
|
|
527
531
|
notes.push(`mcpServers declares "${name}", which is already in use in this session -- this agent's own "${name}" is connected as "${pick}" (its tools are named mcp__${pick}__<tool>)`);
|
|
528
532
|
}
|
|
@@ -531,6 +535,7 @@ function allocateChildScopedServers(declared, parentVisible, prefer, parentDecla
|
|
|
531
535
|
servers,
|
|
532
536
|
actual,
|
|
533
537
|
notes,
|
|
538
|
+
reservedRenames,
|
|
534
539
|
release: () => {
|
|
535
540
|
if (released)
|
|
536
541
|
return;
|
|
@@ -775,7 +780,7 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
775
780
|
...!ownServers && parentMcp?.stateSource !== undefined ? { mcpServerStateSource: parentMcp.stateSource } : {},
|
|
776
781
|
...!ownServers && parentMcp?.controlSeam !== undefined ? { mcpControlSeam: parentMcp.controlSeam } : {},
|
|
777
782
|
...(() => {
|
|
778
|
-
const merged = { ...parentMcp?.serverRenames ?? {}, ...Object.fromEntries(renames.map(([declared, actual]) => [actual, declared])) };
|
|
783
|
+
const merged = { ...parentMcp?.serverRenames ?? {}, ...Object.fromEntries(renames.filter(([declared]) => !servers.reservedRenames.has(declared)).map(([declared, actual]) => [actual, declared])) };
|
|
779
784
|
return Object.keys(merged).length > 0 ? { mcpServerRenames: merged } : {};
|
|
780
785
|
})(),
|
|
781
786
|
...parentMcp?.visibleServerNames !== undefined ? { inheritedMcpServerNames: ownServerNames.size === 0 ? parentMcp.visibleServerNames : () => parentMcp.visibleServerNames().filter((name) => !ownServerNames.has(name)) } : {},
|
|
@@ -886,7 +891,12 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
886
891
|
const complementDeny = allToolNames.filter((name) => !allowSet.has(name));
|
|
887
892
|
const disallowedTools = [...new Set([...complementDeny, ...req.definition?.disallowedTools ?? []])];
|
|
888
893
|
const allowedAgentTypes = allowedAgentTypesFromTools(req.definition?.tools);
|
|
889
|
-
const capabilities = [
|
|
894
|
+
const capabilities = [
|
|
895
|
+
...new Set(effectiveTools.flatMap((name) => {
|
|
896
|
+
const descriptor = getRegisteredTool(name)?.descriptor;
|
|
897
|
+
return [...descriptor?.capabilityRequirements ?? [], ...descriptor?.availability.requiresAnyCapability ?? []];
|
|
898
|
+
}))
|
|
899
|
+
];
|
|
890
900
|
const definitionWarnings = req.definition !== undefined ? validateAgentDefinition(req.definition) : [];
|
|
891
901
|
const parentMcp = runCtx.getParentMcpState?.();
|
|
892
902
|
const parentAgents = runCtx.getParentAgents?.();
|
|
@@ -955,6 +965,8 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
955
965
|
...deps.parentBrand !== undefined ? { brand: deps.parentBrand } : {},
|
|
956
966
|
...deps.parentAutoMemory !== undefined ? { autoMemory: deps.parentAutoMemory } : {},
|
|
957
967
|
...deps.parentWeb !== undefined ? { web: deps.parentWeb } : {},
|
|
968
|
+
...deps.parentReservedMcpServerNames !== undefined ? { reservedMcpServerNames: deps.parentReservedMcpServerNames } : {},
|
|
969
|
+
...deps.parentLegacyToolNames !== undefined ? { legacyToolNames: deps.parentLegacyToolNames } : {},
|
|
958
970
|
model: resolvedModel.effectiveModel,
|
|
959
971
|
permissionMode: inherit.policy.effectiveMode,
|
|
960
972
|
allowDangerouslySkipPermissions: inherit.policy.effectiveMode === "bypassPermissions",
|
|
@@ -965,6 +977,7 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
965
977
|
...req.fork === true && inherit.effectiveEffort !== undefined && typeof inherit.effectiveEffort !== "number" ? { effort: inherit.effectiveEffort } : {},
|
|
966
978
|
...req.fork === true && inherit.effectiveThinking !== undefined ? { thinking: inherit.effectiveThinking } : {},
|
|
967
979
|
disallowedTools,
|
|
980
|
+
tools: [...effectiveTools],
|
|
968
981
|
capabilities,
|
|
969
982
|
...allowedAgentTypes !== undefined ? { allowedAgentTypes } : {},
|
|
970
983
|
forwardSubagentText: deps.forwardSubagentText === true,
|
|
@@ -980,7 +993,7 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
980
993
|
const resolvedSystemPrompt = [inherit.systemPrompt, req.fork === true ? undefined : req.definition?.prompt].filter((s) => s !== undefined && s.length > 0).join(`
|
|
981
994
|
|
|
982
995
|
`);
|
|
983
|
-
let serverAllocation = allocateChildScopedServers(childScopedMcpServers, parentVisibleServers(), undefined, parentMcp?.declaredServers);
|
|
996
|
+
let serverAllocation = allocateChildScopedServers(childScopedMcpServers, parentVisibleServers(), undefined, parentMcp?.declaredServers, deps.parentReservedMcpServerNames);
|
|
984
997
|
pendingServerRelease = serverAllocation.release;
|
|
985
998
|
definitionWarnings.push(...serverAllocation.notes);
|
|
986
999
|
const firstTurnText = req.fork === true ? buildForkDirectiveText({ prompt: req.prompt, ...req.isolation === "worktree" ? { worktree: { parentRoot: inherit.sessionRoot, worktreeRoot: workspace.root } } : {} }) : [req.definition?.initialPrompt, req.prompt, definitionWarnings.length > 0 ? `
|
|
@@ -1093,7 +1106,7 @@ async function spawnChildEngine(req, inherit, runCtx, deps) {
|
|
|
1093
1106
|
pendingResume = undefined;
|
|
1094
1107
|
if (ticket.stopped)
|
|
1095
1108
|
return { status: "unavailable", messageId: msg.messageId, retryable: false, reason: `child ${agentId} was stopped while resuming` };
|
|
1096
|
-
serverAllocation = allocateChildScopedServers(childScopedMcpServers, parentVisibleServers(), previous, parentMcp?.declaredServers);
|
|
1109
|
+
serverAllocation = allocateChildScopedServers(childScopedMcpServers, parentVisibleServers(), previous, parentMcp?.declaredServers, deps.parentReservedMcpServerNames);
|
|
1097
1110
|
const moved = [...serverAllocation.actual].filter(([declared, name]) => previous.get(declared) !== name).map(([declared, name]) => `this agent's own "${declared}" is now connected as "${name}" (its tools are named mcp__${name}__<tool>)`);
|
|
1098
1111
|
startGeneration(generationConfig(resumeMode), rebuilt, moved.length > 0 ? `${msg.body}
|
|
1099
1112
|
|
|
@@ -1159,6 +1172,8 @@ function registerDefaultChildEngineFactory(opts) {
|
|
|
1159
1172
|
...config.brand !== undefined ? { parentBrand: config.brand } : {},
|
|
1160
1173
|
...config.autoMemory !== undefined ? { parentAutoMemory: config.autoMemory } : {},
|
|
1161
1174
|
...config.web !== undefined ? { parentWeb: config.web } : {},
|
|
1175
|
+
...config.reservedMcpServerNames !== undefined ? { parentReservedMcpServerNames: config.reservedMcpServerNames } : {},
|
|
1176
|
+
...config.legacyToolNames !== undefined ? { parentLegacyToolNames: config.legacyToolNames } : {},
|
|
1162
1177
|
...opts.systemPromptAssembler !== undefined ? { systemPromptAssembler: opts.systemPromptAssembler } : {},
|
|
1163
1178
|
...opts.skillRuntime !== undefined ? { skillRuntime: opts.skillRuntime } : {},
|
|
1164
1179
|
...opts.structuredOutput !== undefined ? { structuredOutput: opts.structuredOutput } : {},
|
|
@@ -1264,7 +1279,7 @@ function createToolSecretResolver(deps) {
|
|
|
1264
1279
|
}
|
|
1265
1280
|
|
|
1266
1281
|
// src/provider/bridge.ts
|
|
1267
|
-
import { normalizeThrown, shouldRequestSummary, stripOpaque } from "@yanlinglabs/winter-provider-runtime";
|
|
1282
|
+
import { imageBudgetFor, normalizeThrown, shouldRequestSummary, stripOpaque, withinImageBudget } from "@yanlinglabs/winter-provider-runtime";
|
|
1268
1283
|
function createIdentityHistoryRenderer() {
|
|
1269
1284
|
return {
|
|
1270
1285
|
render(messages, _chain, target) {
|
|
@@ -1299,9 +1314,14 @@ function adapterAsProvider(resolved, ctx, opts = {}) {
|
|
|
1299
1314
|
modelKey: resolved.modelKey,
|
|
1300
1315
|
...Array.isArray(resolved.descriptor?.inputModalities?.value) ? { readsImages: resolved.descriptor.inputModalities.value.includes("image") } : {}
|
|
1301
1316
|
};
|
|
1317
|
+
const imageBudget = imageBudgetFor({
|
|
1318
|
+
family: adapter.family,
|
|
1319
|
+
providerId: resolved.providerId,
|
|
1320
|
+
...typeof resolved.descriptor?.contextWindow?.value === "number" ? { contextWindow: resolved.descriptor.contextWindow.value } : {}
|
|
1321
|
+
});
|
|
1302
1322
|
return {
|
|
1303
1323
|
async generate(input) {
|
|
1304
|
-
const rendered = renderer.render(input.messages, opts.chain?.() ?? new Map, target);
|
|
1324
|
+
const rendered = withinImageBudget(renderer.render(input.messages, opts.chain?.() ?? new Map, target), imageBudget);
|
|
1305
1325
|
const request = {
|
|
1306
1326
|
model: input.model === undefined || input.model === resolved.modelKey ? resolved.providerModelId : input.model,
|
|
1307
1327
|
messages: rendered,
|
|
@@ -1452,7 +1472,7 @@ async function foldProviderStream(stream, sink) {
|
|
|
1452
1472
|
emitter.messageStop(event.stopReason);
|
|
1453
1473
|
break;
|
|
1454
1474
|
case "error":
|
|
1455
|
-
throw providerErrorToTurnError(event.error, committed && !(event.error.contextOverflow === true && ordered.length === 0));
|
|
1475
|
+
throw providerErrorToTurnError(event.error, committed && !((event.error.contextOverflow === true || event.error.imageOverflow === true) && ordered.length === 0));
|
|
1456
1476
|
}
|
|
1457
1477
|
}
|
|
1458
1478
|
} catch (err) {
|
|
@@ -1604,7 +1624,8 @@ function providerErrorToTurnError(error, committed = false) {
|
|
|
1604
1624
|
code: error.code,
|
|
1605
1625
|
retryable: error.retryable,
|
|
1606
1626
|
committed,
|
|
1607
|
-
...error.contextOverflow === true ? { contextOverflow: true } : {}
|
|
1627
|
+
...error.contextOverflow === true ? { contextOverflow: true } : {},
|
|
1628
|
+
...error.imageOverflow === true ? { imageOverflow: true } : {}
|
|
1608
1629
|
});
|
|
1609
1630
|
}
|
|
1610
1631
|
function toProviderTurnError(err, committed = false) {
|
|
@@ -1618,7 +1639,14 @@ function toProviderTurnError(err, committed = false) {
|
|
|
1618
1639
|
const message = raw.length > MAX_ERROR_MESSAGE_CHARS ? `${raw.slice(0, MAX_ERROR_MESSAGE_CHARS)}...` : raw;
|
|
1619
1640
|
const normalized = normalizeThrown(err);
|
|
1620
1641
|
const status = typeof err === "object" && err !== null && typeof err.status === "number" ? err.status : normalized.status;
|
|
1621
|
-
return new ProviderTurnError(`provider request failed: ${message}`, {
|
|
1642
|
+
return new ProviderTurnError(`provider request failed: ${message}`, {
|
|
1643
|
+
...status !== undefined ? { status } : {},
|
|
1644
|
+
code: normalized.code,
|
|
1645
|
+
retryable: normalized.retryable,
|
|
1646
|
+
committed,
|
|
1647
|
+
...normalized.contextOverflow === true ? { contextOverflow: true } : {},
|
|
1648
|
+
...normalized.imageOverflow === true ? { imageOverflow: true } : {}
|
|
1649
|
+
});
|
|
1622
1650
|
}
|
|
1623
1651
|
function stampNativeState(turn, origin) {
|
|
1624
1652
|
if (turn.nativeState === undefined)
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
createRpcBridge2,
|
|
3
3
|
registerTool2,
|
|
4
4
|
onCompaction2,
|
|
5
|
+
buildWorkflowWorkerSeatbeltProfile2,
|
|
5
6
|
WinterPathsError2,
|
|
6
7
|
sessionTempDir2,
|
|
7
8
|
ensureTasksDir2,
|
|
8
9
|
resolveProjectDirName2,
|
|
9
|
-
buildWorkflowWorkerSeatbeltProfile2,
|
|
10
10
|
WORKFLOW_SANDBOX_REFUSED_EXIT_CODE2,
|
|
11
11
|
DEFAULT_CONTEXT_WINDOW_TOKENS2,
|
|
12
12
|
createContextAccountant2,
|
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
DEFAULT_CLASSIFIER_TIMEOUT_MS2,
|
|
56
56
|
createModelClassifier2,
|
|
57
57
|
selectClassifierRoute2
|
|
58
|
-
} from "./index-
|
|
58
|
+
} from "./index-5xe59vkt.js";
|
|
59
59
|
import"./index-1hef2gff.js";
|
|
60
60
|
import"./index-mwew595z.js";
|
|
61
61
|
import"./index-2wgfv0pa.js";
|
package/dist/mcp/control.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ import type { McpControlSeam } from "./control-seam.js";
|
|
|
2
2
|
import type { McpLifecycleInternals } from "./lifecycle.js";
|
|
3
3
|
export declare function createMcpControlSeam(internals: McpLifecycleInternals, opts?: {
|
|
4
4
|
reservedServerName?: string;
|
|
5
|
+
hostReservedServerNames?: readonly string[];
|
|
5
6
|
}): McpControlSeam;
|
package/dist/mcp/lifecycle.d.ts
CHANGED
|
@@ -85,7 +85,10 @@ export declare function resolveMcpServerSources(sources: readonly McpServerSourc
|
|
|
85
85
|
strictMcpConfig?: boolean;
|
|
86
86
|
trustedWorkspace: boolean;
|
|
87
87
|
reservedServerName?: string;
|
|
88
|
+
hostReservedServerNames?: readonly string[];
|
|
88
89
|
}): ResolveMcpServerSourcesResult;
|
|
90
|
+
/** The text budget of an MCP result that carries images (≈ the default `MAX_MCP_OUTPUT_TOKENS` of 25000 at 4 characters a token). */
|
|
91
|
+
export declare const MCP_IMAGE_RESULT_TEXT_CHARS = 100000;
|
|
89
92
|
export interface McpDiscoveryCache {
|
|
90
93
|
get(key: string): McpToolInfo[] | undefined;
|
|
91
94
|
set(key: string, tools: McpToolInfo[]): void;
|
|
@@ -109,6 +112,8 @@ export interface McpLifecycleDeps {
|
|
|
109
112
|
* `addAndConnect` guard is the second door onto the same rule. Defaults to `WINTER_SERVER_NAME`.
|
|
110
113
|
*/
|
|
111
114
|
reservedServerName?: string;
|
|
115
|
+
/** `RuntimeConfig.reservedMcpServerNames`, for the control seam's live `mcp_set_servers` guard: a non-sdk server is refused under any of them. */
|
|
116
|
+
hostReservedServerNames?: readonly string[];
|
|
112
117
|
/** WS-23: the session cwd every stdio server of this lifecycle is spawned in (see `ConnectMcpServerOptions.cwd`). */
|
|
113
118
|
cwd?: string;
|
|
114
119
|
/**
|
package/dist/provider/mock.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare const P5_FIXTURE_SKILL_NAME = "p5probe";
|
|
|
27
27
|
* `{"answer":"42","doubled":"4242"}` on every leg.
|
|
28
28
|
*/
|
|
29
29
|
export declare const P5_WORKFLOW_SCRIPT: string;
|
|
30
|
-
export type TestProviderName = "boom" | "tooluse" | "hang" | "reflect" | "modeswitch" | "bgtask" | "lanea" | "laneb" | "lanec" | "laned" | "lanee" | "mcpsdk" | "subagent" | "childmsg" | "subagentperm" | "toolsearch" | "p5compact" | "p5structured" | "p5structuredfail" | "p5skill" | "p5checkpoint" | "p5workflow";
|
|
30
|
+
export type TestProviderName = "boom" | "tooluse" | "hang" | "reflect" | "modeswitch" | "bgtask" | "lanea" | "laneb" | "lanec" | "laned" | "lanee" | "mcpsdk" | "subagent" | "childmsg" | "subagentperm" | "toolsearch" | "p5compact" | "p5structured" | "p5structuredfail" | "p5skill" | "p5checkpoint" | "p5workflow" | "readimage" | "calls";
|
|
31
31
|
export declare function isTestProviderName(v: string): v is TestProviderName;
|
|
32
32
|
/**
|
|
33
33
|
* The name the reserved `winter-test/<name>` namespace uses for the plain echo double.
|
|
@@ -43,6 +43,43 @@ export declare class SandboxConfigError extends Error {
|
|
|
43
43
|
}
|
|
44
44
|
export declare function resolveNetworkPosture(network: SandboxNetworkSettings | undefined): boolean;
|
|
45
45
|
export declare function canonicalizePath(p: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Fix round 14 (Winter-specific hardening, NOT itself a claude port -- empirically discovered and
|
|
48
|
+
* verified while implementing `buildReadDenyWritePermitBlock` above, disclosed prominently rather
|
|
49
|
+
* than smoothed over): `pR`'s own trailing re-permit is a BLANKET, UNCONDITIONAL
|
|
50
|
+
* `(allow file-write-unlink file-write-create (subpath <every write root>))`, ported faithfully per
|
|
51
|
+
* the controller's own explicit instruction. Real `sandbox-exec` runs proved this is not merely
|
|
52
|
+
* "narrower than a later wildcard deny wins" (round 13's own finding, about an EARLIER explicit deny
|
|
53
|
+
* surviving a LATER broad `file-write*` allow) -- it runs the OTHER direction too: an EARLIER
|
|
54
|
+
* EXPLICIT `file-write-unlink`/`file-write-create` ALLOW is not overridden by a LATER, broader
|
|
55
|
+
* `(deny file-write* ...)` for the SAME target either. Seatbelt appears to give a clause naming
|
|
56
|
+
* `file-write-unlink`/`file-write-create` explicitly priority over one that only reaches those
|
|
57
|
+
* operations via the `file-write*` wildcard, independent of which clause is textually first or last.
|
|
58
|
+
*
|
|
59
|
+
* Every OTHER Winter-owned write-protection floor in this module that used only the `file-write*`
|
|
60
|
+
* wildcard was therefore silently punched through for CREATE and UNLINK/RENAME specifically (never
|
|
61
|
+
* for `file-write-data`, `file-write-mode`, etc., which this re-permit never names) by this ONE new
|
|
62
|
+
* block: the control-plane carve-outs (WS-12 §5.2's own "the seatbelt is the only enforcement point
|
|
63
|
+
* left" floor -- verified empirically: `mkdir -p .winter && echo '{}' > .winter/permissions.local.json`
|
|
64
|
+
* and `rm .winter/settings.json` both SUCCEEDED against the unpatched fix), the checkpoint/backup
|
|
65
|
+
* store write-deny (T8 rider 25's own identical floor), and a GLOB-shaped `denyWrite` entry (a plain
|
|
66
|
+
* `denyWritePaths` entry was already safe -- `Ch`'s own write-side ancestor-rename block, round 12,
|
|
67
|
+
* already emits an explicit `(subpath <path>)` deny for it; `Ch`'s own GLOB branch, by contrast, only
|
|
68
|
+
* ever emits a `(literal <fixedPrefix>)` -- protecting the prefix DIRECTORY's own identity against a
|
|
69
|
+
* rename-shuffle, never the glob-matched files themselves).
|
|
70
|
+
*
|
|
71
|
+
* The fix, verified against real `sandbox-exec` (a `(deny file-write* file-write-unlink
|
|
72
|
+
* file-write-create (regex ...))` clause DOES win back the CREATE it needs to, confirmed by a direct
|
|
73
|
+
* before/after run rather than assumed): every one of those floors now names
|
|
74
|
+
* `file-write-unlink`/`file-write-create` EXPLICITLY, alongside the `file-write*` wildcard it already
|
|
75
|
+
* carried (for the OTHER write operations the wildcard alone still covers correctly) -- this constant
|
|
76
|
+
* is that shared operation-name list, applied wherever `file-write*` ALONE previously appeared on a
|
|
77
|
+
* deny this round's own re-permit could otherwise reach. `fR` (`buildReadDenyKeepInPlaceBlock`) is
|
|
78
|
+
* deliberately NOT touched here: it already names `file-write-unlink` explicitly (never `create`, by
|
|
79
|
+
* claude's own design -- see that function's own header), so it was never in the affected set.
|
|
80
|
+
*/
|
|
81
|
+
/** The runtime's image working directory under the winter/store home (tools/image-prep.ts) -- write-denied to the shell. */
|
|
82
|
+
export declare const IMAGE_PREP_DIRNAME = "image-prep";
|
|
46
83
|
/**
|
|
47
84
|
* P7a fix r1 (Important-1): render ONE brand token as a case-insensitive, regex-escaped SBPL literal.
|
|
48
85
|
*
|
package/dist/store/dialect.d.ts
CHANGED
|
@@ -478,7 +478,7 @@ export declare class TranscriptWriter implements SessionPersistence {
|
|
|
478
478
|
private dialectRecord;
|
|
479
479
|
static readBack(store: SessionStore, key: SessionKey): Promise<SessionStoreEntry[]>;
|
|
480
480
|
}
|
|
481
|
-
export declare const RUNTIME_ENGINE_VERSION = "0.0.
|
|
481
|
+
export declare const RUNTIME_ENGINE_VERSION = "0.0.38";
|
|
482
482
|
export interface ResolvedEngineSession {
|
|
483
483
|
config: RuntimeConfig;
|
|
484
484
|
store: SessionPersistence | undefined;
|
|
@@ -130,6 +130,10 @@ export interface ChildEngineFactoryDeps {
|
|
|
130
130
|
*/
|
|
131
131
|
parentAutoMemory?: RuntimeConfig["autoMemory"];
|
|
132
132
|
parentWeb?: RuntimeConfig["web"];
|
|
133
|
+
/** `RuntimeConfig.reservedMcpServerNames`: an agent's own inline server never takes one (it is connected under a free name instead). */
|
|
134
|
+
parentReservedMcpServerNames?: RuntimeConfig["reservedMcpServerNames"];
|
|
135
|
+
/** `RuntimeConfig.legacyToolNames`, so a child resolves the host's old tool names as its parent does. */
|
|
136
|
+
parentLegacyToolNames?: RuntimeConfig["legacyToolNames"];
|
|
133
137
|
/**
|
|
134
138
|
* The session's pricing, stated-model and tool-secret seams, so a CHILD's engine has what its
|
|
135
139
|
* parent's has. `priceUsage` is what makes a child's generations priced at all; the cost then
|
|
@@ -189,6 +193,27 @@ export interface ChildEngineFactoryDeps {
|
|
|
189
193
|
*/
|
|
190
194
|
compactionControllerFactory?: () => CompactionController;
|
|
191
195
|
}
|
|
196
|
+
type ChildMcpServers = NonNullable<RuntimeConfig["mcpServers"]>;
|
|
197
|
+
interface ChildServerAllocation {
|
|
198
|
+
/** The servers this generation connects itself, under the names it holds. */
|
|
199
|
+
servers: ChildMcpServers;
|
|
200
|
+
/** Declared name -> the name it is connected under (only for servers this generation connects). */
|
|
201
|
+
actual: Map<string, string>;
|
|
202
|
+
/** One legible line per server not connected under its declared name. */
|
|
203
|
+
notes: string[];
|
|
204
|
+
/**
|
|
205
|
+
* Declared names renamed because the HOST reserves them (`RuntimeConfig.reservedMcpServerNames`). Such a
|
|
206
|
+
* server is NOT "the server the definition named" for permission purposes: its declared spelling is the
|
|
207
|
+
* host's own server's, so it never becomes an extra identity (`mcpServerRenames`) — a saved rule on the
|
|
208
|
+
* host's `mcp__<reserved>__<tool>` must not reach it. Only its connected name governs it.
|
|
209
|
+
*/
|
|
210
|
+
reservedRenames: Set<string>;
|
|
211
|
+
/** Releases every name this allocation claimed. Idempotent. */
|
|
212
|
+
release: () => void;
|
|
213
|
+
}
|
|
214
|
+
export declare function allocateChildScopedServers(declared: ChildMcpServers, parentVisible: ReadonlySet<string>, prefer?: ReadonlyMap<string, string>, parentDeclared?: Readonly<Record<string, {
|
|
215
|
+
type?: string;
|
|
216
|
+
}>>, reserved?: readonly string[]): ChildServerAllocation;
|
|
192
217
|
export declare function createChildEngineFactory(deps: ChildEngineFactoryDeps): ChildEngineFactory;
|
|
193
218
|
/**
|
|
194
219
|
* What a child reports when the SPENDING CEILING ended it (whole-branch review MINOR 9). One constant,
|
|
@@ -196,3 +221,4 @@ export declare function createChildEngineFactory(deps: ChildEngineFactoryDeps):
|
|
|
196
221
|
* not be told a retry is worth trying: the ceiling is the session's, so a retry hits it again.
|
|
197
222
|
*/
|
|
198
223
|
export declare const CHILD_BUDGET_STOP_TEXT = "The agent stopped because the session reached its spending limit (maxBudgetUsd), so no further model calls could be made. Retrying will not help -- continue with what it produced, or ask the user to raise the session's budget.";
|
|
224
|
+
export {};
|
package/dist/testing.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
runEngine2,
|
|
4
4
|
echoProvider2,
|
|
5
5
|
resolveEngineSession2
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-5xe59vkt.js";
|
|
7
7
|
import"./index-1hef2gff.js";
|
|
8
8
|
import"./index-mwew595z.js";
|
|
9
9
|
import"./index-2wgfv0pa.js";
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
buildProductionWiring,
|
|
15
15
|
withAutoSkillPermissions,
|
|
16
16
|
restoreChildRoster
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-z3970wsj.js";
|
|
18
18
|
import {
|
|
19
19
|
Queue
|
|
20
20
|
} from "./index-97t2rmtf.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const SEARCH_CANONICAL_NAME = "Search";
|
|
2
|
+
export declare const SEARCH_ANSWER_CAPABILITY = "winter.search-answer";
|
|
3
|
+
/** The Winter daemon's own description, verbatim (it is the interface the models were shown). */
|
|
4
|
+
export declare const SEARCH_DESCRIPTION = "Search the web and get back a written answer with its sources, in a single call. Ask a real question, not keywords \u2014 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.";
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { ToolResultBlock } from "./registry.js";
|
|
2
|
+
/**
|
|
3
|
+
* The exact refusal a model with no image input gets (the host's composer shows the same words). Exported
|
|
4
|
+
* so a host or a test matches it rather than retyping it.
|
|
5
|
+
*/
|
|
6
|
+
export declare const MODEL_DOES_NOT_SUPPORT_IMAGES = "The selected model doesn't support images";
|
|
7
|
+
/** The image types every provider adapter can carry (Anthropic, Bedrock, Gemini and OpenAI all accept exactly these four). */
|
|
8
|
+
export declare const DELIVERABLE_IMAGE_TYPES: ReadonlySet<string>;
|
|
9
|
+
/**
|
|
10
|
+
* The largest image handed to a model, in raw bytes: 3.75 MiB, whose base64 is exactly 5 MiB.
|
|
11
|
+
*
|
|
12
|
+
* The strictest per-image limit among the providers Winter drives: "5 MB (base64-encoded) on Amazon
|
|
13
|
+
* Bedrock and Google Cloud" for Claude (https://platform.claude.com/docs/en/build-with-claude/vision,
|
|
14
|
+
* "Request limits"; the Claude API itself takes 10 MB). An image over a provider's limit stays in the
|
|
15
|
+
* conversation and fails EVERY later request, so the limit is applied here, before the image enters
|
|
16
|
+
* history, not in an adapter. A SOURCE file may be larger (up to `IMAGE_MAX_INPUT_BYTES`): it is shrunk
|
|
17
|
+
* and, if it must be, re-encoded as JPEG to fit (`prepareImageForModel`).
|
|
18
|
+
*/
|
|
19
|
+
export declare const READ_IMAGE_MAX_BYTES = 3932160;
|
|
20
|
+
/** The largest width or height any provider accepts: "The maximum dimensions per image are 8000x8000 px" (same page). */
|
|
21
|
+
export declare const READ_IMAGE_MAX_DIMENSION = 8000;
|
|
22
|
+
/** What the bytes actually are, from their magic numbers -- never trusting the extension. `undefined` when none matches. */
|
|
23
|
+
export declare function sniffImageType(buf: Uint8Array): string | undefined;
|
|
24
|
+
export declare function parsePngDimensions(buf: Uint8Array): {
|
|
25
|
+
width: number;
|
|
26
|
+
height: number;
|
|
27
|
+
} | undefined;
|
|
28
|
+
export declare function parseGifDimensions(buf: Uint8Array): {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
} | undefined;
|
|
32
|
+
export declare function parseBmpDimensions(buf: Uint8Array): {
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
} | undefined;
|
|
36
|
+
export declare function parseJpegDimensions(buf: Uint8Array): {
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
} | undefined;
|
|
40
|
+
/** WebP's canvas size from its VP8 (lossy), VP8L (lossless) or VP8X (extended) header. */
|
|
41
|
+
export declare function parseWebpDimensions(buf: Uint8Array): {
|
|
42
|
+
width: number;
|
|
43
|
+
height: number;
|
|
44
|
+
} | undefined;
|
|
45
|
+
/** A TIFF's first image size, from its first IFD's ImageWidth (256) / ImageLength (257) tags (SHORT or LONG). */
|
|
46
|
+
export declare function parseTiffDimensions(buf: Uint8Array): {
|
|
47
|
+
width: number;
|
|
48
|
+
height: number;
|
|
49
|
+
} | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* A HEIC's size from its `ispe` (image spatial extents) boxes: the LARGEST one, since a grid image carries
|
|
52
|
+
* a small `ispe` per tile and one for the whole picture. A scan for well-formed `ispe` boxes (size 20,
|
|
53
|
+
* version/flags 0), not a full box walk; `sips -g`'s answer is taken too and the larger wins.
|
|
54
|
+
*/
|
|
55
|
+
export declare function parseHeicDimensions(buf: Uint8Array): {
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
} | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* A GIF's size for the PIXEL CHECK: the larger of its logical screen and every frame's own descriptor --
|
|
61
|
+
* a tiny logical screen can front a frame that declares 60000x60000 px, and the decoder allocates for the
|
|
62
|
+
* frame.
|
|
63
|
+
*/
|
|
64
|
+
export declare function parseGifExtent(buf: Uint8Array): {
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
} | undefined;
|
|
68
|
+
export declare function parseImageDimensions(mediaType: string, bytes: Uint8Array): {
|
|
69
|
+
width: number;
|
|
70
|
+
height: number;
|
|
71
|
+
} | undefined;
|
|
72
|
+
/**
|
|
73
|
+
* The long edge an image is shrunk to before a model sees it: 1568 px, the size above which Claude's
|
|
74
|
+
* standard tier downsizes an image anyway ("Resolution and token cost",
|
|
75
|
+
* https://platform.claude.com/docs/en/build-with-claude/vision). Sending more only costs request bytes:
|
|
76
|
+
* the per-request limits (32 MB on the Claude API, 20 MB of inline data on Gemini) fill up after a few
|
|
77
|
+
* full-size screenshots, and past 20 images Anthropic refuses any image over 2000 px. At 1568 none of
|
|
78
|
+
* that is reached by an ordinary session.
|
|
79
|
+
*/
|
|
80
|
+
export declare const IMAGE_MAX_LONG_EDGE = 1568;
|
|
81
|
+
/** The largest file Read or an MCP result may hand the resizer at all (a guard against absurd inputs, not a provider limit). */
|
|
82
|
+
export declare const IMAGE_MAX_INPUT_BYTES: number;
|
|
83
|
+
/**
|
|
84
|
+
* The most pixels an image may DECLARE before anything decodes it: 100 megapixels. A few-kilobyte PNG can
|
|
85
|
+
* declare 40000x40000 px, and decoding that (sips does, to resize it) takes gigabytes; a declared size is
|
|
86
|
+
* all a header costs. 100 MP is above any camera or screenshot in ordinary use (a 12K frame is ~80 MP).
|
|
87
|
+
*/
|
|
88
|
+
export declare const IMAGE_MAX_PIXELS = 100000000;
|
|
89
|
+
/** An image ready for a model: the bytes to send, and what happened to them on the way. */
|
|
90
|
+
export interface PreparedImage {
|
|
91
|
+
ok: true;
|
|
92
|
+
mediaType: string;
|
|
93
|
+
bytes: Buffer;
|
|
94
|
+
width?: number;
|
|
95
|
+
height?: number;
|
|
96
|
+
/** The source as found. */
|
|
97
|
+
original: {
|
|
98
|
+
mediaType: string;
|
|
99
|
+
bytes: number;
|
|
100
|
+
width?: number;
|
|
101
|
+
height?: number;
|
|
102
|
+
};
|
|
103
|
+
/** True when the pixels were scaled down. */
|
|
104
|
+
resized: boolean;
|
|
105
|
+
/** Set when the format changed (a conversion, or a JPEG re-encode to fit the byte limit): the quality used for a JPEG. */
|
|
106
|
+
jpegQuality?: number;
|
|
107
|
+
}
|
|
108
|
+
export type ImagePreparation = PreparedImage | {
|
|
109
|
+
ok: false;
|
|
110
|
+
reason: string;
|
|
111
|
+
};
|
|
112
|
+
export interface PrepareImageOptions {
|
|
113
|
+
/**
|
|
114
|
+
* The runtime-owned directory the per-call working directory is made in: `<winter or store home>/
|
|
115
|
+
* image-prep` (`imagePrepWorkRoot`), which the Bash sandbox's seatbelt profile write-denies
|
|
116
|
+
* (sandbox/profile.ts, `IMAGE_PREP_DIRNAME`) -- NEVER the session temp dir, which a sandboxed shell can
|
|
117
|
+
* write (a link planted there would make `sips` write through it). A getter, read only when `sips`
|
|
118
|
+
* actually runs. `undefined`: no private place to work, so nothing is converted or resized.
|
|
119
|
+
*/
|
|
120
|
+
workRoot: () => string | undefined;
|
|
121
|
+
/** Test seam: the `sips` binary (a missing path simulates a platform without it). Default `/usr/bin/sips`. */
|
|
122
|
+
sipsPath?: string;
|
|
123
|
+
/** Test seam: the byte limit. Default `READ_IMAGE_MAX_BYTES`. */
|
|
124
|
+
maxBytes?: number;
|
|
125
|
+
/** The turn's abort: an interrupt kills a running `sips`. */
|
|
126
|
+
signal?: AbortSignal;
|
|
127
|
+
}
|
|
128
|
+
/** A short, model-facing description of what happened, e.g. `image/png, 25856 bytes, 1568x1018, resized from 3024x1964`. */
|
|
129
|
+
export declare function describePreparedImage(image: PreparedImage): string;
|
|
130
|
+
/**
|
|
131
|
+
* Reads `path` only if it is a regular file, and never through a link: `O_NOFOLLOW` refuses a symlink at
|
|
132
|
+
* open time, and the type is checked on the OPENED descriptor, so nothing can be swapped in between the
|
|
133
|
+
* check and the read. `undefined` for anything else (a link, a directory, a missing file).
|
|
134
|
+
*/
|
|
135
|
+
export declare function readRegularFileNoFollow(path: string): Buffer | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Where image work happens for a session: `<store home or winter home>/image-prep` -- the two anchors the
|
|
138
|
+
* seatbelt profile write-denies `image-prep` under, both handed to a session EXPLICITLY by its host. With
|
|
139
|
+
* neither, `undefined`: nothing is resized or converted (an image already within every limit is still
|
|
140
|
+
* sent as it is). There is deliberately NO fallback, and here is why each candidate is unsafe:
|
|
141
|
+
* - `~/<homeDirName>`: a guess at the user's real winter home, which a process given no home must not
|
|
142
|
+
* write into (and which a test would reach);
|
|
143
|
+
* - the session temp dir, the cwd, the outputs dir and any `allowWrite` entry: sandbox-WRITABLE roots,
|
|
144
|
+
* where a sandboxed command could plant a link;
|
|
145
|
+
* - `$TMPDIR` (the per-user temp dir): its DIRECT children are sandbox-writable (the profile's mktemp
|
|
146
|
+
* allowance), so a folder made there could be renamed away and replaced by a link mid-run.
|
|
147
|
+
*/
|
|
148
|
+
export declare function imagePrepWorkRoot(ctx: {
|
|
149
|
+
storeHome?: string;
|
|
150
|
+
winterHome?: string;
|
|
151
|
+
}): string | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* How many frames a GIF holds (image descriptors), walking its block structure; `undefined` when the
|
|
154
|
+
* structure cannot be walked. OpenAI takes only a "non-animated GIF"
|
|
155
|
+
* (https://developers.openai.com/api/docs/guides/images-vision) and Claude reads only the first frame.
|
|
156
|
+
*/
|
|
157
|
+
export declare function gifFrameCount(buf: Uint8Array): number | undefined;
|
|
158
|
+
/** Every frame's own size (its image descriptor), in order; `undefined` when the structure cannot be walked to its end. */
|
|
159
|
+
export declare function gifFrameSizes(buf: Uint8Array): Array<{
|
|
160
|
+
width: number;
|
|
161
|
+
height: number;
|
|
162
|
+
}> | undefined;
|
|
163
|
+
/**
|
|
164
|
+
* Makes `source` ready for a model, or says why it cannot be.
|
|
165
|
+
*
|
|
166
|
+
* - The type is SNIFFED from the bytes; something that is none of PNG/JPEG/GIF/WebP/BMP/TIFF/HEIC is refused.
|
|
167
|
+
* - An image whose long edge is over `IMAGE_MAX_LONG_EDGE` is scaled down to it (aspect kept).
|
|
168
|
+
* - The format is kept where a provider takes it: PNG stays PNG and JPEG stays JPEG (quality 85 when
|
|
169
|
+
* re-encoded). `sips` cannot WRITE WebP, so a WebP that has to be rewritten becomes PNG. EVERY GIF
|
|
170
|
+
* becomes PNG (its first frame): Gemini reads no GIF, OpenAI no animated one, Claude only the first
|
|
171
|
+
* frame. BMP and TIFF, which no provider takes, become PNG; HEIC (a camera photo) becomes JPEG. A WebP
|
|
172
|
+
* that needs no rewrite is sent as it is.
|
|
173
|
+
* - An image DECLARING more than `IMAGE_MAX_PIXELS` is refused before anything decodes it.
|
|
174
|
+
* - Still over `READ_IMAGE_MAX_BYTES`? Re-encoded as JPEG at quality 85, 70, 55, then 40; still over,
|
|
175
|
+
* refused. Never truncated.
|
|
176
|
+
* - Without `sips`, or when it fails: the image as it is if it is already a deliverable type within the
|
|
177
|
+
* byte limit and `READ_IMAGE_MAX_DIMENSION` (and, for a GIF, a single frame), else a refusal naming the
|
|
178
|
+
* reason.
|
|
179
|
+
* - `sips` works in a FRESH private directory per call (`mkdtemp`) under `imagePrepWorkRoot` -- a tree
|
|
180
|
+
* the Bash sandbox is denied writing to, so no link can be planted where `sips` writes -- its input is
|
|
181
|
+
* written with `wx`, and its output is read through `readRegularFileNoFollow` (no link, no swap).
|
|
182
|
+
*/
|
|
183
|
+
export declare function prepareImageForModel(source: Buffer, opts: PrepareImageOptions): Promise<ImagePreparation>;
|
|
184
|
+
/** One part of a result before it is made model-ready: text, or an image's raw bytes. */
|
|
185
|
+
export type RawResultPart = {
|
|
186
|
+
type: "text";
|
|
187
|
+
text: string;
|
|
188
|
+
} | {
|
|
189
|
+
type: "image";
|
|
190
|
+
bytes: Buffer;
|
|
191
|
+
};
|
|
192
|
+
export interface ModelResult {
|
|
193
|
+
/** The model-facing blocks, in order (adjacent text merged). */
|
|
194
|
+
blocks: ToolResultBlock[];
|
|
195
|
+
/** A text rendering of the same result (each image as `[image: …]`) -- for hooks, logs and a text-only reader. */
|
|
196
|
+
text: string;
|
|
197
|
+
/** True when at least one image block survived. */
|
|
198
|
+
hasImage: boolean;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Makes a mixed text/image result model-ready: every image goes through `prepareImageForModel`, and an
|
|
202
|
+
* image that cannot be shown -- a text-only model (`readsImages: false`), a refusal, or images past the
|
|
203
|
+
* result's total of `maxBytes` -- becomes a one-line `[image omitted: …]` note IN ITS PLACE, never
|
|
204
|
+
* dropped and never base64 text.
|
|
205
|
+
*/
|
|
206
|
+
export declare function resultBlocksForModel(parts: readonly RawResultPart[], opts: PrepareImageOptions & {
|
|
207
|
+
readsImages: boolean;
|
|
208
|
+
}): Promise<ModelResult>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A real, decodable 8-bit RGB PNG of `width` x `height`. `noise: true` fills it with random pixels (it
|
|
3
|
+
* does not compress, so it is large); otherwise a repeating gradient (tiny once compressed).
|
|
4
|
+
*/
|
|
5
|
+
export declare function realPng(width: number, height: number, opts?: {
|
|
6
|
+
noise?: boolean;
|
|
7
|
+
}): Buffer;
|
|
@@ -31,6 +31,8 @@ export interface ExaSearchHit {
|
|
|
31
31
|
/** The result's highlight text, capped at `EXA_HIGHLIGHTS_MAX_CHARACTERS`. Empty when the backend sent none. */
|
|
32
32
|
highlight: string;
|
|
33
33
|
publishedDate?: string;
|
|
34
|
+
/** The result's `favicon` when the backend sent one (Exa's /search reference: "The URL of the favicon for the search result's domain"), https only. Never model-visible: `WebSearch` hands it to the HOST as `ToolResultPayload.siteIcons`. Absent from the basic text form and, as measured 2026-09-18, from the advanced JSON form too -- read when present. */
|
|
35
|
+
favicon?: string;
|
|
34
36
|
}
|
|
35
37
|
export interface ExaSearchParams {
|
|
36
38
|
query: string;
|