@dogpile/sdk 0.2.2 → 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 +15 -0
- package/dist/browser/index.js +1044 -507
- package/dist/browser/index.js.map +1 -1
- package/dist/index.d.ts +5 -1
- 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 +1 -0
- package/dist/runtime/broadcast.d.ts.map +1 -1
- package/dist/runtime/broadcast.js +28 -19
- package/dist/runtime/broadcast.js.map +1 -1
- package/dist/runtime/coordinator.d.ts +1 -0
- package/dist/runtime/coordinator.d.ts.map +1 -1
- package/dist/runtime/coordinator.js +46 -21
- package/dist/runtime/coordinator.js.map +1 -1
- package/dist/runtime/engine.d.ts.map +1 -1
- package/dist/runtime/engine.js +5 -0
- package/dist/runtime/engine.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 +1 -0
- package/dist/runtime/sequential.d.ts.map +1 -1
- package/dist/runtime/sequential.js +25 -16
- package/dist/runtime/sequential.js.map +1 -1
- package/dist/runtime/shared.d.ts +1 -0
- package/dist/runtime/shared.d.ts.map +1 -1
- package/dist/runtime/shared.js +25 -19
- package/dist/runtime/shared.js.map +1 -1
- package/dist/runtime/termination.d.ts +6 -1
- package/dist/runtime/termination.d.ts.map +1 -1
- package/dist/runtime/termination.js +75 -0
- package/dist/runtime/termination.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/runtime/validation.d.ts.map +1 -1
- package/dist/runtime/validation.js +22 -0
- package/dist/runtime/validation.js.map +1 -1
- package/dist/runtime/wrap-up.d.ts +26 -0
- package/dist/runtime/wrap-up.d.ts.map +1 -0
- package/dist/runtime/wrap-up.js +178 -0
- package/dist/runtime/wrap-up.js.map +1 -0
- 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 +74 -935
- package/dist/types.d.ts.map +1 -1
- package/package.json +28 -1
- package/src/index.ts +7 -1
- package/src/runtime/broadcast.ts +50 -35
- package/src/runtime/coordinator.ts +84 -43
- package/src/runtime/engine.ts +6 -0
- 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 +46 -31
- package/src/runtime/shared.ts +46 -35
- package/src/runtime/termination.ts +100 -0
- 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/runtime/validation.ts +25 -0
- package/src/runtime/wrap-up.ts +257 -0
- 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 +174 -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;
|
|
@@ -638,6 +671,10 @@ function firstOf(...conditions) {
|
|
|
638
671
|
* own normalized inputs so one stop class cannot accidentally satisfy another.
|
|
639
672
|
*/
|
|
640
673
|
function evaluateTermination(condition, context) {
|
|
674
|
+
if (isTerminationFloorBlocked(condition, context)) return {
|
|
675
|
+
type: "continue",
|
|
676
|
+
condition
|
|
677
|
+
};
|
|
641
678
|
switch (condition.kind) {
|
|
642
679
|
case "budget": return evaluateBudget(condition, context);
|
|
643
680
|
case "firstOf": return evaluateFirstOf(condition, context).decision;
|
|
@@ -695,6 +732,17 @@ function evaluateTerminationStop(condition, context) {
|
|
|
695
732
|
return stopRecord(condition, decision);
|
|
696
733
|
}
|
|
697
734
|
/**
|
|
735
|
+
* Warn when a protocol-level termination floor cannot be satisfied because a
|
|
736
|
+
* lower iteration cap will stop the run first.
|
|
737
|
+
*/
|
|
738
|
+
function warnOnProtocolTerminationMisconfiguration(protocol, terminate, warn = console.warn) {
|
|
739
|
+
const minTurns = protocolMinTurns(protocol);
|
|
740
|
+
if (minTurns === void 0 || !terminate) return;
|
|
741
|
+
const limitingIterationBudget = smallestIterationBudget(terminate);
|
|
742
|
+
if (limitingIterationBudget === void 0 || limitingIterationBudget >= minTurns) return;
|
|
743
|
+
warn(`[dogpile] protocol.minTurns (${minTurns}) exceeds terminate budget maxIterations (${limitingIterationBudget}); maxIterations will win.`);
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
698
746
|
* Combine independently evaluated termination decisions with SDK precedence.
|
|
699
747
|
*
|
|
700
748
|
* Budget caps win over judge decisions, and judge decisions win over
|
|
@@ -843,6 +891,48 @@ function stopPrecedence(reason) {
|
|
|
843
891
|
if (reason.startsWith("judge:")) return 1;
|
|
844
892
|
return 2;
|
|
845
893
|
}
|
|
894
|
+
function isTerminationFloorBlocked(condition, context) {
|
|
895
|
+
if (condition.kind !== "convergence" && condition.kind !== "judge") return false;
|
|
896
|
+
const floor = protocolTerminationFloor(context.protocolConfig);
|
|
897
|
+
if (floor === void 0 || floor <= 0) return false;
|
|
898
|
+
return protocolProgress(context) < floor;
|
|
899
|
+
}
|
|
900
|
+
function protocolTerminationFloor(protocol) {
|
|
901
|
+
if (!protocol) return;
|
|
902
|
+
switch (protocol.kind) {
|
|
903
|
+
case "broadcast": return protocol.minRounds;
|
|
904
|
+
case "coordinator":
|
|
905
|
+
case "sequential":
|
|
906
|
+
case "shared": return protocol.minTurns;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
function protocolProgress(context) {
|
|
910
|
+
return context.protocolIteration ?? context.iteration ?? context.transcript.length;
|
|
911
|
+
}
|
|
912
|
+
function protocolMinTurns(protocol) {
|
|
913
|
+
switch (protocol.kind) {
|
|
914
|
+
case "broadcast": return;
|
|
915
|
+
case "coordinator":
|
|
916
|
+
case "sequential":
|
|
917
|
+
case "shared": return protocol.minTurns;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
function smallestIterationBudget(condition) {
|
|
921
|
+
switch (condition.kind) {
|
|
922
|
+
case "budget": return condition.maxIterations;
|
|
923
|
+
case "convergence":
|
|
924
|
+
case "judge": return;
|
|
925
|
+
case "firstOf": {
|
|
926
|
+
let smallest;
|
|
927
|
+
for (const child of condition.conditions) {
|
|
928
|
+
const budget = smallestIterationBudget(child);
|
|
929
|
+
if (budget === void 0) continue;
|
|
930
|
+
smallest = smallest === void 0 ? budget : Math.min(smallest, budget);
|
|
931
|
+
}
|
|
932
|
+
return smallest;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
846
936
|
function judgeStopDetail(decision, minScore) {
|
|
847
937
|
return {
|
|
848
938
|
decision: decision.type,
|
|
@@ -921,6 +1011,7 @@ function validateDogpileOptions(options) {
|
|
|
921
1011
|
validateOptionalTemperature(options.temperature, "temperature");
|
|
922
1012
|
validateOptionalBudgetCaps(options.budget, "budget");
|
|
923
1013
|
validateOptionalTerminationCondition(options.terminate, "terminate");
|
|
1014
|
+
validateOptionalWrapUpHint(options.wrapUpHint, "wrapUpHint");
|
|
924
1015
|
validateOptionalFunction(options.evaluate, "evaluate");
|
|
925
1016
|
validateOptionalSeed(options.seed, "seed");
|
|
926
1017
|
validateOptionalAbortSignal(options.signal, "signal");
|
|
@@ -941,6 +1032,7 @@ function validateEngineOptions(options) {
|
|
|
941
1032
|
validateOptionalTemperature(options.temperature, "temperature");
|
|
942
1033
|
validateOptionalBudgetCaps(options.budget, "budget");
|
|
943
1034
|
validateOptionalTerminationCondition(options.terminate, "terminate");
|
|
1035
|
+
validateOptionalWrapUpHint(options.wrapUpHint, "wrapUpHint");
|
|
944
1036
|
validateOptionalFunction(options.evaluate, "evaluate");
|
|
945
1037
|
validateOptionalSeed(options.seed, "seed");
|
|
946
1038
|
validateOptionalAbortSignal(options.signal, "signal");
|
|
@@ -973,10 +1065,12 @@ function validateProtocolConfig(value, path) {
|
|
|
973
1065
|
case "sequential":
|
|
974
1066
|
case "shared":
|
|
975
1067
|
validateOptionalPositiveInteger(record.maxTurns, `${path}.maxTurns`);
|
|
1068
|
+
validateOptionalNonNegativeInteger(record.minTurns, `${path}.minTurns`);
|
|
976
1069
|
if (kind === "shared") validateOptionalString(record.organizationalMemory, `${path}.organizationalMemory`);
|
|
977
1070
|
return;
|
|
978
1071
|
case "broadcast":
|
|
979
1072
|
validateOptionalPositiveInteger(record.maxRounds, `${path}.maxRounds`);
|
|
1073
|
+
validateOptionalNonNegativeInteger(record.minRounds, `${path}.minRounds`);
|
|
980
1074
|
return;
|
|
981
1075
|
}
|
|
982
1076
|
}
|
|
@@ -1144,6 +1238,20 @@ function validateJudgeRubric(value, path) {
|
|
|
1144
1238
|
function validateOptionalTemperature(value, path) {
|
|
1145
1239
|
validateOptionalNumberInRange(value, path, 0, 2);
|
|
1146
1240
|
}
|
|
1241
|
+
function validateOptionalWrapUpHint(value, path) {
|
|
1242
|
+
if (value === void 0) return;
|
|
1243
|
+
const record = requireRecord(value, path);
|
|
1244
|
+
validateOptionalNonNegativeInteger(record.atIteration, `${path}.atIteration`);
|
|
1245
|
+
validateOptionalNumberInRange(record.atFraction, `${path}.atFraction`, 0, 1);
|
|
1246
|
+
validateOptionalFunction(record.inject, `${path}.inject`);
|
|
1247
|
+
if (record.atIteration === void 0 && record.atFraction === void 0) invalidConfiguration({
|
|
1248
|
+
path,
|
|
1249
|
+
rule: "object",
|
|
1250
|
+
message: "wrapUpHint must configure atIteration or atFraction.",
|
|
1251
|
+
expected: "WrapUpHintConfig with atIteration or atFraction",
|
|
1252
|
+
actual: value
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1147
1255
|
function validateOptionalSeed(value, path) {
|
|
1148
1256
|
if (value === void 0) return;
|
|
1149
1257
|
if (typeof value === "string") return;
|
|
@@ -1327,7 +1435,7 @@ function describeValue(value) {
|
|
|
1327
1435
|
return typeof value;
|
|
1328
1436
|
}
|
|
1329
1437
|
//#endregion
|
|
1330
|
-
//#region src/runtime/tools.ts
|
|
1438
|
+
//#region src/runtime/tools/built-in.ts
|
|
1331
1439
|
var webSearchIdentity = {
|
|
1332
1440
|
id: "dogpile.tools.webSearch",
|
|
1333
1441
|
namespace: "dogpile",
|
|
@@ -1413,9 +1521,6 @@ function builtInDogpileToolIdentity(name) {
|
|
|
1413
1521
|
function builtInDogpileToolInputSchema(name) {
|
|
1414
1522
|
return name === "webSearch" ? webSearchInputSchema : codeExecInputSchema;
|
|
1415
1523
|
}
|
|
1416
|
-
/**
|
|
1417
|
-
* Return the default permission declarations for one built-in tool name.
|
|
1418
|
-
*/
|
|
1419
1524
|
function builtInDogpileToolPermissions(name) {
|
|
1420
1525
|
return name === "webSearch" ? webSearchPermissions : codeExecPermissions;
|
|
1421
1526
|
}
|
|
@@ -1426,348 +1531,91 @@ function validateBuiltInDogpileToolInput(name, input) {
|
|
|
1426
1531
|
issues
|
|
1427
1532
|
};
|
|
1428
1533
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
validateRuntimeToolRegistrations(options.tools);
|
|
1441
|
-
const tools = Array.from(options.tools);
|
|
1442
|
-
let callCount = 0;
|
|
1443
|
-
return {
|
|
1444
|
-
tools,
|
|
1445
|
-
async execute(request) {
|
|
1446
|
-
const tool = tools.find((candidate) => candidate.identity.id === request.toolId);
|
|
1447
|
-
const identity = tool?.identity ?? {
|
|
1448
|
-
id: request.toolId,
|
|
1449
|
-
name: request.toolId
|
|
1450
|
-
};
|
|
1451
|
-
const callIndex = callCount;
|
|
1452
|
-
callCount += 1;
|
|
1453
|
-
const toolCallId = request.toolCallId ?? options.makeToolCallId?.(identity, callIndex) ?? defaultToolCallId(options.runId, callIndex);
|
|
1454
|
-
const context = createExecutionContext(options, request, toolCallId);
|
|
1455
|
-
options.emit?.({
|
|
1456
|
-
type: "tool-call",
|
|
1457
|
-
runId: options.runId,
|
|
1458
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1459
|
-
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,
|
|
1460
1545
|
tool: identity,
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
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 } : {}
|
|
1464
1556
|
});
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
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,
|
|
1471
1570
|
tool: identity,
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
...request.role ? { role: request.role } : {}
|
|
1475
|
-
});
|
|
1476
|
-
return result;
|
|
1571
|
+
output
|
|
1572
|
+
};
|
|
1477
1573
|
}
|
|
1478
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Return a JSON-serializable manifest for tools visible to a protocol run.
|
|
1482
|
-
*/
|
|
1483
|
-
function runtimeToolManifest(tools) {
|
|
1484
|
-
return tools.map((tool) => {
|
|
1485
|
-
const inputSchema = {
|
|
1486
|
-
kind: tool.inputSchema.kind,
|
|
1487
|
-
schema: tool.inputSchema.schema,
|
|
1488
|
-
...tool.inputSchema.description ? { description: tool.inputSchema.description } : {}
|
|
1489
|
-
};
|
|
1490
|
-
return {
|
|
1491
|
-
identity: runtimeToolIdentityManifest(tool.identity),
|
|
1492
|
-
inputSchema,
|
|
1493
|
-
permissions: Array.from(tool.permissions ?? []).map(runtimeToolPermissionManifest)
|
|
1494
|
-
};
|
|
1495
1574
|
});
|
|
1496
1575
|
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
*/
|
|
1501
|
-
function runtimeToolAvailability(tools) {
|
|
1502
|
-
const manifest = runtimeToolManifest(tools);
|
|
1503
|
-
return manifest.length > 0 ? { tools: manifest } : {};
|
|
1504
|
-
}
|
|
1505
|
-
/**
|
|
1506
|
-
* Execute normalized tool requests returned by a provider response.
|
|
1507
|
-
*/
|
|
1508
|
-
async function executeModelResponseToolRequests(options) {
|
|
1509
|
-
const toolCalls = [];
|
|
1510
|
-
for (const request of options.response.toolRequests ?? []) {
|
|
1511
|
-
const result = await options.executor.execute({
|
|
1512
|
-
...request,
|
|
1513
|
-
agentId: request.agentId ?? options.agentId,
|
|
1514
|
-
role: request.role ?? options.role,
|
|
1515
|
-
turn: request.turn ?? options.turn,
|
|
1516
|
-
metadata: mergeToolMetadata(options.metadata, request.metadata)
|
|
1517
|
-
});
|
|
1518
|
-
toolCalls.push({
|
|
1519
|
-
toolCallId: result.toolCallId,
|
|
1520
|
-
tool: result.tool,
|
|
1521
|
-
input: request.input,
|
|
1522
|
-
result
|
|
1523
|
-
});
|
|
1524
|
-
}
|
|
1525
|
-
return toolCalls;
|
|
1526
|
-
}
|
|
1527
|
-
function runtimeToolIdentityManifest(identity) {
|
|
1528
|
-
return {
|
|
1529
|
-
id: identity.id,
|
|
1530
|
-
name: identity.name,
|
|
1531
|
-
...identity.namespace ? { namespace: identity.namespace } : {},
|
|
1532
|
-
...identity.version ? { version: identity.version } : {},
|
|
1533
|
-
...identity.description ? { description: identity.description } : {}
|
|
1534
|
-
};
|
|
1535
|
-
}
|
|
1536
|
-
function runtimeToolPermissionManifest(permission) {
|
|
1537
|
-
if (permission.kind === "network") return {
|
|
1538
|
-
kind: permission.kind,
|
|
1539
|
-
...permission.allowHosts ? { allowHosts: Array.from(permission.allowHosts) } : {},
|
|
1540
|
-
...permission.allowPrivateNetwork === void 0 ? {} : { allowPrivateNetwork: permission.allowPrivateNetwork }
|
|
1541
|
-
};
|
|
1542
|
-
if (permission.kind === "code-execution") return {
|
|
1543
|
-
kind: permission.kind,
|
|
1544
|
-
sandbox: permission.sandbox,
|
|
1545
|
-
...permission.languages ? { languages: Array.from(permission.languages) } : {},
|
|
1546
|
-
...permission.allowNetwork === void 0 ? {} : { allowNetwork: permission.allowNetwork }
|
|
1547
|
-
};
|
|
1576
|
+
function createCodeExecToolAdapter(options) {
|
|
1577
|
+
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
1578
|
+
const permissions = options.permissions ?? codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
1548
1579
|
return {
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
};
|
|
1589
|
-
}
|
|
1590
|
-
async function executeRuntimeTool(tool, identity, input, context) {
|
|
1591
|
-
if (!tool) return {
|
|
1592
|
-
type: "error",
|
|
1593
|
-
toolCallId: context.toolCallId,
|
|
1594
|
-
tool: identity,
|
|
1595
|
-
error: {
|
|
1596
|
-
code: "unavailable",
|
|
1597
|
-
message: `Runtime tool "${identity.id}" is not registered.`,
|
|
1598
|
-
retryable: false
|
|
1599
|
-
}
|
|
1600
|
-
};
|
|
1601
|
-
const validation = validateRuntimeToolInput(tool, input);
|
|
1602
|
-
if (validation.type === "invalid") return {
|
|
1603
|
-
type: "error",
|
|
1604
|
-
toolCallId: context.toolCallId,
|
|
1605
|
-
tool: identity,
|
|
1606
|
-
error: {
|
|
1607
|
-
code: "invalid-input",
|
|
1608
|
-
message: "Runtime tool input failed validation.",
|
|
1609
|
-
retryable: false,
|
|
1610
|
-
detail: { issues: validation.issues.map((issue) => ({
|
|
1611
|
-
code: issue.code,
|
|
1612
|
-
path: issue.path,
|
|
1613
|
-
message: issue.message,
|
|
1614
|
-
...issue.detail ? { detail: issue.detail } : {}
|
|
1615
|
-
})) }
|
|
1616
|
-
}
|
|
1617
|
-
};
|
|
1618
|
-
try {
|
|
1619
|
-
return await tool.execute(input, context);
|
|
1620
|
-
} catch (error) {
|
|
1621
|
-
return {
|
|
1622
|
-
type: "error",
|
|
1623
|
-
toolCallId: context.toolCallId,
|
|
1624
|
-
tool: identity,
|
|
1625
|
-
error: normalizeRuntimeToolAdapterError(error)
|
|
1626
|
-
};
|
|
1627
|
-
}
|
|
1628
|
-
}
|
|
1629
|
-
function validateRuntimeToolInput(tool, input) {
|
|
1630
|
-
if (typeof tool.validateInput !== "function") return { type: "valid" };
|
|
1631
|
-
return tool.validateInput(input);
|
|
1632
|
-
}
|
|
1633
|
-
function mergeToolMetadata(base, request) {
|
|
1634
|
-
return {
|
|
1635
|
-
...base ?? {},
|
|
1636
|
-
...request ?? {}
|
|
1637
|
-
};
|
|
1638
|
-
}
|
|
1639
|
-
function defaultToolCallId(runId, callIndex) {
|
|
1640
|
-
return `${runId}:tool-${callIndex + 1}`;
|
|
1641
|
-
}
|
|
1642
|
-
/**
|
|
1643
|
-
* Convert an unknown adapter failure into Dogpile's serializable error data.
|
|
1644
|
-
*/
|
|
1645
|
-
function normalizeRuntimeToolAdapterError(error) {
|
|
1646
|
-
if (isRuntimeToolAdapterError(error)) return error;
|
|
1647
|
-
if (error instanceof DOMException && error.name === "AbortError") return {
|
|
1648
|
-
code: "aborted",
|
|
1649
|
-
message: error.message || "Tool execution was aborted.",
|
|
1650
|
-
retryable: true,
|
|
1651
|
-
detail: { name: error.name }
|
|
1652
|
-
};
|
|
1653
|
-
if (error instanceof Error) return {
|
|
1654
|
-
code: "backend-error",
|
|
1655
|
-
message: error.message,
|
|
1656
|
-
retryable: false,
|
|
1657
|
-
detail: { name: error.name }
|
|
1658
|
-
};
|
|
1659
|
-
return {
|
|
1660
|
-
code: "unknown",
|
|
1661
|
-
message: "Tool execution failed with a non-Error value.",
|
|
1662
|
-
retryable: false,
|
|
1663
|
-
detail: { valueType: typeof error }
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
/**
|
|
1667
|
-
* Create Dogpile's built-in fetch-based web search adapter.
|
|
1668
|
-
*
|
|
1669
|
-
* @remarks
|
|
1670
|
-
* The adapter is backend-neutral: by default it sends a GET request with
|
|
1671
|
-
* `q` and `limit` query parameters, then accepts either `{ results: [...] }`
|
|
1672
|
-
* or a bare array of result objects from the response JSON. Callers can replace
|
|
1673
|
-
* request construction or response parsing for a specific search API while
|
|
1674
|
-
* keeping Dogpile's shared runtime tool contract, identity, permissions, input
|
|
1675
|
-
* validation, and serializable errors.
|
|
1676
|
-
*/
|
|
1677
|
-
function createWebSearchToolAdapter(options) {
|
|
1678
|
-
const identity = mergeIdentity(webSearchIdentity, options.identity);
|
|
1679
|
-
return normalizeBuiltInDogpileTool({
|
|
1680
|
-
name: "webSearch",
|
|
1681
|
-
...options.identity ? { identity: options.identity } : {},
|
|
1682
|
-
...options.permissions ? { permissions: options.permissions } : {},
|
|
1683
|
-
async execute(input, context) {
|
|
1684
|
-
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
1685
|
-
if (!fetchImplementation) return {
|
|
1686
|
-
type: "error",
|
|
1687
|
-
toolCallId: context.toolCallId,
|
|
1688
|
-
tool: identity,
|
|
1689
|
-
error: {
|
|
1690
|
-
code: "unavailable",
|
|
1691
|
-
message: "No fetch implementation is available for webSearch.",
|
|
1692
|
-
retryable: false
|
|
1693
|
-
}
|
|
1694
|
-
};
|
|
1695
|
-
const request = options.buildRequest ? options.buildRequest(input, context) : defaultWebSearchRequest(options, input, context);
|
|
1696
|
-
const response = await fetchImplementation(request.url, {
|
|
1697
|
-
...request.init,
|
|
1698
|
-
...context.abortSignal ? { signal: context.abortSignal } : {}
|
|
1699
|
-
});
|
|
1700
|
-
if (!response.ok) throw {
|
|
1701
|
-
code: response.status >= 500 ? "unavailable" : "backend-error",
|
|
1702
|
-
message: `Web search backend returned HTTP ${response.status}.`,
|
|
1703
|
-
retryable: response.status === 408 || response.status === 429 || response.status >= 500,
|
|
1704
|
-
detail: {
|
|
1705
|
-
status: response.status,
|
|
1706
|
-
statusText: response.statusText
|
|
1707
|
-
}
|
|
1708
|
-
};
|
|
1709
|
-
const output = options.parseResponse ? await options.parseResponse(response, input, context) : await defaultWebSearchResponseParser(response);
|
|
1710
|
-
return {
|
|
1711
|
-
type: "success",
|
|
1712
|
-
toolCallId: context.toolCallId,
|
|
1713
|
-
tool: identity,
|
|
1714
|
-
output
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
});
|
|
1718
|
-
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Create Dogpile's built-in code execution adapter around a caller-owned sandbox.
|
|
1721
|
-
*
|
|
1722
|
-
* @remarks
|
|
1723
|
-
* Dogpile core stays runtime-portable and never evaluates code itself. This
|
|
1724
|
-
* adapter supplies the stable `codeExec` identity, schema, permissions,
|
|
1725
|
-
* validation, timeout defaults, abort handling, and serializable errors while
|
|
1726
|
-
* the host application owns the sandbox boundary.
|
|
1727
|
-
*/
|
|
1728
|
-
function createCodeExecToolAdapter(options) {
|
|
1729
|
-
const identity = mergeIdentity(codeExecIdentity, options.identity);
|
|
1730
|
-
const permissions = options.permissions ?? codeExecPermissionsFor(options.languages ?? codeExecLanguages, options.allowNetwork ?? false);
|
|
1731
|
-
return {
|
|
1732
|
-
identity,
|
|
1733
|
-
inputSchema: codeExecInputSchemaFor(options.languages ?? codeExecLanguages),
|
|
1734
|
-
permissions,
|
|
1735
|
-
validateInput: (input) => validateCodeExecAdapterInput(input, options),
|
|
1736
|
-
async execute(input, context) {
|
|
1737
|
-
const validation = validateCodeExecAdapterInput(input, options);
|
|
1738
|
-
if (validation.type === "invalid") return {
|
|
1739
|
-
type: "error",
|
|
1740
|
-
toolCallId: context.toolCallId,
|
|
1741
|
-
tool: identity,
|
|
1742
|
-
error: {
|
|
1743
|
-
code: "invalid-input",
|
|
1744
|
-
message: "Invalid codeExec tool input.",
|
|
1745
|
-
retryable: false,
|
|
1746
|
-
detail: { issues: validation.issues }
|
|
1747
|
-
}
|
|
1748
|
-
};
|
|
1749
|
-
const timeoutMs = input.timeoutMs ?? options.defaultTimeoutMs;
|
|
1750
|
-
const executionInput = timeoutMs === void 0 ? input : {
|
|
1751
|
-
...input,
|
|
1752
|
-
timeoutMs
|
|
1753
|
-
};
|
|
1754
|
-
try {
|
|
1755
|
-
const output = await executeSandboxWithPolicy(options.execute, executionInput, context, timeoutMs);
|
|
1756
|
-
return {
|
|
1757
|
-
type: "success",
|
|
1758
|
-
toolCallId: context.toolCallId,
|
|
1759
|
-
tool: identity,
|
|
1760
|
-
output
|
|
1761
|
-
};
|
|
1762
|
-
} catch (error) {
|
|
1763
|
-
return {
|
|
1764
|
-
type: "error",
|
|
1765
|
-
toolCallId: context.toolCallId,
|
|
1766
|
-
tool: identity,
|
|
1767
|
-
error: normalizeRuntimeToolAdapterError(error)
|
|
1768
|
-
};
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
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
|
+
}
|
|
1618
|
+
}
|
|
1771
1619
|
};
|
|
1772
1620
|
}
|
|
1773
1621
|
function normalizeBuiltInDogpileTool(definition) {
|
|
@@ -1796,9 +1644,6 @@ function normalizeBuiltInDogpileTool(definition) {
|
|
|
1796
1644
|
}
|
|
1797
1645
|
}
|
|
1798
1646
|
}
|
|
1799
|
-
/**
|
|
1800
|
-
* Normalize configured built-in Dogpile tool executors into runtime tools.
|
|
1801
|
-
*/
|
|
1802
1647
|
function normalizeBuiltInDogpileTools(tools) {
|
|
1803
1648
|
const normalized = [];
|
|
1804
1649
|
if (tools.webSearch) normalized.push(normalizeBuiltInDogpileTool(asWebSearchDefinition(tools.webSearch)));
|
|
@@ -1815,7 +1660,7 @@ async function executeBuiltInTool(identity, execute, input, context, name) {
|
|
|
1815
1660
|
code: "invalid-input",
|
|
1816
1661
|
message: `Invalid ${name} tool input.`,
|
|
1817
1662
|
retryable: false,
|
|
1818
|
-
detail: { issues: validation.issues }
|
|
1663
|
+
detail: { issues: serializeValidationIssues(validation.issues) }
|
|
1819
1664
|
}
|
|
1820
1665
|
};
|
|
1821
1666
|
try {
|
|
@@ -1850,6 +1695,27 @@ function mergeIdentity(defaultIdentity, options) {
|
|
|
1850
1695
|
...options.description !== void 0 ? { description: options.description } : {}
|
|
1851
1696
|
};
|
|
1852
1697
|
}
|
|
1698
|
+
function validateCodeExecAdapterInput(input, options) {
|
|
1699
|
+
const issues = [...validateCodeExecInput(input)];
|
|
1700
|
+
const languages = options.languages ?? codeExecLanguages;
|
|
1701
|
+
if (typeof input.language === "string" && isCodeExecLanguage(input.language) && !languages.includes(input.language)) issues.push({
|
|
1702
|
+
code: "invalid-value",
|
|
1703
|
+
path: "language",
|
|
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
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1853
1719
|
function defaultWebSearchRequest(options, input, _context) {
|
|
1854
1720
|
const url = new URL(String(options.endpoint));
|
|
1855
1721
|
url.searchParams.set("q", input.query);
|
|
@@ -1969,107 +1835,471 @@ function jsonString(value, fieldName) {
|
|
|
1969
1835
|
return value;
|
|
1970
1836
|
}
|
|
1971
1837
|
function optionalJsonString(value, fieldName) {
|
|
1972
|
-
if (value === void 0) return;
|
|
1838
|
+
if (value === void 0) return void 0;
|
|
1973
1839
|
if (typeof value !== "string") throw {
|
|
1974
1840
|
code: "backend-error",
|
|
1975
1841
|
message: `Web search result ${fieldName} must be a string when present.`,
|
|
1976
1842
|
retryable: false
|
|
1977
1843
|
};
|
|
1978
|
-
return value;
|
|
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
|
+
};
|
|
2091
|
+
}
|
|
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) } : {}
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
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
|
+
}
|
|
2116
|
+
};
|
|
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
|
+
};
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function mergeToolMetadata(base, request) {
|
|
2146
|
+
return {
|
|
2147
|
+
...base ?? {},
|
|
2148
|
+
...request ?? {}
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
function defaultToolCallId(runId, callIndex) {
|
|
2152
|
+
return `${runId}:tool-${callIndex + 1}`;
|
|
2153
|
+
}
|
|
2154
|
+
//#endregion
|
|
2155
|
+
//#region src/runtime/wrap-up.ts
|
|
2156
|
+
function createWrapUpHintController(options) {
|
|
2157
|
+
const hint = options.wrapUpHint;
|
|
2158
|
+
const effectiveBudget = effectiveWrapUpBudget(options.budget, options.terminate);
|
|
2159
|
+
let emitted = false;
|
|
2160
|
+
return {
|
|
2161
|
+
context(context) {
|
|
2162
|
+
return wrapUpEvaluationContext({
|
|
2163
|
+
...context,
|
|
2164
|
+
tier: options.tier,
|
|
2165
|
+
...effectiveBudget !== void 0 ? { budget: effectiveBudget } : {}
|
|
2166
|
+
});
|
|
2167
|
+
},
|
|
2168
|
+
inject(messages, context) {
|
|
2169
|
+
if (!hint || emitted) return messages;
|
|
2170
|
+
const evaluationContext = wrapUpEvaluationContext({
|
|
2171
|
+
...context,
|
|
2172
|
+
tier: options.tier,
|
|
2173
|
+
...effectiveBudget !== void 0 ? { budget: effectiveBudget } : {}
|
|
2174
|
+
});
|
|
2175
|
+
if (!shouldInjectWrapUpHint(hint, evaluationContext)) return messages;
|
|
2176
|
+
const content = (hint.inject ?? defaultWrapUpHint)(evaluationContext);
|
|
2177
|
+
emitted = true;
|
|
2178
|
+
return [
|
|
2179
|
+
messages[0] ?? {
|
|
2180
|
+
role: "system",
|
|
2181
|
+
content: ""
|
|
2182
|
+
},
|
|
2183
|
+
{
|
|
2184
|
+
role: "system",
|
|
2185
|
+
content
|
|
2186
|
+
},
|
|
2187
|
+
...messages.slice(1)
|
|
2188
|
+
];
|
|
2189
|
+
}
|
|
2190
|
+
};
|
|
1979
2191
|
}
|
|
1980
|
-
function
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
2192
|
+
function wrapUpEvaluationContext(options) {
|
|
2193
|
+
const iteration = options.iteration ?? options.transcript.length;
|
|
2194
|
+
const elapsedMs = options.elapsedMs;
|
|
2195
|
+
return {
|
|
2196
|
+
runId: options.runId,
|
|
2197
|
+
protocol: options.protocol,
|
|
2198
|
+
tier: options.tier,
|
|
2199
|
+
cost: options.cost,
|
|
2200
|
+
events: options.events,
|
|
2201
|
+
transcript: options.transcript,
|
|
2202
|
+
...options.protocolConfig !== void 0 ? { protocolConfig: options.protocolConfig } : {},
|
|
2203
|
+
...iteration !== void 0 ? { iteration } : {},
|
|
2204
|
+
...options.protocolIteration !== void 0 ? { protocolIteration: options.protocolIteration } : {},
|
|
2205
|
+
...elapsedMs !== void 0 ? { elapsedMs } : {},
|
|
2206
|
+
...options.budget !== void 0 ? { budget: options.budget } : {},
|
|
2207
|
+
...options.budget !== void 0 ? { remainingBudget: remainingBudget(options.budget, {
|
|
2208
|
+
cost: options.cost,
|
|
2209
|
+
iteration,
|
|
2210
|
+
elapsedMs
|
|
2211
|
+
}) } : {},
|
|
2212
|
+
...options.metadata !== void 0 ? { metadata: options.metadata } : {}
|
|
1986
2213
|
};
|
|
1987
|
-
return value;
|
|
1988
2214
|
}
|
|
1989
|
-
function
|
|
1990
|
-
|
|
2215
|
+
function shouldInjectWrapUpHint(hint, context) {
|
|
2216
|
+
const iteration = context.iteration ?? context.transcript.length;
|
|
2217
|
+
if (hint.atIteration !== void 0 && iteration >= hint.atIteration) return true;
|
|
2218
|
+
if (hint.atFraction === void 0 || context.budget === void 0) return false;
|
|
2219
|
+
const fraction = hint.atFraction;
|
|
2220
|
+
return capFractionReached(iteration, context.budget.maxIterations, fraction) || capFractionReached(context.elapsedMs, context.budget.timeoutMs, fraction);
|
|
1991
2221
|
}
|
|
1992
|
-
function
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
code: input.query === void 0 ? "missing-field" : "invalid-type",
|
|
1996
|
-
path: "query",
|
|
1997
|
-
message: "webSearch.query must be a string."
|
|
1998
|
-
});
|
|
1999
|
-
else if (input.query.trim().length === 0) issues.push({
|
|
2000
|
-
code: "invalid-value",
|
|
2001
|
-
path: "query",
|
|
2002
|
-
message: "webSearch.query must not be empty."
|
|
2003
|
-
});
|
|
2004
|
-
if (input.maxResults !== void 0) {
|
|
2005
|
-
if (typeof input.maxResults !== "number" || !Number.isFinite(input.maxResults)) issues.push({
|
|
2006
|
-
code: "invalid-type",
|
|
2007
|
-
path: "maxResults",
|
|
2008
|
-
message: "webSearch.maxResults must be a finite number."
|
|
2009
|
-
});
|
|
2010
|
-
else if (input.maxResults < 1) issues.push({
|
|
2011
|
-
code: "out-of-range",
|
|
2012
|
-
path: "maxResults",
|
|
2013
|
-
message: "webSearch.maxResults must be greater than or equal to 1.",
|
|
2014
|
-
detail: { minimum: 1 }
|
|
2015
|
-
});
|
|
2016
|
-
}
|
|
2017
|
-
return issues;
|
|
2222
|
+
function capFractionReached(current, limit, fraction) {
|
|
2223
|
+
if (current === void 0 || limit === void 0 || limit <= 0) return false;
|
|
2224
|
+
return current / limit >= fraction;
|
|
2018
2225
|
}
|
|
2019
|
-
function
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
"python",
|
|
2034
|
-
"bash",
|
|
2035
|
-
"shell"
|
|
2036
|
-
] }
|
|
2037
|
-
});
|
|
2038
|
-
if (typeof input.code !== "string") issues.push({
|
|
2039
|
-
code: input.code === void 0 ? "missing-field" : "invalid-type",
|
|
2040
|
-
path: "code",
|
|
2041
|
-
message: "codeExec.code must be a string."
|
|
2042
|
-
});
|
|
2043
|
-
if (input.timeoutMs !== void 0) {
|
|
2044
|
-
if (typeof input.timeoutMs !== "number" || !Number.isFinite(input.timeoutMs)) issues.push({
|
|
2045
|
-
code: "invalid-type",
|
|
2046
|
-
path: "timeoutMs",
|
|
2047
|
-
message: "codeExec.timeoutMs must be a finite number."
|
|
2048
|
-
});
|
|
2049
|
-
else if (input.timeoutMs < 1) issues.push({
|
|
2050
|
-
code: "out-of-range",
|
|
2051
|
-
path: "timeoutMs",
|
|
2052
|
-
message: "codeExec.timeoutMs must be greater than or equal to 1.",
|
|
2053
|
-
detail: { minimum: 1 }
|
|
2054
|
-
});
|
|
2226
|
+
function remainingBudget(budget, current) {
|
|
2227
|
+
return {
|
|
2228
|
+
...budget.maxIterations !== void 0 && current.iteration !== void 0 ? { iterations: Math.max(0, budget.maxIterations - current.iteration) } : {},
|
|
2229
|
+
...budget.timeoutMs !== void 0 && current.elapsedMs !== void 0 ? { timeoutMs: Math.max(0, budget.timeoutMs - current.elapsedMs) } : {},
|
|
2230
|
+
...budget.maxUsd !== void 0 ? { usd: Math.max(0, budget.maxUsd - current.cost.usd) } : {},
|
|
2231
|
+
...budget.maxTokens !== void 0 ? { tokens: Math.max(0, budget.maxTokens - current.cost.totalTokens) } : {}
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
function defaultWrapUpHint(context) {
|
|
2235
|
+
const parts = [];
|
|
2236
|
+
const remaining = context.remainingBudget;
|
|
2237
|
+
if (remaining?.iterations !== void 0) {
|
|
2238
|
+
const label = remaining.iterations === 1 ? "turn" : "turns";
|
|
2239
|
+
parts.push(`${remaining.iterations} ${label} remaining`);
|
|
2055
2240
|
}
|
|
2056
|
-
|
|
2241
|
+
if (remaining?.timeoutMs !== void 0) parts.push(`${formatRemainingTime(remaining.timeoutMs)} remaining`);
|
|
2242
|
+
return `[wrap-up] ${parts.length === 0 ? "You are approaching a configured hard limit." : `You are approaching a hard limit with ${parts.join(" and ")}.`} If you have enough context, package your work now and return a final-ready answer.`;
|
|
2243
|
+
}
|
|
2244
|
+
function formatRemainingTime(timeoutMs) {
|
|
2245
|
+
if (timeoutMs >= 1e3) return `${(timeoutMs / 1e3).toFixed(1)}s`;
|
|
2246
|
+
return `${timeoutMs}ms`;
|
|
2247
|
+
}
|
|
2248
|
+
function effectiveWrapUpBudget(budget, terminate) {
|
|
2249
|
+
const terminationBudget = budgetCapsFromTermination(terminate);
|
|
2250
|
+
if (!budget && !terminationBudget) return;
|
|
2251
|
+
const maxUsd = minCap(budget?.maxUsd, terminationBudget?.maxUsd);
|
|
2252
|
+
const maxTokens = minCap(budget?.maxTokens, terminationBudget?.maxTokens);
|
|
2253
|
+
const maxIterations = minCap(budget?.maxIterations, terminationBudget?.maxIterations);
|
|
2254
|
+
const timeoutMs = minCap(budget?.timeoutMs, terminationBudget?.timeoutMs);
|
|
2255
|
+
return {
|
|
2256
|
+
...maxUsd !== void 0 ? { maxUsd } : {},
|
|
2257
|
+
...maxTokens !== void 0 ? { maxTokens } : {},
|
|
2258
|
+
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
2259
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
2260
|
+
};
|
|
2057
2261
|
}
|
|
2058
|
-
function
|
|
2059
|
-
|
|
2262
|
+
function budgetCapsFromTermination(condition) {
|
|
2263
|
+
if (!condition) return;
|
|
2264
|
+
switch (condition.kind) {
|
|
2265
|
+
case "budget": return {
|
|
2266
|
+
...condition.maxUsd !== void 0 ? { maxUsd: condition.maxUsd } : {},
|
|
2267
|
+
...condition.maxTokens !== void 0 ? { maxTokens: condition.maxTokens } : {},
|
|
2268
|
+
...condition.maxIterations !== void 0 ? { maxIterations: condition.maxIterations } : {},
|
|
2269
|
+
...condition.timeoutMs !== void 0 ? { timeoutMs: condition.timeoutMs } : {}
|
|
2270
|
+
};
|
|
2271
|
+
case "firstOf": {
|
|
2272
|
+
let merged;
|
|
2273
|
+
for (const child of condition.conditions) merged = mergeBudgetCaps(merged, budgetCapsFromTermination(child));
|
|
2274
|
+
return merged;
|
|
2275
|
+
}
|
|
2276
|
+
case "convergence":
|
|
2277
|
+
case "judge": return;
|
|
2278
|
+
}
|
|
2060
2279
|
}
|
|
2061
|
-
function
|
|
2062
|
-
if (
|
|
2063
|
-
|
|
2064
|
-
|
|
2280
|
+
function mergeBudgetCaps(left, right) {
|
|
2281
|
+
if (!left) return right;
|
|
2282
|
+
if (!right) return left;
|
|
2283
|
+
const maxUsd = minCap(left.maxUsd, right.maxUsd);
|
|
2284
|
+
const maxTokens = minCap(left.maxTokens, right.maxTokens);
|
|
2285
|
+
const maxIterations = minCap(left.maxIterations, right.maxIterations);
|
|
2286
|
+
const timeoutMs = minCap(left.timeoutMs, right.timeoutMs);
|
|
2287
|
+
return {
|
|
2288
|
+
...maxUsd !== void 0 ? { maxUsd } : {},
|
|
2289
|
+
...maxTokens !== void 0 ? { maxTokens } : {},
|
|
2290
|
+
...maxIterations !== void 0 ? { maxIterations } : {},
|
|
2291
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
2292
|
+
};
|
|
2065
2293
|
}
|
|
2066
|
-
function
|
|
2067
|
-
|
|
2294
|
+
function minCap(left, right) {
|
|
2295
|
+
if (left === void 0) return right;
|
|
2296
|
+
if (right === void 0) return left;
|
|
2297
|
+
return Math.min(left, right);
|
|
2068
2298
|
}
|
|
2069
2299
|
//#endregion
|
|
2070
2300
|
//#region src/runtime/broadcast.ts
|
|
2071
2301
|
async function runBroadcast(options) {
|
|
2072
|
-
const runId = createRunId
|
|
2302
|
+
const runId = createRunId();
|
|
2073
2303
|
const events = [];
|
|
2074
2304
|
const transcript = [];
|
|
2075
2305
|
const protocolDecisions = [];
|
|
@@ -2078,9 +2308,17 @@ async function runBroadcast(options) {
|
|
|
2078
2308
|
const maxRounds = options.protocol.maxRounds ?? 2;
|
|
2079
2309
|
let firstRoundContributions = [];
|
|
2080
2310
|
let lastContributions = [];
|
|
2081
|
-
const startedAtMs = nowMs
|
|
2311
|
+
const startedAtMs = nowMs();
|
|
2082
2312
|
let stopped = false;
|
|
2083
2313
|
let termination;
|
|
2314
|
+
const wrapUpHint = createWrapUpHintController({
|
|
2315
|
+
protocol: options.protocol,
|
|
2316
|
+
tier: options.tier,
|
|
2317
|
+
...options.budget ? { budget: options.budget } : {},
|
|
2318
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
2319
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {}
|
|
2320
|
+
});
|
|
2321
|
+
warnOnProtocolTerminationMisconfiguration(options.protocol, options.terminate);
|
|
2084
2322
|
const emit = (event) => {
|
|
2085
2323
|
events.push(event);
|
|
2086
2324
|
options.emit?.(event);
|
|
@@ -2134,13 +2372,21 @@ async function runBroadcast(options) {
|
|
|
2134
2372
|
round,
|
|
2135
2373
|
...toolAvailability
|
|
2136
2374
|
},
|
|
2137
|
-
messages: [{
|
|
2375
|
+
messages: wrapUpHint.inject([{
|
|
2138
2376
|
role: "system",
|
|
2139
2377
|
content: buildSystemPrompt$3(agent)
|
|
2140
2378
|
}, {
|
|
2141
2379
|
role: "user",
|
|
2142
2380
|
content: input
|
|
2143
|
-
}]
|
|
2381
|
+
}], {
|
|
2382
|
+
runId,
|
|
2383
|
+
protocol: "broadcast",
|
|
2384
|
+
cost: totalCost,
|
|
2385
|
+
events,
|
|
2386
|
+
transcript,
|
|
2387
|
+
iteration: transcript.length,
|
|
2388
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2389
|
+
})
|
|
2144
2390
|
};
|
|
2145
2391
|
const response = await generateModelTurn({
|
|
2146
2392
|
model: options.model,
|
|
@@ -2149,7 +2395,7 @@ async function runBroadcast(options) {
|
|
|
2149
2395
|
agent,
|
|
2150
2396
|
input,
|
|
2151
2397
|
emit,
|
|
2152
|
-
callId: providerCallIdFor
|
|
2398
|
+
callId: providerCallIdFor(runId, providerCalls.length + agentIndex + 1),
|
|
2153
2399
|
onProviderCall(call) {
|
|
2154
2400
|
providerCallSlots[agentIndex] = call;
|
|
2155
2401
|
}
|
|
@@ -2305,16 +2551,17 @@ async function runBroadcast(options) {
|
|
|
2305
2551
|
function stopIfNeeded() {
|
|
2306
2552
|
throwIfAborted(options.signal, options.model.id);
|
|
2307
2553
|
if (stopped || !options.terminate) return stopped;
|
|
2308
|
-
const stopRecord = evaluateTerminationStop(options.terminate, {
|
|
2554
|
+
const stopRecord = evaluateTerminationStop(options.terminate, wrapUpHint.context({
|
|
2309
2555
|
runId,
|
|
2310
2556
|
protocol: "broadcast",
|
|
2311
|
-
|
|
2557
|
+
protocolConfig: options.protocol,
|
|
2558
|
+
protocolIteration: broadcastRoundsCompleted(events),
|
|
2312
2559
|
cost: totalCost,
|
|
2313
2560
|
events,
|
|
2314
2561
|
transcript,
|
|
2315
2562
|
iteration: transcript.length,
|
|
2316
|
-
elapsedMs: elapsedMs
|
|
2317
|
-
});
|
|
2563
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2564
|
+
}));
|
|
2318
2565
|
if (!stopRecord) return false;
|
|
2319
2566
|
stopped = true;
|
|
2320
2567
|
termination = stopRecord;
|
|
@@ -2329,13 +2576,16 @@ async function runBroadcast(options) {
|
|
|
2329
2576
|
reason: record.budgetReason ?? "cost",
|
|
2330
2577
|
cost: totalCost,
|
|
2331
2578
|
iteration: transcript.length,
|
|
2332
|
-
elapsedMs: elapsedMs
|
|
2579
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
2333
2580
|
detail: record.detail ?? {}
|
|
2334
2581
|
};
|
|
2335
2582
|
emit(event);
|
|
2336
2583
|
recordProtocolDecision(event, { transcriptEntryCount: transcript.length });
|
|
2337
2584
|
}
|
|
2338
2585
|
}
|
|
2586
|
+
function broadcastRoundsCompleted(events) {
|
|
2587
|
+
return events.filter((event) => event.type === "broadcast").length;
|
|
2588
|
+
}
|
|
2339
2589
|
function buildSystemPrompt$3(agent) {
|
|
2340
2590
|
const instruction = agent.instructions ? `\nInstructions: ${agent.instructions}` : "";
|
|
2341
2591
|
return `You are ${agent.id}, acting as ${agent.role} in a Broadcast multi-agent protocol.${instruction}`;
|
|
@@ -2356,22 +2606,10 @@ function responseCost$3(response) {
|
|
|
2356
2606
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
2357
2607
|
};
|
|
2358
2608
|
}
|
|
2359
|
-
function createRunId$3() {
|
|
2360
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
2361
|
-
}
|
|
2362
|
-
function nowMs$3() {
|
|
2363
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
2364
|
-
}
|
|
2365
|
-
function elapsedMs$3(startedAtMs) {
|
|
2366
|
-
return Math.max(0, nowMs$3() - startedAtMs);
|
|
2367
|
-
}
|
|
2368
|
-
function providerCallIdFor$2(runId, oneBasedIndex) {
|
|
2369
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
2370
|
-
}
|
|
2371
2609
|
//#endregion
|
|
2372
2610
|
//#region src/runtime/coordinator.ts
|
|
2373
2611
|
async function runCoordinator(options) {
|
|
2374
|
-
const runId = createRunId
|
|
2612
|
+
const runId = createRunId();
|
|
2375
2613
|
const events = [];
|
|
2376
2614
|
const transcript = [];
|
|
2377
2615
|
const protocolDecisions = [];
|
|
@@ -2380,9 +2618,17 @@ async function runCoordinator(options) {
|
|
|
2380
2618
|
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
2381
2619
|
const activeAgents = options.agents.slice(0, maxTurns);
|
|
2382
2620
|
const coordinator = activeAgents[0];
|
|
2383
|
-
const startedAtMs = nowMs
|
|
2621
|
+
const startedAtMs = nowMs();
|
|
2384
2622
|
let stopped = false;
|
|
2385
2623
|
let termination;
|
|
2624
|
+
const wrapUpHint = createWrapUpHintController({
|
|
2625
|
+
protocol: options.protocol,
|
|
2626
|
+
tier: options.tier,
|
|
2627
|
+
...options.budget ? { budget: options.budget } : {},
|
|
2628
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
2629
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {}
|
|
2630
|
+
});
|
|
2631
|
+
warnOnProtocolTerminationMisconfiguration(options.protocol, options.terminate);
|
|
2386
2632
|
const emit = (event) => {
|
|
2387
2633
|
events.push(event);
|
|
2388
2634
|
options.emit?.(event);
|
|
@@ -2432,6 +2678,9 @@ async function runCoordinator(options) {
|
|
|
2432
2678
|
providerCalls,
|
|
2433
2679
|
toolExecutor,
|
|
2434
2680
|
toolAvailability,
|
|
2681
|
+
events,
|
|
2682
|
+
startedAtMs,
|
|
2683
|
+
wrapUpHint,
|
|
2435
2684
|
emit,
|
|
2436
2685
|
recordProtocolDecision
|
|
2437
2686
|
});
|
|
@@ -2448,11 +2697,16 @@ async function runCoordinator(options) {
|
|
|
2448
2697
|
options,
|
|
2449
2698
|
runId,
|
|
2450
2699
|
turn: transcript.length + index + 1,
|
|
2451
|
-
providerCallId: providerCallIdFor
|
|
2700
|
+
providerCallId: providerCallIdFor(runId, providerCalls.length + index + 1),
|
|
2452
2701
|
providerCallIndex: index,
|
|
2453
2702
|
providerCallSlots,
|
|
2454
2703
|
toolExecutor,
|
|
2455
2704
|
toolAvailability,
|
|
2705
|
+
totalCost,
|
|
2706
|
+
events,
|
|
2707
|
+
transcript: planTranscript,
|
|
2708
|
+
startedAtMs,
|
|
2709
|
+
wrapUpHint,
|
|
2456
2710
|
emit
|
|
2457
2711
|
})));
|
|
2458
2712
|
providerCalls.push(...providerCallSlots.filter((call) => call !== void 0));
|
|
@@ -2499,6 +2753,9 @@ async function runCoordinator(options) {
|
|
|
2499
2753
|
providerCalls,
|
|
2500
2754
|
toolExecutor,
|
|
2501
2755
|
toolAvailability,
|
|
2756
|
+
events,
|
|
2757
|
+
startedAtMs,
|
|
2758
|
+
wrapUpHint,
|
|
2502
2759
|
emit,
|
|
2503
2760
|
recordProtocolDecision
|
|
2504
2761
|
});
|
|
@@ -2579,16 +2836,17 @@ async function runCoordinator(options) {
|
|
|
2579
2836
|
function stopIfNeeded() {
|
|
2580
2837
|
throwIfAborted(options.signal, options.model.id);
|
|
2581
2838
|
if (stopped || !options.terminate) return stopped;
|
|
2582
|
-
const stopRecord = evaluateTerminationStop(options.terminate, {
|
|
2839
|
+
const stopRecord = evaluateTerminationStop(options.terminate, wrapUpHint.context({
|
|
2583
2840
|
runId,
|
|
2584
2841
|
protocol: "coordinator",
|
|
2585
|
-
|
|
2842
|
+
protocolConfig: options.protocol,
|
|
2843
|
+
protocolIteration: transcript.length,
|
|
2586
2844
|
cost: totalCost,
|
|
2587
2845
|
events,
|
|
2588
2846
|
transcript,
|
|
2589
2847
|
iteration: transcript.length,
|
|
2590
|
-
elapsedMs: elapsedMs
|
|
2591
|
-
});
|
|
2848
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
2849
|
+
}));
|
|
2592
2850
|
if (!stopRecord) return false;
|
|
2593
2851
|
stopped = true;
|
|
2594
2852
|
termination = stopRecord;
|
|
@@ -2603,7 +2861,7 @@ async function runCoordinator(options) {
|
|
|
2603
2861
|
reason: record.budgetReason ?? "cost",
|
|
2604
2862
|
cost: totalCost,
|
|
2605
2863
|
iteration: transcript.length,
|
|
2606
|
-
elapsedMs: elapsedMs
|
|
2864
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
2607
2865
|
detail: record.detail ?? {}
|
|
2608
2866
|
};
|
|
2609
2867
|
emit(event);
|
|
@@ -2625,13 +2883,21 @@ async function runCoordinatorTurn(turn) {
|
|
|
2625
2883
|
phase: turn.phase,
|
|
2626
2884
|
...turn.toolAvailability
|
|
2627
2885
|
},
|
|
2628
|
-
messages: [{
|
|
2886
|
+
messages: turn.wrapUpHint.inject([{
|
|
2629
2887
|
role: "system",
|
|
2630
2888
|
content: buildSystemPrompt$2(turn.agent, turn.coordinator)
|
|
2631
2889
|
}, {
|
|
2632
2890
|
role: "user",
|
|
2633
2891
|
content: turn.input
|
|
2634
|
-
}]
|
|
2892
|
+
}], {
|
|
2893
|
+
runId: turn.runId,
|
|
2894
|
+
protocol: "coordinator",
|
|
2895
|
+
cost: turn.totalCost,
|
|
2896
|
+
events: turn.events,
|
|
2897
|
+
transcript: turn.transcript,
|
|
2898
|
+
iteration: turn.transcript.length,
|
|
2899
|
+
elapsedMs: elapsedMs(turn.startedAtMs)
|
|
2900
|
+
})
|
|
2635
2901
|
};
|
|
2636
2902
|
const response = await generateModelTurn({
|
|
2637
2903
|
model: turn.options.model,
|
|
@@ -2698,13 +2964,21 @@ async function runCoordinatorWorkerTurn(turn) {
|
|
|
2698
2964
|
phase: "worker",
|
|
2699
2965
|
...turn.toolAvailability
|
|
2700
2966
|
},
|
|
2701
|
-
messages: [{
|
|
2967
|
+
messages: turn.wrapUpHint.inject([{
|
|
2702
2968
|
role: "system",
|
|
2703
2969
|
content: buildSystemPrompt$2(turn.agent, turn.coordinator)
|
|
2704
2970
|
}, {
|
|
2705
2971
|
role: "user",
|
|
2706
2972
|
content: turn.input
|
|
2707
|
-
}]
|
|
2973
|
+
}], {
|
|
2974
|
+
runId: turn.runId,
|
|
2975
|
+
protocol: "coordinator",
|
|
2976
|
+
cost: turn.totalCost,
|
|
2977
|
+
events: turn.events,
|
|
2978
|
+
transcript: turn.transcript,
|
|
2979
|
+
iteration: turn.turn - 1,
|
|
2980
|
+
elapsedMs: elapsedMs(turn.startedAtMs)
|
|
2981
|
+
})
|
|
2708
2982
|
};
|
|
2709
2983
|
const response = await generateModelTurn({
|
|
2710
2984
|
model: turn.options.model,
|
|
@@ -2761,22 +3035,10 @@ function responseCost$2(response) {
|
|
|
2761
3035
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
2762
3036
|
};
|
|
2763
3037
|
}
|
|
2764
|
-
function createRunId$2() {
|
|
2765
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
2766
|
-
}
|
|
2767
|
-
function nowMs$2() {
|
|
2768
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
2769
|
-
}
|
|
2770
|
-
function elapsedMs$2(startedAtMs) {
|
|
2771
|
-
return Math.max(0, nowMs$2() - startedAtMs);
|
|
2772
|
-
}
|
|
2773
|
-
function providerCallIdFor$1(runId, oneBasedIndex) {
|
|
2774
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
2775
|
-
}
|
|
2776
3038
|
//#endregion
|
|
2777
3039
|
//#region src/runtime/sequential.ts
|
|
2778
3040
|
async function runSequential(options) {
|
|
2779
|
-
const runId = createRunId
|
|
3041
|
+
const runId = createRunId();
|
|
2780
3042
|
const events = [];
|
|
2781
3043
|
const transcript = [];
|
|
2782
3044
|
const protocolDecisions = [];
|
|
@@ -2784,9 +3046,17 @@ async function runSequential(options) {
|
|
|
2784
3046
|
let totalCost = emptyCost();
|
|
2785
3047
|
const maxTurns = options.protocol.maxTurns ?? options.agents.length;
|
|
2786
3048
|
const activeAgents = options.agents.slice(0, maxTurns);
|
|
2787
|
-
const startedAtMs = nowMs
|
|
3049
|
+
const startedAtMs = nowMs();
|
|
2788
3050
|
let stopped = false;
|
|
2789
3051
|
let termination;
|
|
3052
|
+
const wrapUpHint = createWrapUpHintController({
|
|
3053
|
+
protocol: options.protocol,
|
|
3054
|
+
tier: options.tier,
|
|
3055
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3056
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
3057
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {}
|
|
3058
|
+
});
|
|
3059
|
+
warnOnProtocolTerminationMisconfiguration(options.protocol, options.terminate);
|
|
2790
3060
|
const emit = (event) => {
|
|
2791
3061
|
events.push(event);
|
|
2792
3062
|
options.emit?.(event);
|
|
@@ -2838,13 +3108,21 @@ async function runSequential(options) {
|
|
|
2838
3108
|
turn,
|
|
2839
3109
|
...toolAvailability
|
|
2840
3110
|
},
|
|
2841
|
-
messages: [{
|
|
3111
|
+
messages: wrapUpHint.inject([{
|
|
2842
3112
|
role: "system",
|
|
2843
3113
|
content: buildSystemPrompt$1(agent)
|
|
2844
3114
|
}, {
|
|
2845
3115
|
role: "user",
|
|
2846
3116
|
content: input
|
|
2847
|
-
}]
|
|
3117
|
+
}], {
|
|
3118
|
+
runId,
|
|
3119
|
+
protocol: "sequential",
|
|
3120
|
+
cost: totalCost,
|
|
3121
|
+
events,
|
|
3122
|
+
transcript,
|
|
3123
|
+
iteration: transcript.length,
|
|
3124
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3125
|
+
})
|
|
2848
3126
|
};
|
|
2849
3127
|
const response = await generateModelTurn({
|
|
2850
3128
|
model: options.model,
|
|
@@ -2969,16 +3247,17 @@ async function runSequential(options) {
|
|
|
2969
3247
|
function stopIfNeeded() {
|
|
2970
3248
|
throwIfAborted(options.signal, options.model.id);
|
|
2971
3249
|
if (stopped || !options.terminate) return stopped;
|
|
2972
|
-
const stopRecord = evaluateTerminationStop(options.terminate, {
|
|
3250
|
+
const stopRecord = evaluateTerminationStop(options.terminate, wrapUpHint.context({
|
|
2973
3251
|
runId,
|
|
2974
3252
|
protocol: "sequential",
|
|
2975
|
-
|
|
3253
|
+
protocolConfig: options.protocol,
|
|
3254
|
+
protocolIteration: transcript.length,
|
|
2976
3255
|
cost: totalCost,
|
|
2977
3256
|
events,
|
|
2978
3257
|
transcript,
|
|
2979
3258
|
iteration: transcript.length,
|
|
2980
|
-
elapsedMs: elapsedMs
|
|
2981
|
-
});
|
|
3259
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3260
|
+
}));
|
|
2982
3261
|
if (!stopRecord) return false;
|
|
2983
3262
|
stopped = true;
|
|
2984
3263
|
termination = stopRecord;
|
|
@@ -2993,7 +3272,7 @@ async function runSequential(options) {
|
|
|
2993
3272
|
reason: record.budgetReason ?? "cost",
|
|
2994
3273
|
cost: totalCost,
|
|
2995
3274
|
iteration: transcript.length,
|
|
2996
|
-
elapsedMs: elapsedMs
|
|
3275
|
+
elapsedMs: elapsedMs(startedAtMs),
|
|
2997
3276
|
detail: record.detail ?? {}
|
|
2998
3277
|
};
|
|
2999
3278
|
emit(event);
|
|
@@ -3016,15 +3295,6 @@ function responseCost$1(response) {
|
|
|
3016
3295
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
3017
3296
|
};
|
|
3018
3297
|
}
|
|
3019
|
-
function createRunId$1() {
|
|
3020
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
3021
|
-
}
|
|
3022
|
-
function nowMs$1() {
|
|
3023
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
3024
|
-
}
|
|
3025
|
-
function elapsedMs$1(startedAtMs) {
|
|
3026
|
-
return Math.max(0, nowMs$1() - startedAtMs);
|
|
3027
|
-
}
|
|
3028
3298
|
//#endregion
|
|
3029
3299
|
//#region src/runtime/shared.ts
|
|
3030
3300
|
async function runShared(options) {
|
|
@@ -3040,6 +3310,14 @@ async function runShared(options) {
|
|
|
3040
3310
|
const startedAtMs = nowMs();
|
|
3041
3311
|
let stopped = false;
|
|
3042
3312
|
let termination;
|
|
3313
|
+
const wrapUpHint = createWrapUpHintController({
|
|
3314
|
+
protocol: options.protocol,
|
|
3315
|
+
tier: options.tier,
|
|
3316
|
+
...options.budget ? { budget: options.budget } : {},
|
|
3317
|
+
...options.terminate ? { terminate: options.terminate } : {},
|
|
3318
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {}
|
|
3319
|
+
});
|
|
3320
|
+
warnOnProtocolTerminationMisconfiguration(options.protocol, options.terminate);
|
|
3043
3321
|
const emit = (event) => {
|
|
3044
3322
|
events.push(event);
|
|
3045
3323
|
options.emit?.(event);
|
|
@@ -3092,13 +3370,21 @@ async function runShared(options) {
|
|
|
3092
3370
|
turn,
|
|
3093
3371
|
...toolAvailability
|
|
3094
3372
|
},
|
|
3095
|
-
messages: [{
|
|
3373
|
+
messages: wrapUpHint.inject([{
|
|
3096
3374
|
role: "system",
|
|
3097
3375
|
content: buildSystemPrompt(agent)
|
|
3098
3376
|
}, {
|
|
3099
3377
|
role: "user",
|
|
3100
3378
|
content: input
|
|
3101
|
-
}]
|
|
3379
|
+
}], {
|
|
3380
|
+
runId,
|
|
3381
|
+
protocol: "shared",
|
|
3382
|
+
cost: totalCost,
|
|
3383
|
+
events,
|
|
3384
|
+
transcript,
|
|
3385
|
+
iteration: transcript.length,
|
|
3386
|
+
elapsedMs: elapsedMs(startedAtMs)
|
|
3387
|
+
})
|
|
3102
3388
|
};
|
|
3103
3389
|
const response = await generateModelTurn({
|
|
3104
3390
|
model: options.model,
|
|
@@ -3235,16 +3521,17 @@ async function runShared(options) {
|
|
|
3235
3521
|
function stopIfNeeded() {
|
|
3236
3522
|
throwIfAborted(options.signal, options.model.id);
|
|
3237
3523
|
if (stopped || !options.terminate) return stopped;
|
|
3238
|
-
const stopRecord = evaluateTerminationStop(options.terminate, {
|
|
3524
|
+
const stopRecord = evaluateTerminationStop(options.terminate, wrapUpHint.context({
|
|
3239
3525
|
runId,
|
|
3240
3526
|
protocol: "shared",
|
|
3241
|
-
|
|
3527
|
+
protocolConfig: options.protocol,
|
|
3528
|
+
protocolIteration: transcript.length,
|
|
3242
3529
|
cost: totalCost,
|
|
3243
3530
|
events,
|
|
3244
3531
|
transcript,
|
|
3245
3532
|
iteration: transcript.length,
|
|
3246
3533
|
elapsedMs: elapsedMs(startedAtMs)
|
|
3247
|
-
});
|
|
3534
|
+
}));
|
|
3248
3535
|
if (!stopRecord) return false;
|
|
3249
3536
|
stopped = true;
|
|
3250
3537
|
termination = stopRecord;
|
|
@@ -3284,18 +3571,6 @@ function responseCost(response) {
|
|
|
3284
3571
|
totalTokens: response.usage?.totalTokens ?? 0
|
|
3285
3572
|
};
|
|
3286
3573
|
}
|
|
3287
|
-
function createRunId() {
|
|
3288
|
-
return globalThis.crypto?.randomUUID?.() ?? `run-${Date.now().toString(36)}`;
|
|
3289
|
-
}
|
|
3290
|
-
function nowMs() {
|
|
3291
|
-
return globalThis.performance?.now() ?? Date.now();
|
|
3292
|
-
}
|
|
3293
|
-
function elapsedMs(startedAtMs) {
|
|
3294
|
-
return Math.max(0, nowMs() - startedAtMs);
|
|
3295
|
-
}
|
|
3296
|
-
function providerCallIdFor(runId, oneBasedIndex) {
|
|
3297
|
-
return `${runId}:provider-call:${oneBasedIndex}`;
|
|
3298
|
-
}
|
|
3299
3574
|
//#endregion
|
|
3300
3575
|
//#region src/runtime/engine.ts
|
|
3301
3576
|
var defaultHighLevelProtocol = "sequential";
|
|
@@ -3333,6 +3608,7 @@ function createEngine(options) {
|
|
|
3333
3608
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3334
3609
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3335
3610
|
...terminate ? { terminate } : {},
|
|
3611
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
|
|
3336
3612
|
...options.evaluate ? { evaluate: options.evaluate } : {}
|
|
3337
3613
|
});
|
|
3338
3614
|
},
|
|
@@ -3715,6 +3991,7 @@ function runProtocol(options) {
|
|
|
3715
3991
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3716
3992
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3717
3993
|
...options.terminate ? { terminate: options.terminate } : {},
|
|
3994
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
|
|
3718
3995
|
...options.emit ? { emit: options.emit } : {}
|
|
3719
3996
|
});
|
|
3720
3997
|
case "broadcast": return runBroadcast({
|
|
@@ -3729,6 +4006,7 @@ function runProtocol(options) {
|
|
|
3729
4006
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3730
4007
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3731
4008
|
...options.terminate ? { terminate: options.terminate } : {},
|
|
4009
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
|
|
3732
4010
|
...options.emit ? { emit: options.emit } : {}
|
|
3733
4011
|
});
|
|
3734
4012
|
case "coordinator": return runCoordinator({
|
|
@@ -3743,6 +4021,7 @@ function runProtocol(options) {
|
|
|
3743
4021
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3744
4022
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3745
4023
|
...options.terminate ? { terminate: options.terminate } : {},
|
|
4024
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
|
|
3746
4025
|
...options.emit ? { emit: options.emit } : {}
|
|
3747
4026
|
});
|
|
3748
4027
|
case "shared": return runShared({
|
|
@@ -3757,6 +4036,7 @@ function runProtocol(options) {
|
|
|
3757
4036
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
3758
4037
|
...options.signal !== void 0 ? { signal: options.signal } : {},
|
|
3759
4038
|
...options.terminate ? { terminate: options.terminate } : {},
|
|
4039
|
+
...options.wrapUpHint ? { wrapUpHint: options.wrapUpHint } : {},
|
|
3760
4040
|
...options.emit ? { emit: options.emit } : {}
|
|
3761
4041
|
});
|
|
3762
4042
|
}
|
|
@@ -4200,6 +4480,263 @@ function isJsonValue(value) {
|
|
|
4200
4480
|
return false;
|
|
4201
4481
|
}
|
|
4202
4482
|
//#endregion
|
|
4203
|
-
|
|
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 };
|
|
4204
4741
|
|
|
4205
4742
|
//# sourceMappingURL=index.js.map
|