@runtypelabs/sdk 4.9.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1782 -377
- package/dist/index.d.cts +2003 -87
- package/dist/index.d.ts +2003 -87
- package/dist/index.mjs +1662 -298
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
-
};
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
|
|
11
1
|
// src/stream-utils.ts
|
|
12
|
-
var stream_utils_exports = {};
|
|
13
|
-
__export(stream_utils_exports, {
|
|
14
|
-
flowErrorMessage: () => flowErrorMessage,
|
|
15
|
-
parseFinalBuffer: () => parseFinalBuffer,
|
|
16
|
-
parseSSEChunk: () => parseSSEChunk,
|
|
17
|
-
processStream: () => processStream,
|
|
18
|
-
stepDeltaText: () => stepDeltaText,
|
|
19
|
-
stepDisplayName: () => stepDisplayName,
|
|
20
|
-
streamEvents: () => streamEvents
|
|
21
|
-
});
|
|
22
2
|
function parseSSEChunk(chunk, buffer) {
|
|
23
3
|
buffer += chunk;
|
|
24
4
|
const lines = buffer.split("\n");
|
|
@@ -268,14 +248,8 @@ async function* streamEvents(response) {
|
|
|
268
248
|
reader.releaseLock();
|
|
269
249
|
}
|
|
270
250
|
}
|
|
271
|
-
var init_stream_utils = __esm({
|
|
272
|
-
"src/stream-utils.ts"() {
|
|
273
|
-
"use strict";
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
251
|
|
|
277
252
|
// src/flow-result.ts
|
|
278
|
-
init_stream_utils();
|
|
279
253
|
var FlowResult = class {
|
|
280
254
|
constructor(response, summary) {
|
|
281
255
|
this.consumed = false;
|
|
@@ -411,7 +385,6 @@ var FlowResult = class {
|
|
|
411
385
|
};
|
|
412
386
|
|
|
413
387
|
// src/flow-builder.ts
|
|
414
|
-
init_stream_utils();
|
|
415
388
|
async function validateInlineFlow(client, args, savedFlowHint) {
|
|
416
389
|
if (args.existingFlowId) {
|
|
417
390
|
throw new Error(
|
|
@@ -1121,20 +1094,20 @@ var FlowBuilder = class {
|
|
|
1121
1094
|
*/
|
|
1122
1095
|
build() {
|
|
1123
1096
|
const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
|
|
1124
|
-
const
|
|
1097
|
+
const request2 = { flow };
|
|
1125
1098
|
if (this.recordConfig) {
|
|
1126
|
-
|
|
1099
|
+
request2.record = this.recordConfig;
|
|
1127
1100
|
}
|
|
1128
1101
|
if (this.messagesConfig) {
|
|
1129
|
-
|
|
1102
|
+
request2.messages = this.messagesConfig;
|
|
1130
1103
|
}
|
|
1131
1104
|
if (this.inputsConfig) {
|
|
1132
|
-
|
|
1105
|
+
request2.inputs = this.inputsConfig;
|
|
1133
1106
|
}
|
|
1134
1107
|
if (Object.keys(this.optionsConfig).length > 0) {
|
|
1135
|
-
|
|
1108
|
+
request2.options = this.optionsConfig;
|
|
1136
1109
|
}
|
|
1137
|
-
return
|
|
1110
|
+
return request2;
|
|
1138
1111
|
}
|
|
1139
1112
|
/**
|
|
1140
1113
|
* Validate this prospective flow against the public validation endpoint
|
|
@@ -1371,22 +1344,22 @@ function resolveBatchExecutionId(pausedTools) {
|
|
|
1371
1344
|
return "";
|
|
1372
1345
|
}
|
|
1373
1346
|
|
|
1374
|
-
// src/flows-
|
|
1375
|
-
function
|
|
1347
|
+
// src/flows-ensure.ts
|
|
1348
|
+
function isPlainObject(value) {
|
|
1376
1349
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1377
1350
|
}
|
|
1378
|
-
function
|
|
1379
|
-
if (!
|
|
1351
|
+
function normalizeConfigForHash(config) {
|
|
1352
|
+
if (!isPlainObject(config)) return {};
|
|
1380
1353
|
const normalized = {};
|
|
1381
1354
|
for (const key of Object.keys(config).sort()) {
|
|
1382
1355
|
const value = config[key];
|
|
1383
1356
|
if (value === void 0) continue;
|
|
1384
1357
|
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
1385
|
-
normalized[key] =
|
|
1358
|
+
normalized[key] = normalizeConfigForHash(value);
|
|
1386
1359
|
} else if (Array.isArray(value)) {
|
|
1387
1360
|
normalized[key] = value.map((item) => {
|
|
1388
1361
|
if (item !== null && typeof item === "object" && !Array.isArray(item)) {
|
|
1389
|
-
return
|
|
1362
|
+
return normalizeConfigForHash(item);
|
|
1390
1363
|
}
|
|
1391
1364
|
return item;
|
|
1392
1365
|
});
|
|
@@ -1397,28 +1370,249 @@ function normalizeConfig(config) {
|
|
|
1397
1370
|
return normalized;
|
|
1398
1371
|
}
|
|
1399
1372
|
function normalizeStepForHash(step) {
|
|
1400
|
-
const stepObj =
|
|
1373
|
+
const stepObj = isPlainObject(step) ? step : {};
|
|
1401
1374
|
return {
|
|
1402
1375
|
type: typeof stepObj.type === "string" ? stepObj.type : "",
|
|
1403
1376
|
name: typeof stepObj.name === "string" ? stepObj.name : "",
|
|
1404
1377
|
enabled: stepObj.enabled !== false,
|
|
1405
1378
|
...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
|
|
1406
|
-
config:
|
|
1379
|
+
config: normalizeConfigForHash(stepObj.config),
|
|
1407
1380
|
order: typeof stepObj.order === "number" ? stepObj.order : 0
|
|
1408
1381
|
};
|
|
1409
1382
|
}
|
|
1410
1383
|
async function computeFlowContentHash(steps) {
|
|
1411
1384
|
const normalized = [...steps].sort((a, b) => {
|
|
1412
|
-
const orderA =
|
|
1413
|
-
const orderB =
|
|
1385
|
+
const orderA = isPlainObject(a) && typeof a.order === "number" ? a.order : 0;
|
|
1386
|
+
const orderB = isPlainObject(b) && typeof b.order === "number" ? b.order : 0;
|
|
1414
1387
|
return orderA - orderB;
|
|
1415
1388
|
}).map(normalizeStepForHash);
|
|
1416
1389
|
const serialized = JSON.stringify(normalized);
|
|
1417
1390
|
const encoded = new TextEncoder().encode(serialized);
|
|
1418
1391
|
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
1419
|
-
|
|
1420
|
-
|
|
1392
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1393
|
+
}
|
|
1394
|
+
var DEFINE_FLOW_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "steps"]);
|
|
1395
|
+
var DEFINE_FLOW_STEP_KEYS = /* @__PURE__ */ new Set([
|
|
1396
|
+
"type",
|
|
1397
|
+
"name",
|
|
1398
|
+
"order",
|
|
1399
|
+
"enabled",
|
|
1400
|
+
"when",
|
|
1401
|
+
"config"
|
|
1402
|
+
]);
|
|
1403
|
+
function collectStepNonPortableToolRefs(config, path) {
|
|
1404
|
+
const found = [];
|
|
1405
|
+
const tools = config.tools;
|
|
1406
|
+
const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
|
|
1407
|
+
const scanArray = (value, subPath) => {
|
|
1408
|
+
if (!Array.isArray(value)) return;
|
|
1409
|
+
value.forEach((ref, i) => {
|
|
1410
|
+
if (isAccountScoped(ref)) found.push(`${subPath}[${i}]`);
|
|
1411
|
+
});
|
|
1412
|
+
};
|
|
1413
|
+
const scanKeys = (value, subPath) => {
|
|
1414
|
+
if (!isPlainObject(value)) return;
|
|
1415
|
+
for (const key of Object.keys(value)) {
|
|
1416
|
+
if (isAccountScoped(key)) found.push(`${subPath}.${key}`);
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
if (isPlainObject(tools)) {
|
|
1420
|
+
scanArray(tools.toolIds, `${path}.tools.toolIds`);
|
|
1421
|
+
scanKeys(tools.toolConfigs, `${path}.tools.toolConfigs`);
|
|
1422
|
+
scanKeys(tools.perToolLimits, `${path}.tools.perToolLimits`);
|
|
1423
|
+
if (isPlainObject(tools.approval)) {
|
|
1424
|
+
scanArray(tools.approval.require, `${path}.tools.approval.require`);
|
|
1425
|
+
}
|
|
1426
|
+
if (isPlainObject(tools.subagentConfig)) {
|
|
1427
|
+
scanArray(tools.subagentConfig.toolPool, `${path}.tools.subagentConfig.toolPool`);
|
|
1428
|
+
}
|
|
1429
|
+
if (isPlainObject(tools.codeModeConfig)) {
|
|
1430
|
+
scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
for (const branch of ["trueSteps", "falseSteps"]) {
|
|
1434
|
+
const nested = config[branch];
|
|
1435
|
+
if (!Array.isArray(nested)) continue;
|
|
1436
|
+
nested.forEach((nestedStep, i) => {
|
|
1437
|
+
if (isPlainObject(nestedStep) && isPlainObject(nestedStep.config)) {
|
|
1438
|
+
found.push(
|
|
1439
|
+
...collectStepNonPortableToolRefs(
|
|
1440
|
+
nestedStep.config,
|
|
1441
|
+
`${path}.${branch}[${i}].config`
|
|
1442
|
+
)
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
return found;
|
|
1448
|
+
}
|
|
1449
|
+
function defineFlow(input) {
|
|
1450
|
+
if (!input || typeof input !== "object") {
|
|
1451
|
+
throw new Error("defineFlow requires a definition object");
|
|
1452
|
+
}
|
|
1453
|
+
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
1454
|
+
throw new Error('defineFlow requires a non-empty string "name"');
|
|
1455
|
+
}
|
|
1456
|
+
const unknownKeys = Object.keys(input).filter((key) => !DEFINE_FLOW_TOP_LEVEL_KEYS.has(key));
|
|
1457
|
+
if (unknownKeys.length > 0) {
|
|
1458
|
+
throw new Error(
|
|
1459
|
+
`defineFlow: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name and steps. (Description is not part of the v1 ensure surface.)`
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1462
|
+
if (!Array.isArray(input.steps) || input.steps.length === 0) {
|
|
1463
|
+
throw new Error('defineFlow requires a non-empty "steps" array');
|
|
1464
|
+
}
|
|
1465
|
+
const steps = input.steps.map((step, index) => {
|
|
1466
|
+
if (!isPlainObject(step)) {
|
|
1467
|
+
throw new Error(`defineFlow: steps[${index}] must be an object`);
|
|
1468
|
+
}
|
|
1469
|
+
if (typeof step.type !== "string" || step.type.length === 0) {
|
|
1470
|
+
throw new Error(`defineFlow: steps[${index}] requires a non-empty string "type"`);
|
|
1471
|
+
}
|
|
1472
|
+
if (typeof step.name !== "string" || step.name.length === 0) {
|
|
1473
|
+
throw new Error(`defineFlow: steps[${index}] requires a non-empty string "name"`);
|
|
1474
|
+
}
|
|
1475
|
+
const unknownStepKeys = Object.keys(step).filter((key) => !DEFINE_FLOW_STEP_KEYS.has(key));
|
|
1476
|
+
if (unknownStepKeys.length > 0) {
|
|
1477
|
+
throw new Error(
|
|
1478
|
+
`defineFlow: steps[${index}] has unknown field(s): ${unknownStepKeys.join(", ")}. Allowed step fields are type, name, order, enabled, when, config. (Step ids are server artifacts and not part of a portable definition.)`
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
const config = isPlainObject(step.config) ? step.config : void 0;
|
|
1482
|
+
if (config) {
|
|
1483
|
+
const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
|
|
1484
|
+
if (nonPortable.length > 0) {
|
|
1485
|
+
throw new Error(
|
|
1486
|
+
`defineFlow: account-scoped tool reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references instead. Name-based resolution of saved tools is a planned follow-up.`
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
return {
|
|
1491
|
+
type: step.type,
|
|
1492
|
+
name: step.name,
|
|
1493
|
+
// Explicit 1-based order (the flow builder's convention) so the local
|
|
1494
|
+
// probe hash agrees with the server's persisted step order.
|
|
1495
|
+
order: typeof step.order === "number" ? step.order : index + 1,
|
|
1496
|
+
...step.enabled !== void 0 ? { enabled: step.enabled } : {},
|
|
1497
|
+
...typeof step.when === "string" ? { when: step.when } : {},
|
|
1498
|
+
...config ? { config } : {}
|
|
1499
|
+
};
|
|
1500
|
+
});
|
|
1501
|
+
return { name: input.name, steps };
|
|
1502
|
+
}
|
|
1503
|
+
var FlowEnsureConflictError = class extends Error {
|
|
1504
|
+
constructor(body) {
|
|
1505
|
+
super(body.error ?? `Flow ensure conflict: ${body.code}`);
|
|
1506
|
+
this.name = "FlowEnsureConflictError";
|
|
1507
|
+
this.code = body.code;
|
|
1508
|
+
this.lastModifiedSource = body.lastModifiedSource;
|
|
1509
|
+
this.modifiedAt = body.modifiedAt;
|
|
1510
|
+
this.currentHash = body.currentHash;
|
|
1511
|
+
}
|
|
1512
|
+
};
|
|
1513
|
+
var FlowDriftError = class extends Error {
|
|
1514
|
+
constructor(plan) {
|
|
1515
|
+
super(
|
|
1516
|
+
`Flow "${plan.flowId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.flows.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
|
|
1517
|
+
);
|
|
1518
|
+
this.name = "FlowDriftError";
|
|
1519
|
+
this.plan = plan;
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
function parseRequestError(err) {
|
|
1523
|
+
if (!(err instanceof Error)) return { status: null, body: null };
|
|
1524
|
+
const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
|
|
1525
|
+
if (!match) return { status: null, body: null };
|
|
1526
|
+
try {
|
|
1527
|
+
return { status: Number(match[1]), body: JSON.parse(match[2]) };
|
|
1528
|
+
} catch {
|
|
1529
|
+
return { status: Number(match[1]), body: null };
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function toConflictError(err) {
|
|
1533
|
+
const { status, body } = parseRequestError(err);
|
|
1534
|
+
if (status !== 409 || !isPlainObject(body)) return null;
|
|
1535
|
+
const code = body.code;
|
|
1536
|
+
if (code !== "external_modification" && code !== "remote_changed") return null;
|
|
1537
|
+
return new FlowEnsureConflictError(
|
|
1538
|
+
body
|
|
1539
|
+
);
|
|
1540
|
+
}
|
|
1541
|
+
var serverHashMemo = /* @__PURE__ */ new WeakMap();
|
|
1542
|
+
function memoFor(client) {
|
|
1543
|
+
let memo = serverHashMemo.get(client);
|
|
1544
|
+
if (!memo) {
|
|
1545
|
+
memo = /* @__PURE__ */ new Map();
|
|
1546
|
+
serverHashMemo.set(client, memo);
|
|
1547
|
+
}
|
|
1548
|
+
return memo;
|
|
1421
1549
|
}
|
|
1550
|
+
function memoize(memo, memoKey, result) {
|
|
1551
|
+
if (result.result !== "plan") memo.set(memoKey, result.contentHash);
|
|
1552
|
+
}
|
|
1553
|
+
async function request(client, body) {
|
|
1554
|
+
try {
|
|
1555
|
+
return await client.post(
|
|
1556
|
+
"/flows/ensure",
|
|
1557
|
+
body
|
|
1558
|
+
);
|
|
1559
|
+
} catch (err) {
|
|
1560
|
+
const conflict = toConflictError(err);
|
|
1561
|
+
if (conflict) throw conflict;
|
|
1562
|
+
throw err;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
async function ensureFlow(client, definition, options = {}) {
|
|
1566
|
+
const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
|
|
1567
|
+
const passthrough = {
|
|
1568
|
+
...onConflict ? { onConflict } : {},
|
|
1569
|
+
...release ? { release } : {},
|
|
1570
|
+
...expectedRemoteHash ? { expectedRemoteHash } : {}
|
|
1571
|
+
};
|
|
1572
|
+
if (dryRun || expectNoChanges) {
|
|
1573
|
+
const plan = await request(client, {
|
|
1574
|
+
name: definition.name,
|
|
1575
|
+
definition,
|
|
1576
|
+
dryRun: true,
|
|
1577
|
+
...passthrough
|
|
1578
|
+
});
|
|
1579
|
+
if (plan.result !== "plan") {
|
|
1580
|
+
throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
|
|
1581
|
+
}
|
|
1582
|
+
if (expectNoChanges && plan.changes !== "none") {
|
|
1583
|
+
throw new FlowDriftError(plan);
|
|
1584
|
+
}
|
|
1585
|
+
return plan;
|
|
1586
|
+
}
|
|
1587
|
+
const memo = memoFor(client);
|
|
1588
|
+
const localHash = await computeFlowContentHash(definition.steps);
|
|
1589
|
+
const memoKey = `${definition.name} ${localHash}`;
|
|
1590
|
+
const contentHash = memo.get(memoKey) ?? localHash;
|
|
1591
|
+
const probe = await request(client, {
|
|
1592
|
+
name: definition.name,
|
|
1593
|
+
contentHash,
|
|
1594
|
+
...passthrough
|
|
1595
|
+
});
|
|
1596
|
+
if (probe.result !== "definitionRequired") {
|
|
1597
|
+
memoize(memo, memoKey, probe);
|
|
1598
|
+
return probe;
|
|
1599
|
+
}
|
|
1600
|
+
const converged = await request(client, {
|
|
1601
|
+
name: definition.name,
|
|
1602
|
+
definition,
|
|
1603
|
+
...passthrough
|
|
1604
|
+
});
|
|
1605
|
+
if (converged.result === "definitionRequired") {
|
|
1606
|
+
throw new Error("Server reported definitionRequired for a full-definition request");
|
|
1607
|
+
}
|
|
1608
|
+
memoize(memo, memoKey, converged);
|
|
1609
|
+
return converged;
|
|
1610
|
+
}
|
|
1611
|
+
async function pullFlow(client, name) {
|
|
1612
|
+
return client.get("/flows/pull", { name });
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// src/flows-namespace.ts
|
|
1422
1616
|
var FlowsNamespace = class {
|
|
1423
1617
|
constructor(getClient) {
|
|
1424
1618
|
this.getClient = getClient;
|
|
@@ -1426,8 +1620,11 @@ var FlowsNamespace = class {
|
|
|
1426
1620
|
/**
|
|
1427
1621
|
* Create or update a flow by name (upsert mode)
|
|
1428
1622
|
*
|
|
1429
|
-
* The recommended pattern for code-first flow management
|
|
1430
|
-
*
|
|
1623
|
+
* The recommended pattern for code-first flow management when you want to
|
|
1624
|
+
* save AND run in one dispatch. For a deploy-time, non-executing converge
|
|
1625
|
+
* (CI/CD config-as-code), use {@link ensure} instead — upsert and ensure
|
|
1626
|
+
* are siblings, not versions of each other: upsert is the runtime verb
|
|
1627
|
+
* (save-and-run), ensure is the deploy verb (converge only).
|
|
1431
1628
|
*
|
|
1432
1629
|
* @example
|
|
1433
1630
|
* ```typescript
|
|
@@ -1442,6 +1639,33 @@ var FlowsNamespace = class {
|
|
|
1442
1639
|
upsert(config) {
|
|
1443
1640
|
return new RuntypeFlowBuilder(this.getClient, "upsert", config);
|
|
1444
1641
|
}
|
|
1642
|
+
/**
|
|
1643
|
+
* Idempotently converge a `defineFlow` definition onto the platform —
|
|
1644
|
+
* the deploy-time, non-executing sibling of {@link upsert}. Hash-first:
|
|
1645
|
+
* the steady state is one tiny probe request. Creates an immutable version
|
|
1646
|
+
* snapshot on every change; never deletes; never executes the flow.
|
|
1647
|
+
*
|
|
1648
|
+
* @example
|
|
1649
|
+
* ```typescript
|
|
1650
|
+
* const def = defineFlow({ name: 'Onboarding Digest', steps: [...] })
|
|
1651
|
+
*
|
|
1652
|
+
* // Converge (CI/deploy).
|
|
1653
|
+
* const result = await Runtype.flows.ensure(def)
|
|
1654
|
+
*
|
|
1655
|
+
* // PR drift gate.
|
|
1656
|
+
* await Runtype.flows.ensure(def, { expectNoChanges: true })
|
|
1657
|
+
* ```
|
|
1658
|
+
*/
|
|
1659
|
+
async ensure(definition, options = {}) {
|
|
1660
|
+
return ensureFlow(this.getClient(), definition, options);
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Pull the canonical definition + provenance for a flow by name — the
|
|
1664
|
+
* absorb-drift direction of the ensure protocol.
|
|
1665
|
+
*/
|
|
1666
|
+
async pull(name) {
|
|
1667
|
+
return pullFlow(this.getClient(), name);
|
|
1668
|
+
}
|
|
1445
1669
|
/**
|
|
1446
1670
|
* Create a virtual flow (one-off, not saved)
|
|
1447
1671
|
*
|
|
@@ -2127,9 +2351,8 @@ var RuntypeFlowBuilder = class {
|
|
|
2127
2351
|
onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
|
|
2128
2352
|
onError: (error) => callbacks?.onError?.(error)
|
|
2129
2353
|
};
|
|
2130
|
-
const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
|
|
2131
2354
|
try {
|
|
2132
|
-
for await (const event of
|
|
2355
|
+
for await (const event of streamEvents(response)) {
|
|
2133
2356
|
collectLocalToolAwait(pausedTools, event);
|
|
2134
2357
|
switch (event.type) {
|
|
2135
2358
|
case "flow_start":
|
|
@@ -2139,10 +2362,10 @@ var RuntypeFlowBuilder = class {
|
|
|
2139
2362
|
wrappedCallbacks.onStepStart?.(event);
|
|
2140
2363
|
break;
|
|
2141
2364
|
case "step_delta":
|
|
2142
|
-
wrappedCallbacks.onStepDelta?.(
|
|
2365
|
+
wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
|
|
2143
2366
|
break;
|
|
2144
2367
|
case "step_complete": {
|
|
2145
|
-
accumulatedSummary.results?.set(
|
|
2368
|
+
accumulatedSummary.results?.set(stepDisplayName(event), event.result);
|
|
2146
2369
|
wrappedCallbacks.onStepComplete?.(event.result, event);
|
|
2147
2370
|
break;
|
|
2148
2371
|
}
|
|
@@ -2150,7 +2373,7 @@ var RuntypeFlowBuilder = class {
|
|
|
2150
2373
|
wrappedCallbacks.onFlowComplete?.(event);
|
|
2151
2374
|
break;
|
|
2152
2375
|
case "flow_error":
|
|
2153
|
-
wrappedCallbacks.onError?.(new Error(
|
|
2376
|
+
wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
|
|
2154
2377
|
break;
|
|
2155
2378
|
}
|
|
2156
2379
|
}
|
|
@@ -2225,7 +2448,8 @@ var RuntypeFlowBuilder = class {
|
|
|
2225
2448
|
return [toolName, await localTools[toolName](parameters)];
|
|
2226
2449
|
} catch (error) {
|
|
2227
2450
|
throw new Error(
|
|
2228
|
-
`Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}
|
|
2451
|
+
`Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
2452
|
+
{ cause: error }
|
|
2229
2453
|
);
|
|
2230
2454
|
}
|
|
2231
2455
|
})
|
|
@@ -2258,15 +2482,15 @@ var RuntypeFlowBuilder = class {
|
|
|
2258
2482
|
build() {
|
|
2259
2483
|
const flowMode = this.mode === "existing" ? "existing" : this.mode;
|
|
2260
2484
|
const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
|
|
2261
|
-
const
|
|
2485
|
+
const request2 = { flow };
|
|
2262
2486
|
if (this.recordConfig) {
|
|
2263
|
-
|
|
2487
|
+
request2.record = this.recordConfig;
|
|
2264
2488
|
}
|
|
2265
2489
|
if (this.messagesConfig) {
|
|
2266
|
-
|
|
2490
|
+
request2.messages = this.messagesConfig;
|
|
2267
2491
|
}
|
|
2268
2492
|
if (this.inputsConfig) {
|
|
2269
|
-
|
|
2493
|
+
request2.inputs = this.inputsConfig;
|
|
2270
2494
|
}
|
|
2271
2495
|
const options = {
|
|
2272
2496
|
flowMode,
|
|
@@ -2284,8 +2508,8 @@ var RuntypeFlowBuilder = class {
|
|
|
2284
2508
|
if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
|
|
2285
2509
|
options.upsertOptions = this.upsertOptions;
|
|
2286
2510
|
}
|
|
2287
|
-
|
|
2288
|
-
return
|
|
2511
|
+
request2.options = options;
|
|
2512
|
+
return request2;
|
|
2289
2513
|
}
|
|
2290
2514
|
/**
|
|
2291
2515
|
* Validate this prospective flow against the public validation endpoint
|
|
@@ -2945,6 +3169,8 @@ var SkillsNamespace = class {
|
|
|
2945
3169
|
}
|
|
2946
3170
|
/**
|
|
2947
3171
|
* List skills for the authenticated owner, optionally filtered by status.
|
|
3172
|
+
* Returns just the rows (one page); pass `cursor`/`limit` to page, or use
|
|
3173
|
+
* {@link listPage} when you need the pagination envelope.
|
|
2948
3174
|
*
|
|
2949
3175
|
* @example
|
|
2950
3176
|
* ```typescript
|
|
@@ -2952,10 +3178,23 @@ var SkillsNamespace = class {
|
|
|
2952
3178
|
* ```
|
|
2953
3179
|
*/
|
|
2954
3180
|
async list(params) {
|
|
2955
|
-
const
|
|
2956
|
-
const res = await client.get("/skills", params);
|
|
3181
|
+
const res = await this.listPage(params);
|
|
2957
3182
|
return res.data;
|
|
2958
3183
|
}
|
|
3184
|
+
/**
|
|
3185
|
+
* List skills with the cursor-pagination envelope (mirrors the tools list
|
|
3186
|
+
* shape: `{ data, pagination }`).
|
|
3187
|
+
*
|
|
3188
|
+
* @example
|
|
3189
|
+
* ```typescript
|
|
3190
|
+
* const page1 = await Runtype.skills.listPage({ limit: 50, includeCount: true })
|
|
3191
|
+
* const page2 = await Runtype.skills.listPage({ limit: 50, cursor: page1.pagination?.nextCursor ?? undefined })
|
|
3192
|
+
* ```
|
|
3193
|
+
*/
|
|
3194
|
+
async listPage(params) {
|
|
3195
|
+
const client = this.getClient();
|
|
3196
|
+
return client.get("/skills", params);
|
|
3197
|
+
}
|
|
2959
3198
|
/**
|
|
2960
3199
|
* Get a skill and its full version history.
|
|
2961
3200
|
*
|
|
@@ -3048,6 +3287,260 @@ var SkillsNamespace = class {
|
|
|
3048
3287
|
}
|
|
3049
3288
|
};
|
|
3050
3289
|
|
|
3290
|
+
// src/agents-namespace.ts
|
|
3291
|
+
var AGENT_CONFIG_KEYS = [
|
|
3292
|
+
"model",
|
|
3293
|
+
"systemPrompt",
|
|
3294
|
+
"temperature",
|
|
3295
|
+
"topP",
|
|
3296
|
+
"topK",
|
|
3297
|
+
"frequencyPenalty",
|
|
3298
|
+
"presencePenalty",
|
|
3299
|
+
"seed",
|
|
3300
|
+
"tools",
|
|
3301
|
+
"reasoning",
|
|
3302
|
+
"advisor",
|
|
3303
|
+
"loopConfig",
|
|
3304
|
+
"voice",
|
|
3305
|
+
"errorHandling",
|
|
3306
|
+
"artifacts",
|
|
3307
|
+
"loggingPolicy",
|
|
3308
|
+
"temporal",
|
|
3309
|
+
"memory"
|
|
3310
|
+
];
|
|
3311
|
+
var AGENT_CONFIG_KEY_LIST = [...AGENT_CONFIG_KEYS].sort();
|
|
3312
|
+
function isPlainObject2(value) {
|
|
3313
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3314
|
+
}
|
|
3315
|
+
function normalizeValue(value) {
|
|
3316
|
+
if (Array.isArray(value)) {
|
|
3317
|
+
return value.map((item) => normalizeValue(item));
|
|
3318
|
+
}
|
|
3319
|
+
if (isPlainObject2(value)) {
|
|
3320
|
+
const normalized = {};
|
|
3321
|
+
for (const key of Object.keys(value).sort()) {
|
|
3322
|
+
const entry = value[key];
|
|
3323
|
+
if (entry === void 0 || entry === null) continue;
|
|
3324
|
+
normalized[key] = normalizeValue(entry);
|
|
3325
|
+
}
|
|
3326
|
+
return normalized;
|
|
3327
|
+
}
|
|
3328
|
+
return value;
|
|
3329
|
+
}
|
|
3330
|
+
function normalizeAgentDefinition(definition) {
|
|
3331
|
+
const config = {};
|
|
3332
|
+
const rawConfig = isPlainObject2(definition.config) ? definition.config : {};
|
|
3333
|
+
for (const key of AGENT_CONFIG_KEY_LIST) {
|
|
3334
|
+
const value = rawConfig[key];
|
|
3335
|
+
if (value === void 0 || value === null) continue;
|
|
3336
|
+
config[key] = normalizeValue(value);
|
|
3337
|
+
}
|
|
3338
|
+
return {
|
|
3339
|
+
name: definition.name,
|
|
3340
|
+
...definition.description ? { description: definition.description } : {},
|
|
3341
|
+
...definition.icon ? { icon: definition.icon } : {},
|
|
3342
|
+
config
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
async function computeAgentContentHash(definition) {
|
|
3346
|
+
const serialized = JSON.stringify(normalizeAgentDefinition(definition));
|
|
3347
|
+
const encoded = new TextEncoder().encode(serialized);
|
|
3348
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
|
|
3349
|
+
return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
3350
|
+
}
|
|
3351
|
+
var DEFINE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "description", "icon", ...AGENT_CONFIG_KEYS]);
|
|
3352
|
+
function collectNonPortableToolRefs(config) {
|
|
3353
|
+
const tools = config.tools;
|
|
3354
|
+
if (!isPlainObject2(tools)) return [];
|
|
3355
|
+
const found = [];
|
|
3356
|
+
const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
|
|
3357
|
+
const scanArray = (value, path) => {
|
|
3358
|
+
if (!Array.isArray(value)) return;
|
|
3359
|
+
value.forEach((ref, i) => {
|
|
3360
|
+
if (isAccountScoped(ref)) found.push(`${path}[${i}]`);
|
|
3361
|
+
});
|
|
3362
|
+
};
|
|
3363
|
+
const scanKeys = (value, path) => {
|
|
3364
|
+
if (!isPlainObject2(value)) return;
|
|
3365
|
+
for (const key of Object.keys(value)) {
|
|
3366
|
+
if (isAccountScoped(key)) found.push(`${path}.${key}`);
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
scanArray(tools.toolIds, "tools.toolIds");
|
|
3370
|
+
scanKeys(tools.toolConfigs, "tools.toolConfigs");
|
|
3371
|
+
scanKeys(tools.perToolLimits, "tools.perToolLimits");
|
|
3372
|
+
if (isPlainObject2(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
|
|
3373
|
+
if (isPlainObject2(tools.subagentConfig)) {
|
|
3374
|
+
scanArray(tools.subagentConfig.toolPool, "tools.subagentConfig.toolPool");
|
|
3375
|
+
}
|
|
3376
|
+
if (isPlainObject2(tools.codeModeConfig)) {
|
|
3377
|
+
scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
|
|
3378
|
+
}
|
|
3379
|
+
return found;
|
|
3380
|
+
}
|
|
3381
|
+
function defineAgent(input) {
|
|
3382
|
+
if (!input || typeof input !== "object") {
|
|
3383
|
+
throw new Error("defineAgent requires a definition object");
|
|
3384
|
+
}
|
|
3385
|
+
if (typeof input.name !== "string" || input.name.length === 0) {
|
|
3386
|
+
throw new Error('defineAgent requires a non-empty string "name"');
|
|
3387
|
+
}
|
|
3388
|
+
const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOP_LEVEL_KEYS.has(key));
|
|
3389
|
+
if (unknownKeys.length > 0) {
|
|
3390
|
+
throw new Error(
|
|
3391
|
+
`defineAgent: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, icon, and the agent runtime config surface (${AGENT_CONFIG_KEY_LIST.join(", ")}).`
|
|
3392
|
+
);
|
|
3393
|
+
}
|
|
3394
|
+
const config = {};
|
|
3395
|
+
for (const key of AGENT_CONFIG_KEYS) {
|
|
3396
|
+
const value = input[key];
|
|
3397
|
+
if (value !== void 0) config[key] = value;
|
|
3398
|
+
}
|
|
3399
|
+
const nonPortable = collectNonPortableToolRefs(config);
|
|
3400
|
+
if (nonPortable.length > 0) {
|
|
3401
|
+
throw new Error(
|
|
3402
|
+
`defineAgent: account-scoped tool reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references instead. Name-based resolution of saved tools is a planned follow-up.`
|
|
3403
|
+
);
|
|
3404
|
+
}
|
|
3405
|
+
return {
|
|
3406
|
+
name: input.name,
|
|
3407
|
+
...input.description !== void 0 ? { description: input.description } : {},
|
|
3408
|
+
...input.icon !== void 0 ? { icon: input.icon } : {},
|
|
3409
|
+
config
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
var AgentEnsureConflictError = class extends Error {
|
|
3413
|
+
constructor(body) {
|
|
3414
|
+
super(body.error ?? `Agent ensure conflict: ${body.code}`);
|
|
3415
|
+
this.name = "AgentEnsureConflictError";
|
|
3416
|
+
this.code = body.code;
|
|
3417
|
+
this.lastModifiedSource = body.lastModifiedSource;
|
|
3418
|
+
this.modifiedAt = body.modifiedAt;
|
|
3419
|
+
this.currentHash = body.currentHash;
|
|
3420
|
+
}
|
|
3421
|
+
};
|
|
3422
|
+
var AgentDriftError = class extends Error {
|
|
3423
|
+
constructor(plan) {
|
|
3424
|
+
super(
|
|
3425
|
+
`Agent "${plan.agentId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.agents.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
|
|
3426
|
+
);
|
|
3427
|
+
this.name = "AgentDriftError";
|
|
3428
|
+
this.plan = plan;
|
|
3429
|
+
}
|
|
3430
|
+
};
|
|
3431
|
+
function parseRequestError2(err) {
|
|
3432
|
+
if (!(err instanceof Error)) return { status: null, body: null };
|
|
3433
|
+
const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
|
|
3434
|
+
if (!match) return { status: null, body: null };
|
|
3435
|
+
try {
|
|
3436
|
+
return { status: Number(match[1]), body: JSON.parse(match[2]) };
|
|
3437
|
+
} catch {
|
|
3438
|
+
return { status: Number(match[1]), body: null };
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
function toConflictError2(err) {
|
|
3442
|
+
const { status, body } = parseRequestError2(err);
|
|
3443
|
+
if (status !== 409 || !isPlainObject2(body)) return null;
|
|
3444
|
+
const code = body.code;
|
|
3445
|
+
if (code !== "external_modification" && code !== "remote_changed") return null;
|
|
3446
|
+
return new AgentEnsureConflictError(
|
|
3447
|
+
body
|
|
3448
|
+
);
|
|
3449
|
+
}
|
|
3450
|
+
var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
|
|
3451
|
+
function memoFor2(client) {
|
|
3452
|
+
let memo = serverHashMemo2.get(client);
|
|
3453
|
+
if (!memo) {
|
|
3454
|
+
memo = /* @__PURE__ */ new Map();
|
|
3455
|
+
serverHashMemo2.set(client, memo);
|
|
3456
|
+
}
|
|
3457
|
+
return memo;
|
|
3458
|
+
}
|
|
3459
|
+
var AgentsNamespace = class {
|
|
3460
|
+
constructor(getClient) {
|
|
3461
|
+
this.getClient = getClient;
|
|
3462
|
+
}
|
|
3463
|
+
/**
|
|
3464
|
+
* Idempotently converge a definition onto the platform. Hash-first: probes
|
|
3465
|
+
* with a content hash, and only ships the full definition when the server
|
|
3466
|
+
* reports a miss (`definitionRequired`). Creates an immutable version
|
|
3467
|
+
* snapshot on every change; never deletes.
|
|
3468
|
+
*/
|
|
3469
|
+
async ensure(definition, options = {}) {
|
|
3470
|
+
const client = this.getClient();
|
|
3471
|
+
const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
|
|
3472
|
+
const passthrough = {
|
|
3473
|
+
...onConflict ? { onConflict } : {},
|
|
3474
|
+
...release ? { release } : {},
|
|
3475
|
+
...expectedRemoteHash ? { expectedRemoteHash } : {}
|
|
3476
|
+
};
|
|
3477
|
+
if (dryRun || expectNoChanges) {
|
|
3478
|
+
const plan = await this.request(client, {
|
|
3479
|
+
name: definition.name,
|
|
3480
|
+
definition,
|
|
3481
|
+
dryRun: true,
|
|
3482
|
+
...passthrough
|
|
3483
|
+
});
|
|
3484
|
+
if (plan.result !== "plan") {
|
|
3485
|
+
throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
|
|
3486
|
+
}
|
|
3487
|
+
if (expectNoChanges && plan.changes !== "none") {
|
|
3488
|
+
throw new AgentDriftError(plan);
|
|
3489
|
+
}
|
|
3490
|
+
return plan;
|
|
3491
|
+
}
|
|
3492
|
+
const memo = memoFor2(client);
|
|
3493
|
+
const localHash = await computeAgentContentHash({
|
|
3494
|
+
...definition,
|
|
3495
|
+
config: definition.config
|
|
3496
|
+
});
|
|
3497
|
+
const memoKey = `${definition.name}\0${localHash}`;
|
|
3498
|
+
const contentHash = memo.get(memoKey) ?? localHash;
|
|
3499
|
+
const probe = await this.request(client, {
|
|
3500
|
+
name: definition.name,
|
|
3501
|
+
contentHash,
|
|
3502
|
+
...passthrough
|
|
3503
|
+
});
|
|
3504
|
+
if (probe.result !== "definitionRequired") {
|
|
3505
|
+
this.memoize(memo, memoKey, probe);
|
|
3506
|
+
return probe;
|
|
3507
|
+
}
|
|
3508
|
+
const converged = await this.request(client, {
|
|
3509
|
+
name: definition.name,
|
|
3510
|
+
definition,
|
|
3511
|
+
...passthrough
|
|
3512
|
+
});
|
|
3513
|
+
if (converged.result === "definitionRequired") {
|
|
3514
|
+
throw new Error("Server reported definitionRequired for a full-definition request");
|
|
3515
|
+
}
|
|
3516
|
+
this.memoize(memo, memoKey, converged);
|
|
3517
|
+
return converged;
|
|
3518
|
+
}
|
|
3519
|
+
/**
|
|
3520
|
+
* Pull the canonical definition + provenance for an agent by name — the
|
|
3521
|
+
* absorb-drift direction. The contentHash reflects the live agent state.
|
|
3522
|
+
*/
|
|
3523
|
+
async pull(name) {
|
|
3524
|
+
const client = this.getClient();
|
|
3525
|
+
return client.get("/agents/pull", { name });
|
|
3526
|
+
}
|
|
3527
|
+
memoize(memo, memoKey, result) {
|
|
3528
|
+
if (result.result !== "plan") memo.set(memoKey, result.contentHash);
|
|
3529
|
+
}
|
|
3530
|
+
async request(client, body) {
|
|
3531
|
+
try {
|
|
3532
|
+
return await client.post(
|
|
3533
|
+
"/agents/ensure",
|
|
3534
|
+
body
|
|
3535
|
+
);
|
|
3536
|
+
} catch (err) {
|
|
3537
|
+
const conflict = toConflictError2(err);
|
|
3538
|
+
if (conflict) throw conflict;
|
|
3539
|
+
throw err;
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
};
|
|
3543
|
+
|
|
3051
3544
|
// src/transform.ts
|
|
3052
3545
|
function transformResponse(data) {
|
|
3053
3546
|
return data;
|
|
@@ -3208,7 +3701,7 @@ var RuntypeClient = class {
|
|
|
3208
3701
|
} catch (error) {
|
|
3209
3702
|
clearTimeout(timeoutId);
|
|
3210
3703
|
if (error instanceof Error && error.name === "AbortError") {
|
|
3211
|
-
throw new Error(`Request timeout after ${this.timeout}ms
|
|
3704
|
+
throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
|
|
3212
3705
|
}
|
|
3213
3706
|
throw error;
|
|
3214
3707
|
}
|
|
@@ -3235,7 +3728,7 @@ var RuntypeClient = class {
|
|
|
3235
3728
|
} catch (error) {
|
|
3236
3729
|
clearTimeout(timeoutId);
|
|
3237
3730
|
if (error instanceof Error && error.name === "AbortError") {
|
|
3238
|
-
throw new Error(`Request timeout after ${this.timeout}ms
|
|
3731
|
+
throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
|
|
3239
3732
|
}
|
|
3240
3733
|
throw error;
|
|
3241
3734
|
}
|
|
@@ -3410,6 +3903,32 @@ var Runtype = class {
|
|
|
3410
3903
|
static get skills() {
|
|
3411
3904
|
return new SkillsNamespace(() => this.getClient());
|
|
3412
3905
|
}
|
|
3906
|
+
/**
|
|
3907
|
+
* Agents namespace - Agent config-as-code (define / ensure / pull)
|
|
3908
|
+
*
|
|
3909
|
+
* @example
|
|
3910
|
+
* ```typescript
|
|
3911
|
+
* import { defineAgent, Runtype } from '@runtypelabs/sdk'
|
|
3912
|
+
*
|
|
3913
|
+
* const assistant = defineAgent({
|
|
3914
|
+
* name: 'Pricing Assistant',
|
|
3915
|
+
* model: 'claude-sonnet-4-6',
|
|
3916
|
+
* systemPrompt: renderPrompt(pricingData),
|
|
3917
|
+
* })
|
|
3918
|
+
*
|
|
3919
|
+
* // Converge at deploy time (idempotent; one tiny probe in steady state)
|
|
3920
|
+
* await Runtype.agents.ensure(assistant)
|
|
3921
|
+
*
|
|
3922
|
+
* // CI drift gate
|
|
3923
|
+
* await Runtype.agents.ensure(assistant, { expectNoChanges: true })
|
|
3924
|
+
*
|
|
3925
|
+
* // Absorb a dashboard edit back into the repo
|
|
3926
|
+
* const { definition } = await Runtype.agents.pull('Pricing Assistant')
|
|
3927
|
+
* ```
|
|
3928
|
+
*/
|
|
3929
|
+
static get agents() {
|
|
3930
|
+
return new AgentsNamespace(() => this.getClient());
|
|
3931
|
+
}
|
|
3413
3932
|
};
|
|
3414
3933
|
|
|
3415
3934
|
// src/generated-tool-gate.ts
|
|
@@ -3632,8 +4151,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
|
|
|
3632
4151
|
...decision.tool ? { tool: decision.tool } : {}
|
|
3633
4152
|
};
|
|
3634
4153
|
}
|
|
3635
|
-
function attachRuntimeToolsToDispatchRequest(
|
|
3636
|
-
const stepList =
|
|
4154
|
+
function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {}) {
|
|
4155
|
+
const stepList = request2.flow.steps;
|
|
3637
4156
|
if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
|
|
3638
4157
|
throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
|
|
3639
4158
|
}
|
|
@@ -3676,9 +4195,9 @@ function attachRuntimeToolsToDispatchRequest(request, runtimeTools, options = {}
|
|
|
3676
4195
|
}
|
|
3677
4196
|
};
|
|
3678
4197
|
return {
|
|
3679
|
-
...
|
|
4198
|
+
...request2,
|
|
3680
4199
|
flow: {
|
|
3681
|
-
...
|
|
4200
|
+
...request2.flow,
|
|
3682
4201
|
// `clonedSteps` is a structural clone of `request.flow.steps` (already
|
|
3683
4202
|
// `FlowStepDefinition[]`); only the prompt step's `config.tools` was
|
|
3684
4203
|
// merged, so every step's `type` discriminant is preserved. The clone is
|
|
@@ -3688,18 +4207,56 @@ function attachRuntimeToolsToDispatchRequest(request, runtimeTools, options = {}
|
|
|
3688
4207
|
}
|
|
3689
4208
|
};
|
|
3690
4209
|
}
|
|
3691
|
-
function applyGeneratedRuntimeToolProposalToDispatchRequest(
|
|
4210
|
+
function applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options = {}) {
|
|
3692
4211
|
const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
|
|
3693
4212
|
if (!decision.approved || !decision.tool) {
|
|
3694
|
-
return { decision, request };
|
|
4213
|
+
return { decision, request: request2 };
|
|
3695
4214
|
}
|
|
3696
|
-
const nextRequest = attachRuntimeToolsToDispatchRequest(
|
|
4215
|
+
const nextRequest = attachRuntimeToolsToDispatchRequest(request2, [decision.tool], options.attach);
|
|
3697
4216
|
return {
|
|
3698
4217
|
decision,
|
|
3699
4218
|
request: nextRequest
|
|
3700
4219
|
};
|
|
3701
4220
|
}
|
|
3702
4221
|
|
|
4222
|
+
// src/offload-markers.ts
|
|
4223
|
+
var LEDGER_ARTIFACT_LINE_PREFIX = "Ledger artifact: ";
|
|
4224
|
+
function formatChars(charLength) {
|
|
4225
|
+
return charLength.toLocaleString("en-US");
|
|
4226
|
+
}
|
|
4227
|
+
function buildSendViewOffloadMarker(details) {
|
|
4228
|
+
return `[${details.toolName} output (${formatChars(details.charLength)} chars) saved to ${details.filePath} \u2014 use read_file to retrieve if needed]`;
|
|
4229
|
+
}
|
|
4230
|
+
function buildLedgerOffloadReference(details) {
|
|
4231
|
+
return [
|
|
4232
|
+
`[Output offloaded as ${details.outputId} \u2014 ${formatChars(details.charLength)} chars stored in the marathon context ledger]`,
|
|
4233
|
+
`${LEDGER_ARTIFACT_LINE_PREFIX}${details.relativePath}`,
|
|
4234
|
+
`Preview: ${details.preview}${details.truncated ? "..." : ""}`,
|
|
4235
|
+
"",
|
|
4236
|
+
`Use read_offloaded_output with id "${details.outputId}" to retrieve the full output if needed.`
|
|
4237
|
+
].join("\n");
|
|
4238
|
+
}
|
|
4239
|
+
var DECLARED_CHARS_PATTERNS = [
|
|
4240
|
+
/—\s*([\d,]+)\s+chars?\s+(?:stored|saved)/i,
|
|
4241
|
+
/\(([\d,]+)\s+chars?\)\s+saved/i
|
|
4242
|
+
];
|
|
4243
|
+
function extractDeclaredToolResultChars(value) {
|
|
4244
|
+
if (typeof value !== "string") return void 0;
|
|
4245
|
+
for (const pattern of DECLARED_CHARS_PATTERNS) {
|
|
4246
|
+
const match = pattern.exec(value);
|
|
4247
|
+
if (!match?.[1]) continue;
|
|
4248
|
+
const parsed = Number.parseInt(match[1].replace(/,/g, ""), 10);
|
|
4249
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
4250
|
+
}
|
|
4251
|
+
return void 0;
|
|
4252
|
+
}
|
|
4253
|
+
function parseOffloadedOutputId(value) {
|
|
4254
|
+
return /\bread_offloaded_output\s+with\s+id\s+"([^"]+)"/i.exec(value)?.[1] || /\[Output offloaded as\s+([a-zA-Z0-9_-]+)/i.exec(value)?.[1] || void 0;
|
|
4255
|
+
}
|
|
4256
|
+
function parseLedgerArtifactRelativePath(value) {
|
|
4257
|
+
return value.split("\n").find((line) => line.startsWith(LEDGER_ARTIFACT_LINE_PREFIX))?.slice(LEDGER_ARTIFACT_LINE_PREFIX.length).trim();
|
|
4258
|
+
}
|
|
4259
|
+
|
|
3703
4260
|
// src/workflow-utils.ts
|
|
3704
4261
|
function normalizeCandidatePath(candidatePath) {
|
|
3705
4262
|
return candidatePath.trim().replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+/g, "/");
|
|
@@ -3753,6 +4310,261 @@ function sanitizeTaskSlug(taskName) {
|
|
|
3753
4310
|
return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
3754
4311
|
}
|
|
3755
4312
|
|
|
4313
|
+
// src/workflows/hook-registry.ts
|
|
4314
|
+
var BUILTIN_NAMESPACE = "builtin";
|
|
4315
|
+
var HOOK_REF_PATTERN = /^[a-z0-9_-]+:[a-z0-9_-]+$/;
|
|
4316
|
+
function isWorkflowHookRef(value) {
|
|
4317
|
+
return typeof value === "string" && HOOK_REF_PATTERN.test(value);
|
|
4318
|
+
}
|
|
4319
|
+
var registry = /* @__PURE__ */ new Map();
|
|
4320
|
+
function registerWorkflowHook(name, entry) {
|
|
4321
|
+
if (!isWorkflowHookRef(name)) {
|
|
4322
|
+
throw new Error(
|
|
4323
|
+
`Invalid workflow hook name "${name}": must be "<namespace>:<id>" using lowercase letters, digits, "-" or "_" (e.g. "acme:my-completion").`
|
|
4324
|
+
);
|
|
4325
|
+
}
|
|
4326
|
+
if (name.startsWith(`${BUILTIN_NAMESPACE}:`)) {
|
|
4327
|
+
throw new Error(
|
|
4328
|
+
`Cannot register "${name}": the "builtin:" namespace is reserved. Register under your own namespace and reference it from the workflow config instead.`
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
registry.set(name, entry);
|
|
4332
|
+
}
|
|
4333
|
+
function registerBuiltinWorkflowHook(name, entry) {
|
|
4334
|
+
if (!name.startsWith(`${BUILTIN_NAMESPACE}:`) || !isWorkflowHookRef(name)) {
|
|
4335
|
+
throw new Error(`Builtin workflow hooks must be named "builtin:<id>" (got "${name}").`);
|
|
4336
|
+
}
|
|
4337
|
+
if (registry.has(name)) return;
|
|
4338
|
+
registry.set(name, entry);
|
|
4339
|
+
}
|
|
4340
|
+
function resolveWorkflowHook(name, expectedKind) {
|
|
4341
|
+
const entry = registry.get(name);
|
|
4342
|
+
if (!entry) {
|
|
4343
|
+
const known = listWorkflowHooks().filter((hook) => hook.kind === expectedKind).map((hook) => hook.name);
|
|
4344
|
+
throw new Error(
|
|
4345
|
+
`Unknown workflow hook "${name}". ` + (known.length > 0 ? `Registered '${expectedKind}' hooks: ${known.join(", ")}.` : `No '${expectedKind}' hooks are registered.`) + " Custom hooks must be registered (e.g. via a playbook plugin) before the workflow is compiled."
|
|
4346
|
+
);
|
|
4347
|
+
}
|
|
4348
|
+
if (entry.kind !== expectedKind) {
|
|
4349
|
+
throw new Error(
|
|
4350
|
+
`Workflow hook "${name}" is registered as '${entry.kind}' but referenced from a '${expectedKind}' slot.`
|
|
4351
|
+
);
|
|
4352
|
+
}
|
|
4353
|
+
return entry.fn;
|
|
4354
|
+
}
|
|
4355
|
+
function listWorkflowHooks() {
|
|
4356
|
+
return [...registry.entries()].map(([name, entry]) => ({ name, kind: entry.kind }));
|
|
4357
|
+
}
|
|
4358
|
+
function unregisterWorkflowHook(name) {
|
|
4359
|
+
if (name.startsWith(`${BUILTIN_NAMESPACE}:`)) return false;
|
|
4360
|
+
return registry.delete(name);
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4363
|
+
// src/workflows/workflow-config.ts
|
|
4364
|
+
var DISCOVERY_TOOLS = /* @__PURE__ */ new Set([
|
|
4365
|
+
"search_repo",
|
|
4366
|
+
"glob_files",
|
|
4367
|
+
"tree_directory",
|
|
4368
|
+
"list_directory"
|
|
4369
|
+
]);
|
|
4370
|
+
var DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS = 2;
|
|
4371
|
+
function definePlaybook(playbook) {
|
|
4372
|
+
return playbook;
|
|
4373
|
+
}
|
|
4374
|
+
function interpolateWorkflowTemplate(template, state) {
|
|
4375
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
4376
|
+
const value = state[key];
|
|
4377
|
+
if (value === void 0 || value === null) return `{{${key}}}`;
|
|
4378
|
+
return String(value);
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
function buildIsComplete(criteria, configName, milestoneName) {
|
|
4382
|
+
if (!criteria) return () => false;
|
|
4383
|
+
switch (criteria.type) {
|
|
4384
|
+
case "evidence":
|
|
4385
|
+
return (ctx) => {
|
|
4386
|
+
const minFiles = criteria.minReadFiles ?? 1;
|
|
4387
|
+
return (ctx.state.recentReadPaths?.length ?? 0) >= minFiles;
|
|
4388
|
+
};
|
|
4389
|
+
case "sessions": {
|
|
4390
|
+
let baselineSessionCount;
|
|
4391
|
+
return (ctx) => {
|
|
4392
|
+
const minSessions = criteria.minSessions ?? 1;
|
|
4393
|
+
if (baselineSessionCount === void 0) {
|
|
4394
|
+
baselineSessionCount = ctx.state.sessions.length;
|
|
4395
|
+
}
|
|
4396
|
+
return ctx.state.sessions.length - baselineSessionCount >= minSessions;
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
case "planWritten":
|
|
4400
|
+
return (ctx) => {
|
|
4401
|
+
return ctx.trace.planWritten;
|
|
4402
|
+
};
|
|
4403
|
+
case "never":
|
|
4404
|
+
return () => false;
|
|
4405
|
+
default: {
|
|
4406
|
+
if (isWorkflowHookRef(criteria.type)) {
|
|
4407
|
+
return resolveWorkflowHook(criteria.type, "completion");
|
|
4408
|
+
}
|
|
4409
|
+
throw new Error(
|
|
4410
|
+
`Workflow config '${configName}': milestone '${milestoneName}' has unknown completionCriteria.type "${criteria.type}" (expected evidence | sessions | planWritten | never, or a 'completion' hook reference).`
|
|
4411
|
+
);
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
}
|
|
4415
|
+
function buildPolicyIntercept(policy, configName, deps) {
|
|
4416
|
+
if (!policy.blockedTools?.length && !policy.blockDiscoveryTools && !policy.allowedReadGlobs?.length && !policy.allowedWriteGlobs?.length && !policy.requirePlanBeforeWrite) {
|
|
4417
|
+
return void 0;
|
|
4418
|
+
}
|
|
4419
|
+
const blockedSet = new Set(
|
|
4420
|
+
(policy.blockedTools ?? []).map((t) => t.trim()).filter(Boolean)
|
|
4421
|
+
);
|
|
4422
|
+
const readGlobs = policy.allowedReadGlobs ?? [];
|
|
4423
|
+
const writeGlobs = policy.allowedWriteGlobs ?? [];
|
|
4424
|
+
const matchPathGlobs = deps.matchPathGlobs;
|
|
4425
|
+
if ((readGlobs.length > 0 || writeGlobs.length > 0) && !matchPathGlobs) {
|
|
4426
|
+
throw new Error(
|
|
4427
|
+
`Workflow config '${configName}': policy uses allowedReadGlobs/allowedWriteGlobs but no glob matcher was provided to compileWorkflowConfig (pass deps.matchPathGlobs).`
|
|
4428
|
+
);
|
|
4429
|
+
}
|
|
4430
|
+
return (toolName, args, ctx) => {
|
|
4431
|
+
if (blockedSet.has(toolName)) {
|
|
4432
|
+
return `Blocked by playbook policy: ${toolName} is not allowed for this task.`;
|
|
4433
|
+
}
|
|
4434
|
+
if (policy.blockDiscoveryTools && DISCOVERY_TOOLS.has(toolName)) {
|
|
4435
|
+
return `Blocked by playbook policy: discovery tools are disabled for this task.`;
|
|
4436
|
+
}
|
|
4437
|
+
const pathArg = typeof args.path === "string" && args.path.trim() ? ctx.normalizePath(String(args.path)) : void 0;
|
|
4438
|
+
if (pathArg) {
|
|
4439
|
+
const isWrite = toolName === "write_file" || toolName === "restore_file_checkpoint";
|
|
4440
|
+
const isRead = toolName === "read_file";
|
|
4441
|
+
if (isRead && readGlobs.length > 0) {
|
|
4442
|
+
const allowed = matchPathGlobs(pathArg, readGlobs);
|
|
4443
|
+
if (!allowed) {
|
|
4444
|
+
return `Blocked by playbook policy: ${toolName} path "${pathArg}" is outside allowed read globs: ${readGlobs.join(", ")}`;
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
if (isWrite && writeGlobs.length > 0) {
|
|
4448
|
+
const planPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4449
|
+
if (planPath && pathArg === planPath) {
|
|
4450
|
+
} else {
|
|
4451
|
+
const allowed = matchPathGlobs(pathArg, writeGlobs);
|
|
4452
|
+
if (!allowed) {
|
|
4453
|
+
return `Blocked by playbook policy: ${toolName} path "${pathArg}" is outside allowed write globs: ${writeGlobs.join(", ")}`;
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
if (isWrite && policy.requirePlanBeforeWrite && !ctx.state.planWritten && !ctx.trace.planWritten) {
|
|
4458
|
+
const planPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4459
|
+
if (!planPath || pathArg !== planPath) {
|
|
4460
|
+
return `Blocked by playbook policy: write the plan before creating other files.`;
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
return void 0;
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
function buildPolicyGuidance(policy) {
|
|
4468
|
+
if (!policy) return [];
|
|
4469
|
+
const lines = [];
|
|
4470
|
+
if (policy.requirePlanBeforeWrite) {
|
|
4471
|
+
lines.push(
|
|
4472
|
+
"Policy: write the plan file before any other file. Once the plan is written, other writes are allowed in the same turn."
|
|
4473
|
+
);
|
|
4474
|
+
}
|
|
4475
|
+
if (policy.allowedWriteGlobs?.length) {
|
|
4476
|
+
lines.push(
|
|
4477
|
+
`Policy: file writes are only allowed for paths matching: ${policy.allowedWriteGlobs.join(", ")} (the plan file is always allowed).`
|
|
4478
|
+
);
|
|
4479
|
+
}
|
|
4480
|
+
if (policy.outputRoot) {
|
|
4481
|
+
lines.push(`Policy: create new files under "${policy.outputRoot.replace(/\/$/, "")}/".`);
|
|
4482
|
+
}
|
|
4483
|
+
if (policy.allowedReadGlobs?.length) {
|
|
4484
|
+
lines.push(
|
|
4485
|
+
`Policy: file reads are only allowed for paths matching: ${policy.allowedReadGlobs.join(", ")}.`
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
if (policy.blockDiscoveryTools) {
|
|
4489
|
+
lines.push(
|
|
4490
|
+
"Policy: broad discovery tools (search_repo, glob_files, tree_directory, list_directory) are disabled for this task."
|
|
4491
|
+
);
|
|
4492
|
+
}
|
|
4493
|
+
if (policy.blockedTools?.length) {
|
|
4494
|
+
lines.push(`Policy: these tools are disabled for this task: ${policy.blockedTools.join(", ")}.`);
|
|
4495
|
+
}
|
|
4496
|
+
return lines;
|
|
4497
|
+
}
|
|
4498
|
+
function resolveSlotHook(value, kind) {
|
|
4499
|
+
if (value === void 0) return void 0;
|
|
4500
|
+
if (typeof value === "function") return value;
|
|
4501
|
+
return resolveWorkflowHook(value, kind);
|
|
4502
|
+
}
|
|
4503
|
+
function compileMilestone(milestone, config, policyIntercept, policyGuidance) {
|
|
4504
|
+
const buildInstructions = typeof milestone.instructions === "function" ? milestone.instructions : isWorkflowHookRef(milestone.instructions) ? resolveWorkflowHook(milestone.instructions, "instructions") : (state) => {
|
|
4505
|
+
const header = `--- Workflow Phase: ${milestone.name} ---`;
|
|
4506
|
+
const desc = milestone.description ? `
|
|
4507
|
+
${milestone.description}` : "";
|
|
4508
|
+
const instructions = interpolateWorkflowTemplate(
|
|
4509
|
+
milestone.instructions,
|
|
4510
|
+
state
|
|
4511
|
+
);
|
|
4512
|
+
return `${header}${desc}
|
|
4513
|
+
${instructions}`;
|
|
4514
|
+
};
|
|
4515
|
+
const guidanceHook = typeof milestone.toolGuidance === "function" ? milestone.toolGuidance : milestone.toolGuidance !== void 0 && isWorkflowHookRef(milestone.toolGuidance) ? resolveWorkflowHook(milestone.toolGuidance, "toolGuidance") : void 0;
|
|
4516
|
+
const buildToolGuidance = (state) => {
|
|
4517
|
+
const base = guidanceHook ? guidanceHook(state) : milestone.toolGuidance ?? [];
|
|
4518
|
+
return policyGuidance.length > 0 ? [...base, ...policyGuidance] : base;
|
|
4519
|
+
};
|
|
4520
|
+
const customIntercept = resolveSlotHook(milestone.intercept, "intercept");
|
|
4521
|
+
const interceptToolCall = policyIntercept && customIntercept ? (toolName, args, ctx) => policyIntercept(toolName, args, ctx) ?? customIntercept(toolName, args, ctx) : policyIntercept ?? customIntercept;
|
|
4522
|
+
const transitionHook = typeof milestone.transitionSummary === "function" ? milestone.transitionSummary : milestone.transitionSummary !== void 0 && isWorkflowHookRef(milestone.transitionSummary) ? resolveWorkflowHook(milestone.transitionSummary, "transitionSummary") : void 0;
|
|
4523
|
+
const buildTransitionSummary = milestone.transitionSummary === void 0 ? void 0 : transitionHook ?? ((state, nextPhaseName) => interpolateWorkflowTemplate(milestone.transitionSummary, state).replace(
|
|
4524
|
+
/\{\{nextPhase\}\}/g,
|
|
4525
|
+
nextPhaseName
|
|
4526
|
+
));
|
|
4527
|
+
const recoveryHook = typeof milestone.recovery === "function" ? milestone.recovery : milestone.recovery !== void 0 && isWorkflowHookRef(milestone.recovery) ? resolveWorkflowHook(milestone.recovery, "recovery") : void 0;
|
|
4528
|
+
const buildRecoveryMessage = milestone.recovery === void 0 ? void 0 : recoveryHook ?? ((state) => {
|
|
4529
|
+
const inline = milestone.recovery;
|
|
4530
|
+
const threshold = inline.afterEmptySessions ?? DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS;
|
|
4531
|
+
if ((state.consecutiveEmptySessions ?? 0) < threshold) return void 0;
|
|
4532
|
+
return interpolateWorkflowTemplate(inline.message, state);
|
|
4533
|
+
});
|
|
4534
|
+
const canAcceptCompletion = milestone.canAcceptCompletion === void 0 ? void 0 : typeof milestone.canAcceptCompletion === "function" ? milestone.canAcceptCompletion : isWorkflowHookRef(milestone.canAcceptCompletion) ? resolveWorkflowHook(milestone.canAcceptCompletion, "acceptCompletion") : () => milestone.canAcceptCompletion;
|
|
4535
|
+
const isComplete = typeof milestone.completionCriteria === "function" ? milestone.completionCriteria : buildIsComplete(milestone.completionCriteria, config.name, milestone.name);
|
|
4536
|
+
return {
|
|
4537
|
+
name: milestone.name,
|
|
4538
|
+
description: milestone.description,
|
|
4539
|
+
buildInstructions,
|
|
4540
|
+
buildToolGuidance,
|
|
4541
|
+
isComplete,
|
|
4542
|
+
...interceptToolCall ? { interceptToolCall } : {},
|
|
4543
|
+
...buildTransitionSummary ? { buildTransitionSummary } : {},
|
|
4544
|
+
...buildRecoveryMessage ? { buildRecoveryMessage } : {},
|
|
4545
|
+
...milestone.forceEndTurn ? { shouldForceEndTurn: resolveSlotHook(milestone.forceEndTurn, "forceEndTurn") } : {},
|
|
4546
|
+
...canAcceptCompletion ? { canAcceptCompletion } : {}
|
|
4547
|
+
};
|
|
4548
|
+
}
|
|
4549
|
+
function compileWorkflowConfig(config, deps = {}) {
|
|
4550
|
+
const policyIntercept = config.policy ? buildPolicyIntercept(config.policy, config.name, deps) : void 0;
|
|
4551
|
+
const policyGuidance = buildPolicyGuidance(config.policy);
|
|
4552
|
+
const phases = config.milestones.map(
|
|
4553
|
+
(milestone) => compileMilestone(milestone, config, policyIntercept, policyGuidance)
|
|
4554
|
+
);
|
|
4555
|
+
const classifyVariant4 = resolveSlotHook(config.classifyVariant, "classify");
|
|
4556
|
+
const generateBootstrapContext2 = resolveSlotHook(config.bootstrap, "bootstrap");
|
|
4557
|
+
const buildCandidateBlock2 = resolveSlotHook(config.candidateBlock, "candidateBlock");
|
|
4558
|
+
return {
|
|
4559
|
+
name: config.name,
|
|
4560
|
+
phases,
|
|
4561
|
+
...config.stallPolicy ? { stallPolicy: config.stallPolicy } : {},
|
|
4562
|
+
...classifyVariant4 ? { classifyVariant: classifyVariant4 } : {},
|
|
4563
|
+
...generateBootstrapContext2 ? { generateBootstrapContext: generateBootstrapContext2 } : {},
|
|
4564
|
+
...buildCandidateBlock2 ? { buildCandidateBlock: buildCandidateBlock2 } : {}
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
|
|
3756
4568
|
// src/workflows/default-workflow.ts
|
|
3757
4569
|
function isExternalTask(state) {
|
|
3758
4570
|
return state.workflowVariant === "external";
|
|
@@ -3902,6 +4714,45 @@ function summarizeTextBlock(value, maxLines = 4) {
|
|
|
3902
4714
|
if (!text) return "";
|
|
3903
4715
|
return text.split("\n").map((line) => line.trim()).filter(Boolean).slice(0, maxLines).join(" | ").slice(0, 240);
|
|
3904
4716
|
}
|
|
4717
|
+
function interceptProductWriteTarget(toolName, normalizedPathArg, ctx, guardLabel) {
|
|
4718
|
+
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4719
|
+
const normalizedBestCandidatePath = ctx.state.bestCandidatePath ? ctx.normalizePath(ctx.state.bestCandidatePath) : void 0;
|
|
4720
|
+
if (!ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4721
|
+
const allowedWriteTargets = new Set(
|
|
4722
|
+
[
|
|
4723
|
+
normalizedPlanPath,
|
|
4724
|
+
normalizedBestCandidatePath,
|
|
4725
|
+
...(ctx.state.recentReadPaths || []).map((readPath) => ctx.normalizePath(readPath)),
|
|
4726
|
+
...ctx.trace.readPaths.map((readPath) => ctx.normalizePath(readPath))
|
|
4727
|
+
].filter((value) => Boolean(value))
|
|
4728
|
+
);
|
|
4729
|
+
if (!allowedWriteTargets.has(normalizedPathArg)) {
|
|
4730
|
+
return [
|
|
4731
|
+
`Blocked by marathon ${guardLabel}: ${toolName} is limited to the confirmed target, the plan file, or files already discovered/read for this task.`,
|
|
4732
|
+
`Do not create scratch files like "${normalizedPathArg}".`,
|
|
4733
|
+
normalizedBestCandidatePath ? `Edit "${normalizedBestCandidatePath}" or another previously discovered repo file instead.` : "Read the current target file before writing."
|
|
4734
|
+
].join(" ");
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
if (ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4738
|
+
const outputRoot = ctx.state.outputRoot ? ctx.state.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "") || void 0 : void 0;
|
|
4739
|
+
if (!outputRoot) {
|
|
4740
|
+
return [
|
|
4741
|
+
`Blocked by marathon ${guardLabel}: creation tasks require outputRoot. Writes outside the plan are not allowed.`,
|
|
4742
|
+
`Plan path: "${normalizedPlanPath}". Create files only under the configured output root.`
|
|
4743
|
+
].join(" ");
|
|
4744
|
+
}
|
|
4745
|
+
const rootPrefix = outputRoot + "/";
|
|
4746
|
+
const isUnderRoot = normalizedPathArg === outputRoot || normalizedPathArg.startsWith(rootPrefix);
|
|
4747
|
+
if (!isUnderRoot) {
|
|
4748
|
+
return [
|
|
4749
|
+
`Blocked by marathon ${guardLabel}: ${toolName} must target the plan or paths under outputRoot "${outputRoot}/".`,
|
|
4750
|
+
`"${normalizedPathArg}" is outside the allowed output root.`
|
|
4751
|
+
].join(" ");
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
return void 0;
|
|
4755
|
+
}
|
|
3905
4756
|
var researchPhase = {
|
|
3906
4757
|
name: "research",
|
|
3907
4758
|
description: "Inspect the repo and identify the correct target file",
|
|
@@ -3986,11 +4837,15 @@ var researchPhase = {
|
|
|
3986
4837
|
const normalizedPathArg2 = typeof _args.path === "string" && _args.path.trim() ? ctx.normalizePath(String(_args.path)) : void 0;
|
|
3987
4838
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
3988
4839
|
if (normalizedPathArg2 && normalizedPlanPath && normalizedPathArg2 !== normalizedPlanPath) {
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4840
|
+
const planWritten = ctx.trace.planWritten || Boolean(ctx.state.planWritten);
|
|
4841
|
+
if (!planWritten) {
|
|
4842
|
+
return [
|
|
4843
|
+
`Blocked by marathon research guard: ${toolName} cannot create product files during the research phase.`,
|
|
4844
|
+
"Complete research first, then the system will advance you to planning.",
|
|
4845
|
+
`You may write the plan to "${normalizedPlanPath}" once research is complete.`
|
|
4846
|
+
].join(" ");
|
|
4847
|
+
}
|
|
4848
|
+
return interceptProductWriteTarget(toolName, normalizedPathArg2, ctx, "research guard");
|
|
3994
4849
|
}
|
|
3995
4850
|
}
|
|
3996
4851
|
return void 0;
|
|
@@ -4113,19 +4968,24 @@ var planningPhase = {
|
|
|
4113
4968
|
"Research is complete. Write the implementation plan for building this from scratch.",
|
|
4114
4969
|
`Write the plan markdown to exactly: ${planPath}`,
|
|
4115
4970
|
"List the files you will create, their locations, purpose, and any dependencies to install.",
|
|
4971
|
+
...state.outputRoot ? [
|
|
4972
|
+
`All new files must be created under "${state.outputRoot}" \u2014 writes outside that directory are blocked, so plan every file location inside it.`
|
|
4973
|
+
] : [],
|
|
4116
4974
|
'Include a "Verification steps" section listing the concrete checks you will run before TASK_COMPLETE.',
|
|
4117
|
-
"If the plan already exists, update that same plan file instead of creating a different one."
|
|
4975
|
+
"If the plan already exists, update that same plan file instead of creating a different one.",
|
|
4976
|
+
"Once the plan is written, you may begin creating the planned files in the same turn."
|
|
4118
4977
|
].join("\n");
|
|
4119
4978
|
}
|
|
4120
4979
|
return [
|
|
4121
4980
|
"--- Workflow Phase: Planning ---",
|
|
4122
4981
|
"Research is complete. Your current job is to write the implementation plan before any product-file edits.",
|
|
4123
4982
|
`Write the plan markdown to exactly: ${planPath}`,
|
|
4124
|
-
"Do NOT edit the target product file
|
|
4983
|
+
"Do NOT edit the target product file before the plan exists.",
|
|
4125
4984
|
"The plan should summarize UX findings, explain why the current best candidate is the right file, and list concrete execution steps.",
|
|
4126
4985
|
'The plan must include a "Preserve existing functionality" section that lists current behaviors, linked files, integrations, and constraints that must keep working.',
|
|
4127
4986
|
'The plan must include a "Verification steps" section listing the concrete checks you will run before TASK_COMPLETE.',
|
|
4128
|
-
"If the plan already exists, update that same plan file instead of creating a different one."
|
|
4987
|
+
"If the plan already exists, update that same plan file instead of creating a different one.",
|
|
4988
|
+
"Once the plan is written, you may begin editing the target file in the same turn."
|
|
4129
4989
|
].join("\n");
|
|
4130
4990
|
},
|
|
4131
4991
|
buildToolGuidance(state) {
|
|
@@ -4153,10 +5013,14 @@ var planningPhase = {
|
|
|
4153
5013
|
const normalizedPlanPath = ctx.state.planPath ? ctx.normalizePath(ctx.state.planPath) : void 0;
|
|
4154
5014
|
const isWriteLikeTool = toolName === "write_file" || toolName === "edit_file" || toolName === "restore_file_checkpoint";
|
|
4155
5015
|
if (isWriteLikeTool && normalizedPathArg && normalizedPlanPath && normalizedPathArg !== normalizedPlanPath) {
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
5016
|
+
const planWritten = ctx.trace.planWritten || Boolean(ctx.state.planWritten);
|
|
5017
|
+
if (!planWritten) {
|
|
5018
|
+
return [
|
|
5019
|
+
`Blocked by marathon planning guard: ${toolName} must target the exact plan path during planning.`,
|
|
5020
|
+
`Write the plan to "${normalizedPlanPath}" before editing any product files.`
|
|
5021
|
+
].join(" ");
|
|
5022
|
+
}
|
|
5023
|
+
return interceptProductWriteTarget(toolName, normalizedPathArg, ctx, "planning guard");
|
|
4160
5024
|
}
|
|
4161
5025
|
return void 0;
|
|
4162
5026
|
},
|
|
@@ -4216,6 +5080,9 @@ var executionPhase = {
|
|
|
4216
5080
|
},
|
|
4217
5081
|
buildToolGuidance(state) {
|
|
4218
5082
|
return [
|
|
5083
|
+
...state.isCreationTask && state.outputRoot ? [
|
|
5084
|
+
`Creation guard: create new files under "${state.outputRoot}". Writes outside it are blocked \u2014 the plan file is the only exception.`
|
|
5085
|
+
] : [],
|
|
4219
5086
|
...state.bestCandidatePath ? [
|
|
4220
5087
|
`Execution-phase guard: broad discovery tools (search_repo, glob_files, tree_directory, list_directory) are locked while executing against "${state.bestCandidatePath}".`
|
|
4221
5088
|
] : [
|
|
@@ -4257,40 +5124,13 @@ var executionPhase = {
|
|
|
4257
5124
|
`After that, you may update "${normalizedPlanPath}" with progress.`
|
|
4258
5125
|
].join(" ");
|
|
4259
5126
|
}
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
].filter((value) => Boolean(value))
|
|
4268
|
-
);
|
|
4269
|
-
if (!allowedWriteTargets.has(normalizedPathArg)) {
|
|
4270
|
-
return [
|
|
4271
|
-
`Blocked by marathon execution guard: ${toolName} is limited to the confirmed target, the plan file, or files already discovered/read for this task.`,
|
|
4272
|
-
`Do not create scratch files like "${normalizedPathArg}".`,
|
|
4273
|
-
normalizedBestCandidatePath ? `Edit "${normalizedBestCandidatePath}" or another previously discovered repo file instead.` : "Read the current target file before writing."
|
|
4274
|
-
].join(" ");
|
|
4275
|
-
}
|
|
4276
|
-
}
|
|
4277
|
-
if (ctx.state.isCreationTask && normalizedPathArg && normalizedPathArg !== normalizedPlanPath) {
|
|
4278
|
-
const outputRoot = ctx.state.outputRoot ? ctx.state.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "") || void 0 : void 0;
|
|
4279
|
-
if (!outputRoot) {
|
|
4280
|
-
return [
|
|
4281
|
-
`Blocked by marathon execution guard: creation tasks require outputRoot. Writes outside the plan are not allowed.`,
|
|
4282
|
-
`Plan path: "${normalizedPlanPath}". Create files only under the configured output root.`
|
|
4283
|
-
].join(" ");
|
|
4284
|
-
}
|
|
4285
|
-
const rootPrefix = outputRoot + "/";
|
|
4286
|
-
const isUnderRoot = normalizedPathArg === outputRoot || normalizedPathArg.startsWith(rootPrefix);
|
|
4287
|
-
if (!isUnderRoot) {
|
|
4288
|
-
return [
|
|
4289
|
-
`Blocked by marathon execution guard: ${toolName} must target the plan or paths under outputRoot "${outputRoot}/".`,
|
|
4290
|
-
`"${normalizedPathArg}" is outside the allowed output root.`
|
|
4291
|
-
].join(" ");
|
|
4292
|
-
}
|
|
4293
|
-
}
|
|
5127
|
+
const writeTargetBlock = interceptProductWriteTarget(
|
|
5128
|
+
toolName,
|
|
5129
|
+
normalizedPathArg,
|
|
5130
|
+
ctx,
|
|
5131
|
+
"execution guard"
|
|
5132
|
+
);
|
|
5133
|
+
if (writeTargetBlock) return writeTargetBlock;
|
|
4294
5134
|
}
|
|
4295
5135
|
return void 0;
|
|
4296
5136
|
},
|
|
@@ -4523,13 +5363,163 @@ function buildCandidateBlock(state) {
|
|
|
4523
5363
|
...state.bestCandidateReason ? [`Why: ${state.bestCandidateReason}`] : []
|
|
4524
5364
|
].join("\n");
|
|
4525
5365
|
}
|
|
4526
|
-
var
|
|
5366
|
+
var builtinHooksRegistered = false;
|
|
5367
|
+
function ensureDefaultWorkflowHooks() {
|
|
5368
|
+
if (builtinHooksRegistered) return;
|
|
5369
|
+
builtinHooksRegistered = true;
|
|
5370
|
+
registerBuiltinWorkflowHook("builtin:classify-task-variant", {
|
|
5371
|
+
kind: "classify",
|
|
5372
|
+
fn: classifyVariant
|
|
5373
|
+
});
|
|
5374
|
+
registerBuiltinWorkflowHook("builtin:repo-bootstrap-discovery", {
|
|
5375
|
+
kind: "bootstrap",
|
|
5376
|
+
fn: generateBootstrapContext
|
|
5377
|
+
});
|
|
5378
|
+
registerBuiltinWorkflowHook("builtin:best-candidate-block", {
|
|
5379
|
+
kind: "candidateBlock",
|
|
5380
|
+
fn: buildCandidateBlock
|
|
5381
|
+
});
|
|
5382
|
+
registerBuiltinWorkflowHook("builtin:research-instructions", {
|
|
5383
|
+
kind: "instructions",
|
|
5384
|
+
fn: researchPhase.buildInstructions
|
|
5385
|
+
});
|
|
5386
|
+
registerBuiltinWorkflowHook("builtin:research-tool-guidance", {
|
|
5387
|
+
kind: "toolGuidance",
|
|
5388
|
+
fn: researchPhase.buildToolGuidance
|
|
5389
|
+
});
|
|
5390
|
+
registerBuiltinWorkflowHook("builtin:research-complete", {
|
|
5391
|
+
kind: "completion",
|
|
5392
|
+
fn: researchPhase.isComplete
|
|
5393
|
+
});
|
|
5394
|
+
registerBuiltinWorkflowHook("builtin:research-transition-summary", {
|
|
5395
|
+
kind: "transitionSummary",
|
|
5396
|
+
fn: researchPhase.buildTransitionSummary
|
|
5397
|
+
});
|
|
5398
|
+
registerBuiltinWorkflowHook("builtin:research-guard", {
|
|
5399
|
+
kind: "intercept",
|
|
5400
|
+
fn: researchPhase.interceptToolCall
|
|
5401
|
+
});
|
|
5402
|
+
registerBuiltinWorkflowHook("builtin:research-recovery", {
|
|
5403
|
+
kind: "recovery",
|
|
5404
|
+
fn: researchPhase.buildRecoveryMessage
|
|
5405
|
+
});
|
|
5406
|
+
registerBuiltinWorkflowHook("builtin:research-force-end-turn", {
|
|
5407
|
+
kind: "forceEndTurn",
|
|
5408
|
+
fn: researchPhase.shouldForceEndTurn
|
|
5409
|
+
});
|
|
5410
|
+
registerBuiltinWorkflowHook("builtin:research-accept-completion", {
|
|
5411
|
+
kind: "acceptCompletion",
|
|
5412
|
+
fn: researchPhase.canAcceptCompletion
|
|
5413
|
+
});
|
|
5414
|
+
registerBuiltinWorkflowHook("builtin:planning-instructions", {
|
|
5415
|
+
kind: "instructions",
|
|
5416
|
+
fn: planningPhase.buildInstructions
|
|
5417
|
+
});
|
|
5418
|
+
registerBuiltinWorkflowHook("builtin:planning-tool-guidance", {
|
|
5419
|
+
kind: "toolGuidance",
|
|
5420
|
+
fn: planningPhase.buildToolGuidance
|
|
5421
|
+
});
|
|
5422
|
+
registerBuiltinWorkflowHook("builtin:planning-complete", {
|
|
5423
|
+
kind: "completion",
|
|
5424
|
+
fn: planningPhase.isComplete
|
|
5425
|
+
});
|
|
5426
|
+
registerBuiltinWorkflowHook("builtin:planning-transition-summary", {
|
|
5427
|
+
kind: "transitionSummary",
|
|
5428
|
+
fn: planningPhase.buildTransitionSummary
|
|
5429
|
+
});
|
|
5430
|
+
registerBuiltinWorkflowHook("builtin:planning-guard", {
|
|
5431
|
+
kind: "intercept",
|
|
5432
|
+
fn: planningPhase.interceptToolCall
|
|
5433
|
+
});
|
|
5434
|
+
registerBuiltinWorkflowHook("builtin:planning-recovery", {
|
|
5435
|
+
kind: "recovery",
|
|
5436
|
+
fn: planningPhase.buildRecoveryMessage
|
|
5437
|
+
});
|
|
5438
|
+
registerBuiltinWorkflowHook("builtin:planning-force-end-turn", {
|
|
5439
|
+
kind: "forceEndTurn",
|
|
5440
|
+
fn: planningPhase.shouldForceEndTurn
|
|
5441
|
+
});
|
|
5442
|
+
registerBuiltinWorkflowHook("builtin:execution-instructions", {
|
|
5443
|
+
kind: "instructions",
|
|
5444
|
+
fn: executionPhase.buildInstructions
|
|
5445
|
+
});
|
|
5446
|
+
registerBuiltinWorkflowHook("builtin:execution-tool-guidance", {
|
|
5447
|
+
kind: "toolGuidance",
|
|
5448
|
+
fn: executionPhase.buildToolGuidance
|
|
5449
|
+
});
|
|
5450
|
+
registerBuiltinWorkflowHook("builtin:execution-guard", {
|
|
5451
|
+
kind: "intercept",
|
|
5452
|
+
fn: executionPhase.interceptToolCall
|
|
5453
|
+
});
|
|
5454
|
+
registerBuiltinWorkflowHook("builtin:execution-recovery", {
|
|
5455
|
+
kind: "recovery",
|
|
5456
|
+
fn: executionPhase.buildRecoveryMessage
|
|
5457
|
+
});
|
|
5458
|
+
registerBuiltinWorkflowHook("builtin:execution-force-end-turn", {
|
|
5459
|
+
kind: "forceEndTurn",
|
|
5460
|
+
fn: executionPhase.shouldForceEndTurn
|
|
5461
|
+
});
|
|
5462
|
+
registerBuiltinWorkflowHook("builtin:execution-accept-completion", {
|
|
5463
|
+
kind: "acceptCompletion",
|
|
5464
|
+
fn: executionPhase.canAcceptCompletion
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
var defaultWorkflowConfig = {
|
|
4527
5468
|
name: "default",
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
5469
|
+
// Empty-session escalation. The counter only counts tool actions, so
|
|
5470
|
+
// narration-only sessions ("I'll create the files now" with no tool calls)
|
|
5471
|
+
// escalate here even though the phase recovery conditions keyed on
|
|
5472
|
+
// hadTextOutput skip them: nudge after the first actionless session, signal
|
|
5473
|
+
// model escalation after the second (a no-op unless the caller configured a
|
|
5474
|
+
// fallback model), and stop as 'stalled' after the third — the same total
|
|
5475
|
+
// session budget as before stallPolicy existed.
|
|
5476
|
+
stallPolicy: { nudgeAfter: 1, escalateModelAfter: 2, stopAfter: 3 },
|
|
5477
|
+
classifyVariant: "builtin:classify-task-variant",
|
|
5478
|
+
bootstrap: "builtin:repo-bootstrap-discovery",
|
|
5479
|
+
candidateBlock: "builtin:best-candidate-block",
|
|
5480
|
+
milestones: [
|
|
5481
|
+
{
|
|
5482
|
+
name: "research",
|
|
5483
|
+
description: "Inspect the repo and identify the correct target file",
|
|
5484
|
+
instructions: "builtin:research-instructions",
|
|
5485
|
+
toolGuidance: "builtin:research-tool-guidance",
|
|
5486
|
+
completionCriteria: { type: "builtin:research-complete" },
|
|
5487
|
+
intercept: "builtin:research-guard",
|
|
5488
|
+
transitionSummary: "builtin:research-transition-summary",
|
|
5489
|
+
recovery: "builtin:research-recovery",
|
|
5490
|
+
forceEndTurn: "builtin:research-force-end-turn",
|
|
5491
|
+
canAcceptCompletion: "builtin:research-accept-completion"
|
|
5492
|
+
},
|
|
5493
|
+
{
|
|
5494
|
+
name: "planning",
|
|
5495
|
+
description: "Write the implementation plan before editing product files",
|
|
5496
|
+
instructions: "builtin:planning-instructions",
|
|
5497
|
+
toolGuidance: "builtin:planning-tool-guidance",
|
|
5498
|
+
completionCriteria: { type: "builtin:planning-complete" },
|
|
5499
|
+
intercept: "builtin:planning-guard",
|
|
5500
|
+
transitionSummary: "builtin:planning-transition-summary",
|
|
5501
|
+
recovery: "builtin:planning-recovery",
|
|
5502
|
+
forceEndTurn: "builtin:planning-force-end-turn"
|
|
5503
|
+
// canAcceptCompletion intentionally absent: the hand-written planning
|
|
5504
|
+
// phase never defined it, and the SDK accepts completion when the slot
|
|
5505
|
+
// is undefined. Keep parity.
|
|
5506
|
+
},
|
|
5507
|
+
{
|
|
5508
|
+
name: "execution",
|
|
5509
|
+
description: "Execute the plan by editing target files",
|
|
5510
|
+
instructions: "builtin:execution-instructions",
|
|
5511
|
+
toolGuidance: "builtin:execution-tool-guidance",
|
|
5512
|
+
// Execution never auto-advances; completion is agent-driven via TASK_COMPLETE
|
|
5513
|
+
completionCriteria: { type: "never" },
|
|
5514
|
+
intercept: "builtin:execution-guard",
|
|
5515
|
+
recovery: "builtin:execution-recovery",
|
|
5516
|
+
forceEndTurn: "builtin:execution-force-end-turn",
|
|
5517
|
+
canAcceptCompletion: "builtin:execution-accept-completion"
|
|
5518
|
+
}
|
|
5519
|
+
]
|
|
4532
5520
|
};
|
|
5521
|
+
ensureDefaultWorkflowHooks();
|
|
5522
|
+
var defaultWorkflow = compileWorkflowConfig(defaultWorkflowConfig);
|
|
4533
5523
|
|
|
4534
5524
|
// src/workflows/deploy-workflow.ts
|
|
4535
5525
|
var scaffoldPhase = {
|
|
@@ -4941,6 +5931,34 @@ var gameWorkflow = {
|
|
|
4941
5931
|
}
|
|
4942
5932
|
};
|
|
4943
5933
|
|
|
5934
|
+
// src/workflows/stall-policy.ts
|
|
5935
|
+
var DEFAULT_STALL_STOP_AFTER = 3;
|
|
5936
|
+
function isPositiveInteger(value) {
|
|
5937
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 1;
|
|
5938
|
+
}
|
|
5939
|
+
function resolveStallStopAfter(policy) {
|
|
5940
|
+
return isPositiveInteger(policy?.stopAfter) ? policy.stopAfter : DEFAULT_STALL_STOP_AFTER;
|
|
5941
|
+
}
|
|
5942
|
+
function shouldRequestModelEscalation(policy, consecutiveEmptySessions) {
|
|
5943
|
+
const threshold = policy?.escalateModelAfter;
|
|
5944
|
+
if (!isPositiveInteger(threshold)) return false;
|
|
5945
|
+
return consecutiveEmptySessions === threshold;
|
|
5946
|
+
}
|
|
5947
|
+
function shouldInjectEmptySessionNudge(policy, consecutiveEmptySessions) {
|
|
5948
|
+
const threshold = policy?.nudgeAfter;
|
|
5949
|
+
if (!isPositiveInteger(threshold)) return false;
|
|
5950
|
+
return consecutiveEmptySessions >= threshold;
|
|
5951
|
+
}
|
|
5952
|
+
function buildEmptySessionNudge(consecutiveEmptySessions) {
|
|
5953
|
+
const sessionPhrase = consecutiveEmptySessions === 1 ? "Your previous session ended" : `Your previous ${consecutiveEmptySessions} sessions ended`;
|
|
5954
|
+
return [
|
|
5955
|
+
"Recovery instruction:",
|
|
5956
|
+
`${sessionPhrase} without a single tool call. Describing what you plan to do does nothing \u2014 only tool calls make progress.`,
|
|
5957
|
+
"Your next response MUST include at least one tool call (for example write_file, edit_file, read_file, or run_check) that advances the task.",
|
|
5958
|
+
"If a previous tool call was blocked, re-read the block message and satisfy its requirement instead of ending the turn."
|
|
5959
|
+
].join("\n");
|
|
5960
|
+
}
|
|
5961
|
+
|
|
4944
5962
|
// src/endpoints.ts
|
|
4945
5963
|
var FlowsEndpoint = class {
|
|
4946
5964
|
constructor(client) {
|
|
@@ -5431,15 +6449,15 @@ var DispatchEndpoint = class {
|
|
|
5431
6449
|
* Attach approved runtime tools to a prompt step in a redispatch request.
|
|
5432
6450
|
* Returns a new request object and does not mutate the original.
|
|
5433
6451
|
*/
|
|
5434
|
-
attachApprovedRuntimeTools(
|
|
5435
|
-
return attachRuntimeToolsToDispatchRequest(
|
|
6452
|
+
attachApprovedRuntimeTools(request2, runtimeTools, options) {
|
|
6453
|
+
return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
|
|
5436
6454
|
}
|
|
5437
6455
|
/**
|
|
5438
6456
|
* Validate a generated runtime tool proposal and attach it to the redispatch
|
|
5439
6457
|
* request if approved, in one call.
|
|
5440
6458
|
*/
|
|
5441
|
-
applyGeneratedRuntimeToolProposal(
|
|
5442
|
-
return applyGeneratedRuntimeToolProposalToDispatchRequest(
|
|
6459
|
+
applyGeneratedRuntimeToolProposal(request2, proposal, options) {
|
|
6460
|
+
return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
|
|
5443
6461
|
}
|
|
5444
6462
|
};
|
|
5445
6463
|
var ChatEndpoint = class {
|
|
@@ -5944,6 +6962,22 @@ async function processAgentStream(body, callbacks) {
|
|
|
5944
6962
|
reader.releaseLock();
|
|
5945
6963
|
}
|
|
5946
6964
|
}
|
|
6965
|
+
function sleepWithAbort(delayMs, signal) {
|
|
6966
|
+
return new Promise((resolve) => {
|
|
6967
|
+
const onAbort = () => {
|
|
6968
|
+
clearTimeout(timer);
|
|
6969
|
+
resolve();
|
|
6970
|
+
};
|
|
6971
|
+
const timer = setTimeout(() => {
|
|
6972
|
+
signal?.removeEventListener("abort", onAbort);
|
|
6973
|
+
resolve();
|
|
6974
|
+
}, delayMs);
|
|
6975
|
+
if (signal) {
|
|
6976
|
+
if (signal.aborted) onAbort();
|
|
6977
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
6978
|
+
}
|
|
6979
|
+
});
|
|
6980
|
+
}
|
|
5947
6981
|
var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
|
|
5948
6982
|
type: "object",
|
|
5949
6983
|
properties: {
|
|
@@ -5975,8 +7009,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
|
|
|
5975
7009
|
},
|
|
5976
7010
|
required: ["name", "description", "toolType", "parametersSchema", "config"]
|
|
5977
7011
|
};
|
|
5978
|
-
function appendRuntimeToolsToAgentRequest(
|
|
5979
|
-
const existing =
|
|
7012
|
+
function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
|
|
7013
|
+
const existing = request2.tools?.runtimeTools || [];
|
|
5980
7014
|
const existingNames = new Set(existing.map((tool) => tool.name));
|
|
5981
7015
|
const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
|
|
5982
7016
|
name: tool.name,
|
|
@@ -5986,9 +7020,9 @@ function appendRuntimeToolsToAgentRequest(request, runtimeTools) {
|
|
|
5986
7020
|
...tool.config ? { config: tool.config } : {}
|
|
5987
7021
|
}));
|
|
5988
7022
|
return {
|
|
5989
|
-
...
|
|
7023
|
+
...request2,
|
|
5990
7024
|
tools: {
|
|
5991
|
-
...
|
|
7025
|
+
...request2.tools,
|
|
5992
7026
|
runtimeTools: [...existing, ...converted]
|
|
5993
7027
|
}
|
|
5994
7028
|
};
|
|
@@ -6064,21 +7098,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6064
7098
|
* Attach approved runtime tools to an agent execute request.
|
|
6065
7099
|
* Returns a new request object and does not mutate the original.
|
|
6066
7100
|
*/
|
|
6067
|
-
attachApprovedRuntimeTools(
|
|
6068
|
-
return appendRuntimeToolsToAgentRequest(
|
|
7101
|
+
attachApprovedRuntimeTools(request2, runtimeTools) {
|
|
7102
|
+
return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
|
|
6069
7103
|
}
|
|
6070
7104
|
/**
|
|
6071
7105
|
* Validate a generated runtime tool proposal and append it to an agent execute
|
|
6072
7106
|
* request if approved, in one call.
|
|
6073
7107
|
*/
|
|
6074
|
-
applyGeneratedRuntimeToolProposal(
|
|
7108
|
+
applyGeneratedRuntimeToolProposal(request2, proposal, options) {
|
|
6075
7109
|
const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
|
|
6076
7110
|
if (!decision.approved || !decision.tool) {
|
|
6077
|
-
return { decision, request };
|
|
7111
|
+
return { decision, request: request2 };
|
|
6078
7112
|
}
|
|
6079
7113
|
return {
|
|
6080
7114
|
decision,
|
|
6081
|
-
request: appendRuntimeToolsToAgentRequest(
|
|
7115
|
+
request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
|
|
6082
7116
|
};
|
|
6083
7117
|
}
|
|
6084
7118
|
/**
|
|
@@ -6108,13 +7142,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6108
7142
|
* // ...
|
|
6109
7143
|
* ```
|
|
6110
7144
|
*/
|
|
6111
|
-
async executeStream(id, data) {
|
|
7145
|
+
async executeStream(id, data, init) {
|
|
6112
7146
|
return this.client.requestStream(`/agents/${id}/execute`, {
|
|
6113
7147
|
method: "POST",
|
|
6114
7148
|
body: JSON.stringify({
|
|
6115
7149
|
...data,
|
|
6116
7150
|
streamResponse: true
|
|
6117
|
-
})
|
|
7151
|
+
}),
|
|
7152
|
+
...init?.signal ? { signal: init.signal } : {}
|
|
6118
7153
|
});
|
|
6119
7154
|
}
|
|
6120
7155
|
/**
|
|
@@ -6210,56 +7245,94 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6210
7245
|
runtimeTools: [...data.tools?.runtimeTools || [], ...runtimeTools]
|
|
6211
7246
|
}
|
|
6212
7247
|
};
|
|
6213
|
-
const
|
|
6214
|
-
if (!response.ok) {
|
|
6215
|
-
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
6216
|
-
throw new Error(error.error || `HTTP ${response.status}`);
|
|
6217
|
-
}
|
|
6218
|
-
let currentBody = response.body;
|
|
7248
|
+
const abortSignal = options?.abortSignal;
|
|
6219
7249
|
let accumulatedOutput = "";
|
|
6220
7250
|
let lastKnownCost = 0;
|
|
6221
7251
|
let lastKnownTokens;
|
|
7252
|
+
let lastSeenExecutionId = "";
|
|
6222
7253
|
let pauseCount = 0;
|
|
6223
7254
|
let discoveryPauseCount = 0;
|
|
6224
7255
|
let consecutiveDiscoveryPauseCount = 0;
|
|
6225
7256
|
const toolNameCounts = {};
|
|
6226
7257
|
let recentActionKeys = [];
|
|
6227
7258
|
const toolMessages = [];
|
|
7259
|
+
const finishAborted = (executionId) => {
|
|
7260
|
+
const abortCompleteEvent = {
|
|
7261
|
+
type: "agent_complete",
|
|
7262
|
+
executionId,
|
|
7263
|
+
seq: 0,
|
|
7264
|
+
agentId: id,
|
|
7265
|
+
success: true,
|
|
7266
|
+
iterations: 1,
|
|
7267
|
+
stopReason: "end_turn",
|
|
7268
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7269
|
+
totalCost: lastKnownCost,
|
|
7270
|
+
...lastKnownTokens ? { totalTokens: lastKnownTokens } : {},
|
|
7271
|
+
finalOutput: [accumulatedOutput.trim(), "Session aborted by user request."].filter(Boolean).join("\n\n"),
|
|
7272
|
+
duration: 0
|
|
7273
|
+
};
|
|
7274
|
+
callbacks?.onAgentComplete?.(abortCompleteEvent);
|
|
7275
|
+
return { completeEvent: abortCompleteEvent, toolMessages };
|
|
7276
|
+
};
|
|
7277
|
+
let response;
|
|
7278
|
+
try {
|
|
7279
|
+
response = await this.executeStream(id, requestData, {
|
|
7280
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
7281
|
+
});
|
|
7282
|
+
} catch (error) {
|
|
7283
|
+
if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
|
|
7284
|
+
throw error;
|
|
7285
|
+
}
|
|
7286
|
+
if (!response.ok) {
|
|
7287
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
7288
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
7289
|
+
}
|
|
7290
|
+
let currentBody = response.body;
|
|
6228
7291
|
while (true) {
|
|
6229
7292
|
let pausedEvent = null;
|
|
6230
7293
|
let completeEvent = null;
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
7294
|
+
try {
|
|
7295
|
+
await processAgentStream(currentBody, {
|
|
7296
|
+
...callbacks,
|
|
7297
|
+
onAgentStart: (event) => {
|
|
7298
|
+
lastSeenExecutionId = event.executionId;
|
|
7299
|
+
callbacks?.onAgentStart?.(event);
|
|
7300
|
+
},
|
|
7301
|
+
onTurnDelta: (event) => {
|
|
7302
|
+
if (event.contentType === "text") {
|
|
7303
|
+
accumulatedOutput += event.delta;
|
|
7304
|
+
}
|
|
7305
|
+
callbacks?.onTurnDelta?.(event);
|
|
7306
|
+
},
|
|
7307
|
+
onTurnComplete: (event) => {
|
|
7308
|
+
if (typeof event.cost === "number") {
|
|
7309
|
+
lastKnownCost = event.cost;
|
|
7310
|
+
}
|
|
7311
|
+
if (event.tokens) {
|
|
7312
|
+
lastKnownTokens = event.tokens;
|
|
7313
|
+
}
|
|
7314
|
+
callbacks?.onTurnComplete?.(event);
|
|
7315
|
+
},
|
|
7316
|
+
onAgentPaused: (event) => {
|
|
7317
|
+
pausedEvent = event;
|
|
7318
|
+
callbacks?.onAgentPaused?.(event);
|
|
7319
|
+
},
|
|
7320
|
+
onAgentComplete: (event) => {
|
|
7321
|
+
if (!event.finalOutput && accumulatedOutput) {
|
|
7322
|
+
event.finalOutput = accumulatedOutput;
|
|
7323
|
+
}
|
|
7324
|
+
completeEvent = event;
|
|
7325
|
+
callbacks?.onAgentComplete?.(event);
|
|
6255
7326
|
}
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
7327
|
+
});
|
|
7328
|
+
} catch (error) {
|
|
7329
|
+
if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
|
|
7330
|
+
throw error;
|
|
7331
|
+
}
|
|
6260
7332
|
if (completeEvent) return { completeEvent, toolMessages };
|
|
6261
7333
|
if (pausedEvent) {
|
|
6262
7334
|
const { toolName, toolId, parameters, executionId } = pausedEvent;
|
|
7335
|
+
lastSeenExecutionId = executionId;
|
|
6263
7336
|
const toolDef = localTools[toolName];
|
|
6264
7337
|
if (!toolDef) {
|
|
6265
7338
|
throw new Error(`Local tool "${toolName}" required but not provided`);
|
|
@@ -6374,6 +7447,19 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6374
7447
|
callbacks?.onAgentComplete?.(forcedCompleteEvent);
|
|
6375
7448
|
return { completeEvent: forcedCompleteEvent, toolMessages };
|
|
6376
7449
|
}
|
|
7450
|
+
if (abortSignal?.aborted) {
|
|
7451
|
+
callbacks?.onLocalToolExecutionComplete?.({
|
|
7452
|
+
executionId,
|
|
7453
|
+
toolCallId: toolId,
|
|
7454
|
+
toolName,
|
|
7455
|
+
parameters: parsedParams,
|
|
7456
|
+
result: toolResult,
|
|
7457
|
+
success: true,
|
|
7458
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7459
|
+
durationMs: Date.now() - localExecutionStartedAtMs
|
|
7460
|
+
});
|
|
7461
|
+
return finishAborted(executionId);
|
|
7462
|
+
}
|
|
6377
7463
|
if (options?.shouldInterrupt?.()) {
|
|
6378
7464
|
callbacks?.onLocalToolExecutionComplete?.({
|
|
6379
7465
|
executionId,
|
|
@@ -6407,15 +7493,22 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6407
7493
|
callbacks?.onAgentComplete?.(interruptCompleteEvent);
|
|
6408
7494
|
return { completeEvent: interruptCompleteEvent, toolMessages };
|
|
6409
7495
|
}
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
7496
|
+
let resumeResponse;
|
|
7497
|
+
try {
|
|
7498
|
+
resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
|
|
7499
|
+
method: "POST",
|
|
7500
|
+
body: JSON.stringify({
|
|
7501
|
+
executionId,
|
|
7502
|
+
toolOutputs: { [toolName]: toolResult },
|
|
7503
|
+
streamResponse: true,
|
|
7504
|
+
debugMode: data.debugMode
|
|
7505
|
+
}),
|
|
7506
|
+
...abortSignal ? { signal: abortSignal } : {}
|
|
7507
|
+
});
|
|
7508
|
+
} catch (error) {
|
|
7509
|
+
if (abortSignal?.aborted) return finishAborted(executionId);
|
|
7510
|
+
throw error;
|
|
7511
|
+
}
|
|
6419
7512
|
if (!resumeResponse.ok) {
|
|
6420
7513
|
const error = await resumeResponse.json().catch(() => ({ error: "Unknown error" }));
|
|
6421
7514
|
throw new Error(error.error || `HTTP ${resumeResponse.status}`);
|
|
@@ -6433,6 +7526,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6433
7526
|
currentBody = resumeResponse.body;
|
|
6434
7527
|
continue;
|
|
6435
7528
|
}
|
|
7529
|
+
if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
|
|
6436
7530
|
return null;
|
|
6437
7531
|
}
|
|
6438
7532
|
}
|
|
@@ -6704,7 +7798,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6704
7798
|
reasons.push("Best candidate file has not been verified (read back after writing)");
|
|
6705
7799
|
}
|
|
6706
7800
|
if (state.verificationRequired && !state.lastVerificationPassed && !trace.verificationPassed) {
|
|
6707
|
-
reasons.push(
|
|
7801
|
+
reasons.push(
|
|
7802
|
+
"Verification has not passed \u2014 run a verification command (run_check) before completing"
|
|
7803
|
+
);
|
|
6708
7804
|
}
|
|
6709
7805
|
return reasons.length > 0 ? reasons.join("; ") : "Completion gates not satisfied for the current workflow phase";
|
|
6710
7806
|
}
|
|
@@ -6747,32 +7843,71 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6747
7843
|
);
|
|
6748
7844
|
}
|
|
6749
7845
|
/**
|
|
6750
|
-
*
|
|
6751
|
-
*
|
|
7846
|
+
* Resolve the replay window: the base index sliced off the full history and
|
|
7847
|
+
* the durable summary message that stands in for everything before it.
|
|
7848
|
+
* The base is honored ONLY when the latest summary actually exists —
|
|
7849
|
+
* slicing history with no summary substitute would silently drop prior
|
|
7850
|
+
* context (defensive against states that carried a base but lost their
|
|
7851
|
+
* summaries). The base is also clamped so a stale pointer (fork truncation,
|
|
7852
|
+
* trimmed legacy state) never slices past the end of the array.
|
|
7853
|
+
*/
|
|
7854
|
+
resolveWindowReplay(state, messageCount) {
|
|
7855
|
+
const base = state.contextWindowBaseIndex ?? 0;
|
|
7856
|
+
if (!Number.isFinite(base) || base <= 0) {
|
|
7857
|
+
return { windowBase: 0, windowSummaryMessages: [] };
|
|
7858
|
+
}
|
|
7859
|
+
const summaries = state.contextCompactionSummaries;
|
|
7860
|
+
const latest = summaries?.[summaries.length - 1];
|
|
7861
|
+
if (!latest) {
|
|
7862
|
+
return { windowBase: 0, windowSummaryMessages: [] };
|
|
7863
|
+
}
|
|
7864
|
+
return {
|
|
7865
|
+
windowBase: Math.min(Math.floor(base), messageCount),
|
|
7866
|
+
windowSummaryMessages: [{ role: "system", content: latest.content }]
|
|
7867
|
+
};
|
|
7868
|
+
}
|
|
7869
|
+
/**
|
|
7870
|
+
* Derive the message view sent to the model based on context mode and window
|
|
7871
|
+
* setting. This never mutates persisted marathon history; masking/offloading
|
|
7872
|
+
* is a send-time view over the full-fidelity ledger/history.
|
|
6752
7873
|
*/
|
|
6753
|
-
|
|
6754
|
-
if (mode === "full-inline") return;
|
|
7874
|
+
deriveToolContextMessages(messages, taskName, mode, window) {
|
|
7875
|
+
if (mode === "full-inline") return [...messages];
|
|
7876
|
+
const maskMessage = (msg) => ({
|
|
7877
|
+
...msg,
|
|
7878
|
+
toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
|
|
7879
|
+
});
|
|
7880
|
+
const view = [...messages];
|
|
6755
7881
|
if (window === "session") {
|
|
6756
|
-
|
|
7882
|
+
const lastUserIndex = view.reduce(
|
|
7883
|
+
(lastIndex, message, index) => message.role === "user" ? index : lastIndex,
|
|
7884
|
+
-1
|
|
7885
|
+
);
|
|
7886
|
+
for (let index = 0; index < view.length; index++) {
|
|
7887
|
+
if (lastUserIndex >= 0 && index > lastUserIndex) continue;
|
|
7888
|
+
const msg = view[index];
|
|
7889
|
+
if (!msg) continue;
|
|
6757
7890
|
if (msg.role === "tool" && msg.toolResults) {
|
|
6758
|
-
|
|
7891
|
+
view[index] = maskMessage(msg);
|
|
6759
7892
|
}
|
|
6760
7893
|
}
|
|
6761
7894
|
} else {
|
|
6762
|
-
const newToolResultCount = newToolMessages.filter((m) => m.role === "tool").length;
|
|
6763
|
-
const keepInlineFromExisting = Math.max(0, window - newToolResultCount);
|
|
6764
7895
|
const toolResultIndices = [];
|
|
6765
|
-
for (let i = 0; i <
|
|
6766
|
-
|
|
7896
|
+
for (let i = 0; i < view.length; i++) {
|
|
7897
|
+
const message = view[i];
|
|
7898
|
+
if (message?.role === "tool" && message.toolResults) {
|
|
6767
7899
|
toolResultIndices.push(i);
|
|
6768
7900
|
}
|
|
6769
7901
|
}
|
|
6770
|
-
const compactUpTo = toolResultIndices.length -
|
|
7902
|
+
const compactUpTo = toolResultIndices.length - window;
|
|
6771
7903
|
for (let j = 0; j < compactUpTo && j < toolResultIndices.length; j++) {
|
|
6772
|
-
const
|
|
6773
|
-
msg
|
|
7904
|
+
const index = toolResultIndices[j];
|
|
7905
|
+
const msg = index === void 0 ? void 0 : view[index];
|
|
7906
|
+
if (!msg) continue;
|
|
7907
|
+
view[index] = maskMessage(msg);
|
|
6774
7908
|
}
|
|
6775
7909
|
}
|
|
7910
|
+
return view;
|
|
6776
7911
|
}
|
|
6777
7912
|
compactOneResult(tr, taskName, mode) {
|
|
6778
7913
|
if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
|
|
@@ -6808,10 +7943,20 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6808
7943
|
}
|
|
6809
7944
|
const slug = this.sanitizeTaskSlug(taskName || "task");
|
|
6810
7945
|
const dir = `.runtype/marathons/${slug}/tool-outputs`;
|
|
6811
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
6812
7946
|
const filePath = `${dir}/${toolCallId}.txt`;
|
|
6813
|
-
|
|
6814
|
-
|
|
7947
|
+
try {
|
|
7948
|
+
if (!fs.existsSync(filePath)) {
|
|
7949
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
7950
|
+
fs.writeFileSync(filePath, resultStr, "utf-8");
|
|
7951
|
+
}
|
|
7952
|
+
} catch {
|
|
7953
|
+
return result;
|
|
7954
|
+
}
|
|
7955
|
+
return buildSendViewOffloadMarker({
|
|
7956
|
+
toolName,
|
|
7957
|
+
charLength: resultStr.length,
|
|
7958
|
+
filePath
|
|
7959
|
+
});
|
|
6815
7960
|
}
|
|
6816
7961
|
getDefaultPlanPath(taskName) {
|
|
6817
7962
|
return getDefaultPlanPath(taskName);
|
|
@@ -6933,6 +8078,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6933
8078
|
const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
|
|
6934
8079
|
const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
|
|
6935
8080
|
const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
|
|
8081
|
+
const contextCompactionSummaries = (resumeState.contextCompactionSummaries ?? []).slice(-20);
|
|
6936
8082
|
const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
|
|
6937
8083
|
const bestCandidatePath = normalizedBestCandidatePath && !this.isMarathonArtifactPath(normalizedBestCandidatePath) ? normalizedBestCandidatePath : [...candidatePaths, ...recentReadPaths].sort(
|
|
6938
8084
|
(left, right) => this.scoreCandidatePath(right) - this.scoreCandidatePath(left)
|
|
@@ -6952,10 +8098,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
6952
8098
|
bestCandidateVerified: Boolean(resumeState.bestCandidateVerified),
|
|
6953
8099
|
...resumeState.verificationRequired !== void 0 ? { verificationRequired: resumeState.verificationRequired } : {},
|
|
6954
8100
|
lastVerificationPassed: Boolean(resumeState.lastVerificationPassed),
|
|
6955
|
-
...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? {
|
|
8101
|
+
...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? {
|
|
8102
|
+
consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions
|
|
8103
|
+
} : {},
|
|
6956
8104
|
...resumeState.isCreationTask !== void 0 ? { isCreationTask: resumeState.isCreationTask } : {},
|
|
6957
8105
|
...resumeState.workflowVariant !== void 0 ? { workflowVariant: resumeState.workflowVariant } : {},
|
|
6958
8106
|
...resumeState.workflowState !== void 0 ? { workflowState: resumeState.workflowState } : {},
|
|
8107
|
+
...contextCompactionSummaries.length ? { contextCompactionSummaries } : {},
|
|
8108
|
+
...typeof resumeState.contextWindowBaseIndex === "number" && Number.isFinite(resumeState.contextWindowBaseIndex) && resumeState.contextWindowBaseIndex > 0 ? { contextWindowBaseIndex: Math.floor(resumeState.contextWindowBaseIndex) } : {},
|
|
6959
8109
|
...typeof resumeState.outputRoot === "string" && resumeState.outputRoot.trim() ? { outputRoot: resumeState.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/") } : {}
|
|
6960
8110
|
};
|
|
6961
8111
|
}
|
|
@@ -7355,8 +8505,11 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7355
8505
|
}
|
|
7356
8506
|
buildStuckTurnRecoveryMessage(state, workflow) {
|
|
7357
8507
|
const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
|
|
7358
|
-
|
|
7359
|
-
|
|
8508
|
+
const phaseMessage = currentPhase?.buildRecoveryMessage?.(state);
|
|
8509
|
+
if (phaseMessage) return phaseMessage;
|
|
8510
|
+
const emptySessions = state.consecutiveEmptySessions || 0;
|
|
8511
|
+
if (shouldInjectEmptySessionNudge(workflow.stallPolicy, emptySessions)) {
|
|
8512
|
+
return buildEmptySessionNudge(emptySessions);
|
|
7360
8513
|
}
|
|
7361
8514
|
return void 0;
|
|
7362
8515
|
}
|
|
@@ -7421,8 +8574,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7421
8574
|
} : {},
|
|
7422
8575
|
...seededResumeState?.candidatePaths ? { candidatePaths: seededResumeState.candidatePaths } : {},
|
|
7423
8576
|
...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
|
|
7424
|
-
...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {}
|
|
8577
|
+
...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {},
|
|
8578
|
+
...seededResumeState?.contextCompactionSummaries?.length ? { contextCompactionSummaries: seededResumeState.contextCompactionSummaries } : {},
|
|
8579
|
+
...typeof seededResumeState?.contextWindowBaseIndex === "number" ? { contextWindowBaseIndex: seededResumeState.contextWindowBaseIndex } : {}
|
|
7425
8580
|
};
|
|
8581
|
+
if (options.previousMessages && options.previousMessages.length > 0) {
|
|
8582
|
+
state.messages = options.previousMessages.map((message) => structuredClone(message));
|
|
8583
|
+
}
|
|
7426
8584
|
state.workflowVariant = classifiedVariant;
|
|
7427
8585
|
state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
|
|
7428
8586
|
state.outputRoot = seededResumeState?.outputRoot ?? (state.isCreationTask ? "public/" : void 0);
|
|
@@ -7455,6 +8613,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7455
8613
|
}
|
|
7456
8614
|
}
|
|
7457
8615
|
for (let session = 0; session < maxSessions; session++) {
|
|
8616
|
+
if (options.abortSignal?.aborted) {
|
|
8617
|
+
state.status = "paused";
|
|
8618
|
+
break;
|
|
8619
|
+
}
|
|
7458
8620
|
const phaseAtSessionStart = state.workflowPhase;
|
|
7459
8621
|
const sessionTrace = this.createEmptyToolTrace();
|
|
7460
8622
|
const sessionLocalTools = this.wrapLocalToolsForTrace(
|
|
@@ -7473,6 +8635,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7473
8635
|
state.originalMessage = options.message;
|
|
7474
8636
|
}
|
|
7475
8637
|
const queuedSteeringMessages = options.getQueuedUserMessages?.() ?? [];
|
|
8638
|
+
if (queuedSteeringMessages.length > 0) {
|
|
8639
|
+
state.consecutiveEmptySessions = 0;
|
|
8640
|
+
state.stallEscalationRequested = void 0;
|
|
8641
|
+
}
|
|
7476
8642
|
const preparedSession = await this.prepareSessionContext(
|
|
7477
8643
|
options.message,
|
|
7478
8644
|
state,
|
|
@@ -7491,7 +8657,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7491
8657
|
localTools: options.localTools,
|
|
7492
8658
|
builtinToolSchemas,
|
|
7493
8659
|
onContextCompaction: options.onContextCompaction,
|
|
7494
|
-
onContextNotice: options.onContextNotice
|
|
8660
|
+
onContextNotice: options.onContextNotice,
|
|
8661
|
+
toolContextMode: options.toolContextMode || "hot-tail",
|
|
8662
|
+
toolWindow: options.toolWindow ?? "session"
|
|
7495
8663
|
},
|
|
7496
8664
|
queuedSteeringMessages
|
|
7497
8665
|
);
|
|
@@ -7521,7 +8689,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7521
8689
|
sessionCallbacks,
|
|
7522
8690
|
{
|
|
7523
8691
|
onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow),
|
|
7524
|
-
shouldInterrupt: options.hasQueuedUserMessages
|
|
8692
|
+
shouldInterrupt: options.hasQueuedUserMessages,
|
|
8693
|
+
...options.abortSignal ? { abortSignal: options.abortSignal } : {}
|
|
7525
8694
|
},
|
|
7526
8695
|
state.taskName
|
|
7527
8696
|
);
|
|
@@ -7670,22 +8839,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7670
8839
|
}
|
|
7671
8840
|
}
|
|
7672
8841
|
if (!state.messages) state.messages = [];
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
state.messages.push(...newMessages);
|
|
7678
|
-
} else {
|
|
8842
|
+
const sentUserMessage = messages[messages.length - 1];
|
|
8843
|
+
if (sentUserMessage?.role === "user") {
|
|
8844
|
+
state.messages.push(sentUserMessage);
|
|
8845
|
+
} else if (state.messages.length === 0) {
|
|
7679
8846
|
state.messages.push(...messages);
|
|
7680
8847
|
}
|
|
7681
8848
|
if (sessionToolMessages.length > 0) {
|
|
7682
|
-
this.compactToolResults(
|
|
7683
|
-
state.messages,
|
|
7684
|
-
sessionToolMessages,
|
|
7685
|
-
state.taskName,
|
|
7686
|
-
options.toolContextMode || "hot-tail",
|
|
7687
|
-
options.toolWindow ?? "session"
|
|
7688
|
-
);
|
|
7689
8849
|
state.messages.push(...sessionToolMessages);
|
|
7690
8850
|
}
|
|
7691
8851
|
const assistantContent = effectiveSessionOutput || `[Session ${session + 1} completed (${sessionResult.stopReason}). No text output captured.]`;
|
|
@@ -7705,7 +8865,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7705
8865
|
workflow
|
|
7706
8866
|
);
|
|
7707
8867
|
if (detectedTaskCompletion && !acceptedTaskCompletion) {
|
|
7708
|
-
state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(
|
|
8868
|
+
state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(
|
|
8869
|
+
state,
|
|
8870
|
+
sessionTrace
|
|
8871
|
+
);
|
|
7709
8872
|
if (state.verificationRequired && !state.lastVerificationPassed && !sessionTrace.verificationPassed && !sessionTrace.verificationAttempted) {
|
|
7710
8873
|
state.consecutiveBlockedVerificationSessions = (state.consecutiveBlockedVerificationSessions || 0) + 1;
|
|
7711
8874
|
if ((state.consecutiveBlockedVerificationSessions || 0) >= 2) {
|
|
@@ -7716,32 +8879,36 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7716
8879
|
} else {
|
|
7717
8880
|
state.lastCompletionRejectionReason = void 0;
|
|
7718
8881
|
}
|
|
7719
|
-
const sessionHadActions = sessionTrace.wroteFiles || sessionTrace.readFiles || sessionTrace.discoveryPerformed || sessionTrace.verificationAttempted;
|
|
8882
|
+
const sessionHadActions = sessionTrace.wroteFiles || sessionTrace.readFiles || sessionTrace.discoveryPerformed || sessionTrace.verificationAttempted || options.hasQueuedUserMessages?.() === true;
|
|
7720
8883
|
if (sessionHadActions) {
|
|
7721
8884
|
state.consecutiveEmptySessions = 0;
|
|
8885
|
+
state.stallEscalationRequested = void 0;
|
|
7722
8886
|
} else {
|
|
7723
8887
|
state.consecutiveEmptySessions = (state.consecutiveEmptySessions || 0) + 1;
|
|
8888
|
+
if (shouldRequestModelEscalation(workflow.stallPolicy, state.consecutiveEmptySessions)) {
|
|
8889
|
+
state.stallEscalationRequested = true;
|
|
8890
|
+
}
|
|
7724
8891
|
}
|
|
7725
8892
|
if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
|
|
7726
8893
|
const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
|
|
7727
|
-
const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(
|
|
8894
|
+
const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(
|
|
8895
|
+
state,
|
|
8896
|
+
sessionTrace
|
|
8897
|
+
) : true;
|
|
7728
8898
|
if (gatesSatisfied) {
|
|
7729
8899
|
state.status = "complete";
|
|
7730
8900
|
}
|
|
7731
8901
|
} else if (sessionResult.stopReason === "error") {
|
|
7732
8902
|
if (_AgentsEndpoint.isRetryableSessionError(sessionResult.error) && consecutiveServerNetworkErrors < maxServerNetworkRetries) {
|
|
7733
8903
|
consecutiveServerNetworkErrors++;
|
|
7734
|
-
const delayMs = Math.min(
|
|
7735
|
-
5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1),
|
|
7736
|
-
3e4
|
|
7737
|
-
);
|
|
8904
|
+
const delayMs = Math.min(5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1), 3e4);
|
|
7738
8905
|
const delaySec = Math.round(delayMs / 1e3);
|
|
7739
8906
|
await this.emitContextNotice(options.onContextNotice, {
|
|
7740
8907
|
kind: "server_network_retry",
|
|
7741
8908
|
sessionIndex: session,
|
|
7742
8909
|
message: `Server network error: ${sessionResult.error}. Retrying in ${delaySec}s (attempt ${consecutiveServerNetworkErrors}/${maxServerNetworkRetries})...`
|
|
7743
8910
|
});
|
|
7744
|
-
await
|
|
8911
|
+
await sleepWithAbort(delayMs, options.abortSignal);
|
|
7745
8912
|
} else {
|
|
7746
8913
|
state.status = "error";
|
|
7747
8914
|
}
|
|
@@ -7749,13 +8916,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7749
8916
|
state.status = "budget_exceeded";
|
|
7750
8917
|
} else if (acceptedTaskCompletion) {
|
|
7751
8918
|
state.status = "complete";
|
|
7752
|
-
} else if ((state.consecutiveEmptySessions || 0) >=
|
|
8919
|
+
} else if ((state.consecutiveEmptySessions || 0) >= resolveStallStopAfter(workflow.stallPolicy)) {
|
|
7753
8920
|
state.status = "stalled";
|
|
7754
8921
|
} else if (maxCost && state.totalCost >= maxCost) {
|
|
7755
8922
|
state.status = "budget_exceeded";
|
|
7756
8923
|
} else if (session + 1 >= maxSessions) {
|
|
7757
8924
|
state.status = "max_sessions";
|
|
7758
8925
|
}
|
|
8926
|
+
if (options.abortSignal?.aborted) {
|
|
8927
|
+
state.status = "paused";
|
|
8928
|
+
}
|
|
7759
8929
|
if (options.trackProgress) {
|
|
7760
8930
|
recordId = await this.syncProgressRecord(state, recordId);
|
|
7761
8931
|
}
|
|
@@ -7832,6 +9002,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7832
9002
|
return 0;
|
|
7833
9003
|
}
|
|
7834
9004
|
}
|
|
9005
|
+
extractDeclaredToolResultChars(value) {
|
|
9006
|
+
return extractDeclaredToolResultChars(value);
|
|
9007
|
+
}
|
|
7835
9008
|
estimateMessageContentTokens(content) {
|
|
7836
9009
|
if (typeof content === "string") return this.estimateTextTokens(content);
|
|
7837
9010
|
return content.reduce((total, part) => {
|
|
@@ -7850,12 +9023,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7850
9023
|
0
|
|
7851
9024
|
);
|
|
7852
9025
|
}
|
|
7853
|
-
estimateToolResultTokens(toolResults) {
|
|
9026
|
+
estimateToolResultTokens(toolResults, options) {
|
|
7854
9027
|
if (!toolResults || toolResults.length === 0) return 0;
|
|
7855
|
-
return toolResults.reduce(
|
|
7856
|
-
|
|
7857
|
-
0
|
|
7858
|
-
|
|
9028
|
+
return toolResults.reduce((sum, toolResult) => {
|
|
9029
|
+
const resultTokens = this.estimateUnknownTokens(toolResult.result);
|
|
9030
|
+
const declaredChars = options?.useDeclaredSize ? this.extractDeclaredToolResultChars(toolResult.result) : void 0;
|
|
9031
|
+
const declaredTokens = typeof declaredChars === "number" ? Math.ceil(declaredChars / 4) : 0;
|
|
9032
|
+
return sum + 12 + this.estimateTextTokens(toolResult.toolName) + Math.max(resultTokens, declaredTokens);
|
|
9033
|
+
}, 0);
|
|
7859
9034
|
}
|
|
7860
9035
|
estimateMessageTokens(message) {
|
|
7861
9036
|
return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
|
|
@@ -7863,13 +9038,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7863
9038
|
estimateConversationTokens(messages) {
|
|
7864
9039
|
return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
|
|
7865
9040
|
}
|
|
7866
|
-
estimateConversationBreakdown(messages) {
|
|
9041
|
+
estimateConversationBreakdown(messages, options) {
|
|
7867
9042
|
let historyTokens = 0;
|
|
7868
9043
|
let toolOutputTokens = 0;
|
|
7869
9044
|
for (const message of messages) {
|
|
7870
9045
|
const contentTokens = this.estimateMessageContentTokens(message.content);
|
|
7871
9046
|
const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
|
|
7872
|
-
const toolResultTokens = this.estimateToolResultTokens(message.toolResults
|
|
9047
|
+
const toolResultTokens = this.estimateToolResultTokens(message.toolResults, {
|
|
9048
|
+
useDeclaredSize: options?.useDeclaredToolResultSizes
|
|
9049
|
+
});
|
|
7873
9050
|
const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
|
|
7874
9051
|
if (message.role === "tool") {
|
|
7875
9052
|
toolOutputTokens += messageTotal;
|
|
@@ -7923,13 +9100,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7923
9100
|
const compactInstructions = config.compactInstructions;
|
|
7924
9101
|
return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
|
|
7925
9102
|
}
|
|
7926
|
-
|
|
9103
|
+
extractArtifactReferencesFromMessages(messages = []) {
|
|
7927
9104
|
const references = /* @__PURE__ */ new Set();
|
|
7928
9105
|
const offloadPrefix = "[Output saved to ";
|
|
7929
|
-
|
|
9106
|
+
const ledgerArtifactPrefix = LEDGER_ARTIFACT_LINE_PREFIX;
|
|
9107
|
+
const savedToPattern = /saved to\s+([^—\]\n]+?)(?:\s+—|\]|\n|$)/gi;
|
|
9108
|
+
for (const message of messages) {
|
|
7930
9109
|
if (!message.toolResults) continue;
|
|
7931
9110
|
for (const toolResult of message.toolResults) {
|
|
7932
9111
|
if (typeof toolResult.result !== "string") continue;
|
|
9112
|
+
for (const line of toolResult.result.split("\n")) {
|
|
9113
|
+
if (line.startsWith(ledgerArtifactPrefix)) {
|
|
9114
|
+
references.add(line.slice(ledgerArtifactPrefix.length).trim());
|
|
9115
|
+
}
|
|
9116
|
+
}
|
|
9117
|
+
for (const match of toolResult.result.matchAll(savedToPattern)) {
|
|
9118
|
+
const pathText = match[1]?.trim();
|
|
9119
|
+
if (pathText) references.add(pathText);
|
|
9120
|
+
}
|
|
7933
9121
|
let startIndex = 0;
|
|
7934
9122
|
while (startIndex < toolResult.result.length) {
|
|
7935
9123
|
const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
|
|
@@ -7947,6 +9135,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7947
9135
|
}
|
|
7948
9136
|
}
|
|
7949
9137
|
}
|
|
9138
|
+
return Array.from(references);
|
|
9139
|
+
}
|
|
9140
|
+
extractArtifactReferences(state, additionalReferences = []) {
|
|
9141
|
+
const references = /* @__PURE__ */ new Set([
|
|
9142
|
+
...this.extractArtifactReferencesFromMessages(state.messages ?? []),
|
|
9143
|
+
...additionalReferences
|
|
9144
|
+
]);
|
|
7950
9145
|
if (state.planPath) {
|
|
7951
9146
|
references.add(state.planPath);
|
|
7952
9147
|
}
|
|
@@ -7954,6 +9149,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
|
|
|
7954
9149
|
}
|
|
7955
9150
|
buildContextBudgetBreakdown(details) {
|
|
7956
9151
|
const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
|
|
9152
|
+
const sourceConversationBreakdown = details.sourceHistoryMessages ? this.estimateConversationBreakdown(details.sourceHistoryMessages, {
|
|
9153
|
+
useDeclaredToolResultSizes: true
|
|
9154
|
+
}) : conversationBreakdown;
|
|
7957
9155
|
const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
|
|
7958
9156
|
const toolDefinitionTokens = this.estimateToolDefinitionTokens(
|
|
7959
9157
|
details.localTools,
|
|
@@ -7970,15 +9168,26 @@ ${details.summaryText}
|
|
|
7970
9168
|
|
|
7971
9169
|
Do NOT redo any of the above work.`
|
|
7972
9170
|
) : void 0;
|
|
9171
|
+
const sendEstimatedInputTokens = conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
|
|
9172
|
+
const estimatedInputTokens = sourceConversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
|
|
9173
|
+
const toolOutputReductionTokens = Math.max(
|
|
9174
|
+
0,
|
|
9175
|
+
sourceConversationBreakdown.toolOutputTokens - conversationBreakdown.toolOutputTokens
|
|
9176
|
+
);
|
|
7973
9177
|
return {
|
|
7974
|
-
historyTokens:
|
|
7975
|
-
toolOutputTokens:
|
|
9178
|
+
historyTokens: sourceConversationBreakdown.historyTokens,
|
|
9179
|
+
toolOutputTokens: sourceConversationBreakdown.toolOutputTokens,
|
|
7976
9180
|
currentTurnTokens,
|
|
7977
9181
|
toolDefinitionTokens,
|
|
7978
9182
|
...summaryTokens ? { summaryTokens } : {},
|
|
7979
9183
|
...reservedOutputTokens ? { reservedOutputTokens } : {},
|
|
7980
9184
|
...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
|
|
7981
|
-
|
|
9185
|
+
...toolOutputReductionTokens > 0 ? {
|
|
9186
|
+
sendEstimatedInputTokens,
|
|
9187
|
+
sendToolOutputTokens: conversationBreakdown.toolOutputTokens,
|
|
9188
|
+
toolOutputReductionTokens
|
|
9189
|
+
} : {},
|
|
9190
|
+
estimatedInputTokens
|
|
7982
9191
|
};
|
|
7983
9192
|
}
|
|
7984
9193
|
async emitContextCompactionEvent(onContextCompaction, event) {
|
|
@@ -8018,16 +9227,33 @@ Do NOT redo any of the above work.`
|
|
|
8018
9227
|
state,
|
|
8019
9228
|
userContent,
|
|
8020
9229
|
details.compactInstructions,
|
|
8021
|
-
details.mode
|
|
9230
|
+
details.mode,
|
|
9231
|
+
details.artifactReferences
|
|
8022
9232
|
);
|
|
9233
|
+
const summaryMessage = compactMessages[0];
|
|
9234
|
+
if (summaryMessage?.role === "system" && typeof summaryMessage.content === "string") {
|
|
9235
|
+
const existingSummaries = state.contextCompactionSummaries ?? [];
|
|
9236
|
+
state.contextCompactionSummaries = [
|
|
9237
|
+
...existingSummaries,
|
|
9238
|
+
{
|
|
9239
|
+
id: `ctx_${sessionIndex + 1}_${existingSummaries.length + 1}`,
|
|
9240
|
+
sessionIndex: sessionIndex + 1,
|
|
9241
|
+
mode: details.mode,
|
|
9242
|
+
strategy: details.strategy,
|
|
9243
|
+
content: summaryMessage.content,
|
|
9244
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9245
|
+
}
|
|
9246
|
+
].slice(-20);
|
|
9247
|
+
}
|
|
9248
|
+
state.contextWindowBaseIndex = state.messages?.length ?? 0;
|
|
8023
9249
|
await this.emitContextCompactionEvent(details.onContextCompaction, {
|
|
8024
9250
|
phase: "complete",
|
|
8025
9251
|
...baseEvent
|
|
8026
9252
|
});
|
|
8027
9253
|
return compactMessages;
|
|
8028
9254
|
}
|
|
8029
|
-
buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
|
|
8030
|
-
const summary = this.generateCompactSummary(state, compactInstructions);
|
|
9255
|
+
buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto", artifactReferences) {
|
|
9256
|
+
const summary = this.generateCompactSummary(state, compactInstructions, artifactReferences);
|
|
8031
9257
|
const prefix = mode === "forced" ? this.getForcedCompactionSummaryPrefix(state) : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
|
|
8032
9258
|
return [
|
|
8033
9259
|
{
|
|
@@ -8044,16 +9270,11 @@ Do NOT redo any of the above work.`
|
|
|
8044
9270
|
}
|
|
8045
9271
|
];
|
|
8046
9272
|
}
|
|
8047
|
-
isCompactHistoryMessageSet(messages) {
|
|
8048
|
-
if (messages.length === 0) return false;
|
|
8049
|
-
const firstMessage = messages[0];
|
|
8050
|
-
return firstMessage?.role === "system" && typeof firstMessage.content === "string" && (firstMessage.content.startsWith(_AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX) || firstMessage.content.startsWith(_AgentsEndpoint.RESUMED_COMPACT_SUMMARY_PREFIX) || firstMessage.content.startsWith(_AgentsEndpoint.COMPLETED_COMPACT_SUMMARY_PREFIX));
|
|
8051
|
-
}
|
|
8052
9273
|
/**
|
|
8053
9274
|
* Generate a compact summary of prior work for continuation context.
|
|
8054
9275
|
* Used when compact mode is enabled to keep token usage low.
|
|
8055
9276
|
*/
|
|
8056
|
-
generateCompactSummary(state, compactInstructions) {
|
|
9277
|
+
generateCompactSummary(state, compactInstructions, additionalArtifactReferences = []) {
|
|
8057
9278
|
const recentSessions = (state.sessions ?? []).slice(-5);
|
|
8058
9279
|
const sessionSummaries = recentSessions.map(
|
|
8059
9280
|
(session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
|
|
@@ -8067,7 +9288,7 @@ Do NOT redo any of the above work.`
|
|
|
8067
9288
|
)
|
|
8068
9289
|
).slice(0, 8);
|
|
8069
9290
|
const verificationSummary = state.bestCandidateVerified ? "Latest candidate verified." : state.bestCandidateNeedsVerification ? "Latest candidate still needs verification." : state.lastVerificationPassed ? "Latest verification passed." : state.verificationRequired ? "Verification is still required." : "No verification requirement recorded.";
|
|
8070
|
-
const artifactReferences = this.extractArtifactReferences(state);
|
|
9291
|
+
const artifactReferences = this.extractArtifactReferences(state, additionalArtifactReferences);
|
|
8071
9292
|
const pendingNextStep = state.lastStopReason === "complete" ? "Confirm nothing else remains before declaring the task complete." : `Continue the ${state.workflowPhase || "research"} phase without redoing prior work.`;
|
|
8072
9293
|
const instructions = compactInstructions || this.buildDefaultCompactInstructions();
|
|
8073
9294
|
return [
|
|
@@ -8282,14 +9503,23 @@ Do NOT redo any of the above work.`
|
|
|
8282
9503
|
const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
|
|
8283
9504
|
const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
|
|
8284
9505
|
const multiSessionInstruction = `This is a multi-session task (session ${sessionIndex + 1}/${maxSessions}). When you have fully completed the task, end your response with TASK_COMPLETE on its own line.`;
|
|
8285
|
-
const steeringLines = steeringMessages && steeringMessages.length > 0 ? [
|
|
8286
|
-
"--- User steering (queued during the previous session) ---",
|
|
8287
|
-
...steeringMessages,
|
|
8288
|
-
""
|
|
8289
|
-
] : [];
|
|
9506
|
+
const steeringLines = steeringMessages && steeringMessages.length > 0 ? ["--- User steering (queued during the previous session) ---", ...steeringMessages, ""] : [];
|
|
8290
9507
|
if (continuationContext && sessionIndex === 0) {
|
|
8291
|
-
const
|
|
8292
|
-
|
|
9508
|
+
const resumeWindow = this.resolveWindowReplay(
|
|
9509
|
+
state,
|
|
9510
|
+
continuationContext.previousMessages.length
|
|
9511
|
+
);
|
|
9512
|
+
const sourceReplayHistoryMessages = [
|
|
9513
|
+
...resumeWindow.windowSummaryMessages,
|
|
9514
|
+
...this.sanitizeReplayHistoryMessages(
|
|
9515
|
+
continuationContext.previousMessages.slice(resumeWindow.windowBase)
|
|
9516
|
+
)
|
|
9517
|
+
];
|
|
9518
|
+
const replayHistoryMessages = this.deriveToolContextMessages(
|
|
9519
|
+
sourceReplayHistoryMessages,
|
|
9520
|
+
state.taskName,
|
|
9521
|
+
compactionOptions?.toolContextMode || "hot-tail",
|
|
9522
|
+
compactionOptions?.toolWindow ?? "session"
|
|
8293
9523
|
);
|
|
8294
9524
|
const continuationGuardrail = this.buildContinuationGuardrail(state);
|
|
8295
9525
|
const defaultContinueMessage = "Continue the task. Review your prior work above and proceed with any remaining work. If everything is already complete, respond with TASK_COMPLETE.";
|
|
@@ -8313,9 +9543,15 @@ Do NOT redo any of the above work.`
|
|
|
8313
9543
|
content: userContent
|
|
8314
9544
|
}
|
|
8315
9545
|
];
|
|
8316
|
-
const
|
|
9546
|
+
const replayArtifactReferences = this.extractArtifactReferencesFromMessages(replayHistoryMessages);
|
|
9547
|
+
const summaryText = this.generateCompactSummary(
|
|
9548
|
+
state,
|
|
9549
|
+
compactInstructions,
|
|
9550
|
+
replayArtifactReferences
|
|
9551
|
+
);
|
|
8317
9552
|
const breakdown = this.buildContextBudgetBreakdown({
|
|
8318
9553
|
historyMessages: replayHistoryMessages,
|
|
9554
|
+
sourceHistoryMessages: sourceReplayHistoryMessages,
|
|
8319
9555
|
currentTurnContent: userContent,
|
|
8320
9556
|
localTools: compactionOptions?.localTools,
|
|
8321
9557
|
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
@@ -8341,7 +9577,8 @@ Do NOT redo any of the above work.`
|
|
|
8341
9577
|
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
8342
9578
|
breakdown,
|
|
8343
9579
|
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
8344
|
-
compactInstructions
|
|
9580
|
+
compactInstructions,
|
|
9581
|
+
artifactReferences: replayArtifactReferences
|
|
8345
9582
|
}
|
|
8346
9583
|
),
|
|
8347
9584
|
requestContextManagement
|
|
@@ -8365,7 +9602,8 @@ Do NOT redo any of the above work.`
|
|
|
8365
9602
|
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
8366
9603
|
breakdown,
|
|
8367
9604
|
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
8368
|
-
compactInstructions
|
|
9605
|
+
compactInstructions,
|
|
9606
|
+
artifactReferences: replayArtifactReferences
|
|
8369
9607
|
}
|
|
8370
9608
|
),
|
|
8371
9609
|
requestContextManagement
|
|
@@ -8416,23 +9654,45 @@ Do NOT redo any of the above work.`
|
|
|
8416
9654
|
"Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
|
|
8417
9655
|
].join("\n");
|
|
8418
9656
|
const MAX_HISTORY_MESSAGES = 60;
|
|
8419
|
-
|
|
8420
|
-
|
|
8421
|
-
|
|
8422
|
-
|
|
9657
|
+
const { windowBase, windowSummaryMessages } = this.resolveWindowReplay(
|
|
9658
|
+
state,
|
|
9659
|
+
state.messages.length
|
|
9660
|
+
);
|
|
9661
|
+
let sourceHistoryMessages = this.sanitizeReplayHistoryMessages(
|
|
9662
|
+
state.messages.slice(windowBase)
|
|
9663
|
+
);
|
|
9664
|
+
if (sourceHistoryMessages.length > MAX_HISTORY_MESSAGES) {
|
|
9665
|
+
const trimmedHistory = this.trimReplayHistoryMessages(
|
|
9666
|
+
sourceHistoryMessages,
|
|
9667
|
+
MAX_HISTORY_MESSAGES
|
|
9668
|
+
);
|
|
9669
|
+
sourceHistoryMessages = trimmedHistory.historyMessages;
|
|
8423
9670
|
if (trimmedHistory.trimmedCount > 0) {
|
|
8424
|
-
|
|
9671
|
+
sourceHistoryMessages = [
|
|
8425
9672
|
{
|
|
8426
9673
|
role: "system",
|
|
8427
9674
|
content: `[${trimmedHistory.trimmedCount} earlier messages trimmed to stay within context limits. Original task: ${(state.originalMessage || originalMessage).slice(0, 500)}]`
|
|
8428
9675
|
},
|
|
8429
|
-
...
|
|
9676
|
+
...sourceHistoryMessages
|
|
8430
9677
|
];
|
|
8431
9678
|
}
|
|
8432
9679
|
}
|
|
8433
|
-
|
|
9680
|
+
sourceHistoryMessages = [...windowSummaryMessages, ...sourceHistoryMessages];
|
|
9681
|
+
const historyMessages = this.deriveToolContextMessages(
|
|
9682
|
+
sourceHistoryMessages,
|
|
9683
|
+
state.taskName,
|
|
9684
|
+
compactionOptions?.toolContextMode || "hot-tail",
|
|
9685
|
+
compactionOptions?.toolWindow ?? "session"
|
|
9686
|
+
);
|
|
9687
|
+
const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
|
|
9688
|
+
const summaryText = this.generateCompactSummary(
|
|
9689
|
+
state,
|
|
9690
|
+
compactInstructions,
|
|
9691
|
+
historyArtifactReferences
|
|
9692
|
+
);
|
|
8434
9693
|
const breakdown = this.buildContextBudgetBreakdown({
|
|
8435
9694
|
historyMessages,
|
|
9695
|
+
sourceHistoryMessages,
|
|
8436
9696
|
currentTurnContent: continuationContent,
|
|
8437
9697
|
localTools: compactionOptions?.localTools,
|
|
8438
9698
|
builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
|
|
@@ -8459,7 +9719,8 @@ Do NOT redo any of the above work.`
|
|
|
8459
9719
|
reservedOutputTokens: breakdown.reservedOutputTokens,
|
|
8460
9720
|
breakdown,
|
|
8461
9721
|
onContextCompaction: compactionOptions?.onContextCompaction,
|
|
8462
|
-
compactInstructions
|
|
9722
|
+
compactInstructions,
|
|
9723
|
+
artifactReferences: historyArtifactReferences
|
|
8463
9724
|
}
|
|
8464
9725
|
),
|
|
8465
9726
|
requestContextManagement
|
|
@@ -8933,6 +10194,52 @@ var BillingEndpoint = class {
|
|
|
8933
10194
|
return this.client.get("/billing/spend-analytics", params);
|
|
8934
10195
|
}
|
|
8935
10196
|
};
|
|
10197
|
+
var AppsEndpoint = class {
|
|
10198
|
+
constructor(client) {
|
|
10199
|
+
this.client = client;
|
|
10200
|
+
}
|
|
10201
|
+
/** List apps for the authenticated owner, newest first. */
|
|
10202
|
+
async list() {
|
|
10203
|
+
return this.client.get("/apps");
|
|
10204
|
+
}
|
|
10205
|
+
/** Get an app by id, including its URL and active version pointer. */
|
|
10206
|
+
async get(id) {
|
|
10207
|
+
return this.client.get(`/apps/${id}`);
|
|
10208
|
+
}
|
|
10209
|
+
/** Create an app. A client token scoped to the app origin is auto-provisioned. */
|
|
10210
|
+
async create(data) {
|
|
10211
|
+
return this.client.post("/apps", data);
|
|
10212
|
+
}
|
|
10213
|
+
/** Update name, description, visibility, or status (suspended serves 410). */
|
|
10214
|
+
async update(id, data) {
|
|
10215
|
+
return this.client.patch(`/apps/${id}`, data);
|
|
10216
|
+
}
|
|
10217
|
+
/** Delete an app, its versions, and its hosting. Irreversible. */
|
|
10218
|
+
async delete(id) {
|
|
10219
|
+
return this.client.delete(`/apps/${id}`);
|
|
10220
|
+
}
|
|
10221
|
+
/** List an app's versions, newest first. */
|
|
10222
|
+
async listVersions(id) {
|
|
10223
|
+
return this.client.get(`/apps/${id}/versions`);
|
|
10224
|
+
}
|
|
10225
|
+
/** Upload a zipped bundle (raw application/zip body) as a new version. */
|
|
10226
|
+
async uploadVersion(id, zipBytes) {
|
|
10227
|
+
return this.client.postBinary(`/apps/${id}/versions`, zipBytes, "application/zip");
|
|
10228
|
+
}
|
|
10229
|
+
/**
|
|
10230
|
+
* Upload a bundle from in-memory file maps (the API zips server-side).
|
|
10231
|
+
* Text files in `files`, binary files base64-encoded in `filesBase64`.
|
|
10232
|
+
*/
|
|
10233
|
+
async uploadVersionFiles(id, data) {
|
|
10234
|
+
return this.client.post(`/apps/${id}/versions`, data);
|
|
10235
|
+
}
|
|
10236
|
+
/** Activate an uploaded version (deploy or rollback). */
|
|
10237
|
+
async activate(id, versionId) {
|
|
10238
|
+
return this.client.post(`/apps/${id}/activate`, {
|
|
10239
|
+
versionId
|
|
10240
|
+
});
|
|
10241
|
+
}
|
|
10242
|
+
};
|
|
8936
10243
|
|
|
8937
10244
|
// src/client.ts
|
|
8938
10245
|
function isObjectRecord(value) {
|
|
@@ -8975,6 +10282,7 @@ var RuntypeClient2 = class {
|
|
|
8975
10282
|
this.clientTokens = new ClientTokensEndpoint(this);
|
|
8976
10283
|
this.agents = new AgentsEndpoint(this);
|
|
8977
10284
|
this.secrets = new SecretsEndpoint(this);
|
|
10285
|
+
this.apps = new AppsEndpoint(this);
|
|
8978
10286
|
this.schedules = new SchedulesEndpoint(this);
|
|
8979
10287
|
this.surfaces = new SurfacesEndpoint(this);
|
|
8980
10288
|
this.conversations = new ConversationsEndpoint(this);
|
|
@@ -9021,7 +10329,7 @@ var RuntypeClient2 = class {
|
|
|
9021
10329
|
clearApiKey() {
|
|
9022
10330
|
delete this.headers.Authorization;
|
|
9023
10331
|
}
|
|
9024
|
-
async runWithLocalTools(
|
|
10332
|
+
async runWithLocalTools(request2, localTools, arg3, arg4) {
|
|
9025
10333
|
const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
|
|
9026
10334
|
const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
|
|
9027
10335
|
const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
|
|
@@ -9035,12 +10343,12 @@ var RuntypeClient2 = class {
|
|
|
9035
10343
|
...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
|
|
9036
10344
|
})) : [];
|
|
9037
10345
|
const modifiedRequest = {
|
|
9038
|
-
...
|
|
10346
|
+
...request2,
|
|
9039
10347
|
...derivedClientTools.length > 0 ? {
|
|
9040
|
-
clientTools: [...
|
|
10348
|
+
clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
|
|
9041
10349
|
} : {},
|
|
9042
10350
|
options: {
|
|
9043
|
-
...
|
|
10351
|
+
...request2.options || {},
|
|
9044
10352
|
streamResponse: isStreaming
|
|
9045
10353
|
}
|
|
9046
10354
|
};
|
|
@@ -9056,13 +10364,12 @@ var RuntypeClient2 = class {
|
|
|
9056
10364
|
onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
|
|
9057
10365
|
onError: (error) => callbacks?.onError?.(error)
|
|
9058
10366
|
};
|
|
9059
|
-
const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
|
|
9060
10367
|
const summary = {
|
|
9061
10368
|
results: /* @__PURE__ */ new Map(),
|
|
9062
10369
|
success: true
|
|
9063
10370
|
};
|
|
9064
10371
|
try {
|
|
9065
|
-
for await (const event of
|
|
10372
|
+
for await (const event of streamEvents(response)) {
|
|
9066
10373
|
collectLocalToolAwait(pausedTools, event);
|
|
9067
10374
|
switch (event.type) {
|
|
9068
10375
|
case "flow_start":
|
|
@@ -9072,10 +10379,10 @@ var RuntypeClient2 = class {
|
|
|
9072
10379
|
wrappedCallbacks.onStepStart?.(event);
|
|
9073
10380
|
break;
|
|
9074
10381
|
case "step_delta":
|
|
9075
|
-
wrappedCallbacks.onStepDelta?.(
|
|
10382
|
+
wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
|
|
9076
10383
|
break;
|
|
9077
10384
|
case "step_complete": {
|
|
9078
|
-
summary.results?.set(
|
|
10385
|
+
summary.results?.set(stepDisplayName(event), event.result);
|
|
9079
10386
|
wrappedCallbacks.onStepComplete?.(event.result, event);
|
|
9080
10387
|
break;
|
|
9081
10388
|
}
|
|
@@ -9083,7 +10390,7 @@ var RuntypeClient2 = class {
|
|
|
9083
10390
|
wrappedCallbacks.onFlowComplete?.(event);
|
|
9084
10391
|
break;
|
|
9085
10392
|
case "flow_error":
|
|
9086
|
-
wrappedCallbacks.onError?.(new Error(
|
|
10393
|
+
wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
|
|
9087
10394
|
break;
|
|
9088
10395
|
}
|
|
9089
10396
|
}
|
|
@@ -9162,7 +10469,8 @@ var RuntypeClient2 = class {
|
|
|
9162
10469
|
return [toolName, await handler(parameters)];
|
|
9163
10470
|
} catch (error) {
|
|
9164
10471
|
throw new Error(
|
|
9165
|
-
`Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}
|
|
10472
|
+
`Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
10473
|
+
{ cause: error }
|
|
9166
10474
|
);
|
|
9167
10475
|
}
|
|
9168
10476
|
})
|
|
@@ -9226,6 +10534,21 @@ var RuntypeClient2 = class {
|
|
|
9226
10534
|
});
|
|
9227
10535
|
return transformResponse(response);
|
|
9228
10536
|
}
|
|
10537
|
+
/**
|
|
10538
|
+
* POST request with a raw binary body (e.g. application/zip bundle uploads).
|
|
10539
|
+
*/
|
|
10540
|
+
async postBinary(path, body, contentType) {
|
|
10541
|
+
const url = this.buildUrl(path);
|
|
10542
|
+
const headers = { ...this.headers, "Content-Type": contentType };
|
|
10543
|
+
const response = await this.makeRequest(url, {
|
|
10544
|
+
method: "POST",
|
|
10545
|
+
headers,
|
|
10546
|
+
// TS 5.7 types Uint8Array over ArrayBufferLike, which no longer
|
|
10547
|
+
// overlaps DOM BodyInit; the runtime value is a valid fetch body.
|
|
10548
|
+
body
|
|
10549
|
+
});
|
|
10550
|
+
return transformResponse(response);
|
|
10551
|
+
}
|
|
9229
10552
|
/**
|
|
9230
10553
|
* Generic request that returns raw Response for streaming
|
|
9231
10554
|
*/
|
|
@@ -9326,7 +10649,7 @@ var RuntypeClient2 = class {
|
|
|
9326
10649
|
} catch (error) {
|
|
9327
10650
|
if (timeoutId) clearTimeout(timeoutId);
|
|
9328
10651
|
if (timeoutId && error instanceof Error && error.name === "AbortError") {
|
|
9329
|
-
throw new Error(`Request timeout after ${this.timeout}ms
|
|
10652
|
+
throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
|
|
9330
10653
|
}
|
|
9331
10654
|
throw error;
|
|
9332
10655
|
}
|
|
@@ -9335,8 +10658,18 @@ var RuntypeClient2 = class {
|
|
|
9335
10658
|
* Make HTTP request that returns raw Response (for streaming)
|
|
9336
10659
|
*/
|
|
9337
10660
|
async makeRawRequest(url, options) {
|
|
9338
|
-
const
|
|
10661
|
+
const callerSignal = options.signal ?? null;
|
|
10662
|
+
const controller = this.timeout === null && !callerSignal ? null : new AbortController();
|
|
9339
10663
|
const timeoutId = controller && this.timeout !== null ? setTimeout(() => controller.abort(), this.timeout) : null;
|
|
10664
|
+
if (callerSignal && controller) {
|
|
10665
|
+
if (callerSignal.aborted) {
|
|
10666
|
+
controller.abort(callerSignal.reason);
|
|
10667
|
+
} else {
|
|
10668
|
+
callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), {
|
|
10669
|
+
once: true
|
|
10670
|
+
});
|
|
10671
|
+
}
|
|
10672
|
+
}
|
|
9340
10673
|
try {
|
|
9341
10674
|
const response = await fetch(url, {
|
|
9342
10675
|
...options,
|
|
@@ -9349,8 +10682,8 @@ var RuntypeClient2 = class {
|
|
|
9349
10682
|
return response;
|
|
9350
10683
|
} catch (error) {
|
|
9351
10684
|
if (timeoutId) clearTimeout(timeoutId);
|
|
9352
|
-
if (timeoutId && error instanceof Error && error.name === "AbortError") {
|
|
9353
|
-
throw new Error(`Request timeout after ${this.timeout}ms
|
|
10685
|
+
if (timeoutId && error instanceof Error && error.name === "AbortError" && !callerSignal?.aborted) {
|
|
10686
|
+
throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
|
|
9354
10687
|
}
|
|
9355
10688
|
throw error;
|
|
9356
10689
|
}
|
|
@@ -9396,9 +10729,6 @@ function createClient(config) {
|
|
|
9396
10729
|
return new RuntypeClient2(config);
|
|
9397
10730
|
}
|
|
9398
10731
|
|
|
9399
|
-
// src/index.ts
|
|
9400
|
-
init_stream_utils();
|
|
9401
|
-
|
|
9402
10732
|
// src/batch-builder.ts
|
|
9403
10733
|
var BatchBuilder = class {
|
|
9404
10734
|
constructor() {
|
|
@@ -9456,20 +10786,20 @@ var BatchBuilder = class {
|
|
|
9456
10786
|
if (!this.recordType) {
|
|
9457
10787
|
throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
|
|
9458
10788
|
}
|
|
9459
|
-
const
|
|
10789
|
+
const request2 = {
|
|
9460
10790
|
flowId: this.flowId,
|
|
9461
10791
|
recordType: this.recordType
|
|
9462
10792
|
};
|
|
9463
10793
|
if (Object.keys(this.batchOptions).length > 0) {
|
|
9464
|
-
|
|
10794
|
+
request2.options = this.batchOptions;
|
|
9465
10795
|
}
|
|
9466
10796
|
if (this.filterConfig) {
|
|
9467
|
-
|
|
10797
|
+
request2.filter = this.filterConfig;
|
|
9468
10798
|
}
|
|
9469
10799
|
if (this.limitConfig !== void 0) {
|
|
9470
|
-
|
|
10800
|
+
request2.limit = this.limitConfig;
|
|
9471
10801
|
}
|
|
9472
|
-
return
|
|
10802
|
+
return request2;
|
|
9473
10803
|
}
|
|
9474
10804
|
/**
|
|
9475
10805
|
* Execute the batch operation
|
|
@@ -9626,32 +10956,32 @@ var EvalBuilder = class {
|
|
|
9626
10956
|
"EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
|
|
9627
10957
|
);
|
|
9628
10958
|
}
|
|
9629
|
-
const
|
|
10959
|
+
const request2 = {};
|
|
9630
10960
|
if (this.flowId) {
|
|
9631
|
-
|
|
10961
|
+
request2.flowId = this.flowId;
|
|
9632
10962
|
} else if (this.virtualFlow) {
|
|
9633
|
-
|
|
10963
|
+
request2.flow = this.virtualFlow;
|
|
9634
10964
|
}
|
|
9635
10965
|
if (this.recordType) {
|
|
9636
|
-
|
|
10966
|
+
request2.recordType = this.recordType;
|
|
9637
10967
|
} else if (this.inlineRecords) {
|
|
9638
|
-
|
|
10968
|
+
request2.records = this.inlineRecords;
|
|
9639
10969
|
}
|
|
9640
10970
|
if (this.modelOverrides) {
|
|
9641
|
-
|
|
10971
|
+
request2.modelOverrides = this.modelOverrides;
|
|
9642
10972
|
} else if (this.modelConfigs) {
|
|
9643
|
-
|
|
10973
|
+
request2.modelConfigs = this.modelConfigs;
|
|
9644
10974
|
}
|
|
9645
10975
|
if (Object.keys(this.evalOptions).length > 0) {
|
|
9646
|
-
|
|
10976
|
+
request2.options = this.evalOptions;
|
|
9647
10977
|
}
|
|
9648
10978
|
if (this.filterConfig) {
|
|
9649
|
-
|
|
10979
|
+
request2.filter = this.filterConfig;
|
|
9650
10980
|
}
|
|
9651
10981
|
if (this.limitConfig !== void 0) {
|
|
9652
|
-
|
|
10982
|
+
request2.limit = this.limitConfig;
|
|
9653
10983
|
}
|
|
9654
|
-
return
|
|
10984
|
+
return request2;
|
|
9655
10985
|
}
|
|
9656
10986
|
/**
|
|
9657
10987
|
* Execute the evaluation
|
|
@@ -10117,10 +11447,14 @@ var STEP_TYPE_TO_METHOD = {
|
|
|
10117
11447
|
"memory-summary": "memorySummary"
|
|
10118
11448
|
};
|
|
10119
11449
|
export {
|
|
11450
|
+
AgentDriftError,
|
|
11451
|
+
AgentEnsureConflictError,
|
|
10120
11452
|
AgentVersionsEndpoint,
|
|
10121
11453
|
AgentsEndpoint,
|
|
11454
|
+
AgentsNamespace,
|
|
10122
11455
|
AnalyticsEndpoint,
|
|
10123
11456
|
ApiKeysEndpoint,
|
|
11457
|
+
AppsEndpoint,
|
|
10124
11458
|
BatchBuilder,
|
|
10125
11459
|
BatchesNamespace,
|
|
10126
11460
|
BillingEndpoint,
|
|
@@ -10131,18 +11465,23 @@ export {
|
|
|
10131
11465
|
ClientTokensEndpoint,
|
|
10132
11466
|
ContextTemplatesEndpoint,
|
|
10133
11467
|
ConversationsEndpoint,
|
|
11468
|
+
DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS,
|
|
11469
|
+
DEFAULT_STALL_STOP_AFTER,
|
|
10134
11470
|
DispatchEndpoint,
|
|
10135
11471
|
EvalBuilder,
|
|
10136
11472
|
EvalEndpoint,
|
|
10137
11473
|
EvalRunner,
|
|
10138
11474
|
EvalsNamespace,
|
|
10139
11475
|
FlowBuilder,
|
|
11476
|
+
FlowDriftError,
|
|
11477
|
+
FlowEnsureConflictError,
|
|
10140
11478
|
FlowResult,
|
|
10141
11479
|
FlowStepsEndpoint,
|
|
10142
11480
|
FlowVersionsEndpoint,
|
|
10143
11481
|
FlowsEndpoint,
|
|
10144
11482
|
FlowsNamespace,
|
|
10145
11483
|
IntegrationsEndpoint,
|
|
11484
|
+
LEDGER_ARTIFACT_LINE_PREFIX,
|
|
10146
11485
|
LogsEndpoint,
|
|
10147
11486
|
ModelConfigsEndpoint,
|
|
10148
11487
|
PromptRunner,
|
|
@@ -10165,22 +11504,47 @@ export {
|
|
|
10165
11504
|
UsersEndpoint,
|
|
10166
11505
|
applyGeneratedRuntimeToolProposalToDispatchRequest,
|
|
10167
11506
|
attachRuntimeToolsToDispatchRequest,
|
|
11507
|
+
buildEmptySessionNudge,
|
|
10168
11508
|
buildGeneratedRuntimeToolGateOutput,
|
|
11509
|
+
buildLedgerOffloadReference,
|
|
11510
|
+
buildPolicyGuidance,
|
|
11511
|
+
buildSendViewOffloadMarker,
|
|
11512
|
+
compileWorkflowConfig,
|
|
11513
|
+
computeAgentContentHash,
|
|
11514
|
+
computeFlowContentHash,
|
|
10169
11515
|
createClient,
|
|
10170
11516
|
createExternalTool,
|
|
10171
11517
|
defaultWorkflow,
|
|
11518
|
+
defaultWorkflowConfig,
|
|
11519
|
+
defineAgent,
|
|
11520
|
+
defineFlow,
|
|
11521
|
+
definePlaybook,
|
|
10172
11522
|
deployWorkflow,
|
|
11523
|
+
ensureDefaultWorkflowHooks,
|
|
10173
11524
|
evaluateGeneratedRuntimeToolProposal,
|
|
11525
|
+
extractDeclaredToolResultChars,
|
|
10174
11526
|
gameWorkflow,
|
|
10175
11527
|
getDefaultPlanPath,
|
|
10176
11528
|
getLikelySupportingCandidatePaths,
|
|
11529
|
+
interpolateWorkflowTemplate,
|
|
10177
11530
|
isDiscoveryToolName,
|
|
10178
11531
|
isMarathonArtifactPath,
|
|
10179
11532
|
isPreservationSensitiveTask,
|
|
11533
|
+
isWorkflowHookRef,
|
|
11534
|
+
listWorkflowHooks,
|
|
11535
|
+
normalizeAgentDefinition,
|
|
10180
11536
|
normalizeCandidatePath,
|
|
10181
11537
|
parseFinalBuffer,
|
|
11538
|
+
parseLedgerArtifactRelativePath,
|
|
11539
|
+
parseOffloadedOutputId,
|
|
10182
11540
|
parseSSEChunk,
|
|
10183
11541
|
processStream,
|
|
11542
|
+
registerWorkflowHook,
|
|
11543
|
+
resolveStallStopAfter,
|
|
11544
|
+
resolveWorkflowHook,
|
|
10184
11545
|
sanitizeTaskSlug,
|
|
10185
|
-
|
|
11546
|
+
shouldInjectEmptySessionNudge,
|
|
11547
|
+
shouldRequestModelEscalation,
|
|
11548
|
+
streamEvents,
|
|
11549
|
+
unregisterWorkflowHook
|
|
10186
11550
|
};
|