@dogpile/sdk 0.3.0 → 0.3.1
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/CHANGELOG.md +9 -0
- package/dist/browser/index.js +784 -562
- package/dist/browser/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/runtime/broadcast.d.ts.map +1 -1
- package/dist/runtime/broadcast.js +1 -13
- package/dist/runtime/broadcast.js.map +1 -1
- package/dist/runtime/coordinator.d.ts.map +1 -1
- package/dist/runtime/coordinator.js +1 -13
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/ids.d.ts +19 -0
- package/dist/runtime/ids.d.ts.map +1 -0
- package/dist/runtime/ids.js +36 -0
- package/dist/runtime/ids.js.map +1 -0
- package/dist/runtime/logger.d.ts +61 -0
- package/dist/runtime/logger.d.ts.map +1 -0
- package/dist/runtime/logger.js +114 -0
- package/dist/runtime/logger.js.map +1 -0
- package/dist/runtime/retry.d.ts +99 -0
- package/dist/runtime/retry.d.ts.map +1 -0
- package/dist/runtime/retry.js +181 -0
- package/dist/runtime/retry.js.map +1 -0
- package/dist/runtime/sequential.d.ts.map +1 -1
- package/dist/runtime/sequential.js +1 -10
- package/dist/runtime/sequential.js.map +1 -1
- package/dist/runtime/shared.d.ts.map +1 -1
- package/dist/runtime/shared.js +1 -13
- package/dist/runtime/shared.js.map +1 -1
- package/dist/runtime/tools/built-in.d.ts +99 -0
- package/dist/runtime/tools/built-in.d.ts.map +1 -0
- package/dist/runtime/tools/built-in.js +577 -0
- package/dist/runtime/tools/built-in.js.map +1 -0
- package/dist/runtime/tools/vercel-ai.d.ts +67 -0
- package/dist/runtime/tools/vercel-ai.d.ts.map +1 -0
- package/dist/runtime/tools/vercel-ai.js +148 -0
- package/dist/runtime/tools/vercel-ai.js.map +1 -0
- package/dist/runtime/tools.d.ts +5 -268
- package/dist/runtime/tools.d.ts.map +1 -1
- package/dist/runtime/tools.js +7 -770
- package/dist/runtime/tools.js.map +1 -1
- package/dist/types/benchmark.d.ts +276 -0
- package/dist/types/benchmark.d.ts.map +1 -0
- package/dist/types/benchmark.js +2 -0
- package/dist/types/benchmark.js.map +1 -0
- package/dist/types/events.d.ts +495 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +2 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/replay.d.ts +169 -0
- package/dist/types/replay.d.ts.map +1 -0
- package/dist/types/replay.js +2 -0
- package/dist/types/replay.js.map +1 -0
- package/dist/types.d.ts +6 -935
- package/dist/types.d.ts.map +1 -1
- package/package.json +27 -1
- package/src/index.ts +4 -0
- package/src/runtime/broadcast.ts +1 -16
- package/src/runtime/coordinator.ts +1 -16
- package/src/runtime/ids.ts +41 -0
- package/src/runtime/logger.ts +152 -0
- package/src/runtime/retry.ts +270 -0
- package/src/runtime/sequential.ts +1 -12
- package/src/runtime/shared.ts +1 -16
- package/src/runtime/tools/built-in.ts +875 -0
- package/src/runtime/tools/vercel-ai.ts +269 -0
- package/src/runtime/tools.ts +60 -1255
- package/src/types/benchmark.ts +300 -0
- package/src/types/events.ts +544 -0
- package/src/types/replay.ts +201 -0
- package/src/types.ts +104 -994
package/dist/browser/index.js
CHANGED
|
@@ -67,6 +67,39 @@ function isRecord$2(value) {
|
|
|
67
67
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
68
68
|
}
|
|
69
69
|
//#endregion
|
|
70
|
+
//#region src/runtime/ids.ts
|
|
71
|
+
/**
|
|
72
|
+
* Repo-internal id and timing helpers used across all four protocols.
|
|
73
|
+
*
|
|
74
|
+
* Centralized here so a change to id format or fallback semantics happens in
|
|
75
|
+
* exactly one place — switching `protocol` must not change the run-id contract.
|
|
76
|
+
*/
|
|
77
|
+
/**
|
|
78
|
+
* Generates a fresh run id using `globalThis.crypto.randomUUID`.
|
|
79
|
+
*
|
|
80
|
+
* Throws a `DogpileError` when no UUID source is available rather than falling
|
|
81
|
+
* back to a millisecond-based id (which collides under back-to-back runs in
|
|
82
|
+
* the same tick). Node 22+, Bun latest, and modern browsers all expose
|
|
83
|
+
* `crypto.randomUUID`; environments without it are unsupported by Dogpile.
|
|
84
|
+
*/
|
|
85
|
+
function createRunId() {
|
|
86
|
+
const random = globalThis.crypto?.randomUUID?.();
|
|
87
|
+
if (typeof random === "string" && random.length > 0) return random;
|
|
88
|
+
throw new DogpileError({
|
|
89
|
+
code: "invalid-configuration",
|
|
90
|
+
message: "Dogpile requires globalThis.crypto.randomUUID to mint a run id. Run on Node 22+, Bun latest, or a modern browser ESM environment."
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function nowMs() {
|
|
94
|
+
return globalThis.performance?.now() ?? Date.now();
|
|
95
|
+
}
|
|
96
|
+
function elapsedMs(startedAtMs) {
|
|
97
|
+
return Math.max(0, nowMs() - startedAtMs);
|
|
98
|
+
}
|
|
99
|
+
function providerCallIdFor(runId, oneBasedIndex) {
|
|
100
|
+
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
70
103
|
//#region src/runtime/defaults.ts
|
|
71
104
|
function normalizeProtocol(protocol) {
|
|
72
105
|
if (typeof protocol !== "string") return protocol;
|
|
@@ -1402,7 +1435,7 @@ function describeValue(value) {
|
|
|
1402
1435
|
return typeof value;
|
|
1403
1436
|
}
|
|
1404
1437
|
//#endregion
|
|
1405
|
-
//#region src/runtime/tools.ts
|
|
1438
|
+
//#region src/runtime/tools/built-in.ts
|
|
1406
1439
|
var webSearchIdentity = {
|
|
1407
1440
|
id: "dogpile.tools.webSearch",
|
|
1408
1441
|
namespace: "dogpile",
|
|
@@ -1488,9 +1521,6 @@ function builtInDogpileToolIdentity(name) {
|
|
|
1488
1521
|
function builtInDogpileToolInputSchema(name) {
|
|
1489
1522
|
return name === "webSearch" ? webSearchInputSchema : codeExecInputSchema;
|
|
1490
1523
|
}
|
|
1491
|
-
/**
|
|
1492
|
-
* Return the default permission declarations for one built-in tool name.
|
|
1493
|
-
*/
|
|
1494
1524
|
function builtInDogpileToolPermissions(name) {
|
|
1495
1525
|
return name === "webSearch" ? webSearchPermissions : codeExecPermissions;
|
|
1496
1526
|
}
|
|
@@ -1501,130 +1531,168 @@ function validateBuiltInDogpileToolInput(name, input) {
|
|
|
1501
1531
|
issues
|
|
1502
1532
|
};
|
|
1503
1533
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
validateRuntimeToolRegistrations(options.tools);
|
|
1516
|
-
const tools = Array.from(options.tools);
|
|
1517
|
-
let callCount = 0;
|
|
1518
|
-
return {
|
|
1519
|
-
tools,
|
|
1520
|
-
async execute(request) {
|
|
1521
|
-
const tool = tools.find((candidate) => candidate.identity.id === request.toolId);
|
|
1522
|
-
const identity = tool?.identity ?? {
|
|
1523
|
-
id: request.toolId,
|
|
1524
|
-
name: request.toolId
|
|
1525
|
-
};
|
|
1526
|
-
const callIndex = callCount;
|
|
1527
|
-
callCount += 1;
|
|
1528
|
-
const toolCallId = request.toolCallId ?? options.makeToolCallId?.(identity, callIndex) ?? defaultToolCallId(options.runId, callIndex);
|
|
1529
|
-
const context = createExecutionContext(options, request, toolCallId);
|
|
1530
|
-
options.emit?.({
|
|
1531
|
-
type: "tool-call",
|
|
1532
|
-
runId: options.runId,
|
|
1533
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1534
|
-
toolCallId,
|
|
1534
|
+
function createWebSearchToolAdapter(options) {
|
|
1535
|
+
const identity = mergeIdentity(webSearchIdentity, options.identity);
|
|
1536
|
+
return normalizeBuiltInDogpileTool({
|
|
1537
|
+
name: "webSearch",
|
|
1538
|
+
...options.identity ? { identity: options.identity } : {},
|
|
1539
|
+
...options.permissions ? { permissions: options.permissions } : {},
|
|
1540
|
+
async execute(input, context) {
|
|
1541
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
1542
|
+
if (!fetchImplementation) return {
|
|
1543
|
+
type: "error",
|
|
1544
|
+
toolCallId: context.toolCallId,
|
|
1535
1545
|
tool: identity,
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1546
|
+
error: {
|
|
1547
|
+
code: "unavailable",
|
|
1548
|
+
message: "No fetch implementation is available for webSearch.",
|
|
1549
|
+
retryable: false
|
|
1550
|
+
}
|
|
1551
|
+
};
|
|
1552
|
+
const request = options.buildRequest ? options.buildRequest(input, context) : defaultWebSearchRequest(options, input, context);
|
|
1553
|
+
const response = await fetchImplementation(request.url, {
|
|
1554
|
+
...request.init,
|
|
1555
|
+
...context.abortSignal ? { signal: context.abortSignal } : {}
|
|
1539
1556
|
});
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1557
|
+
if (!response.ok) throw {
|
|
1558
|
+
code: response.status >= 500 ? "unavailable" : "backend-error",
|
|
1559
|
+
message: `Web search backend returned HTTP ${response.status}.`,
|
|
1560
|
+
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
1561
|
+
detail: {
|
|
1562
|
+
status: response.status,
|
|
1563
|
+
statusText: response.statusText
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
const output = options.parseResponse ? await options.parseResponse(response, input, context) : await defaultWebSearchResponseParser(response);
|
|
1567
|
+
return {
|
|
1568
|
+
type: "success",
|
|
1569
|
+
toolCallId: context.toolCallId,
|
|
1546
1570
|
tool: identity,
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1571
|
+
output
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
function createCodeExecToolAdapter(options) {
|
|
1577
|
+
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
1578
|
+
const permissions = options.permissions ?? codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
1579
|
+
return {
|
|
1580
|
+
identity,
|
|
1581
|
+
inputSchema: codeExecInputSchemaFor(options.languages ?? codeExecLanguages),
|
|
1582
|
+
permissions,
|
|
1583
|
+
validateInput: (input) => validateCodeExecAdapterInput(input, options),
|
|
1584
|
+
async execute(input, context) {
|
|
1585
|
+
const validation = validateCodeExecAdapterInput(input, options);
|
|
1586
|
+
if (validation.type === "invalid") return {
|
|
1587
|
+
type: "error",
|
|
1588
|
+
toolCallId: context.toolCallId,
|
|
1589
|
+
tool: identity,
|
|
1590
|
+
error: {
|
|
1591
|
+
code: "invalid-input",
|
|
1592
|
+
message: "Invalid codeExec tool input.",
|
|
1593
|
+
retryable: false,
|
|
1594
|
+
detail: { issues: serializeValidationIssues(validation.issues) }
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
const timeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1598
|
+
const executionInput = timeoutMs === void 0 ? input : {
|
|
1599
|
+
...input,
|
|
1600
|
+
timeoutMs
|
|
1601
|
+
};
|
|
1602
|
+
try {
|
|
1603
|
+
const output = await executeSandboxWithPolicy(options.execute, executionInput, context, timeoutMs);
|
|
1604
|
+
return {
|
|
1605
|
+
type: "success",
|
|
1606
|
+
toolCallId: context.toolCallId,
|
|
1607
|
+
tool: identity,
|
|
1608
|
+
output
|
|
1609
|
+
};
|
|
1610
|
+
} catch (error) {
|
|
1611
|
+
return {
|
|
1612
|
+
type: "error",
|
|
1613
|
+
toolCallId: context.toolCallId,
|
|
1614
|
+
tool: identity,
|
|
1615
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1552
1618
|
}
|
|
1553
1619
|
};
|
|
1554
1620
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1621
|
+
function normalizeBuiltInDogpileTool(definition) {
|
|
1622
|
+
switch (definition.name) {
|
|
1623
|
+
case "webSearch": {
|
|
1624
|
+
const identity = mergeIdentity(webSearchIdentity, definition.identity);
|
|
1625
|
+
const permissions = definition.permissions ?? webSearchPermissions;
|
|
1626
|
+
return {
|
|
1627
|
+
identity,
|
|
1628
|
+
inputSchema: definition.inputSchema ?? webSearchInputSchema,
|
|
1629
|
+
permissions,
|
|
1630
|
+
validateInput: (input) => validateBuiltInDogpileToolInput("webSearch", input),
|
|
1631
|
+
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "webSearch")
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
case "codeExec": {
|
|
1635
|
+
const identity = mergeIdentity(codeExecIdentity, definition.identity);
|
|
1636
|
+
const permissions = definition.permissions ?? codeExecPermissions;
|
|
1637
|
+
return {
|
|
1638
|
+
identity,
|
|
1639
|
+
inputSchema: definition.inputSchema ?? codeExecInputSchema,
|
|
1640
|
+
permissions,
|
|
1641
|
+
validateInput: (input) => validateBuiltInDogpileToolInput("codeExec", input),
|
|
1642
|
+
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "codeExec")
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
function normalizeBuiltInDogpileTools(tools) {
|
|
1648
|
+
const normalized = [];
|
|
1649
|
+
if (tools.webSearch) normalized.push(normalizeBuiltInDogpileTool(asWebSearchDefinition(tools.webSearch)));
|
|
1650
|
+
if (tools.codeExec) normalized.push(normalizeBuiltInDogpileTool(asCodeExecDefinition(tools.codeExec)));
|
|
1651
|
+
return normalized;
|
|
1652
|
+
}
|
|
1653
|
+
async function executeBuiltInTool(identity, execute, input, context, name) {
|
|
1654
|
+
const validation = validateBuiltInDogpileToolInput(name, input);
|
|
1655
|
+
if (validation.type === "invalid") return {
|
|
1656
|
+
type: "error",
|
|
1657
|
+
toolCallId: context.toolCallId,
|
|
1658
|
+
tool: identity,
|
|
1659
|
+
error: {
|
|
1660
|
+
code: "invalid-input",
|
|
1661
|
+
message: `Invalid ${name} tool input.`,
|
|
1662
|
+
retryable: false,
|
|
1663
|
+
detail: { issues: serializeValidationIssues(validation.issues) }
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
try {
|
|
1667
|
+
return await execute(input, context);
|
|
1668
|
+
} catch (error) {
|
|
1565
1669
|
return {
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1670
|
+
type: "error",
|
|
1671
|
+
toolCallId: context.toolCallId,
|
|
1672
|
+
tool: identity,
|
|
1673
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
1569
1674
|
};
|
|
1570
|
-
}
|
|
1675
|
+
}
|
|
1571
1676
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
const manifest = runtimeToolManifest(tools);
|
|
1578
|
-
return manifest.length > 0 ? { tools: manifest } : {};
|
|
1677
|
+
function asWebSearchDefinition(tool) {
|
|
1678
|
+
return typeof tool === "function" ? {
|
|
1679
|
+
name: "webSearch",
|
|
1680
|
+
execute: tool
|
|
1681
|
+
} : tool;
|
|
1579
1682
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
for (const request of options.response.toolRequests ?? []) {
|
|
1586
|
-
const result = await options.executor.execute({
|
|
1587
|
-
...request,
|
|
1588
|
-
agentId: request.agentId ?? options.agentId,
|
|
1589
|
-
role: request.role ?? options.role,
|
|
1590
|
-
turn: request.turn ?? options.turn,
|
|
1591
|
-
metadata: mergeToolMetadata(options.metadata, request.metadata)
|
|
1592
|
-
});
|
|
1593
|
-
toolCalls.push({
|
|
1594
|
-
toolCallId: result.toolCallId,
|
|
1595
|
-
tool: result.tool,
|
|
1596
|
-
input: request.input,
|
|
1597
|
-
result
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
return toolCalls;
|
|
1683
|
+
function asCodeExecDefinition(tool) {
|
|
1684
|
+
return typeof tool === "function" ? {
|
|
1685
|
+
name: "codeExec",
|
|
1686
|
+
execute: tool
|
|
1687
|
+
} : tool;
|
|
1601
1688
|
}
|
|
1602
|
-
function
|
|
1689
|
+
function mergeIdentity(defaultIdentity, options) {
|
|
1690
|
+
if (!options) return defaultIdentity;
|
|
1603
1691
|
return {
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
...
|
|
1607
|
-
...
|
|
1608
|
-
...identity.description ? { description: identity.description } : {}
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
function runtimeToolPermissionManifest(permission) {
|
|
1612
|
-
if (permission.kind === "network") return {
|
|
1613
|
-
kind: permission.kind,
|
|
1614
|
-
...permission.allowHosts ? { allowHosts: Array.from(permission.allowHosts) } : {},
|
|
1615
|
-
...permission.allowPrivateNetwork === void 0 ? {} : { allowPrivateNetwork: permission.allowPrivateNetwork }
|
|
1616
|
-
};
|
|
1617
|
-
if (permission.kind === "code-execution") return {
|
|
1618
|
-
kind: permission.kind,
|
|
1619
|
-
sandbox: permission.sandbox,
|
|
1620
|
-
...permission.languages ? { languages: Array.from(permission.languages) } : {},
|
|
1621
|
-
...permission.allowNetwork === void 0 ? {} : { allowNetwork: permission.allowNetwork }
|
|
1622
|
-
};
|
|
1623
|
-
return {
|
|
1624
|
-
kind: permission.kind,
|
|
1625
|
-
name: permission.name,
|
|
1626
|
-
...permission.description ? { description: permission.description } : {},
|
|
1627
|
-
...permission.metadata ? { metadata: permission.metadata } : {}
|
|
1692
|
+
...defaultIdentity,
|
|
1693
|
+
...options.namespace !== void 0 ? { namespace: options.namespace } : {},
|
|
1694
|
+
...options.version !== void 0 ? { version: options.version } : {},
|
|
1695
|
+
...options.description !== void 0 ? { description: options.description } : {}
|
|
1628
1696
|
};
|
|
1629
1697
|
}
|
|
1630
1698
|
function validateCodeExecAdapterInput(input, options) {
|
|
@@ -1633,296 +1701,19 @@ function validateCodeExecAdapterInput(input, options) {
|
|
|
1633
1701
|
if (typeof input.language === "string" && isCodeExecLanguage(input.language) && !languages.includes(input.language)) issues.push({
|
|
1634
1702
|
code: "invalid-value",
|
|
1635
1703
|
path: "language",
|
|
1636
|
-
message: "codeExec.language is not enabled for this adapter.",
|
|
1637
|
-
detail: { allowed: Array.from(languages) }
|
|
1638
|
-
});
|
|
1639
|
-
const effectiveTimeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1640
|
-
if (effectiveTimeoutMs !== void 0 && options.maxTimeoutMs !== void 0 && Number.isFinite(effectiveTimeoutMs) && Number.isFinite(options.maxTimeoutMs) && effectiveTimeoutMs > options.maxTimeoutMs) issues.push({
|
|
1641
|
-
code: "out-of-range",
|
|
1642
|
-
path: input.timeoutMs === void 0 ? "defaultTimeoutMs" : "timeoutMs",
|
|
1643
|
-
message: `codeExec.timeoutMs must be less than or equal to ${options.maxTimeoutMs}.`,
|
|
1644
|
-
detail: { maximum: options.maxTimeoutMs }
|
|
1645
|
-
});
|
|
1646
|
-
return issues.length === 0 ? { type: "valid" } : {
|
|
1647
|
-
type: "invalid",
|
|
1648
|
-
issues
|
|
1649
|
-
};
|
|
1650
|
-
}
|
|
1651
|
-
function createExecutionContext(options, request, toolCallId) {
|
|
1652
|
-
return {
|
|
1653
|
-
runId: options.runId,
|
|
1654
|
-
toolCallId,
|
|
1655
|
-
protocol: options.protocol,
|
|
1656
|
-
tier: options.tier,
|
|
1657
|
-
...request.agentId ? { agentId: request.agentId } : {},
|
|
1658
|
-
...request.role ? { role: request.role } : {},
|
|
1659
|
-
...request.turn !== void 0 ? { turn: request.turn } : {},
|
|
1660
|
-
...options.getTrace ? { trace: options.getTrace() } : {},
|
|
1661
|
-
...request.abortSignal ?? options.abortSignal ? { abortSignal: request.abortSignal ?? options.abortSignal } : {},
|
|
1662
|
-
...options.metadata || request.metadata ? { metadata: mergeToolMetadata(options.metadata, request.metadata) } : {}
|
|
1663
|
-
};
|
|
1664
|
-
}
|
|
1665
|
-
async function executeRuntimeTool(tool, identity, input, context) {
|
|
1666
|
-
if (!tool) return {
|
|
1667
|
-
type: "error",
|
|
1668
|
-
toolCallId: context.toolCallId,
|
|
1669
|
-
tool: identity,
|
|
1670
|
-
error: {
|
|
1671
|
-
code: "unavailable",
|
|
1672
|
-
message: `Runtime tool "${identity.id}" is not registered.`,
|
|
1673
|
-
retryable: false
|
|
1674
|
-
}
|
|
1675
|
-
};
|
|
1676
|
-
const validation = validateRuntimeToolInput(tool, input);
|
|
1677
|
-
if (validation.type === "invalid") return {
|
|
1678
|
-
type: "error",
|
|
1679
|
-
toolCallId: context.toolCallId,
|
|
1680
|
-
tool: identity,
|
|
1681
|
-
error: {
|
|
1682
|
-
code: "invalid-input",
|
|
1683
|
-
message: "Runtime tool input failed validation.",
|
|
1684
|
-
retryable: false,
|
|
1685
|
-
detail: { issues: validation.issues.map((issue) => ({
|
|
1686
|
-
code: issue.code,
|
|
1687
|
-
path: issue.path,
|
|
1688
|
-
message: issue.message,
|
|
1689
|
-
...issue.detail ? { detail: issue.detail } : {}
|
|
1690
|
-
})) }
|
|
1691
|
-
}
|
|
1692
|
-
};
|
|
1693
|
-
try {
|
|
1694
|
-
return await tool.execute(input, context);
|
|
1695
|
-
} catch (error) {
|
|
1696
|
-
return {
|
|
1697
|
-
type: "error",
|
|
1698
|
-
toolCallId: context.toolCallId,
|
|
1699
|
-
tool: identity,
|
|
1700
|
-
error: normalizeRuntimeToolAdapterError(error)
|
|
1701
|
-
};
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
function validateRuntimeToolInput(tool, input) {
|
|
1705
|
-
if (typeof tool.validateInput !== "function") return { type: "valid" };
|
|
1706
|
-
return tool.validateInput(input);
|
|
1707
|
-
}
|
|
1708
|
-
function mergeToolMetadata(base, request) {
|
|
1709
|
-
return {
|
|
1710
|
-
...base ?? {},
|
|
1711
|
-
...request ?? {}
|
|
1712
|
-
};
|
|
1713
|
-
}
|
|
1714
|
-
function defaultToolCallId(runId, callIndex) {
|
|
1715
|
-
return `${runId}:tool-${callIndex + 1}`;
|
|
1716
|
-
}
|
|
1717
|
-
/**
|
|
1718
|
-
* Convert an unknown adapter failure into Dogpile's serializable error data.
|
|
1719
|
-
*/
|
|
1720
|
-
function normalizeRuntimeToolAdapterError(error) {
|
|
1721
|
-
if (isRuntimeToolAdapterError(error)) return error;
|
|
1722
|
-
if (error instanceof DOMException && error.name === "AbortError") return {
|
|
1723
|
-
code: "aborted",
|
|
1724
|
-
message: error.message || "Tool execution was aborted.",
|
|
1725
|
-
retryable: true,
|
|
1726
|
-
detail: { name: error.name }
|
|
1727
|
-
};
|
|
1728
|
-
if (error instanceof Error) return {
|
|
1729
|
-
code: "backend-error",
|
|
1730
|
-
message: error.message,
|
|
1731
|
-
retryable: false,
|
|
1732
|
-
detail: { name: error.name }
|
|
1733
|
-
};
|
|
1734
|
-
return {
|
|
1735
|
-
code: "unknown",
|
|
1736
|
-
message: "Tool execution failed with a non-Error value.",
|
|
1737
|
-
retryable: false,
|
|
1738
|
-
detail: { valueType: typeof error }
|
|
1739
|
-
};
|
|
1740
|
-
}
|
|
1741
|
-
/**
|
|
1742
|
-
* Create Dogpile's built-in fetch-based web search adapter.
|
|
1743
|
-
*
|
|
1744
|
-
* @remarks
|
|
1745
|
-
* The adapter is backend-neutral: by default it sends a GET request with
|
|
1746
|
-
* `q` and `limit` query parameters, then accepts either `{ results: [...] }`
|
|
1747
|
-
* or a bare array of result objects from the response JSON. Callers can replace
|
|
1748
|
-
* request construction or response parsing for a specific search API while
|
|
1749
|
-
* keeping Dogpile's shared runtime tool contract, identity, permissions, input
|
|
1750
|
-
* validation, and serializable errors.
|
|
1751
|
-
*/
|
|
1752
|
-
function createWebSearchToolAdapter(options) {
|
|
1753
|
-
const identity = mergeIdentity(webSearchIdentity, options.identity);
|
|
1754
|
-
return normalizeBuiltInDogpileTool({
|
|
1755
|
-
name: "webSearch",
|
|
1756
|
-
...options.identity ? { identity: options.identity } : {},
|
|
1757
|
-
...options.permissions ? { permissions: options.permissions } : {},
|
|
1758
|
-
async execute(input, context) {
|
|
1759
|
-
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
1760
|
-
if (!fetchImplementation) return {
|
|
1761
|
-
type: "error",
|
|
1762
|
-
toolCallId: context.toolCallId,
|
|
1763
|
-
tool: identity,
|
|
1764
|
-
error: {
|
|
1765
|
-
code: "unavailable",
|
|
1766
|
-
message: "No fetch implementation is available for webSearch.",
|
|
1767
|
-
retryable: false
|
|
1768
|
-
}
|
|
1769
|
-
};
|
|
1770
|
-
const request = options.buildRequest ? options.buildRequest(input, context) : defaultWebSearchRequest(options, input, context);
|
|
1771
|
-
const response = await fetchImplementation(request.url, {
|
|
1772
|
-
...request.init,
|
|
1773
|
-
...context.abortSignal ? { signal: context.abortSignal } : {}
|
|
1774
|
-
});
|
|
1775
|
-
if (!response.ok) throw {
|
|
1776
|
-
code: response.status >= 500 ? "unavailable" : "backend-error",
|
|
1777
|
-
message: `Web search backend returned HTTP ${response.status}.`,
|
|
1778
|
-
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
1779
|
-
detail: {
|
|
1780
|
-
status: response.status,
|
|
1781
|
-
statusText: response.statusText
|
|
1782
|
-
}
|
|
1783
|
-
};
|
|
1784
|
-
const output = options.parseResponse ? await options.parseResponse(response, input, context) : await defaultWebSearchResponseParser(response);
|
|
1785
|
-
return {
|
|
1786
|
-
type: "success",
|
|
1787
|
-
toolCallId: context.toolCallId,
|
|
1788
|
-
tool: identity,
|
|
1789
|
-
output
|
|
1790
|
-
};
|
|
1791
|
-
}
|
|
1792
|
-
});
|
|
1793
|
-
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Create Dogpile's built-in code execution adapter around a caller-owned sandbox.
|
|
1796
|
-
*
|
|
1797
|
-
* @remarks
|
|
1798
|
-
* Dogpile core stays runtime-portable and never evaluates code itself. This
|
|
1799
|
-
* adapter supplies the stable `codeExec` identity, schema, permissions,
|
|
1800
|
-
* validation, timeout defaults, abort handling, and serializable errors while
|
|
1801
|
-
* the host application owns the sandbox boundary.
|
|
1802
|
-
*/
|
|
1803
|
-
function createCodeExecToolAdapter(options) {
|
|
1804
|
-
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
1805
|
-
const permissions = options.permissions ?? codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
1806
|
-
return {
|
|
1807
|
-
identity,
|
|
1808
|
-
inputSchema: codeExecInputSchemaFor(options.languages ?? codeExecLanguages),
|
|
1809
|
-
permissions,
|
|
1810
|
-
validateInput: (input) => validateCodeExecAdapterInput(input, options),
|
|
1811
|
-
async execute(input, context) {
|
|
1812
|
-
const validation = validateCodeExecAdapterInput(input, options);
|
|
1813
|
-
if (validation.type === "invalid") return {
|
|
1814
|
-
type: "error",
|
|
1815
|
-
toolCallId: context.toolCallId,
|
|
1816
|
-
tool: identity,
|
|
1817
|
-
error: {
|
|
1818
|
-
code: "invalid-input",
|
|
1819
|
-
message: "Invalid codeExec tool input.",
|
|
1820
|
-
retryable: false,
|
|
1821
|
-
detail: { issues: validation.issues }
|
|
1822
|
-
}
|
|
1823
|
-
};
|
|
1824
|
-
const timeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1825
|
-
const executionInput = timeoutMs === void 0 ? input : {
|
|
1826
|
-
...input,
|
|
1827
|
-
timeoutMs
|
|
1828
|
-
};
|
|
1829
|
-
try {
|
|
1830
|
-
const output = await executeSandboxWithPolicy(options.execute, executionInput, context, timeoutMs);
|
|
1831
|
-
return {
|
|
1832
|
-
type: "success",
|
|
1833
|
-
toolCallId: context.toolCallId,
|
|
1834
|
-
tool: identity,
|
|
1835
|
-
output
|
|
1836
|
-
};
|
|
1837
|
-
} catch (error) {
|
|
1838
|
-
return {
|
|
1839
|
-
type: "error",
|
|
1840
|
-
toolCallId: context.toolCallId,
|
|
1841
|
-
tool: identity,
|
|
1842
|
-
error: normalizeRuntimeToolAdapterError(error)
|
|
1843
|
-
};
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
};
|
|
1847
|
-
}
|
|
1848
|
-
function normalizeBuiltInDogpileTool(definition) {
|
|
1849
|
-
switch (definition.name) {
|
|
1850
|
-
case "webSearch": {
|
|
1851
|
-
const identity = mergeIdentity(webSearchIdentity, definition.identity);
|
|
1852
|
-
const permissions = definition.permissions ?? webSearchPermissions;
|
|
1853
|
-
return {
|
|
1854
|
-
identity,
|
|
1855
|
-
inputSchema: definition.inputSchema ?? webSearchInputSchema,
|
|
1856
|
-
permissions,
|
|
1857
|
-
validateInput: (input) => validateBuiltInDogpileToolInput("webSearch", input),
|
|
1858
|
-
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "webSearch")
|
|
1859
|
-
};
|
|
1860
|
-
}
|
|
1861
|
-
case "codeExec": {
|
|
1862
|
-
const identity = mergeIdentity(codeExecIdentity, definition.identity);
|
|
1863
|
-
const permissions = definition.permissions ?? codeExecPermissions;
|
|
1864
|
-
return {
|
|
1865
|
-
identity,
|
|
1866
|
-
inputSchema: definition.inputSchema ?? codeExecInputSchema,
|
|
1867
|
-
permissions,
|
|
1868
|
-
validateInput: (input) => validateBuiltInDogpileToolInput("codeExec", input),
|
|
1869
|
-
execute: (input, context) => executeBuiltInTool(identity, definition.execute, input, context, "codeExec")
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Normalize configured built-in Dogpile tool executors into runtime tools.
|
|
1876
|
-
*/
|
|
1877
|
-
function normalizeBuiltInDogpileTools(tools) {
|
|
1878
|
-
const normalized = [];
|
|
1879
|
-
if (tools.webSearch) normalized.push(normalizeBuiltInDogpileTool(asWebSearchDefinition(tools.webSearch)));
|
|
1880
|
-
if (tools.codeExec) normalized.push(normalizeBuiltInDogpileTool(asCodeExecDefinition(tools.codeExec)));
|
|
1881
|
-
return normalized;
|
|
1882
|
-
}
|
|
1883
|
-
async function executeBuiltInTool(identity, execute, input, context, name) {
|
|
1884
|
-
const validation = validateBuiltInDogpileToolInput(name, input);
|
|
1885
|
-
if (validation.type === "invalid") return {
|
|
1886
|
-
type: "error",
|
|
1887
|
-
toolCallId: context.toolCallId,
|
|
1888
|
-
tool: identity,
|
|
1889
|
-
error: {
|
|
1890
|
-
code: "invalid-input",
|
|
1891
|
-
message: `Invalid ${name} tool input.`,
|
|
1892
|
-
retryable: false,
|
|
1893
|
-
detail: { issues: validation.issues }
|
|
1894
|
-
}
|
|
1895
|
-
};
|
|
1896
|
-
try {
|
|
1897
|
-
return await execute(input, context);
|
|
1898
|
-
} catch (error) {
|
|
1899
|
-
return {
|
|
1900
|
-
type: "error",
|
|
1901
|
-
toolCallId: context.toolCallId,
|
|
1902
|
-
tool: identity,
|
|
1903
|
-
error: normalizeRuntimeToolAdapterError(error)
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
function asWebSearchDefinition(tool) {
|
|
1908
|
-
return typeof tool === "function" ? {
|
|
1909
|
-
name: "webSearch",
|
|
1910
|
-
execute: tool
|
|
1911
|
-
} : tool;
|
|
1912
|
-
}
|
|
1913
|
-
function asCodeExecDefinition(tool) {
|
|
1914
|
-
return typeof tool === "function" ? {
|
|
1915
|
-
name: "codeExec",
|
|
1916
|
-
execute: tool
|
|
1917
|
-
} : tool;
|
|
1918
|
-
}
|
|
1919
|
-
function mergeIdentity(defaultIdentity, options) {
|
|
1920
|
-
if (!options) return defaultIdentity;
|
|
1921
|
-
return {
|
|
1922
|
-
...defaultIdentity,
|
|
1923
|
-
...options.namespace !== void 0 ? { namespace: options.namespace } : {},
|
|
1924
|
-
...options.version !== void 0 ? { version: options.version } : {},
|
|
1925
|
-
...options.description !== void 0 ? { description: options.description } : {}
|
|
1704
|
+
message: "codeExec.language is not enabled for this adapter.",
|
|
1705
|
+
detail: { allowed: Array.from(languages) }
|
|
1706
|
+
});
|
|
1707
|
+
const effectiveTimeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1708
|
+
if (effectiveTimeoutMs !== void 0 && options.maxTimeoutMs !== void 0 && Number.isFinite(effectiveTimeoutMs) && Number.isFinite(options.maxTimeoutMs) && effectiveTimeoutMs > options.maxTimeoutMs) issues.push({
|
|
1709
|
+
code: "out-of-range",
|
|
1710
|
+
path: input.timeoutMs === void 0 ? "defaultTimeoutMs" : "timeoutMs",
|
|
1711
|
+
message: `codeExec.timeoutMs must be less than or equal to ${options.maxTimeoutMs}.`,
|
|
1712
|
+
detail: { maximum: options.maxTimeoutMs }
|
|
1713
|
+
});
|
|
1714
|
+
return issues.length === 0 ? { type: "valid" } : {
|
|
1715
|
+
type: "invalid",
|
|
1716
|
+
issues
|
|
1926
1717
|
};
|
|
1927
1718
|
}
|
|
1928
1719
|
function defaultWebSearchRequest(options, input, _context) {
|
|
@@ -2041,105 +1832,324 @@ function jsonString(value, fieldName) {
|
|
|
2041
1832
|
message: `Web search result ${fieldName} must be a non-empty string.`,
|
|
2042
1833
|
retryable: false
|
|
2043
1834
|
};
|
|
2044
|
-
return value;
|
|
1835
|
+
return value;
|
|
1836
|
+
}
|
|
1837
|
+
function optionalJsonString(value, fieldName) {
|
|
1838
|
+
if (value === void 0) return void 0;
|
|
1839
|
+
if (typeof value !== "string") throw {
|
|
1840
|
+
code: "backend-error",
|
|
1841
|
+
message: `Web search result ${fieldName} must be a string when present.`,
|
|
1842
|
+
retryable: false
|
|
1843
|
+
};
|
|
1844
|
+
return value;
|
|
1845
|
+
}
|
|
1846
|
+
function optionalJsonObject(value, fieldName) {
|
|
1847
|
+
if (value === void 0) return void 0;
|
|
1848
|
+
if (!isJsonObject(value)) throw {
|
|
1849
|
+
code: "backend-error",
|
|
1850
|
+
message: `Web search result ${fieldName} must be a JSON object when present.`,
|
|
1851
|
+
retryable: false
|
|
1852
|
+
};
|
|
1853
|
+
return value;
|
|
1854
|
+
}
|
|
1855
|
+
function isJsonObject(value) {
|
|
1856
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1857
|
+
}
|
|
1858
|
+
function validateWebSearchInput(input) {
|
|
1859
|
+
const issues = [];
|
|
1860
|
+
if (typeof input.query !== "string") issues.push({
|
|
1861
|
+
code: input.query === void 0 ? "missing-field" : "invalid-type",
|
|
1862
|
+
path: "query",
|
|
1863
|
+
message: "webSearch.query must be a string."
|
|
1864
|
+
});
|
|
1865
|
+
else if (input.query.trim().length === 0) issues.push({
|
|
1866
|
+
code: "invalid-value",
|
|
1867
|
+
path: "query",
|
|
1868
|
+
message: "webSearch.query must not be empty."
|
|
1869
|
+
});
|
|
1870
|
+
if (input.maxResults !== void 0) {
|
|
1871
|
+
if (typeof input.maxResults !== "number" || !Number.isFinite(input.maxResults)) issues.push({
|
|
1872
|
+
code: "invalid-type",
|
|
1873
|
+
path: "maxResults",
|
|
1874
|
+
message: "webSearch.maxResults must be a finite number."
|
|
1875
|
+
});
|
|
1876
|
+
else if (input.maxResults < 1) issues.push({
|
|
1877
|
+
code: "out-of-range",
|
|
1878
|
+
path: "maxResults",
|
|
1879
|
+
message: "webSearch.maxResults must be greater than or equal to 1.",
|
|
1880
|
+
detail: { minimum: 1 }
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
return issues;
|
|
1884
|
+
}
|
|
1885
|
+
function validateCodeExecInput(input) {
|
|
1886
|
+
const issues = [];
|
|
1887
|
+
if (typeof input.language !== "string") issues.push({
|
|
1888
|
+
code: input.language === void 0 ? "missing-field" : "invalid-type",
|
|
1889
|
+
path: "language",
|
|
1890
|
+
message: "codeExec.language must be a string."
|
|
1891
|
+
});
|
|
1892
|
+
else if (!isCodeExecLanguage(input.language)) issues.push({
|
|
1893
|
+
code: "invalid-value",
|
|
1894
|
+
path: "language",
|
|
1895
|
+
message: "codeExec.language must be one of javascript, typescript, python, bash, or shell.",
|
|
1896
|
+
detail: { allowed: [
|
|
1897
|
+
"javascript",
|
|
1898
|
+
"typescript",
|
|
1899
|
+
"python",
|
|
1900
|
+
"bash",
|
|
1901
|
+
"shell"
|
|
1902
|
+
] }
|
|
1903
|
+
});
|
|
1904
|
+
if (typeof input.code !== "string") issues.push({
|
|
1905
|
+
code: input.code === void 0 ? "missing-field" : "invalid-type",
|
|
1906
|
+
path: "code",
|
|
1907
|
+
message: "codeExec.code must be a string."
|
|
1908
|
+
});
|
|
1909
|
+
if (input.timeoutMs !== void 0) {
|
|
1910
|
+
if (typeof input.timeoutMs !== "number" || !Number.isFinite(input.timeoutMs)) issues.push({
|
|
1911
|
+
code: "invalid-type",
|
|
1912
|
+
path: "timeoutMs",
|
|
1913
|
+
message: "codeExec.timeoutMs must be a finite number."
|
|
1914
|
+
});
|
|
1915
|
+
else if (input.timeoutMs < 1) issues.push({
|
|
1916
|
+
code: "out-of-range",
|
|
1917
|
+
path: "timeoutMs",
|
|
1918
|
+
message: "codeExec.timeoutMs must be greater than or equal to 1.",
|
|
1919
|
+
detail: { minimum: 1 }
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
return issues;
|
|
1923
|
+
}
|
|
1924
|
+
function isCodeExecLanguage(value) {
|
|
1925
|
+
return value === "javascript" || value === "typescript" || value === "python" || value === "bash" || value === "shell";
|
|
1926
|
+
}
|
|
1927
|
+
function normalizeRuntimeToolAdapterError(error) {
|
|
1928
|
+
if (isRuntimeToolAdapterError(error)) return error;
|
|
1929
|
+
if (error instanceof DOMException && error.name === "AbortError") return {
|
|
1930
|
+
code: "aborted",
|
|
1931
|
+
message: error.message || "Tool execution was aborted.",
|
|
1932
|
+
retryable: true,
|
|
1933
|
+
detail: { name: error.name }
|
|
1934
|
+
};
|
|
1935
|
+
if (error instanceof Error) return {
|
|
1936
|
+
code: "backend-error",
|
|
1937
|
+
message: error.message,
|
|
1938
|
+
retryable: false,
|
|
1939
|
+
detail: { name: error.name }
|
|
1940
|
+
};
|
|
1941
|
+
return {
|
|
1942
|
+
code: "unknown",
|
|
1943
|
+
message: "Tool execution failed with a non-Error value.",
|
|
1944
|
+
retryable: false,
|
|
1945
|
+
detail: { valueType: typeof error }
|
|
1946
|
+
};
|
|
1947
|
+
}
|
|
1948
|
+
function isRuntimeToolAdapterError(error) {
|
|
1949
|
+
if (typeof error !== "object" || error === null || !("code" in error) || !("message" in error)) return false;
|
|
1950
|
+
const candidate = error;
|
|
1951
|
+
return isRuntimeToolAdapterErrorCode(candidate.code) && typeof candidate.message === "string";
|
|
1952
|
+
}
|
|
1953
|
+
function isRuntimeToolAdapterErrorCode(value) {
|
|
1954
|
+
return value === "invalid-input" || value === "permission-denied" || value === "timeout" || value === "aborted" || value === "unavailable" || value === "backend-error" || value === "unknown";
|
|
1955
|
+
}
|
|
1956
|
+
function serializeValidationIssues(issues) {
|
|
1957
|
+
return issues.map((issue) => ({
|
|
1958
|
+
code: issue.code,
|
|
1959
|
+
path: issue.path,
|
|
1960
|
+
message: issue.message,
|
|
1961
|
+
...issue.detail !== void 0 ? { detail: issue.detail } : {}
|
|
1962
|
+
}));
|
|
1963
|
+
}
|
|
1964
|
+
//#endregion
|
|
1965
|
+
//#region src/runtime/tools.ts
|
|
1966
|
+
/**
|
|
1967
|
+
* Create the shared runtime tool executor used by every first-party protocol.
|
|
1968
|
+
*
|
|
1969
|
+
* @remarks
|
|
1970
|
+
* The executor owns call id generation, read-only trace context construction,
|
|
1971
|
+
* adapter validation, error normalization, and matched `tool-call` /
|
|
1972
|
+
* `tool-result` events. Protocols only supply a normalized
|
|
1973
|
+
* {@link RuntimeToolExecutionRequest}, which keeps tool execution independent
|
|
1974
|
+
* of Coordinator, Sequential, Broadcast, or Shared control flow.
|
|
1975
|
+
*/
|
|
1976
|
+
function createRuntimeToolExecutor(options) {
|
|
1977
|
+
validateRuntimeToolRegistrations(options.tools);
|
|
1978
|
+
const tools = Array.from(options.tools);
|
|
1979
|
+
let callCount = 0;
|
|
1980
|
+
return {
|
|
1981
|
+
tools,
|
|
1982
|
+
async execute(request) {
|
|
1983
|
+
const tool = tools.find((candidate) => candidate.identity.id === request.toolId);
|
|
1984
|
+
const identity = tool?.identity ?? {
|
|
1985
|
+
id: request.toolId,
|
|
1986
|
+
name: request.toolId
|
|
1987
|
+
};
|
|
1988
|
+
const callIndex = callCount;
|
|
1989
|
+
callCount += 1;
|
|
1990
|
+
const toolCallId = request.toolCallId ?? options.makeToolCallId?.(identity, callIndex) ?? defaultToolCallId(options.runId, callIndex);
|
|
1991
|
+
const context = createExecutionContext(options, request, toolCallId);
|
|
1992
|
+
options.emit?.({
|
|
1993
|
+
type: "tool-call",
|
|
1994
|
+
runId: options.runId,
|
|
1995
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1996
|
+
toolCallId,
|
|
1997
|
+
tool: identity,
|
|
1998
|
+
input: request.input,
|
|
1999
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
2000
|
+
...request.role ? { role: request.role } : {}
|
|
2001
|
+
});
|
|
2002
|
+
const result = await executeRuntimeTool(tool, identity, request.input, context);
|
|
2003
|
+
options.emit?.({
|
|
2004
|
+
type: "tool-result",
|
|
2005
|
+
runId: options.runId,
|
|
2006
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2007
|
+
toolCallId,
|
|
2008
|
+
tool: identity,
|
|
2009
|
+
result,
|
|
2010
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
2011
|
+
...request.role ? { role: request.role } : {}
|
|
2012
|
+
});
|
|
2013
|
+
return result;
|
|
2014
|
+
}
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
/**
|
|
2018
|
+
* Return a JSON-serializable manifest for tools visible to a protocol run.
|
|
2019
|
+
*/
|
|
2020
|
+
function runtimeToolManifest(tools) {
|
|
2021
|
+
return tools.map((tool) => {
|
|
2022
|
+
const inputSchema = {
|
|
2023
|
+
kind: tool.inputSchema.kind,
|
|
2024
|
+
schema: tool.inputSchema.schema,
|
|
2025
|
+
...tool.inputSchema.description ? { description: tool.inputSchema.description } : {}
|
|
2026
|
+
};
|
|
2027
|
+
return {
|
|
2028
|
+
identity: runtimeToolIdentityManifest(tool.identity),
|
|
2029
|
+
inputSchema,
|
|
2030
|
+
permissions: Array.from(tool.permissions ?? []).map(runtimeToolPermissionManifest)
|
|
2031
|
+
};
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Return request metadata that makes runtime tools visible to provider
|
|
2036
|
+
* adapters, or an empty object when no tools are available.
|
|
2037
|
+
*/
|
|
2038
|
+
function runtimeToolAvailability(tools) {
|
|
2039
|
+
const manifest = runtimeToolManifest(tools);
|
|
2040
|
+
return manifest.length > 0 ? { tools: manifest } : {};
|
|
2041
|
+
}
|
|
2042
|
+
/**
|
|
2043
|
+
* Execute normalized tool requests returned by a provider response.
|
|
2044
|
+
*/
|
|
2045
|
+
async function executeModelResponseToolRequests(options) {
|
|
2046
|
+
const toolCalls = [];
|
|
2047
|
+
for (const request of options.response.toolRequests ?? []) {
|
|
2048
|
+
const result = await options.executor.execute({
|
|
2049
|
+
...request,
|
|
2050
|
+
agentId: request.agentId ?? options.agentId,
|
|
2051
|
+
role: request.role ?? options.role,
|
|
2052
|
+
turn: request.turn ?? options.turn,
|
|
2053
|
+
metadata: mergeToolMetadata(options.metadata, request.metadata)
|
|
2054
|
+
});
|
|
2055
|
+
toolCalls.push({
|
|
2056
|
+
toolCallId: result.toolCallId,
|
|
2057
|
+
tool: result.tool,
|
|
2058
|
+
input: request.input,
|
|
2059
|
+
result
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
return toolCalls;
|
|
2063
|
+
}
|
|
2064
|
+
function runtimeToolIdentityManifest(identity) {
|
|
2065
|
+
return {
|
|
2066
|
+
id: identity.id,
|
|
2067
|
+
name: identity.name,
|
|
2068
|
+
...identity.namespace ? { namespace: identity.namespace } : {},
|
|
2069
|
+
...identity.version ? { version: identity.version } : {},
|
|
2070
|
+
...identity.description ? { description: identity.description } : {}
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
function runtimeToolPermissionManifest(permission) {
|
|
2074
|
+
if (permission.kind === "network") return {
|
|
2075
|
+
kind: permission.kind,
|
|
2076
|
+
...permission.allowHosts ? { allowHosts: Array.from(permission.allowHosts) } : {},
|
|
2077
|
+
...permission.allowPrivateNetwork === void 0 ? {} : { allowPrivateNetwork: permission.allowPrivateNetwork }
|
|
2078
|
+
};
|
|
2079
|
+
if (permission.kind === "code-execution") return {
|
|
2080
|
+
kind: permission.kind,
|
|
2081
|
+
sandbox: permission.sandbox,
|
|
2082
|
+
...permission.languages ? { languages: Array.from(permission.languages) } : {},
|
|
2083
|
+
...permission.allowNetwork === void 0 ? {} : { allowNetwork: permission.allowNetwork }
|
|
2084
|
+
};
|
|
2085
|
+
return {
|
|
2086
|
+
kind: permission.kind,
|
|
2087
|
+
name: permission.name,
|
|
2088
|
+
...permission.description ? { description: permission.description } : {},
|
|
2089
|
+
...permission.metadata ? { metadata: permission.metadata } : {}
|
|
2090
|
+
};
|
|
2045
2091
|
}
|
|
2046
|
-
function
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2092
|
+
function createExecutionContext(options, request, toolCallId) {
|
|
2093
|
+
return {
|
|
2094
|
+
runId: options.runId,
|
|
2095
|
+
toolCallId,
|
|
2096
|
+
protocol: options.protocol,
|
|
2097
|
+
tier: options.tier,
|
|
2098
|
+
...request.agentId ? { agentId: request.agentId } : {},
|
|
2099
|
+
...request.role ? { role: request.role } : {},
|
|
2100
|
+
...request.turn !== void 0 ? { turn: request.turn } : {},
|
|
2101
|
+
...options.getTrace ? { trace: options.getTrace() } : {},
|
|
2102
|
+
...request.abortSignal ?? options.abortSignal ? { abortSignal: request.abortSignal ?? options.abortSignal } : {},
|
|
2103
|
+
...options.metadata || request.metadata ? { metadata: mergeToolMetadata(options.metadata, request.metadata) } : {}
|
|
2052
2104
|
};
|
|
2053
|
-
return value;
|
|
2054
2105
|
}
|
|
2055
|
-
function
|
|
2056
|
-
if (
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2106
|
+
async function executeRuntimeTool(tool, identity, input, context) {
|
|
2107
|
+
if (!tool) return {
|
|
2108
|
+
type: "error",
|
|
2109
|
+
toolCallId: context.toolCallId,
|
|
2110
|
+
tool: identity,
|
|
2111
|
+
error: {
|
|
2112
|
+
code: "unavailable",
|
|
2113
|
+
message: `Runtime tool "${identity.id}" is not registered.`,
|
|
2114
|
+
retryable: false
|
|
2115
|
+
}
|
|
2061
2116
|
};
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
}
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
message: "webSearch.maxResults must be greater than or equal to 1.",
|
|
2089
|
-
detail: { minimum: 1 }
|
|
2090
|
-
});
|
|
2091
|
-
}
|
|
2092
|
-
return issues;
|
|
2093
|
-
}
|
|
2094
|
-
function validateCodeExecInput(input) {
|
|
2095
|
-
const issues = [];
|
|
2096
|
-
if (typeof input.language !== "string") issues.push({
|
|
2097
|
-
code: input.language === void 0 ? "missing-field" : "invalid-type",
|
|
2098
|
-
path: "language",
|
|
2099
|
-
message: "codeExec.language must be a string."
|
|
2100
|
-
});
|
|
2101
|
-
else if (!isCodeExecLanguage(input.language)) issues.push({
|
|
2102
|
-
code: "invalid-value",
|
|
2103
|
-
path: "language",
|
|
2104
|
-
message: "codeExec.language must be one of javascript, typescript, python, bash, or shell.",
|
|
2105
|
-
detail: { allowed: [
|
|
2106
|
-
"javascript",
|
|
2107
|
-
"typescript",
|
|
2108
|
-
"python",
|
|
2109
|
-
"bash",
|
|
2110
|
-
"shell"
|
|
2111
|
-
] }
|
|
2112
|
-
});
|
|
2113
|
-
if (typeof input.code !== "string") issues.push({
|
|
2114
|
-
code: input.code === void 0 ? "missing-field" : "invalid-type",
|
|
2115
|
-
path: "code",
|
|
2116
|
-
message: "codeExec.code must be a string."
|
|
2117
|
-
});
|
|
2118
|
-
if (input.timeoutMs !== void 0) {
|
|
2119
|
-
if (typeof input.timeoutMs !== "number" || !Number.isFinite(input.timeoutMs)) issues.push({
|
|
2120
|
-
code: "invalid-type",
|
|
2121
|
-
path: "timeoutMs",
|
|
2122
|
-
message: "codeExec.timeoutMs must be a finite number."
|
|
2123
|
-
});
|
|
2124
|
-
else if (input.timeoutMs < 1) issues.push({
|
|
2125
|
-
code: "out-of-range",
|
|
2126
|
-
path: "timeoutMs",
|
|
2127
|
-
message: "codeExec.timeoutMs must be greater than or equal to 1.",
|
|
2128
|
-
detail: { minimum: 1 }
|
|
2129
|
-
});
|
|
2117
|
+
const validation = tool.validateInput?.(input);
|
|
2118
|
+
if (validation?.type === "invalid") return {
|
|
2119
|
+
type: "error",
|
|
2120
|
+
toolCallId: context.toolCallId,
|
|
2121
|
+
tool: identity,
|
|
2122
|
+
error: {
|
|
2123
|
+
code: "invalid-input",
|
|
2124
|
+
message: "Runtime tool input failed validation.",
|
|
2125
|
+
retryable: false,
|
|
2126
|
+
detail: { issues: validation.issues.map((issue) => ({
|
|
2127
|
+
code: issue.code,
|
|
2128
|
+
path: issue.path,
|
|
2129
|
+
message: issue.message,
|
|
2130
|
+
...issue.detail ? { detail: issue.detail } : {}
|
|
2131
|
+
})) }
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2134
|
+
try {
|
|
2135
|
+
return await tool.execute(input, context);
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
return {
|
|
2138
|
+
type: "error",
|
|
2139
|
+
toolCallId: context.toolCallId,
|
|
2140
|
+
tool: identity,
|
|
2141
|
+
error: normalizeRuntimeToolAdapterError(error)
|
|
2142
|
+
};
|
|
2130
2143
|
}
|
|
2131
|
-
return issues;
|
|
2132
|
-
}
|
|
2133
|
-
function isCodeExecLanguage(value) {
|
|
2134
|
-
return value === "javascript" || value === "typescript" || value === "python" || value === "bash" || value === "shell";
|
|
2135
2144
|
}
|
|
2136
|
-
function
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2145
|
+
function mergeToolMetadata(base, request) {
|
|
2146
|
+
return {
|
|
2147
|
+
...base ?? {},
|
|
2148
|
+
...request ?? {}
|
|
2149
|
+
};
|
|
2140
2150
|
}
|
|
2141
|
-
function
|
|
2142
|
-
return
|
|
2151
|
+
function defaultToolCallId(runId, callIndex) {
|
|
2152
|
+
return `${runId}:tool-${callIndex + 1}`;
|
|
2143
2153
|
}
|
|
2144
2154
|
//#endregion
|
|
2145
2155
|
//#region src/runtime/wrap-up.ts
|
|
@@ -2289,7 +2299,7 @@ function minCap(left, right) {
|
|
|
2289
2299
|
//#endregion
|
|
2290
2300
|
//#region src/runtime/broadcast.ts
|
|
2291
2301
|
async function runBroadcast(options) {
|
|
2292
|
-
const runId = createRunId
|
|
2302
|
+
const runId = createRunId();
|
|
2293
2303
|
const events = [];
|
|
2294
2304
|
const transcript = [];
|
|
2295
2305
|
const protocolDecisions = [];
|
|
@@ -2298,7 +2308,7 @@ async function runBroadcast(options) {
|
|
|
2298
2308
|
const maxRounds = options.protocol.maxRounds ?? 2;
|
|
2299
2309
|
let firstRoundContributions = [];
|
|
2300
2310
|
let lastContributions = [];
|
|
2301
|
-
const startedAtMs = nowMs
|
|
2311
|
+
const startedAtMs = nowMs();
|
|
2302
2312
|
let stopped = false;
|
|
2303
2313
|
let termination;
|
|
2304
2314
|
const wrapUpHint = createWrapUpHintController({
|
|
@@ -2375,7 +2385,7 @@ async function runBroadcast(options) {
|
|
|
2375
2385
|
events,
|
|
2376
2386
|
transcript,
|
|
2377
2387
|
iteration: transcript.length,
|
|
2378
|
-
elapsedMs: elapsedMs
|
|
2388
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2379
2389
|
})
|
|
2380
2390
|
};
|
|
2381
2391
|
const response = await generateModelTurn({
|
|
@@ -2385,7 +2395,7 @@ async function runBroadcast(options) {
|
|
|
2385
2395
|
agent,
|
|
2386
2396
|
input,
|
|
2387
2397
|
emit,
|
|
2388
|
-
callId: providerCallIdFor
|
|
2398
|
+
callId: providerCallIdFor(runId, providerCalls.length + agentIndex + 1),
|
|
2389
2399
|
onProviderCall(call) {
|
|
2390
2400
|
providerCallSlots[agentIndex] = call;
|
|
2391
2401
|
}
|
|
@@ -2550,7 +2560,7 @@ async function runBroadcast(options) {
|
|
|
2550
2560
|
events,
|
|
2551
2561
|
transcript,
|
|
2552
2562
|
iteration: transcript.length,
|
|
2553
|
-
elapsedMs: elapsedMs
|
|
2563
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2554
2564
|
}));
|
|
2555
2565
|
if (!stopRecord) return false;
|
|
2556
2566
|
stopped = true;
|
|
@@ -2566,7 +2576,7 @@ async function runBroadcast(options) {
|
|
|
2566
2576
|
reason: record.budgetReason ?? "cost",
|
|
2567
2577
|
cost: totalCost,
|
|
2568
2578
|
iteration: transcript.length,
|
|
2569
|
-
elapsedMs: elapsedMs
|
|
2579
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
2570
2580
|
detail: record.detail ?? {}
|
|
2571
2581
|
};
|
|
2572
2582
|
emit(event);
|
|
@@ -2596,22 +2606,10 @@ function responseCost$3(response) {
|
|
|
2596
2606
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
2597
2607
|
};
|
|
2598
2608
|
}
|
|
2599
|
-
function createRunId$3() {
|
|
2600
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
2601
|
-
}
|
|
2602
|
-
function nowMs$3() {
|
|
2603
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
2604
|
-
}
|
|
2605
|
-
function elapsedMs$3(startedAtMs) {
|
|
2606
|
-
return Math.max(0, nowMs$3() - startedAtMs);
|
|
2607
|
-
}
|
|
2608
|
-
function providerCallIdFor$2(runId, oneBasedIndex) {
|
|
2609
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
2610
|
-
}
|
|
2611
2609
|
//#endregion
|
|
2612
2610
|
//#region src/runtime/coordinator.ts
|
|
2613
2611
|
async function runCoordinator(options) {
|
|
2614
|
-
const runId = createRunId
|
|
2612
|
+
const runId = createRunId();
|
|
2615
2613
|
const events = [];
|
|
2616
2614
|
const transcript = [];
|
|
2617
2615
|
const protocolDecisions = [];
|
|
@@ -2620,7 +2618,7 @@ async function runCoordinator(options) {
|
|
|
2620
2618
|
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
2621
2619
|
const activeAgents = options.agents.slice(0, maxTurns);
|
|
2622
2620
|
const coordinator = activeAgents[0];
|
|
2623
|
-
const startedAtMs = nowMs
|
|
2621
|
+
const startedAtMs = nowMs();
|
|
2624
2622
|
let stopped = false;
|
|
2625
2623
|
let termination;
|
|
2626
2624
|
const wrapUpHint = createWrapUpHintController({
|
|
@@ -2699,7 +2697,7 @@ async function runCoordinator(options) {
|
|
|
2699
2697
|
options,
|
|
2700
2698
|
runId,
|
|
2701
2699
|
turn: transcript.length + index + 1,
|
|
2702
|
-
providerCallId: providerCallIdFor
|
|
2700
|
+
providerCallId: providerCallIdFor(runId, providerCalls.length + index + 1),
|
|
2703
2701
|
providerCallIndex: index,
|
|
2704
2702
|
providerCallSlots,
|
|
2705
2703
|
toolExecutor,
|
|
@@ -2847,7 +2845,7 @@ async function runCoordinator(options) {
|
|
|
2847
2845
|
events,
|
|
2848
2846
|
transcript,
|
|
2849
2847
|
iteration: transcript.length,
|
|
2850
|
-
elapsedMs: elapsedMs
|
|
2848
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2851
2849
|
}));
|
|
2852
2850
|
if (!stopRecord) return false;
|
|
2853
2851
|
stopped = true;
|
|
@@ -2863,7 +2861,7 @@ async function runCoordinator(options) {
|
|
|
2863
2861
|
reason: record.budgetReason ?? "cost",
|
|
2864
2862
|
cost: totalCost,
|
|
2865
2863
|
iteration: transcript.length,
|
|
2866
|
-
elapsedMs: elapsedMs
|
|
2864
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
2867
2865
|
detail: record.detail ?? {}
|
|
2868
2866
|
};
|
|
2869
2867
|
emit(event);
|
|
@@ -2898,7 +2896,7 @@ async function runCoordinatorTurn(turn) {
|
|
|
2898
2896
|
events: turn.events,
|
|
2899
2897
|
transcript: turn.transcript,
|
|
2900
2898
|
iteration: turn.transcript.length,
|
|
2901
|
-
elapsedMs: elapsedMs
|
|
2899
|
+
elapsedMs: elapsedMs(turn.startedAtMs)
|
|
2902
2900
|
})
|
|
2903
2901
|
};
|
|
2904
2902
|
const response = await generateModelTurn({
|
|
@@ -2979,7 +2977,7 @@ async function runCoordinatorWorkerTurn(turn) {
|
|
|
2979
2977
|
events: turn.events,
|
|
2980
2978
|
transcript: turn.transcript,
|
|
2981
2979
|
iteration: turn.turn - 1,
|
|
2982
|
-
elapsedMs: elapsedMs
|
|
2980
|
+
elapsedMs: elapsedMs(turn.startedAtMs)
|
|
2983
2981
|
})
|
|
2984
2982
|
};
|
|
2985
2983
|
const response = await generateModelTurn({
|
|
@@ -3037,22 +3035,10 @@ function responseCost$2(response) {
|
|
|
3037
3035
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
3038
3036
|
};
|
|
3039
3037
|
}
|
|
3040
|
-
function createRunId$2() {
|
|
3041
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
3042
|
-
}
|
|
3043
|
-
function nowMs$2() {
|
|
3044
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
3045
|
-
}
|
|
3046
|
-
function elapsedMs$2(startedAtMs) {
|
|
3047
|
-
return Math.max(0, nowMs$2() - startedAtMs);
|
|
3048
|
-
}
|
|
3049
|
-
function providerCallIdFor$1(runId, oneBasedIndex) {
|
|
3050
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
3051
|
-
}
|
|
3052
3038
|
//#endregion
|
|
3053
3039
|
//#region src/runtime/sequential.ts
|
|
3054
3040
|
async function runSequential(options) {
|
|
3055
|
-
const runId = createRunId
|
|
3041
|
+
const runId = createRunId();
|
|
3056
3042
|
const events = [];
|
|
3057
3043
|
const transcript = [];
|
|
3058
3044
|
const protocolDecisions = [];
|
|
@@ -3060,7 +3046,7 @@ async function runSequential(options) {
|
|
|
3060
3046
|
let totalCost = emptyCost();
|
|
3061
3047
|
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
3062
3048
|
const activeAgents = options.agents.slice(0, maxTurns);
|
|
3063
|
-
const startedAtMs = nowMs
|
|
3049
|
+
const startedAtMs = nowMs();
|
|
3064
3050
|
let stopped = false;
|
|
3065
3051
|
let termination;
|
|
3066
3052
|
const wrapUpHint = createWrapUpHintController({
|
|
@@ -3135,7 +3121,7 @@ async function runSequential(options) {
|
|
|
3135
3121
|
events,
|
|
3136
3122
|
transcript,
|
|
3137
3123
|
iteration: transcript.length,
|
|
3138
|
-
elapsedMs: elapsedMs
|
|
3124
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3139
3125
|
})
|
|
3140
3126
|
};
|
|
3141
3127
|
const response = await generateModelTurn({
|
|
@@ -3270,7 +3256,7 @@ async function runSequential(options) {
|
|
|
3270
3256
|
events,
|
|
3271
3257
|
transcript,
|
|
3272
3258
|
iteration: transcript.length,
|
|
3273
|
-
elapsedMs: elapsedMs
|
|
3259
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3274
3260
|
}));
|
|
3275
3261
|
if (!stopRecord) return false;
|
|
3276
3262
|
stopped = true;
|
|
@@ -3286,7 +3272,7 @@ async function runSequential(options) {
|
|
|
3286
3272
|
reason: record.budgetReason ?? "cost",
|
|
3287
3273
|
cost: totalCost,
|
|
3288
3274
|
iteration: transcript.length,
|
|
3289
|
-
elapsedMs: elapsedMs
|
|
3275
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
3290
3276
|
detail: record.detail ?? {}
|
|
3291
3277
|
};
|
|
3292
3278
|
emit(event);
|
|
@@ -3309,15 +3295,6 @@ function responseCost$1(response) {
|
|
|
3309
3295
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
3310
3296
|
};
|
|
3311
3297
|
}
|
|
3312
|
-
function createRunId$1() {
|
|
3313
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
3314
|
-
}
|
|
3315
|
-
function nowMs$1() {
|
|
3316
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
3317
|
-
}
|
|
3318
|
-
function elapsedMs$1(startedAtMs) {
|
|
3319
|
-
return Math.max(0, nowMs$1() - startedAtMs);
|
|
3320
|
-
}
|
|
3321
3298
|
//#endregion
|
|
3322
3299
|
//#region src/runtime/shared.ts
|
|
3323
3300
|
async function runShared(options) {
|
|
@@ -3594,18 +3571,6 @@ function responseCost(response) {
|
|
|
3594
3571
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
3595
3572
|
};
|
|
3596
3573
|
}
|
|
3597
|
-
function createRunId() {
|
|
3598
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
3599
|
-
}
|
|
3600
|
-
function nowMs() {
|
|
3601
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
3602
|
-
}
|
|
3603
|
-
function elapsedMs(startedAtMs) {
|
|
3604
|
-
return Math.max(0, nowMs() - startedAtMs);
|
|
3605
|
-
}
|
|
3606
|
-
function providerCallIdFor(runId, oneBasedIndex) {
|
|
3607
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
3608
|
-
}
|
|
3609
3574
|
//#endregion
|
|
3610
3575
|
//#region src/runtime/engine.ts
|
|
3611
3576
|
var defaultHighLevelProtocol = "sequential";
|
|
@@ -4515,6 +4480,263 @@ function isJsonValue(value) {
|
|
|
4515
4480
|
return false;
|
|
4516
4481
|
}
|
|
4517
4482
|
//#endregion
|
|
4518
|
-
|
|
4483
|
+
//#region src/runtime/logger.ts
|
|
4484
|
+
/** Logger that drops every call. The default when no logger is supplied. */
|
|
4485
|
+
var noopLogger = {
|
|
4486
|
+
debug() {},
|
|
4487
|
+
info() {},
|
|
4488
|
+
warn() {},
|
|
4489
|
+
error() {}
|
|
4490
|
+
};
|
|
4491
|
+
/**
|
|
4492
|
+
* Build a console-backed logger respecting a minimum level.
|
|
4493
|
+
*
|
|
4494
|
+
* The output format is JSON-on-one-line so it can be piped straight into log
|
|
4495
|
+
* collectors. Use `loggerFromEvents` to bridge it to a Dogpile stream handle.
|
|
4496
|
+
*/
|
|
4497
|
+
function consoleLogger(options = {}) {
|
|
4498
|
+
const minLevel = options.level ?? "info";
|
|
4499
|
+
const allowed = (level) => LEVEL_ORDER[level] >= LEVEL_ORDER[minLevel];
|
|
4500
|
+
const emit = (level, message, fields) => {
|
|
4501
|
+
if (!allowed(level)) return;
|
|
4502
|
+
const payload = {
|
|
4503
|
+
level,
|
|
4504
|
+
message
|
|
4505
|
+
};
|
|
4506
|
+
if (fields !== void 0) payload.fields = fields;
|
|
4507
|
+
(level === "error" ? console.error : level === "warn" ? console.warn : console.log)(JSON.stringify(payload));
|
|
4508
|
+
};
|
|
4509
|
+
return {
|
|
4510
|
+
debug: (message, fields) => emit("debug", message, fields),
|
|
4511
|
+
info: (message, fields) => emit("info", message, fields),
|
|
4512
|
+
warn: (message, fields) => emit("warn", message, fields),
|
|
4513
|
+
error: (message, fields) => emit("error", message, fields)
|
|
4514
|
+
};
|
|
4515
|
+
}
|
|
4516
|
+
var LEVEL_ORDER = {
|
|
4517
|
+
debug: 0,
|
|
4518
|
+
info: 1,
|
|
4519
|
+
warn: 2,
|
|
4520
|
+
error: 3
|
|
4521
|
+
};
|
|
4522
|
+
/**
|
|
4523
|
+
* Bridge a `Logger` to a Dogpile stream handle by returning a
|
|
4524
|
+
* `StreamEventSubscriber`. Pass it to `handle.subscribe(...)`.
|
|
4525
|
+
*
|
|
4526
|
+
* Logger throws are caught and routed to `logger.error` so a misbehaving
|
|
4527
|
+
* logger can never crash an in-flight run.
|
|
4528
|
+
*
|
|
4529
|
+
* @example
|
|
4530
|
+
* ```ts
|
|
4531
|
+
* const handle = Dogpile.stream({ intent, model });
|
|
4532
|
+
* handle.subscribe(loggerFromEvents(consoleLogger({ level: "info" })));
|
|
4533
|
+
* const result = await handle.result;
|
|
4534
|
+
* ```
|
|
4535
|
+
*/
|
|
4536
|
+
function loggerFromEvents(logger, options = {}) {
|
|
4537
|
+
const includeSet = options.include ? new Set(options.include) : void 0;
|
|
4538
|
+
return (event) => {
|
|
4539
|
+
const eventType = event.type;
|
|
4540
|
+
if (includeSet && !includeSet.has(eventType)) return;
|
|
4541
|
+
const level = options.levelFor?.(event) ?? defaultLevel(event);
|
|
4542
|
+
const message = describeEvent(event);
|
|
4543
|
+
const fields = summarizeEvent(event);
|
|
4544
|
+
try {
|
|
4545
|
+
logger[level](message, fields);
|
|
4546
|
+
} catch (cause) {
|
|
4547
|
+
try {
|
|
4548
|
+
logger.error("dogpile logger threw while handling event", {
|
|
4549
|
+
eventType,
|
|
4550
|
+
error: cause instanceof Error ? cause.message : String(cause)
|
|
4551
|
+
});
|
|
4552
|
+
} catch {}
|
|
4553
|
+
}
|
|
4554
|
+
};
|
|
4555
|
+
}
|
|
4556
|
+
function defaultLevel(event) {
|
|
4557
|
+
switch (event.type) {
|
|
4558
|
+
case "model-output-chunk": return "debug";
|
|
4559
|
+
case "budget-stop": return "warn";
|
|
4560
|
+
case "error": return "error";
|
|
4561
|
+
case "tool-result": return event.result?.type === "error" ? "warn" : "info";
|
|
4562
|
+
default: return "info";
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4565
|
+
function describeEvent(event) {
|
|
4566
|
+
return `dogpile:${event.type}`;
|
|
4567
|
+
}
|
|
4568
|
+
function summarizeEvent(event) {
|
|
4569
|
+
const fields = { eventType: event.type };
|
|
4570
|
+
const at = event.at;
|
|
4571
|
+
if (typeof at === "string") fields.at = at;
|
|
4572
|
+
const runId = event.runId;
|
|
4573
|
+
if (typeof runId === "string") fields.runId = runId;
|
|
4574
|
+
const agentId = event.agentId;
|
|
4575
|
+
if (typeof agentId === "string") fields.agentId = agentId;
|
|
4576
|
+
const role = event.role;
|
|
4577
|
+
if (typeof role === "string") fields.role = role;
|
|
4578
|
+
return fields;
|
|
4579
|
+
}
|
|
4580
|
+
//#endregion
|
|
4581
|
+
//#region src/runtime/retry.ts
|
|
4582
|
+
/**
|
|
4583
|
+
* Default DogpileError codes that `withRetry` retries when no `retryOn`
|
|
4584
|
+
* predicate is supplied. These map to the transient provider failures listed
|
|
4585
|
+
* in `docs/developer-usage.md`.
|
|
4586
|
+
*/
|
|
4587
|
+
var DEFAULT_RETRYABLE_DOGPILE_CODES = [
|
|
4588
|
+
"provider-rate-limited",
|
|
4589
|
+
"provider-timeout",
|
|
4590
|
+
"provider-unavailable"
|
|
4591
|
+
];
|
|
4592
|
+
var DEFAULTS = {
|
|
4593
|
+
maxAttempts: 3,
|
|
4594
|
+
baseDelayMs: 250,
|
|
4595
|
+
maxDelayMs: 4e3,
|
|
4596
|
+
jitter: "full"
|
|
4597
|
+
};
|
|
4598
|
+
/**
|
|
4599
|
+
* Wrap a `ConfiguredModelProvider` with a retry policy. The wrapper:
|
|
4600
|
+
*
|
|
4601
|
+
* - Preserves the provider `id` so traces remain stable.
|
|
4602
|
+
* - Retries `generate()` calls when the policy says the error is retryable.
|
|
4603
|
+
* - Propagates `AbortSignal` cancellation immediately — never retries after
|
|
4604
|
+
* the caller cancels.
|
|
4605
|
+
* - Honors a `Retry-After`-style hint exposed via `error.detail.retryAfterMs`
|
|
4606
|
+
* when present and the policy did not provide its own `delayForError`.
|
|
4607
|
+
* - Forwards `stream()` calls through unchanged — streaming retries cannot be
|
|
4608
|
+
* safely automated because partial output may have already been observed.
|
|
4609
|
+
*
|
|
4610
|
+
* @example
|
|
4611
|
+
* ```ts
|
|
4612
|
+
* const robustProvider = withRetry(rawProvider, {
|
|
4613
|
+
* maxAttempts: 4,
|
|
4614
|
+
* baseDelayMs: 500,
|
|
4615
|
+
* onRetry: ({ attempt, delayMs, error }) => {
|
|
4616
|
+
* logger.warn("provider retry", { attempt, delayMs, error });
|
|
4617
|
+
* }
|
|
4618
|
+
* });
|
|
4619
|
+
* ```
|
|
4620
|
+
*/
|
|
4621
|
+
function withRetry(provider, policy = {}) {
|
|
4622
|
+
const settings = {
|
|
4623
|
+
maxAttempts: policy.maxAttempts ?? DEFAULTS.maxAttempts,
|
|
4624
|
+
baseDelayMs: policy.baseDelayMs ?? DEFAULTS.baseDelayMs,
|
|
4625
|
+
maxDelayMs: policy.maxDelayMs ?? DEFAULTS.maxDelayMs,
|
|
4626
|
+
jitter: policy.jitter ?? DEFAULTS.jitter,
|
|
4627
|
+
retryOn: policy.retryOn ?? defaultRetryOn,
|
|
4628
|
+
random: policy.random ?? Math.random,
|
|
4629
|
+
sleep: policy.sleep ?? defaultSleep
|
|
4630
|
+
};
|
|
4631
|
+
if (settings.maxAttempts < 1) throw new DogpileError({
|
|
4632
|
+
code: "invalid-configuration",
|
|
4633
|
+
message: "withRetry: maxAttempts must be >= 1.",
|
|
4634
|
+
detail: { maxAttempts: settings.maxAttempts }
|
|
4635
|
+
});
|
|
4636
|
+
if (settings.baseDelayMs < 0 || settings.maxDelayMs < 0) throw new DogpileError({
|
|
4637
|
+
code: "invalid-configuration",
|
|
4638
|
+
message: "withRetry: delay fields must be non-negative.",
|
|
4639
|
+
detail: {
|
|
4640
|
+
baseDelayMs: settings.baseDelayMs,
|
|
4641
|
+
maxDelayMs: settings.maxDelayMs
|
|
4642
|
+
}
|
|
4643
|
+
});
|
|
4644
|
+
const wrapped = {
|
|
4645
|
+
id: provider.id,
|
|
4646
|
+
async generate(request) {
|
|
4647
|
+
let lastError;
|
|
4648
|
+
for (let attempt = 1; attempt <= settings.maxAttempts; attempt++) {
|
|
4649
|
+
if (request.signal?.aborted) throw abortReason(request.signal);
|
|
4650
|
+
try {
|
|
4651
|
+
return await provider.generate(request);
|
|
4652
|
+
} catch (error) {
|
|
4653
|
+
lastError = error;
|
|
4654
|
+
if (isAbortError(error) || request.signal?.aborted) throw error;
|
|
4655
|
+
if (attempt >= settings.maxAttempts || !settings.retryOn(error)) throw error;
|
|
4656
|
+
const delayMs = chooseDelay({
|
|
4657
|
+
attempt,
|
|
4658
|
+
error,
|
|
4659
|
+
settings,
|
|
4660
|
+
policy
|
|
4661
|
+
});
|
|
4662
|
+
policy.onRetry?.({
|
|
4663
|
+
attempt,
|
|
4664
|
+
maxAttempts: settings.maxAttempts,
|
|
4665
|
+
delayMs,
|
|
4666
|
+
error,
|
|
4667
|
+
providerId: provider.id
|
|
4668
|
+
});
|
|
4669
|
+
await settings.sleep(delayMs, request.signal);
|
|
4670
|
+
}
|
|
4671
|
+
}
|
|
4672
|
+
throw lastError ?? new DogpileError({
|
|
4673
|
+
code: "unknown",
|
|
4674
|
+
message: "withRetry: exhausted attempts without throwing or returning."
|
|
4675
|
+
});
|
|
4676
|
+
}
|
|
4677
|
+
};
|
|
4678
|
+
if (typeof provider.stream === "function") {
|
|
4679
|
+
const upstreamStream = provider.stream.bind(provider);
|
|
4680
|
+
wrapped.stream = (request) => upstreamStream(request);
|
|
4681
|
+
}
|
|
4682
|
+
return wrapped;
|
|
4683
|
+
}
|
|
4684
|
+
function chooseDelay(args) {
|
|
4685
|
+
const override = args.policy.delayForError?.(args.error) ?? retryAfterFromError(args.error);
|
|
4686
|
+
if (override !== void 0 && Number.isFinite(override) && override >= 0) return Math.min(args.settings.maxDelayMs, override);
|
|
4687
|
+
const exponential = args.settings.baseDelayMs * 2 ** (args.attempt - 1);
|
|
4688
|
+
const capped = Math.min(args.settings.maxDelayMs, exponential);
|
|
4689
|
+
if (args.settings.jitter === "none") return capped;
|
|
4690
|
+
return Math.floor(capped * args.settings.random());
|
|
4691
|
+
}
|
|
4692
|
+
function defaultRetryOn(error) {
|
|
4693
|
+
if (isAbortError(error)) return false;
|
|
4694
|
+
if (DogpileError.isInstance(error)) {
|
|
4695
|
+
if (error.code === "aborted" || error.code === "invalid-configuration") return false;
|
|
4696
|
+
return DEFAULT_RETRYABLE_DOGPILE_CODES.includes(error.code);
|
|
4697
|
+
}
|
|
4698
|
+
if (error instanceof TypeError) return true;
|
|
4699
|
+
return false;
|
|
4700
|
+
}
|
|
4701
|
+
function isAbortError(error) {
|
|
4702
|
+
if (DogpileError.isInstance(error) && error.code === "aborted") return true;
|
|
4703
|
+
if (typeof error === "object" && error !== null) {
|
|
4704
|
+
if (error.name === "AbortError") return true;
|
|
4705
|
+
}
|
|
4706
|
+
return false;
|
|
4707
|
+
}
|
|
4708
|
+
function abortReason(signal) {
|
|
4709
|
+
return signal.reason ?? new DogpileError({
|
|
4710
|
+
code: "aborted",
|
|
4711
|
+
message: "Request aborted."
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
function retryAfterFromError(error) {
|
|
4715
|
+
if (!DogpileError.isInstance(error)) return void 0;
|
|
4716
|
+
const detail = error.detail;
|
|
4717
|
+
if (!detail || typeof detail !== "object") return void 0;
|
|
4718
|
+
const candidate = detail.retryAfterMs;
|
|
4719
|
+
if (typeof candidate === "number" && Number.isFinite(candidate) && candidate >= 0) return candidate;
|
|
4720
|
+
}
|
|
4721
|
+
function defaultSleep(ms, signal) {
|
|
4722
|
+
if (ms <= 0) return Promise.resolve();
|
|
4723
|
+
return new Promise((resolve, reject) => {
|
|
4724
|
+
if (signal?.aborted) {
|
|
4725
|
+
reject(abortReason(signal));
|
|
4726
|
+
return;
|
|
4727
|
+
}
|
|
4728
|
+
const timer = setTimeout(() => {
|
|
4729
|
+
signal?.removeEventListener("abort", onAbort);
|
|
4730
|
+
resolve();
|
|
4731
|
+
}, ms);
|
|
4732
|
+
const onAbort = () => {
|
|
4733
|
+
clearTimeout(timer);
|
|
4734
|
+
reject(abortReason(signal));
|
|
4735
|
+
};
|
|
4736
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4737
|
+
});
|
|
4738
|
+
}
|
|
4739
|
+
//#endregion
|
|
4740
|
+
export { DEFAULT_RETRYABLE_DOGPILE_CODES, Dogpile, DogpileError, budget, builtInDogpileToolIdentity, builtInDogpileToolInputSchema, builtInDogpileToolPermissions, combineTerminationDecisions, consoleLogger, convergence, createCodeExecToolAdapter, createEngine, createOpenAICompatibleProvider, createRuntimeToolExecutor, createWebSearchToolAdapter, evaluateBudget, evaluateConvergence, evaluateFirstOf, evaluateJudge, evaluateTermination, evaluateTerminationStop, firstOf, judge, loggerFromEvents, noopLogger, normalizeBuiltInDogpileTool, normalizeBuiltInDogpileTools, normalizeRuntimeToolAdapterError, replay, replayStream, run, runtimeToolAvailability, runtimeToolManifest, stream, validateBuiltInDogpileToolInput, withRetry };
|
|
4519
4741
|
|
|
4520
4742
|
//# sourceMappingURL=index.js.map
|