@runtypelabs/sdk 4.9.0 → 4.10.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.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 request = { flow };
1097
+ const request2 = { flow };
1125
1098
  if (this.recordConfig) {
1126
- request.record = this.recordConfig;
1099
+ request2.record = this.recordConfig;
1127
1100
  }
1128
1101
  if (this.messagesConfig) {
1129
- request.messages = this.messagesConfig;
1102
+ request2.messages = this.messagesConfig;
1130
1103
  }
1131
1104
  if (this.inputsConfig) {
1132
- request.inputs = this.inputsConfig;
1105
+ request2.inputs = this.inputsConfig;
1133
1106
  }
1134
1107
  if (Object.keys(this.optionsConfig).length > 0) {
1135
- request.options = this.optionsConfig;
1108
+ request2.options = this.optionsConfig;
1136
1109
  }
1137
- return request;
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-namespace.ts
1375
- function isRecord(value) {
1347
+ // src/flows-ensure.ts
1348
+ function isPlainObject(value) {
1376
1349
  return value !== null && typeof value === "object" && !Array.isArray(value);
1377
1350
  }
1378
- function normalizeConfig(config) {
1379
- if (!isRecord(config)) return {};
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] = normalizeConfig(value);
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 normalizeConfig(item);
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 = isRecord(step) ? step : {};
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: normalizeConfig(stepObj.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 = isRecord(a) && typeof a.order === "number" ? a.order : 0;
1413
- const orderB = isRecord(b) && typeof b.order === "number" ? b.order : 0;
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
- const hashArray = new Uint8Array(hashBuffer);
1420
- return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
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
+ }
1421
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;
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
- * Creates the flow if it doesn't exist, updates if steps changed.
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 streamEvents2(response)) {
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?.(stepDeltaText2(event), event);
2365
+ wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
2143
2366
  break;
2144
2367
  case "step_complete": {
2145
- accumulatedSummary.results?.set(stepDisplayName2(event), event.result);
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(flowErrorMessage2(event)));
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 request = { flow };
2485
+ const request2 = { flow };
2262
2486
  if (this.recordConfig) {
2263
- request.record = this.recordConfig;
2487
+ request2.record = this.recordConfig;
2264
2488
  }
2265
2489
  if (this.messagesConfig) {
2266
- request.messages = this.messagesConfig;
2490
+ request2.messages = this.messagesConfig;
2267
2491
  }
2268
2492
  if (this.inputsConfig) {
2269
- request.inputs = this.inputsConfig;
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
- request.options = options;
2288
- return request;
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 client = this.getClient();
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(request, runtimeTools, options = {}) {
3636
- const stepList = request.flow.steps;
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
- ...request,
4198
+ ...request2,
3680
4199
  flow: {
3681
- ...request.flow,
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(request, proposal, options = {}) {
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(request, [decision.tool], options.attach);
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, "/");
@@ -5431,15 +5988,15 @@ var DispatchEndpoint = class {
5431
5988
  * Attach approved runtime tools to a prompt step in a redispatch request.
5432
5989
  * Returns a new request object and does not mutate the original.
5433
5990
  */
5434
- attachApprovedRuntimeTools(request, runtimeTools, options) {
5435
- return attachRuntimeToolsToDispatchRequest(request, runtimeTools, options);
5991
+ attachApprovedRuntimeTools(request2, runtimeTools, options) {
5992
+ return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
5436
5993
  }
5437
5994
  /**
5438
5995
  * Validate a generated runtime tool proposal and attach it to the redispatch
5439
5996
  * request if approved, in one call.
5440
5997
  */
5441
- applyGeneratedRuntimeToolProposal(request, proposal, options) {
5442
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request, proposal, options);
5998
+ applyGeneratedRuntimeToolProposal(request2, proposal, options) {
5999
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
5443
6000
  }
5444
6001
  };
5445
6002
  var ChatEndpoint = class {
@@ -5944,6 +6501,22 @@ async function processAgentStream(body, callbacks) {
5944
6501
  reader.releaseLock();
5945
6502
  }
5946
6503
  }
6504
+ function sleepWithAbort(delayMs, signal) {
6505
+ return new Promise((resolve) => {
6506
+ const onAbort = () => {
6507
+ clearTimeout(timer);
6508
+ resolve();
6509
+ };
6510
+ const timer = setTimeout(() => {
6511
+ signal?.removeEventListener("abort", onAbort);
6512
+ resolve();
6513
+ }, delayMs);
6514
+ if (signal) {
6515
+ if (signal.aborted) onAbort();
6516
+ else signal.addEventListener("abort", onAbort, { once: true });
6517
+ }
6518
+ });
6519
+ }
5947
6520
  var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
5948
6521
  type: "object",
5949
6522
  properties: {
@@ -5975,8 +6548,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
5975
6548
  },
5976
6549
  required: ["name", "description", "toolType", "parametersSchema", "config"]
5977
6550
  };
5978
- function appendRuntimeToolsToAgentRequest(request, runtimeTools) {
5979
- const existing = request.tools?.runtimeTools || [];
6551
+ function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
6552
+ const existing = request2.tools?.runtimeTools || [];
5980
6553
  const existingNames = new Set(existing.map((tool) => tool.name));
5981
6554
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
5982
6555
  name: tool.name,
@@ -5986,9 +6559,9 @@ function appendRuntimeToolsToAgentRequest(request, runtimeTools) {
5986
6559
  ...tool.config ? { config: tool.config } : {}
5987
6560
  }));
5988
6561
  return {
5989
- ...request,
6562
+ ...request2,
5990
6563
  tools: {
5991
- ...request.tools,
6564
+ ...request2.tools,
5992
6565
  runtimeTools: [...existing, ...converted]
5993
6566
  }
5994
6567
  };
@@ -6064,21 +6637,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6064
6637
  * Attach approved runtime tools to an agent execute request.
6065
6638
  * Returns a new request object and does not mutate the original.
6066
6639
  */
6067
- attachApprovedRuntimeTools(request, runtimeTools) {
6068
- return appendRuntimeToolsToAgentRequest(request, runtimeTools);
6640
+ attachApprovedRuntimeTools(request2, runtimeTools) {
6641
+ return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
6069
6642
  }
6070
6643
  /**
6071
6644
  * Validate a generated runtime tool proposal and append it to an agent execute
6072
6645
  * request if approved, in one call.
6073
6646
  */
6074
- applyGeneratedRuntimeToolProposal(request, proposal, options) {
6647
+ applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6075
6648
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
6076
6649
  if (!decision.approved || !decision.tool) {
6077
- return { decision, request };
6650
+ return { decision, request: request2 };
6078
6651
  }
6079
6652
  return {
6080
6653
  decision,
6081
- request: appendRuntimeToolsToAgentRequest(request, [decision.tool])
6654
+ request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
6082
6655
  };
6083
6656
  }
6084
6657
  /**
@@ -6108,13 +6681,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6108
6681
  * // ...
6109
6682
  * ```
6110
6683
  */
6111
- async executeStream(id, data) {
6684
+ async executeStream(id, data, init) {
6112
6685
  return this.client.requestStream(`/agents/${id}/execute`, {
6113
6686
  method: "POST",
6114
6687
  body: JSON.stringify({
6115
6688
  ...data,
6116
6689
  streamResponse: true
6117
- })
6690
+ }),
6691
+ ...init?.signal ? { signal: init.signal } : {}
6118
6692
  });
6119
6693
  }
6120
6694
  /**
@@ -6210,56 +6784,94 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6210
6784
  runtimeTools: [...data.tools?.runtimeTools || [], ...runtimeTools]
6211
6785
  }
6212
6786
  };
6213
- const response = await this.executeStream(id, requestData);
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;
6787
+ const abortSignal = options?.abortSignal;
6219
6788
  let accumulatedOutput = "";
6220
6789
  let lastKnownCost = 0;
6221
6790
  let lastKnownTokens;
6791
+ let lastSeenExecutionId = "";
6222
6792
  let pauseCount = 0;
6223
6793
  let discoveryPauseCount = 0;
6224
6794
  let consecutiveDiscoveryPauseCount = 0;
6225
6795
  const toolNameCounts = {};
6226
6796
  let recentActionKeys = [];
6227
6797
  const toolMessages = [];
6798
+ const finishAborted = (executionId) => {
6799
+ const abortCompleteEvent = {
6800
+ type: "agent_complete",
6801
+ executionId,
6802
+ seq: 0,
6803
+ agentId: id,
6804
+ success: true,
6805
+ iterations: 1,
6806
+ stopReason: "end_turn",
6807
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
6808
+ totalCost: lastKnownCost,
6809
+ ...lastKnownTokens ? { totalTokens: lastKnownTokens } : {},
6810
+ finalOutput: [accumulatedOutput.trim(), "Session aborted by user request."].filter(Boolean).join("\n\n"),
6811
+ duration: 0
6812
+ };
6813
+ callbacks?.onAgentComplete?.(abortCompleteEvent);
6814
+ return { completeEvent: abortCompleteEvent, toolMessages };
6815
+ };
6816
+ let response;
6817
+ try {
6818
+ response = await this.executeStream(id, requestData, {
6819
+ ...abortSignal ? { signal: abortSignal } : {}
6820
+ });
6821
+ } catch (error) {
6822
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6823
+ throw error;
6824
+ }
6825
+ if (!response.ok) {
6826
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
6827
+ throw new Error(error.error || `HTTP ${response.status}`);
6828
+ }
6829
+ let currentBody = response.body;
6228
6830
  while (true) {
6229
6831
  let pausedEvent = null;
6230
6832
  let completeEvent = null;
6231
- await processAgentStream(currentBody, {
6232
- ...callbacks,
6233
- onTurnDelta: (event) => {
6234
- if (event.contentType === "text") {
6235
- accumulatedOutput += event.delta;
6236
- }
6237
- callbacks?.onTurnDelta?.(event);
6238
- },
6239
- onTurnComplete: (event) => {
6240
- if (typeof event.cost === "number") {
6241
- lastKnownCost = event.cost;
6242
- }
6243
- if (event.tokens) {
6244
- lastKnownTokens = event.tokens;
6245
- }
6246
- callbacks?.onTurnComplete?.(event);
6247
- },
6248
- onAgentPaused: (event) => {
6249
- pausedEvent = event;
6250
- callbacks?.onAgentPaused?.(event);
6251
- },
6252
- onAgentComplete: (event) => {
6253
- if (!event.finalOutput && accumulatedOutput) {
6254
- event.finalOutput = accumulatedOutput;
6833
+ try {
6834
+ await processAgentStream(currentBody, {
6835
+ ...callbacks,
6836
+ onAgentStart: (event) => {
6837
+ lastSeenExecutionId = event.executionId;
6838
+ callbacks?.onAgentStart?.(event);
6839
+ },
6840
+ onTurnDelta: (event) => {
6841
+ if (event.contentType === "text") {
6842
+ accumulatedOutput += event.delta;
6843
+ }
6844
+ callbacks?.onTurnDelta?.(event);
6845
+ },
6846
+ onTurnComplete: (event) => {
6847
+ if (typeof event.cost === "number") {
6848
+ lastKnownCost = event.cost;
6849
+ }
6850
+ if (event.tokens) {
6851
+ lastKnownTokens = event.tokens;
6852
+ }
6853
+ callbacks?.onTurnComplete?.(event);
6854
+ },
6855
+ onAgentPaused: (event) => {
6856
+ pausedEvent = event;
6857
+ callbacks?.onAgentPaused?.(event);
6858
+ },
6859
+ onAgentComplete: (event) => {
6860
+ if (!event.finalOutput && accumulatedOutput) {
6861
+ event.finalOutput = accumulatedOutput;
6862
+ }
6863
+ completeEvent = event;
6864
+ callbacks?.onAgentComplete?.(event);
6255
6865
  }
6256
- completeEvent = event;
6257
- callbacks?.onAgentComplete?.(event);
6258
- }
6259
- });
6866
+ });
6867
+ } catch (error) {
6868
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6869
+ throw error;
6870
+ }
6260
6871
  if (completeEvent) return { completeEvent, toolMessages };
6261
6872
  if (pausedEvent) {
6262
6873
  const { toolName, toolId, parameters, executionId } = pausedEvent;
6874
+ lastSeenExecutionId = executionId;
6263
6875
  const toolDef = localTools[toolName];
6264
6876
  if (!toolDef) {
6265
6877
  throw new Error(`Local tool "${toolName}" required but not provided`);
@@ -6374,6 +6986,19 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6374
6986
  callbacks?.onAgentComplete?.(forcedCompleteEvent);
6375
6987
  return { completeEvent: forcedCompleteEvent, toolMessages };
6376
6988
  }
6989
+ if (abortSignal?.aborted) {
6990
+ callbacks?.onLocalToolExecutionComplete?.({
6991
+ executionId,
6992
+ toolCallId: toolId,
6993
+ toolName,
6994
+ parameters: parsedParams,
6995
+ result: toolResult,
6996
+ success: true,
6997
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
6998
+ durationMs: Date.now() - localExecutionStartedAtMs
6999
+ });
7000
+ return finishAborted(executionId);
7001
+ }
6377
7002
  if (options?.shouldInterrupt?.()) {
6378
7003
  callbacks?.onLocalToolExecutionComplete?.({
6379
7004
  executionId,
@@ -6407,15 +7032,22 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6407
7032
  callbacks?.onAgentComplete?.(interruptCompleteEvent);
6408
7033
  return { completeEvent: interruptCompleteEvent, toolMessages };
6409
7034
  }
6410
- const resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
6411
- method: "POST",
6412
- body: JSON.stringify({
6413
- executionId,
6414
- toolOutputs: { [toolName]: toolResult },
6415
- streamResponse: true,
6416
- debugMode: data.debugMode
6417
- })
6418
- });
7035
+ let resumeResponse;
7036
+ try {
7037
+ resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
7038
+ method: "POST",
7039
+ body: JSON.stringify({
7040
+ executionId,
7041
+ toolOutputs: { [toolName]: toolResult },
7042
+ streamResponse: true,
7043
+ debugMode: data.debugMode
7044
+ }),
7045
+ ...abortSignal ? { signal: abortSignal } : {}
7046
+ });
7047
+ } catch (error) {
7048
+ if (abortSignal?.aborted) return finishAborted(executionId);
7049
+ throw error;
7050
+ }
6419
7051
  if (!resumeResponse.ok) {
6420
7052
  const error = await resumeResponse.json().catch(() => ({ error: "Unknown error" }));
6421
7053
  throw new Error(error.error || `HTTP ${resumeResponse.status}`);
@@ -6433,6 +7065,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6433
7065
  currentBody = resumeResponse.body;
6434
7066
  continue;
6435
7067
  }
7068
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6436
7069
  return null;
6437
7070
  }
6438
7071
  }
@@ -6704,7 +7337,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6704
7337
  reasons.push("Best candidate file has not been verified (read back after writing)");
6705
7338
  }
6706
7339
  if (state.verificationRequired && !state.lastVerificationPassed && !trace.verificationPassed) {
6707
- reasons.push("Verification has not passed \u2014 run a verification command (run_check) before completing");
7340
+ reasons.push(
7341
+ "Verification has not passed \u2014 run a verification command (run_check) before completing"
7342
+ );
6708
7343
  }
6709
7344
  return reasons.length > 0 ? reasons.join("; ") : "Completion gates not satisfied for the current workflow phase";
6710
7345
  }
@@ -6747,32 +7382,71 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6747
7382
  );
6748
7383
  }
6749
7384
  /**
6750
- * Compact old tool results based on context mode and window setting.
6751
- * Modifies messages in-place.
7385
+ * Resolve the replay window: the base index sliced off the full history and
7386
+ * the durable summary message that stands in for everything before it.
7387
+ * The base is honored ONLY when the latest summary actually exists —
7388
+ * slicing history with no summary substitute would silently drop prior
7389
+ * context (defensive against states that carried a base but lost their
7390
+ * summaries). The base is also clamped so a stale pointer (fork truncation,
7391
+ * trimmed legacy state) never slices past the end of the array.
6752
7392
  */
6753
- compactToolResults(messages, newToolMessages, taskName, mode, window) {
6754
- if (mode === "full-inline") return;
7393
+ resolveWindowReplay(state, messageCount) {
7394
+ const base = state.contextWindowBaseIndex ?? 0;
7395
+ if (!Number.isFinite(base) || base <= 0) {
7396
+ return { windowBase: 0, windowSummaryMessages: [] };
7397
+ }
7398
+ const summaries = state.contextCompactionSummaries;
7399
+ const latest = summaries?.[summaries.length - 1];
7400
+ if (!latest) {
7401
+ return { windowBase: 0, windowSummaryMessages: [] };
7402
+ }
7403
+ return {
7404
+ windowBase: Math.min(Math.floor(base), messageCount),
7405
+ windowSummaryMessages: [{ role: "system", content: latest.content }]
7406
+ };
7407
+ }
7408
+ /**
7409
+ * Derive the message view sent to the model based on context mode and window
7410
+ * setting. This never mutates persisted marathon history; masking/offloading
7411
+ * is a send-time view over the full-fidelity ledger/history.
7412
+ */
7413
+ deriveToolContextMessages(messages, taskName, mode, window) {
7414
+ if (mode === "full-inline") return [...messages];
7415
+ const maskMessage = (msg) => ({
7416
+ ...msg,
7417
+ toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
7418
+ });
7419
+ const view = [...messages];
6755
7420
  if (window === "session") {
6756
- for (const msg of messages) {
7421
+ const lastUserIndex = view.reduce(
7422
+ (lastIndex, message, index) => message.role === "user" ? index : lastIndex,
7423
+ -1
7424
+ );
7425
+ for (let index = 0; index < view.length; index++) {
7426
+ if (lastUserIndex >= 0 && index > lastUserIndex) continue;
7427
+ const msg = view[index];
7428
+ if (!msg) continue;
6757
7429
  if (msg.role === "tool" && msg.toolResults) {
6758
- msg.toolResults = msg.toolResults.map((tr) => this.compactOneResult(tr, taskName, mode));
7430
+ view[index] = maskMessage(msg);
6759
7431
  }
6760
7432
  }
6761
7433
  } else {
6762
- const newToolResultCount = newToolMessages.filter((m) => m.role === "tool").length;
6763
- const keepInlineFromExisting = Math.max(0, window - newToolResultCount);
6764
7434
  const toolResultIndices = [];
6765
- for (let i = 0; i < messages.length; i++) {
6766
- if (messages[i].role === "tool" && messages[i].toolResults) {
7435
+ for (let i = 0; i < view.length; i++) {
7436
+ const message = view[i];
7437
+ if (message?.role === "tool" && message.toolResults) {
6767
7438
  toolResultIndices.push(i);
6768
7439
  }
6769
7440
  }
6770
- const compactUpTo = toolResultIndices.length - keepInlineFromExisting;
7441
+ const compactUpTo = toolResultIndices.length - window;
6771
7442
  for (let j = 0; j < compactUpTo && j < toolResultIndices.length; j++) {
6772
- const msg = messages[toolResultIndices[j]];
6773
- msg.toolResults = msg.toolResults.map((tr) => this.compactOneResult(tr, taskName, mode));
7443
+ const index = toolResultIndices[j];
7444
+ const msg = index === void 0 ? void 0 : view[index];
7445
+ if (!msg) continue;
7446
+ view[index] = maskMessage(msg);
6774
7447
  }
6775
7448
  }
7449
+ return view;
6776
7450
  }
6777
7451
  compactOneResult(tr, taskName, mode) {
6778
7452
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
@@ -6808,10 +7482,20 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6808
7482
  }
6809
7483
  const slug = this.sanitizeTaskSlug(taskName || "task");
6810
7484
  const dir = `.runtype/marathons/${slug}/tool-outputs`;
6811
- fs.mkdirSync(dir, { recursive: true });
6812
7485
  const filePath = `${dir}/${toolCallId}.txt`;
6813
- fs.writeFileSync(filePath, resultStr, "utf-8");
6814
- return `[${toolName} output (${resultStr.length} chars) saved to ${filePath} \u2014 use read_file to retrieve if needed]`;
7486
+ try {
7487
+ if (!fs.existsSync(filePath)) {
7488
+ fs.mkdirSync(dir, { recursive: true });
7489
+ fs.writeFileSync(filePath, resultStr, "utf-8");
7490
+ }
7491
+ } catch {
7492
+ return result;
7493
+ }
7494
+ return buildSendViewOffloadMarker({
7495
+ toolName,
7496
+ charLength: resultStr.length,
7497
+ filePath
7498
+ });
6815
7499
  }
6816
7500
  getDefaultPlanPath(taskName) {
6817
7501
  return getDefaultPlanPath(taskName);
@@ -6933,6 +7617,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6933
7617
  const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
6934
7618
  const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
6935
7619
  const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
7620
+ const contextCompactionSummaries = (resumeState.contextCompactionSummaries ?? []).slice(-20);
6936
7621
  const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
6937
7622
  const bestCandidatePath = normalizedBestCandidatePath && !this.isMarathonArtifactPath(normalizedBestCandidatePath) ? normalizedBestCandidatePath : [...candidatePaths, ...recentReadPaths].sort(
6938
7623
  (left, right) => this.scoreCandidatePath(right) - this.scoreCandidatePath(left)
@@ -6952,10 +7637,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6952
7637
  bestCandidateVerified: Boolean(resumeState.bestCandidateVerified),
6953
7638
  ...resumeState.verificationRequired !== void 0 ? { verificationRequired: resumeState.verificationRequired } : {},
6954
7639
  lastVerificationPassed: Boolean(resumeState.lastVerificationPassed),
6955
- ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? { consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions } : {},
7640
+ ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? {
7641
+ consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions
7642
+ } : {},
6956
7643
  ...resumeState.isCreationTask !== void 0 ? { isCreationTask: resumeState.isCreationTask } : {},
6957
7644
  ...resumeState.workflowVariant !== void 0 ? { workflowVariant: resumeState.workflowVariant } : {},
6958
7645
  ...resumeState.workflowState !== void 0 ? { workflowState: resumeState.workflowState } : {},
7646
+ ...contextCompactionSummaries.length ? { contextCompactionSummaries } : {},
7647
+ ...typeof resumeState.contextWindowBaseIndex === "number" && Number.isFinite(resumeState.contextWindowBaseIndex) && resumeState.contextWindowBaseIndex > 0 ? { contextWindowBaseIndex: Math.floor(resumeState.contextWindowBaseIndex) } : {},
6959
7648
  ...typeof resumeState.outputRoot === "string" && resumeState.outputRoot.trim() ? { outputRoot: resumeState.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/") } : {}
6960
7649
  };
6961
7650
  }
@@ -7421,8 +8110,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7421
8110
  } : {},
7422
8111
  ...seededResumeState?.candidatePaths ? { candidatePaths: seededResumeState.candidatePaths } : {},
7423
8112
  ...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
7424
- ...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {}
8113
+ ...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {},
8114
+ ...seededResumeState?.contextCompactionSummaries?.length ? { contextCompactionSummaries: seededResumeState.contextCompactionSummaries } : {},
8115
+ ...typeof seededResumeState?.contextWindowBaseIndex === "number" ? { contextWindowBaseIndex: seededResumeState.contextWindowBaseIndex } : {}
7425
8116
  };
8117
+ if (options.previousMessages && options.previousMessages.length > 0) {
8118
+ state.messages = options.previousMessages.map((message) => structuredClone(message));
8119
+ }
7426
8120
  state.workflowVariant = classifiedVariant;
7427
8121
  state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
7428
8122
  state.outputRoot = seededResumeState?.outputRoot ?? (state.isCreationTask ? "public/" : void 0);
@@ -7455,6 +8149,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7455
8149
  }
7456
8150
  }
7457
8151
  for (let session = 0; session < maxSessions; session++) {
8152
+ if (options.abortSignal?.aborted) {
8153
+ state.status = "paused";
8154
+ break;
8155
+ }
7458
8156
  const phaseAtSessionStart = state.workflowPhase;
7459
8157
  const sessionTrace = this.createEmptyToolTrace();
7460
8158
  const sessionLocalTools = this.wrapLocalToolsForTrace(
@@ -7491,7 +8189,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7491
8189
  localTools: options.localTools,
7492
8190
  builtinToolSchemas,
7493
8191
  onContextCompaction: options.onContextCompaction,
7494
- onContextNotice: options.onContextNotice
8192
+ onContextNotice: options.onContextNotice,
8193
+ toolContextMode: options.toolContextMode || "hot-tail",
8194
+ toolWindow: options.toolWindow ?? "session"
7495
8195
  },
7496
8196
  queuedSteeringMessages
7497
8197
  );
@@ -7521,7 +8221,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7521
8221
  sessionCallbacks,
7522
8222
  {
7523
8223
  onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow),
7524
- shouldInterrupt: options.hasQueuedUserMessages
8224
+ shouldInterrupt: options.hasQueuedUserMessages,
8225
+ ...options.abortSignal ? { abortSignal: options.abortSignal } : {}
7525
8226
  },
7526
8227
  state.taskName
7527
8228
  );
@@ -7670,22 +8371,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7670
8371
  }
7671
8372
  }
7672
8373
  if (!state.messages) state.messages = [];
7673
- if (this.isCompactHistoryMessageSet(messages)) {
7674
- state.messages = [...messages];
7675
- } else if (state.messages.length > 0 && messages.length > state.messages.length) {
7676
- const newMessages = messages.slice(state.messages.length);
7677
- state.messages.push(...newMessages);
7678
- } else {
8374
+ const sentUserMessage = messages[messages.length - 1];
8375
+ if (sentUserMessage?.role === "user") {
8376
+ state.messages.push(sentUserMessage);
8377
+ } else if (state.messages.length === 0) {
7679
8378
  state.messages.push(...messages);
7680
8379
  }
7681
8380
  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
8381
  state.messages.push(...sessionToolMessages);
7690
8382
  }
7691
8383
  const assistantContent = effectiveSessionOutput || `[Session ${session + 1} completed (${sessionResult.stopReason}). No text output captured.]`;
@@ -7705,7 +8397,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7705
8397
  workflow
7706
8398
  );
7707
8399
  if (detectedTaskCompletion && !acceptedTaskCompletion) {
7708
- state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(state, sessionTrace);
8400
+ state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(
8401
+ state,
8402
+ sessionTrace
8403
+ );
7709
8404
  if (state.verificationRequired && !state.lastVerificationPassed && !sessionTrace.verificationPassed && !sessionTrace.verificationAttempted) {
7710
8405
  state.consecutiveBlockedVerificationSessions = (state.consecutiveBlockedVerificationSessions || 0) + 1;
7711
8406
  if ((state.consecutiveBlockedVerificationSessions || 0) >= 2) {
@@ -7724,24 +8419,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7724
8419
  }
7725
8420
  if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
7726
8421
  const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
7727
- const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(state, sessionTrace) : true;
8422
+ const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(
8423
+ state,
8424
+ sessionTrace
8425
+ ) : true;
7728
8426
  if (gatesSatisfied) {
7729
8427
  state.status = "complete";
7730
8428
  }
7731
8429
  } else if (sessionResult.stopReason === "error") {
7732
8430
  if (_AgentsEndpoint.isRetryableSessionError(sessionResult.error) && consecutiveServerNetworkErrors < maxServerNetworkRetries) {
7733
8431
  consecutiveServerNetworkErrors++;
7734
- const delayMs = Math.min(
7735
- 5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1),
7736
- 3e4
7737
- );
8432
+ const delayMs = Math.min(5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1), 3e4);
7738
8433
  const delaySec = Math.round(delayMs / 1e3);
7739
8434
  await this.emitContextNotice(options.onContextNotice, {
7740
8435
  kind: "server_network_retry",
7741
8436
  sessionIndex: session,
7742
8437
  message: `Server network error: ${sessionResult.error}. Retrying in ${delaySec}s (attempt ${consecutiveServerNetworkErrors}/${maxServerNetworkRetries})...`
7743
8438
  });
7744
- await new Promise((resolve) => setTimeout(resolve, delayMs));
8439
+ await sleepWithAbort(delayMs, options.abortSignal);
7745
8440
  } else {
7746
8441
  state.status = "error";
7747
8442
  }
@@ -7756,6 +8451,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7756
8451
  } else if (session + 1 >= maxSessions) {
7757
8452
  state.status = "max_sessions";
7758
8453
  }
8454
+ if (options.abortSignal?.aborted) {
8455
+ state.status = "paused";
8456
+ }
7759
8457
  if (options.trackProgress) {
7760
8458
  recordId = await this.syncProgressRecord(state, recordId);
7761
8459
  }
@@ -7832,6 +8530,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7832
8530
  return 0;
7833
8531
  }
7834
8532
  }
8533
+ extractDeclaredToolResultChars(value) {
8534
+ return extractDeclaredToolResultChars(value);
8535
+ }
7835
8536
  estimateMessageContentTokens(content) {
7836
8537
  if (typeof content === "string") return this.estimateTextTokens(content);
7837
8538
  return content.reduce((total, part) => {
@@ -7850,12 +8551,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7850
8551
  0
7851
8552
  );
7852
8553
  }
7853
- estimateToolResultTokens(toolResults) {
8554
+ estimateToolResultTokens(toolResults, options) {
7854
8555
  if (!toolResults || toolResults.length === 0) return 0;
7855
- return toolResults.reduce(
7856
- (sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
7857
- 0
7858
- );
8556
+ return toolResults.reduce((sum, toolResult) => {
8557
+ const resultTokens = this.estimateUnknownTokens(toolResult.result);
8558
+ const declaredChars = options?.useDeclaredSize ? this.extractDeclaredToolResultChars(toolResult.result) : void 0;
8559
+ const declaredTokens = typeof declaredChars === "number" ? Math.ceil(declaredChars / 4) : 0;
8560
+ return sum + 12 + this.estimateTextTokens(toolResult.toolName) + Math.max(resultTokens, declaredTokens);
8561
+ }, 0);
7859
8562
  }
7860
8563
  estimateMessageTokens(message) {
7861
8564
  return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
@@ -7863,13 +8566,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7863
8566
  estimateConversationTokens(messages) {
7864
8567
  return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
7865
8568
  }
7866
- estimateConversationBreakdown(messages) {
8569
+ estimateConversationBreakdown(messages, options) {
7867
8570
  let historyTokens = 0;
7868
8571
  let toolOutputTokens = 0;
7869
8572
  for (const message of messages) {
7870
8573
  const contentTokens = this.estimateMessageContentTokens(message.content);
7871
8574
  const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
7872
- const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
8575
+ const toolResultTokens = this.estimateToolResultTokens(message.toolResults, {
8576
+ useDeclaredSize: options?.useDeclaredToolResultSizes
8577
+ });
7873
8578
  const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
7874
8579
  if (message.role === "tool") {
7875
8580
  toolOutputTokens += messageTotal;
@@ -7923,13 +8628,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7923
8628
  const compactInstructions = config.compactInstructions;
7924
8629
  return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
7925
8630
  }
7926
- extractArtifactReferences(state) {
8631
+ extractArtifactReferencesFromMessages(messages = []) {
7927
8632
  const references = /* @__PURE__ */ new Set();
7928
8633
  const offloadPrefix = "[Output saved to ";
7929
- for (const message of state.messages ?? []) {
8634
+ const ledgerArtifactPrefix = LEDGER_ARTIFACT_LINE_PREFIX;
8635
+ const savedToPattern = /saved to\s+([^—\]\n]+?)(?:\s+—|\]|\n|$)/gi;
8636
+ for (const message of messages) {
7930
8637
  if (!message.toolResults) continue;
7931
8638
  for (const toolResult of message.toolResults) {
7932
8639
  if (typeof toolResult.result !== "string") continue;
8640
+ for (const line of toolResult.result.split("\n")) {
8641
+ if (line.startsWith(ledgerArtifactPrefix)) {
8642
+ references.add(line.slice(ledgerArtifactPrefix.length).trim());
8643
+ }
8644
+ }
8645
+ for (const match of toolResult.result.matchAll(savedToPattern)) {
8646
+ const pathText = match[1]?.trim();
8647
+ if (pathText) references.add(pathText);
8648
+ }
7933
8649
  let startIndex = 0;
7934
8650
  while (startIndex < toolResult.result.length) {
7935
8651
  const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
@@ -7947,6 +8663,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7947
8663
  }
7948
8664
  }
7949
8665
  }
8666
+ return Array.from(references);
8667
+ }
8668
+ extractArtifactReferences(state, additionalReferences = []) {
8669
+ const references = /* @__PURE__ */ new Set([
8670
+ ...this.extractArtifactReferencesFromMessages(state.messages ?? []),
8671
+ ...additionalReferences
8672
+ ]);
7950
8673
  if (state.planPath) {
7951
8674
  references.add(state.planPath);
7952
8675
  }
@@ -7954,6 +8677,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7954
8677
  }
7955
8678
  buildContextBudgetBreakdown(details) {
7956
8679
  const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
8680
+ const sourceConversationBreakdown = details.sourceHistoryMessages ? this.estimateConversationBreakdown(details.sourceHistoryMessages, {
8681
+ useDeclaredToolResultSizes: true
8682
+ }) : conversationBreakdown;
7957
8683
  const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
7958
8684
  const toolDefinitionTokens = this.estimateToolDefinitionTokens(
7959
8685
  details.localTools,
@@ -7970,15 +8696,26 @@ ${details.summaryText}
7970
8696
 
7971
8697
  Do NOT redo any of the above work.`
7972
8698
  ) : void 0;
8699
+ const sendEstimatedInputTokens = conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
8700
+ const estimatedInputTokens = sourceConversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
8701
+ const toolOutputReductionTokens = Math.max(
8702
+ 0,
8703
+ sourceConversationBreakdown.toolOutputTokens - conversationBreakdown.toolOutputTokens
8704
+ );
7973
8705
  return {
7974
- historyTokens: conversationBreakdown.historyTokens,
7975
- toolOutputTokens: conversationBreakdown.toolOutputTokens,
8706
+ historyTokens: sourceConversationBreakdown.historyTokens,
8707
+ toolOutputTokens: sourceConversationBreakdown.toolOutputTokens,
7976
8708
  currentTurnTokens,
7977
8709
  toolDefinitionTokens,
7978
8710
  ...summaryTokens ? { summaryTokens } : {},
7979
8711
  ...reservedOutputTokens ? { reservedOutputTokens } : {},
7980
8712
  ...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
7981
- estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
8713
+ ...toolOutputReductionTokens > 0 ? {
8714
+ sendEstimatedInputTokens,
8715
+ sendToolOutputTokens: conversationBreakdown.toolOutputTokens,
8716
+ toolOutputReductionTokens
8717
+ } : {},
8718
+ estimatedInputTokens
7982
8719
  };
7983
8720
  }
7984
8721
  async emitContextCompactionEvent(onContextCompaction, event) {
@@ -8018,16 +8755,33 @@ Do NOT redo any of the above work.`
8018
8755
  state,
8019
8756
  userContent,
8020
8757
  details.compactInstructions,
8021
- details.mode
8758
+ details.mode,
8759
+ details.artifactReferences
8022
8760
  );
8761
+ const summaryMessage = compactMessages[0];
8762
+ if (summaryMessage?.role === "system" && typeof summaryMessage.content === "string") {
8763
+ const existingSummaries = state.contextCompactionSummaries ?? [];
8764
+ state.contextCompactionSummaries = [
8765
+ ...existingSummaries,
8766
+ {
8767
+ id: `ctx_${sessionIndex + 1}_${existingSummaries.length + 1}`,
8768
+ sessionIndex: sessionIndex + 1,
8769
+ mode: details.mode,
8770
+ strategy: details.strategy,
8771
+ content: summaryMessage.content,
8772
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8773
+ }
8774
+ ].slice(-20);
8775
+ }
8776
+ state.contextWindowBaseIndex = state.messages?.length ?? 0;
8023
8777
  await this.emitContextCompactionEvent(details.onContextCompaction, {
8024
8778
  phase: "complete",
8025
8779
  ...baseEvent
8026
8780
  });
8027
8781
  return compactMessages;
8028
8782
  }
8029
- buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
8030
- const summary = this.generateCompactSummary(state, compactInstructions);
8783
+ buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto", artifactReferences) {
8784
+ const summary = this.generateCompactSummary(state, compactInstructions, artifactReferences);
8031
8785
  const prefix = mode === "forced" ? this.getForcedCompactionSummaryPrefix(state) : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
8032
8786
  return [
8033
8787
  {
@@ -8044,16 +8798,11 @@ Do NOT redo any of the above work.`
8044
8798
  }
8045
8799
  ];
8046
8800
  }
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
8801
  /**
8053
8802
  * Generate a compact summary of prior work for continuation context.
8054
8803
  * Used when compact mode is enabled to keep token usage low.
8055
8804
  */
8056
- generateCompactSummary(state, compactInstructions) {
8805
+ generateCompactSummary(state, compactInstructions, additionalArtifactReferences = []) {
8057
8806
  const recentSessions = (state.sessions ?? []).slice(-5);
8058
8807
  const sessionSummaries = recentSessions.map(
8059
8808
  (session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
@@ -8067,7 +8816,7 @@ Do NOT redo any of the above work.`
8067
8816
  )
8068
8817
  ).slice(0, 8);
8069
8818
  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);
8819
+ const artifactReferences = this.extractArtifactReferences(state, additionalArtifactReferences);
8071
8820
  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
8821
  const instructions = compactInstructions || this.buildDefaultCompactInstructions();
8073
8822
  return [
@@ -8282,14 +9031,23 @@ Do NOT redo any of the above work.`
8282
9031
  const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
8283
9032
  const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
8284
9033
  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
- ] : [];
9034
+ const steeringLines = steeringMessages && steeringMessages.length > 0 ? ["--- User steering (queued during the previous session) ---", ...steeringMessages, ""] : [];
8290
9035
  if (continuationContext && sessionIndex === 0) {
8291
- const replayHistoryMessages = this.sanitizeReplayHistoryMessages(
8292
- continuationContext.previousMessages
9036
+ const resumeWindow = this.resolveWindowReplay(
9037
+ state,
9038
+ continuationContext.previousMessages.length
9039
+ );
9040
+ const sourceReplayHistoryMessages = [
9041
+ ...resumeWindow.windowSummaryMessages,
9042
+ ...this.sanitizeReplayHistoryMessages(
9043
+ continuationContext.previousMessages.slice(resumeWindow.windowBase)
9044
+ )
9045
+ ];
9046
+ const replayHistoryMessages = this.deriveToolContextMessages(
9047
+ sourceReplayHistoryMessages,
9048
+ state.taskName,
9049
+ compactionOptions?.toolContextMode || "hot-tail",
9050
+ compactionOptions?.toolWindow ?? "session"
8293
9051
  );
8294
9052
  const continuationGuardrail = this.buildContinuationGuardrail(state);
8295
9053
  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 +9071,15 @@ Do NOT redo any of the above work.`
8313
9071
  content: userContent
8314
9072
  }
8315
9073
  ];
8316
- const summaryText = this.generateCompactSummary(state, compactInstructions);
9074
+ const replayArtifactReferences = this.extractArtifactReferencesFromMessages(replayHistoryMessages);
9075
+ const summaryText = this.generateCompactSummary(
9076
+ state,
9077
+ compactInstructions,
9078
+ replayArtifactReferences
9079
+ );
8317
9080
  const breakdown = this.buildContextBudgetBreakdown({
8318
9081
  historyMessages: replayHistoryMessages,
9082
+ sourceHistoryMessages: sourceReplayHistoryMessages,
8319
9083
  currentTurnContent: userContent,
8320
9084
  localTools: compactionOptions?.localTools,
8321
9085
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8341,7 +9105,8 @@ Do NOT redo any of the above work.`
8341
9105
  reservedOutputTokens: breakdown.reservedOutputTokens,
8342
9106
  breakdown,
8343
9107
  onContextCompaction: compactionOptions?.onContextCompaction,
8344
- compactInstructions
9108
+ compactInstructions,
9109
+ artifactReferences: replayArtifactReferences
8345
9110
  }
8346
9111
  ),
8347
9112
  requestContextManagement
@@ -8365,7 +9130,8 @@ Do NOT redo any of the above work.`
8365
9130
  reservedOutputTokens: breakdown.reservedOutputTokens,
8366
9131
  breakdown,
8367
9132
  onContextCompaction: compactionOptions?.onContextCompaction,
8368
- compactInstructions
9133
+ compactInstructions,
9134
+ artifactReferences: replayArtifactReferences
8369
9135
  }
8370
9136
  ),
8371
9137
  requestContextManagement
@@ -8416,23 +9182,45 @@ Do NOT redo any of the above work.`
8416
9182
  "Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
8417
9183
  ].join("\n");
8418
9184
  const MAX_HISTORY_MESSAGES = 60;
8419
- let historyMessages = this.sanitizeReplayHistoryMessages(state.messages);
8420
- if (historyMessages.length > MAX_HISTORY_MESSAGES) {
8421
- const trimmedHistory = this.trimReplayHistoryMessages(historyMessages, MAX_HISTORY_MESSAGES);
8422
- historyMessages = trimmedHistory.historyMessages;
9185
+ const { windowBase, windowSummaryMessages } = this.resolveWindowReplay(
9186
+ state,
9187
+ state.messages.length
9188
+ );
9189
+ let sourceHistoryMessages = this.sanitizeReplayHistoryMessages(
9190
+ state.messages.slice(windowBase)
9191
+ );
9192
+ if (sourceHistoryMessages.length > MAX_HISTORY_MESSAGES) {
9193
+ const trimmedHistory = this.trimReplayHistoryMessages(
9194
+ sourceHistoryMessages,
9195
+ MAX_HISTORY_MESSAGES
9196
+ );
9197
+ sourceHistoryMessages = trimmedHistory.historyMessages;
8423
9198
  if (trimmedHistory.trimmedCount > 0) {
8424
- historyMessages = [
9199
+ sourceHistoryMessages = [
8425
9200
  {
8426
9201
  role: "system",
8427
9202
  content: `[${trimmedHistory.trimmedCount} earlier messages trimmed to stay within context limits. Original task: ${(state.originalMessage || originalMessage).slice(0, 500)}]`
8428
9203
  },
8429
- ...historyMessages
9204
+ ...sourceHistoryMessages
8430
9205
  ];
8431
9206
  }
8432
9207
  }
8433
- const summaryText = this.generateCompactSummary(state, compactInstructions);
9208
+ sourceHistoryMessages = [...windowSummaryMessages, ...sourceHistoryMessages];
9209
+ const historyMessages = this.deriveToolContextMessages(
9210
+ sourceHistoryMessages,
9211
+ state.taskName,
9212
+ compactionOptions?.toolContextMode || "hot-tail",
9213
+ compactionOptions?.toolWindow ?? "session"
9214
+ );
9215
+ const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
9216
+ const summaryText = this.generateCompactSummary(
9217
+ state,
9218
+ compactInstructions,
9219
+ historyArtifactReferences
9220
+ );
8434
9221
  const breakdown = this.buildContextBudgetBreakdown({
8435
9222
  historyMessages,
9223
+ sourceHistoryMessages,
8436
9224
  currentTurnContent: continuationContent,
8437
9225
  localTools: compactionOptions?.localTools,
8438
9226
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8459,7 +9247,8 @@ Do NOT redo any of the above work.`
8459
9247
  reservedOutputTokens: breakdown.reservedOutputTokens,
8460
9248
  breakdown,
8461
9249
  onContextCompaction: compactionOptions?.onContextCompaction,
8462
- compactInstructions
9250
+ compactInstructions,
9251
+ artifactReferences: historyArtifactReferences
8463
9252
  }
8464
9253
  ),
8465
9254
  requestContextManagement
@@ -8933,6 +9722,52 @@ var BillingEndpoint = class {
8933
9722
  return this.client.get("/billing/spend-analytics", params);
8934
9723
  }
8935
9724
  };
9725
+ var AppsEndpoint = class {
9726
+ constructor(client) {
9727
+ this.client = client;
9728
+ }
9729
+ /** List apps for the authenticated owner, newest first. */
9730
+ async list() {
9731
+ return this.client.get("/apps");
9732
+ }
9733
+ /** Get an app by id, including its URL and active version pointer. */
9734
+ async get(id) {
9735
+ return this.client.get(`/apps/${id}`);
9736
+ }
9737
+ /** Create an app. A client token scoped to the app origin is auto-provisioned. */
9738
+ async create(data) {
9739
+ return this.client.post("/apps", data);
9740
+ }
9741
+ /** Update name, description, visibility, or status (suspended serves 410). */
9742
+ async update(id, data) {
9743
+ return this.client.patch(`/apps/${id}`, data);
9744
+ }
9745
+ /** Delete an app, its versions, and its hosting. Irreversible. */
9746
+ async delete(id) {
9747
+ return this.client.delete(`/apps/${id}`);
9748
+ }
9749
+ /** List an app's versions, newest first. */
9750
+ async listVersions(id) {
9751
+ return this.client.get(`/apps/${id}/versions`);
9752
+ }
9753
+ /** Upload a zipped bundle (raw application/zip body) as a new version. */
9754
+ async uploadVersion(id, zipBytes) {
9755
+ return this.client.postBinary(`/apps/${id}/versions`, zipBytes, "application/zip");
9756
+ }
9757
+ /**
9758
+ * Upload a bundle from in-memory file maps (the API zips server-side).
9759
+ * Text files in `files`, binary files base64-encoded in `filesBase64`.
9760
+ */
9761
+ async uploadVersionFiles(id, data) {
9762
+ return this.client.post(`/apps/${id}/versions`, data);
9763
+ }
9764
+ /** Activate an uploaded version (deploy or rollback). */
9765
+ async activate(id, versionId) {
9766
+ return this.client.post(`/apps/${id}/activate`, {
9767
+ versionId
9768
+ });
9769
+ }
9770
+ };
8936
9771
 
8937
9772
  // src/client.ts
8938
9773
  function isObjectRecord(value) {
@@ -8975,6 +9810,7 @@ var RuntypeClient2 = class {
8975
9810
  this.clientTokens = new ClientTokensEndpoint(this);
8976
9811
  this.agents = new AgentsEndpoint(this);
8977
9812
  this.secrets = new SecretsEndpoint(this);
9813
+ this.apps = new AppsEndpoint(this);
8978
9814
  this.schedules = new SchedulesEndpoint(this);
8979
9815
  this.surfaces = new SurfacesEndpoint(this);
8980
9816
  this.conversations = new ConversationsEndpoint(this);
@@ -9021,7 +9857,7 @@ var RuntypeClient2 = class {
9021
9857
  clearApiKey() {
9022
9858
  delete this.headers.Authorization;
9023
9859
  }
9024
- async runWithLocalTools(request, localTools, arg3, arg4) {
9860
+ async runWithLocalTools(request2, localTools, arg3, arg4) {
9025
9861
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
9026
9862
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
9027
9863
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -9035,12 +9871,12 @@ var RuntypeClient2 = class {
9035
9871
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
9036
9872
  })) : [];
9037
9873
  const modifiedRequest = {
9038
- ...request,
9874
+ ...request2,
9039
9875
  ...derivedClientTools.length > 0 ? {
9040
- clientTools: [...request.clientTools ?? [], ...derivedClientTools]
9876
+ clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
9041
9877
  } : {},
9042
9878
  options: {
9043
- ...request.options || {},
9879
+ ...request2.options || {},
9044
9880
  streamResponse: isStreaming
9045
9881
  }
9046
9882
  };
@@ -9056,13 +9892,12 @@ var RuntypeClient2 = class {
9056
9892
  onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
9057
9893
  onError: (error) => callbacks?.onError?.(error)
9058
9894
  };
9059
- const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
9060
9895
  const summary = {
9061
9896
  results: /* @__PURE__ */ new Map(),
9062
9897
  success: true
9063
9898
  };
9064
9899
  try {
9065
- for await (const event of streamEvents2(response)) {
9900
+ for await (const event of streamEvents(response)) {
9066
9901
  collectLocalToolAwait(pausedTools, event);
9067
9902
  switch (event.type) {
9068
9903
  case "flow_start":
@@ -9072,10 +9907,10 @@ var RuntypeClient2 = class {
9072
9907
  wrappedCallbacks.onStepStart?.(event);
9073
9908
  break;
9074
9909
  case "step_delta":
9075
- wrappedCallbacks.onStepDelta?.(stepDeltaText2(event), event);
9910
+ wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
9076
9911
  break;
9077
9912
  case "step_complete": {
9078
- summary.results?.set(stepDisplayName2(event), event.result);
9913
+ summary.results?.set(stepDisplayName(event), event.result);
9079
9914
  wrappedCallbacks.onStepComplete?.(event.result, event);
9080
9915
  break;
9081
9916
  }
@@ -9083,7 +9918,7 @@ var RuntypeClient2 = class {
9083
9918
  wrappedCallbacks.onFlowComplete?.(event);
9084
9919
  break;
9085
9920
  case "flow_error":
9086
- wrappedCallbacks.onError?.(new Error(flowErrorMessage2(event)));
9921
+ wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
9087
9922
  break;
9088
9923
  }
9089
9924
  }
@@ -9162,7 +9997,8 @@ var RuntypeClient2 = class {
9162
9997
  return [toolName, await handler(parameters)];
9163
9998
  } catch (error) {
9164
9999
  throw new Error(
9165
- `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`
10000
+ `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`,
10001
+ { cause: error }
9166
10002
  );
9167
10003
  }
9168
10004
  })
@@ -9226,6 +10062,21 @@ var RuntypeClient2 = class {
9226
10062
  });
9227
10063
  return transformResponse(response);
9228
10064
  }
10065
+ /**
10066
+ * POST request with a raw binary body (e.g. application/zip bundle uploads).
10067
+ */
10068
+ async postBinary(path, body, contentType) {
10069
+ const url = this.buildUrl(path);
10070
+ const headers = { ...this.headers, "Content-Type": contentType };
10071
+ const response = await this.makeRequest(url, {
10072
+ method: "POST",
10073
+ headers,
10074
+ // TS 5.7 types Uint8Array over ArrayBufferLike, which no longer
10075
+ // overlaps DOM BodyInit; the runtime value is a valid fetch body.
10076
+ body
10077
+ });
10078
+ return transformResponse(response);
10079
+ }
9229
10080
  /**
9230
10081
  * Generic request that returns raw Response for streaming
9231
10082
  */
@@ -9326,7 +10177,7 @@ var RuntypeClient2 = class {
9326
10177
  } catch (error) {
9327
10178
  if (timeoutId) clearTimeout(timeoutId);
9328
10179
  if (timeoutId && error instanceof Error && error.name === "AbortError") {
9329
- throw new Error(`Request timeout after ${this.timeout}ms`);
10180
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
9330
10181
  }
9331
10182
  throw error;
9332
10183
  }
@@ -9335,8 +10186,18 @@ var RuntypeClient2 = class {
9335
10186
  * Make HTTP request that returns raw Response (for streaming)
9336
10187
  */
9337
10188
  async makeRawRequest(url, options) {
9338
- const controller = this.timeout === null ? null : new AbortController();
10189
+ const callerSignal = options.signal ?? null;
10190
+ const controller = this.timeout === null && !callerSignal ? null : new AbortController();
9339
10191
  const timeoutId = controller && this.timeout !== null ? setTimeout(() => controller.abort(), this.timeout) : null;
10192
+ if (callerSignal && controller) {
10193
+ if (callerSignal.aborted) {
10194
+ controller.abort(callerSignal.reason);
10195
+ } else {
10196
+ callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), {
10197
+ once: true
10198
+ });
10199
+ }
10200
+ }
9340
10201
  try {
9341
10202
  const response = await fetch(url, {
9342
10203
  ...options,
@@ -9349,8 +10210,8 @@ var RuntypeClient2 = class {
9349
10210
  return response;
9350
10211
  } catch (error) {
9351
10212
  if (timeoutId) clearTimeout(timeoutId);
9352
- if (timeoutId && error instanceof Error && error.name === "AbortError") {
9353
- throw new Error(`Request timeout after ${this.timeout}ms`);
10213
+ if (timeoutId && error instanceof Error && error.name === "AbortError" && !callerSignal?.aborted) {
10214
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
9354
10215
  }
9355
10216
  throw error;
9356
10217
  }
@@ -9396,9 +10257,6 @@ function createClient(config) {
9396
10257
  return new RuntypeClient2(config);
9397
10258
  }
9398
10259
 
9399
- // src/index.ts
9400
- init_stream_utils();
9401
-
9402
10260
  // src/batch-builder.ts
9403
10261
  var BatchBuilder = class {
9404
10262
  constructor() {
@@ -9456,20 +10314,20 @@ var BatchBuilder = class {
9456
10314
  if (!this.recordType) {
9457
10315
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
9458
10316
  }
9459
- const request = {
10317
+ const request2 = {
9460
10318
  flowId: this.flowId,
9461
10319
  recordType: this.recordType
9462
10320
  };
9463
10321
  if (Object.keys(this.batchOptions).length > 0) {
9464
- request.options = this.batchOptions;
10322
+ request2.options = this.batchOptions;
9465
10323
  }
9466
10324
  if (this.filterConfig) {
9467
- request.filter = this.filterConfig;
10325
+ request2.filter = this.filterConfig;
9468
10326
  }
9469
10327
  if (this.limitConfig !== void 0) {
9470
- request.limit = this.limitConfig;
10328
+ request2.limit = this.limitConfig;
9471
10329
  }
9472
- return request;
10330
+ return request2;
9473
10331
  }
9474
10332
  /**
9475
10333
  * Execute the batch operation
@@ -9626,32 +10484,32 @@ var EvalBuilder = class {
9626
10484
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
9627
10485
  );
9628
10486
  }
9629
- const request = {};
10487
+ const request2 = {};
9630
10488
  if (this.flowId) {
9631
- request.flowId = this.flowId;
10489
+ request2.flowId = this.flowId;
9632
10490
  } else if (this.virtualFlow) {
9633
- request.flow = this.virtualFlow;
10491
+ request2.flow = this.virtualFlow;
9634
10492
  }
9635
10493
  if (this.recordType) {
9636
- request.recordType = this.recordType;
10494
+ request2.recordType = this.recordType;
9637
10495
  } else if (this.inlineRecords) {
9638
- request.records = this.inlineRecords;
10496
+ request2.records = this.inlineRecords;
9639
10497
  }
9640
10498
  if (this.modelOverrides) {
9641
- request.modelOverrides = this.modelOverrides;
10499
+ request2.modelOverrides = this.modelOverrides;
9642
10500
  } else if (this.modelConfigs) {
9643
- request.modelConfigs = this.modelConfigs;
10501
+ request2.modelConfigs = this.modelConfigs;
9644
10502
  }
9645
10503
  if (Object.keys(this.evalOptions).length > 0) {
9646
- request.options = this.evalOptions;
10504
+ request2.options = this.evalOptions;
9647
10505
  }
9648
10506
  if (this.filterConfig) {
9649
- request.filter = this.filterConfig;
10507
+ request2.filter = this.filterConfig;
9650
10508
  }
9651
10509
  if (this.limitConfig !== void 0) {
9652
- request.limit = this.limitConfig;
10510
+ request2.limit = this.limitConfig;
9653
10511
  }
9654
- return request;
10512
+ return request2;
9655
10513
  }
9656
10514
  /**
9657
10515
  * Execute the evaluation
@@ -10117,10 +10975,14 @@ var STEP_TYPE_TO_METHOD = {
10117
10975
  "memory-summary": "memorySummary"
10118
10976
  };
10119
10977
  export {
10978
+ AgentDriftError,
10979
+ AgentEnsureConflictError,
10120
10980
  AgentVersionsEndpoint,
10121
10981
  AgentsEndpoint,
10982
+ AgentsNamespace,
10122
10983
  AnalyticsEndpoint,
10123
10984
  ApiKeysEndpoint,
10985
+ AppsEndpoint,
10124
10986
  BatchBuilder,
10125
10987
  BatchesNamespace,
10126
10988
  BillingEndpoint,
@@ -10137,12 +10999,15 @@ export {
10137
10999
  EvalRunner,
10138
11000
  EvalsNamespace,
10139
11001
  FlowBuilder,
11002
+ FlowDriftError,
11003
+ FlowEnsureConflictError,
10140
11004
  FlowResult,
10141
11005
  FlowStepsEndpoint,
10142
11006
  FlowVersionsEndpoint,
10143
11007
  FlowsEndpoint,
10144
11008
  FlowsNamespace,
10145
11009
  IntegrationsEndpoint,
11010
+ LEDGER_ARTIFACT_LINE_PREFIX,
10146
11011
  LogsEndpoint,
10147
11012
  ModelConfigsEndpoint,
10148
11013
  PromptRunner,
@@ -10166,19 +11031,29 @@ export {
10166
11031
  applyGeneratedRuntimeToolProposalToDispatchRequest,
10167
11032
  attachRuntimeToolsToDispatchRequest,
10168
11033
  buildGeneratedRuntimeToolGateOutput,
11034
+ buildLedgerOffloadReference,
11035
+ buildSendViewOffloadMarker,
11036
+ computeAgentContentHash,
11037
+ computeFlowContentHash,
10169
11038
  createClient,
10170
11039
  createExternalTool,
10171
11040
  defaultWorkflow,
11041
+ defineAgent,
11042
+ defineFlow,
10172
11043
  deployWorkflow,
10173
11044
  evaluateGeneratedRuntimeToolProposal,
11045
+ extractDeclaredToolResultChars,
10174
11046
  gameWorkflow,
10175
11047
  getDefaultPlanPath,
10176
11048
  getLikelySupportingCandidatePaths,
10177
11049
  isDiscoveryToolName,
10178
11050
  isMarathonArtifactPath,
10179
11051
  isPreservationSensitiveTask,
11052
+ normalizeAgentDefinition,
10180
11053
  normalizeCandidatePath,
10181
11054
  parseFinalBuffer,
11055
+ parseLedgerArtifactRelativePath,
11056
+ parseOffloadedOutputId,
10182
11057
  parseSSEChunk,
10183
11058
  processStream,
10184
11059
  sanitizeTaskSlug,