@runtypelabs/sdk 4.8.1 → 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
+ }
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 });
1421
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,45 +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 = "";
6789
+ let lastKnownCost = 0;
6790
+ let lastKnownTokens;
6791
+ let lastSeenExecutionId = "";
6220
6792
  let pauseCount = 0;
6221
6793
  let discoveryPauseCount = 0;
6222
6794
  let consecutiveDiscoveryPauseCount = 0;
6223
6795
  const toolNameCounts = {};
6224
6796
  let recentActionKeys = [];
6225
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;
6226
6830
  while (true) {
6227
6831
  let pausedEvent = null;
6228
6832
  let completeEvent = null;
6229
- await processAgentStream(currentBody, {
6230
- ...callbacks,
6231
- onTurnDelta: (event) => {
6232
- if (event.contentType === "text") {
6233
- accumulatedOutput += event.delta;
6234
- }
6235
- callbacks?.onTurnDelta?.(event);
6236
- },
6237
- onAgentPaused: (event) => {
6238
- pausedEvent = event;
6239
- callbacks?.onAgentPaused?.(event);
6240
- },
6241
- onAgentComplete: (event) => {
6242
- if (!event.finalOutput && accumulatedOutput) {
6243
- 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);
6244
6865
  }
6245
- completeEvent = event;
6246
- callbacks?.onAgentComplete?.(event);
6247
- }
6248
- });
6866
+ });
6867
+ } catch (error) {
6868
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6869
+ throw error;
6870
+ }
6249
6871
  if (completeEvent) return { completeEvent, toolMessages };
6250
6872
  if (pausedEvent) {
6251
6873
  const { toolName, toolId, parameters, executionId } = pausedEvent;
6874
+ lastSeenExecutionId = executionId;
6252
6875
  const toolDef = localTools[toolName];
6253
6876
  if (!toolDef) {
6254
6877
  throw new Error(`Local tool "${toolName}" required but not provided`);
@@ -6363,15 +6986,68 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6363
6986
  callbacks?.onAgentComplete?.(forcedCompleteEvent);
6364
6987
  return { completeEvent: forcedCompleteEvent, toolMessages };
6365
6988
  }
6366
- const resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
6367
- method: "POST",
6368
- body: JSON.stringify({
6989
+ if (abortSignal?.aborted) {
6990
+ callbacks?.onLocalToolExecutionComplete?.({
6369
6991
  executionId,
6370
- toolOutputs: { [toolName]: toolResult },
6371
- streamResponse: true,
6372
- debugMode: data.debugMode
6373
- })
6374
- });
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
+ }
7002
+ if (options?.shouldInterrupt?.()) {
7003
+ callbacks?.onLocalToolExecutionComplete?.({
7004
+ executionId,
7005
+ toolCallId: toolId,
7006
+ toolName,
7007
+ parameters: parsedParams,
7008
+ result: toolResult,
7009
+ success: true,
7010
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
7011
+ durationMs: Date.now() - localExecutionStartedAtMs
7012
+ });
7013
+ const interruptCompleteEvent = {
7014
+ type: "agent_complete",
7015
+ executionId,
7016
+ seq: 0,
7017
+ agentId: id,
7018
+ success: true,
7019
+ iterations: 1,
7020
+ stopReason: "end_turn",
7021
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
7022
+ // Carry the spend observed so far so the interrupted session's
7023
+ // cost still lands in marathon totals and budget accounting
7024
+ totalCost: lastKnownCost,
7025
+ ...lastKnownTokens ? { totalTokens: lastKnownTokens } : {},
7026
+ finalOutput: [
7027
+ accumulatedOutput.trim(),
7028
+ "Session ended early: user steering was queued and will be delivered at the start of the next session."
7029
+ ].filter(Boolean).join("\n\n"),
7030
+ duration: 0
7031
+ };
7032
+ callbacks?.onAgentComplete?.(interruptCompleteEvent);
7033
+ return { completeEvent: interruptCompleteEvent, toolMessages };
7034
+ }
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
+ }
6375
7051
  if (!resumeResponse.ok) {
6376
7052
  const error = await resumeResponse.json().catch(() => ({ error: "Unknown error" }));
6377
7053
  throw new Error(error.error || `HTTP ${resumeResponse.status}`);
@@ -6389,6 +7065,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6389
7065
  currentBody = resumeResponse.body;
6390
7066
  continue;
6391
7067
  }
7068
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6392
7069
  return null;
6393
7070
  }
6394
7071
  }
@@ -6660,7 +7337,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6660
7337
  reasons.push("Best candidate file has not been verified (read back after writing)");
6661
7338
  }
6662
7339
  if (state.verificationRequired && !state.lastVerificationPassed && !trace.verificationPassed) {
6663
- 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
+ );
6664
7343
  }
6665
7344
  return reasons.length > 0 ? reasons.join("; ") : "Completion gates not satisfied for the current workflow phase";
6666
7345
  }
@@ -6703,32 +7382,71 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6703
7382
  );
6704
7383
  }
6705
7384
  /**
6706
- * Compact old tool results based on context mode and window setting.
6707
- * 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.
7392
+ */
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.
6708
7412
  */
6709
- compactToolResults(messages, newToolMessages, taskName, mode, window) {
6710
- if (mode === "full-inline") return;
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];
6711
7420
  if (window === "session") {
6712
- 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;
6713
7429
  if (msg.role === "tool" && msg.toolResults) {
6714
- msg.toolResults = msg.toolResults.map((tr) => this.compactOneResult(tr, taskName, mode));
7430
+ view[index] = maskMessage(msg);
6715
7431
  }
6716
7432
  }
6717
7433
  } else {
6718
- const newToolResultCount = newToolMessages.filter((m) => m.role === "tool").length;
6719
- const keepInlineFromExisting = Math.max(0, window - newToolResultCount);
6720
7434
  const toolResultIndices = [];
6721
- for (let i = 0; i < messages.length; i++) {
6722
- 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) {
6723
7438
  toolResultIndices.push(i);
6724
7439
  }
6725
7440
  }
6726
- const compactUpTo = toolResultIndices.length - keepInlineFromExisting;
7441
+ const compactUpTo = toolResultIndices.length - window;
6727
7442
  for (let j = 0; j < compactUpTo && j < toolResultIndices.length; j++) {
6728
- const msg = messages[toolResultIndices[j]];
6729
- 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);
6730
7447
  }
6731
7448
  }
7449
+ return view;
6732
7450
  }
6733
7451
  compactOneResult(tr, taskName, mode) {
6734
7452
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
@@ -6764,10 +7482,20 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6764
7482
  }
6765
7483
  const slug = this.sanitizeTaskSlug(taskName || "task");
6766
7484
  const dir = `.runtype/marathons/${slug}/tool-outputs`;
6767
- fs.mkdirSync(dir, { recursive: true });
6768
7485
  const filePath = `${dir}/${toolCallId}.txt`;
6769
- fs.writeFileSync(filePath, resultStr, "utf-8");
6770
- 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
+ });
6771
7499
  }
6772
7500
  getDefaultPlanPath(taskName) {
6773
7501
  return getDefaultPlanPath(taskName);
@@ -6889,6 +7617,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6889
7617
  const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
6890
7618
  const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
6891
7619
  const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
7620
+ const contextCompactionSummaries = (resumeState.contextCompactionSummaries ?? []).slice(-20);
6892
7621
  const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
6893
7622
  const bestCandidatePath = normalizedBestCandidatePath && !this.isMarathonArtifactPath(normalizedBestCandidatePath) ? normalizedBestCandidatePath : [...candidatePaths, ...recentReadPaths].sort(
6894
7623
  (left, right) => this.scoreCandidatePath(right) - this.scoreCandidatePath(left)
@@ -6908,10 +7637,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6908
7637
  bestCandidateVerified: Boolean(resumeState.bestCandidateVerified),
6909
7638
  ...resumeState.verificationRequired !== void 0 ? { verificationRequired: resumeState.verificationRequired } : {},
6910
7639
  lastVerificationPassed: Boolean(resumeState.lastVerificationPassed),
6911
- ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? { consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions } : {},
7640
+ ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? {
7641
+ consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions
7642
+ } : {},
6912
7643
  ...resumeState.isCreationTask !== void 0 ? { isCreationTask: resumeState.isCreationTask } : {},
6913
7644
  ...resumeState.workflowVariant !== void 0 ? { workflowVariant: resumeState.workflowVariant } : {},
6914
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) } : {},
6915
7648
  ...typeof resumeState.outputRoot === "string" && resumeState.outputRoot.trim() ? { outputRoot: resumeState.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/") } : {}
6916
7649
  };
6917
7650
  }
@@ -7377,8 +8110,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7377
8110
  } : {},
7378
8111
  ...seededResumeState?.candidatePaths ? { candidatePaths: seededResumeState.candidatePaths } : {},
7379
8112
  ...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
7380
- ...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 } : {}
7381
8116
  };
8117
+ if (options.previousMessages && options.previousMessages.length > 0) {
8118
+ state.messages = options.previousMessages.map((message) => structuredClone(message));
8119
+ }
7382
8120
  state.workflowVariant = classifiedVariant;
7383
8121
  state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
7384
8122
  state.outputRoot = seededResumeState?.outputRoot ?? (state.isCreationTask ? "public/" : void 0);
@@ -7411,6 +8149,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7411
8149
  }
7412
8150
  }
7413
8151
  for (let session = 0; session < maxSessions; session++) {
8152
+ if (options.abortSignal?.aborted) {
8153
+ state.status = "paused";
8154
+ break;
8155
+ }
7414
8156
  const phaseAtSessionStart = state.workflowPhase;
7415
8157
  const sessionTrace = this.createEmptyToolTrace();
7416
8158
  const sessionLocalTools = this.wrapLocalToolsForTrace(
@@ -7428,6 +8170,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7428
8170
  if (session === 0 && !options.previousMessages) {
7429
8171
  state.originalMessage = options.message;
7430
8172
  }
8173
+ const queuedSteeringMessages = options.getQueuedUserMessages?.() ?? [];
7431
8174
  const preparedSession = await this.prepareSessionContext(
7432
8175
  options.message,
7433
8176
  state,
@@ -7446,8 +8189,11 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7446
8189
  localTools: options.localTools,
7447
8190
  builtinToolSchemas,
7448
8191
  onContextCompaction: options.onContextCompaction,
7449
- onContextNotice: options.onContextNotice
7450
- }
8192
+ onContextNotice: options.onContextNotice,
8193
+ toolContextMode: options.toolContextMode || "hot-tail",
8194
+ toolWindow: options.toolWindow ?? "session"
8195
+ },
8196
+ queuedSteeringMessages
7451
8197
  );
7452
8198
  const { messages, requestContextManagement, pendingNativeCompactionEvent } = preparedSession;
7453
8199
  let sessionResult;
@@ -7474,7 +8220,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7474
8220
  sessionLocalTools || options.localTools,
7475
8221
  sessionCallbacks,
7476
8222
  {
7477
- onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow)
8223
+ onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow),
8224
+ shouldInterrupt: options.hasQueuedUserMessages,
8225
+ ...options.abortSignal ? { abortSignal: options.abortSignal } : {}
7478
8226
  },
7479
8227
  state.taskName
7480
8228
  );
@@ -7623,22 +8371,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7623
8371
  }
7624
8372
  }
7625
8373
  if (!state.messages) state.messages = [];
7626
- if (this.isCompactHistoryMessageSet(messages)) {
7627
- state.messages = [...messages];
7628
- } else if (state.messages.length > 0 && messages.length > state.messages.length) {
7629
- const newMessages = messages.slice(state.messages.length);
7630
- state.messages.push(...newMessages);
7631
- } 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) {
7632
8378
  state.messages.push(...messages);
7633
8379
  }
7634
8380
  if (sessionToolMessages.length > 0) {
7635
- this.compactToolResults(
7636
- state.messages,
7637
- sessionToolMessages,
7638
- state.taskName,
7639
- options.toolContextMode || "hot-tail",
7640
- options.toolWindow ?? "session"
7641
- );
7642
8381
  state.messages.push(...sessionToolMessages);
7643
8382
  }
7644
8383
  const assistantContent = effectiveSessionOutput || `[Session ${session + 1} completed (${sessionResult.stopReason}). No text output captured.]`;
@@ -7658,7 +8397,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7658
8397
  workflow
7659
8398
  );
7660
8399
  if (detectedTaskCompletion && !acceptedTaskCompletion) {
7661
- state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(state, sessionTrace);
8400
+ state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(
8401
+ state,
8402
+ sessionTrace
8403
+ );
7662
8404
  if (state.verificationRequired && !state.lastVerificationPassed && !sessionTrace.verificationPassed && !sessionTrace.verificationAttempted) {
7663
8405
  state.consecutiveBlockedVerificationSessions = (state.consecutiveBlockedVerificationSessions || 0) + 1;
7664
8406
  if ((state.consecutiveBlockedVerificationSessions || 0) >= 2) {
@@ -7677,24 +8419,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7677
8419
  }
7678
8420
  if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
7679
8421
  const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
7680
- const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(state, sessionTrace) : true;
8422
+ const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(
8423
+ state,
8424
+ sessionTrace
8425
+ ) : true;
7681
8426
  if (gatesSatisfied) {
7682
8427
  state.status = "complete";
7683
8428
  }
7684
8429
  } else if (sessionResult.stopReason === "error") {
7685
8430
  if (_AgentsEndpoint.isRetryableSessionError(sessionResult.error) && consecutiveServerNetworkErrors < maxServerNetworkRetries) {
7686
8431
  consecutiveServerNetworkErrors++;
7687
- const delayMs = Math.min(
7688
- 5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1),
7689
- 3e4
7690
- );
8432
+ const delayMs = Math.min(5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1), 3e4);
7691
8433
  const delaySec = Math.round(delayMs / 1e3);
7692
8434
  await this.emitContextNotice(options.onContextNotice, {
7693
8435
  kind: "server_network_retry",
7694
8436
  sessionIndex: session,
7695
8437
  message: `Server network error: ${sessionResult.error}. Retrying in ${delaySec}s (attempt ${consecutiveServerNetworkErrors}/${maxServerNetworkRetries})...`
7696
8438
  });
7697
- await new Promise((resolve) => setTimeout(resolve, delayMs));
8439
+ await sleepWithAbort(delayMs, options.abortSignal);
7698
8440
  } else {
7699
8441
  state.status = "error";
7700
8442
  }
@@ -7709,6 +8451,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7709
8451
  } else if (session + 1 >= maxSessions) {
7710
8452
  state.status = "max_sessions";
7711
8453
  }
8454
+ if (options.abortSignal?.aborted) {
8455
+ state.status = "paused";
8456
+ }
7712
8457
  if (options.trackProgress) {
7713
8458
  recordId = await this.syncProgressRecord(state, recordId);
7714
8459
  }
@@ -7785,6 +8530,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7785
8530
  return 0;
7786
8531
  }
7787
8532
  }
8533
+ extractDeclaredToolResultChars(value) {
8534
+ return extractDeclaredToolResultChars(value);
8535
+ }
7788
8536
  estimateMessageContentTokens(content) {
7789
8537
  if (typeof content === "string") return this.estimateTextTokens(content);
7790
8538
  return content.reduce((total, part) => {
@@ -7803,12 +8551,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7803
8551
  0
7804
8552
  );
7805
8553
  }
7806
- estimateToolResultTokens(toolResults) {
8554
+ estimateToolResultTokens(toolResults, options) {
7807
8555
  if (!toolResults || toolResults.length === 0) return 0;
7808
- return toolResults.reduce(
7809
- (sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
7810
- 0
7811
- );
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);
7812
8562
  }
7813
8563
  estimateMessageTokens(message) {
7814
8564
  return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
@@ -7816,13 +8566,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7816
8566
  estimateConversationTokens(messages) {
7817
8567
  return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
7818
8568
  }
7819
- estimateConversationBreakdown(messages) {
8569
+ estimateConversationBreakdown(messages, options) {
7820
8570
  let historyTokens = 0;
7821
8571
  let toolOutputTokens = 0;
7822
8572
  for (const message of messages) {
7823
8573
  const contentTokens = this.estimateMessageContentTokens(message.content);
7824
8574
  const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
7825
- const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
8575
+ const toolResultTokens = this.estimateToolResultTokens(message.toolResults, {
8576
+ useDeclaredSize: options?.useDeclaredToolResultSizes
8577
+ });
7826
8578
  const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
7827
8579
  if (message.role === "tool") {
7828
8580
  toolOutputTokens += messageTotal;
@@ -7876,13 +8628,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7876
8628
  const compactInstructions = config.compactInstructions;
7877
8629
  return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
7878
8630
  }
7879
- extractArtifactReferences(state) {
8631
+ extractArtifactReferencesFromMessages(messages = []) {
7880
8632
  const references = /* @__PURE__ */ new Set();
7881
8633
  const offloadPrefix = "[Output saved to ";
7882
- 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) {
7883
8637
  if (!message.toolResults) continue;
7884
8638
  for (const toolResult of message.toolResults) {
7885
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
+ }
7886
8649
  let startIndex = 0;
7887
8650
  while (startIndex < toolResult.result.length) {
7888
8651
  const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
@@ -7900,6 +8663,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7900
8663
  }
7901
8664
  }
7902
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
+ ]);
7903
8673
  if (state.planPath) {
7904
8674
  references.add(state.planPath);
7905
8675
  }
@@ -7907,6 +8677,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7907
8677
  }
7908
8678
  buildContextBudgetBreakdown(details) {
7909
8679
  const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
8680
+ const sourceConversationBreakdown = details.sourceHistoryMessages ? this.estimateConversationBreakdown(details.sourceHistoryMessages, {
8681
+ useDeclaredToolResultSizes: true
8682
+ }) : conversationBreakdown;
7910
8683
  const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
7911
8684
  const toolDefinitionTokens = this.estimateToolDefinitionTokens(
7912
8685
  details.localTools,
@@ -7923,15 +8696,26 @@ ${details.summaryText}
7923
8696
 
7924
8697
  Do NOT redo any of the above work.`
7925
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
+ );
7926
8705
  return {
7927
- historyTokens: conversationBreakdown.historyTokens,
7928
- toolOutputTokens: conversationBreakdown.toolOutputTokens,
8706
+ historyTokens: sourceConversationBreakdown.historyTokens,
8707
+ toolOutputTokens: sourceConversationBreakdown.toolOutputTokens,
7929
8708
  currentTurnTokens,
7930
8709
  toolDefinitionTokens,
7931
8710
  ...summaryTokens ? { summaryTokens } : {},
7932
8711
  ...reservedOutputTokens ? { reservedOutputTokens } : {},
7933
8712
  ...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
7934
- estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
8713
+ ...toolOutputReductionTokens > 0 ? {
8714
+ sendEstimatedInputTokens,
8715
+ sendToolOutputTokens: conversationBreakdown.toolOutputTokens,
8716
+ toolOutputReductionTokens
8717
+ } : {},
8718
+ estimatedInputTokens
7935
8719
  };
7936
8720
  }
7937
8721
  async emitContextCompactionEvent(onContextCompaction, event) {
@@ -7971,16 +8755,33 @@ Do NOT redo any of the above work.`
7971
8755
  state,
7972
8756
  userContent,
7973
8757
  details.compactInstructions,
7974
- details.mode
8758
+ details.mode,
8759
+ details.artifactReferences
7975
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;
7976
8777
  await this.emitContextCompactionEvent(details.onContextCompaction, {
7977
8778
  phase: "complete",
7978
8779
  ...baseEvent
7979
8780
  });
7980
8781
  return compactMessages;
7981
8782
  }
7982
- buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
7983
- const summary = this.generateCompactSummary(state, compactInstructions);
8783
+ buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto", artifactReferences) {
8784
+ const summary = this.generateCompactSummary(state, compactInstructions, artifactReferences);
7984
8785
  const prefix = mode === "forced" ? this.getForcedCompactionSummaryPrefix(state) : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
7985
8786
  return [
7986
8787
  {
@@ -7997,16 +8798,11 @@ Do NOT redo any of the above work.`
7997
8798
  }
7998
8799
  ];
7999
8800
  }
8000
- isCompactHistoryMessageSet(messages) {
8001
- if (messages.length === 0) return false;
8002
- const firstMessage = messages[0];
8003
- 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));
8004
- }
8005
8801
  /**
8006
8802
  * Generate a compact summary of prior work for continuation context.
8007
8803
  * Used when compact mode is enabled to keep token usage low.
8008
8804
  */
8009
- generateCompactSummary(state, compactInstructions) {
8805
+ generateCompactSummary(state, compactInstructions, additionalArtifactReferences = []) {
8010
8806
  const recentSessions = (state.sessions ?? []).slice(-5);
8011
8807
  const sessionSummaries = recentSessions.map(
8012
8808
  (session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
@@ -8020,7 +8816,7 @@ Do NOT redo any of the above work.`
8020
8816
  )
8021
8817
  ).slice(0, 8);
8022
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.";
8023
- const artifactReferences = this.extractArtifactReferences(state);
8819
+ const artifactReferences = this.extractArtifactReferences(state, additionalArtifactReferences);
8024
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.`;
8025
8821
  const instructions = compactInstructions || this.buildDefaultCompactInstructions();
8026
8822
  return [
@@ -8150,7 +8946,7 @@ Do NOT redo any of the above work.`
8150
8946
  );
8151
8947
  return prepared.messages;
8152
8948
  }
8153
- async prepareSessionContext(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions) {
8949
+ async prepareSessionContext(originalMessage, state, sessionIndex, maxSessions, localToolNames, continuationContext, workflow, builtinToolIds, compactionOptions, steeringMessages) {
8154
8950
  const wf = workflow ?? defaultWorkflow;
8155
8951
  const compactInstructions = compactionOptions?.compactInstructions;
8156
8952
  const resolvedStrategy = continuationContext?.compact ? "summary_fallback" : this.resolveCompactStrategy(compactionOptions?.compactStrategy, compactionOptions?.model);
@@ -8235,9 +9031,23 @@ Do NOT redo any of the above work.`
8235
9031
  const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
8236
9032
  const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
8237
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.`;
9034
+ const steeringLines = steeringMessages && steeringMessages.length > 0 ? ["--- User steering (queued during the previous session) ---", ...steeringMessages, ""] : [];
8238
9035
  if (continuationContext && sessionIndex === 0) {
8239
- const replayHistoryMessages = this.sanitizeReplayHistoryMessages(
8240
- 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"
8241
9051
  );
8242
9052
  const continuationGuardrail = this.buildContinuationGuardrail(state);
8243
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.";
@@ -8245,6 +9055,7 @@ Do NOT redo any of the above work.`
8245
9055
  const userContent = [
8246
9056
  continuationGuardrail,
8247
9057
  "",
9058
+ ...steeringLines,
8248
9059
  userMessage,
8249
9060
  phaseBlock,
8250
9061
  toolsBlock,
@@ -8260,9 +9071,15 @@ Do NOT redo any of the above work.`
8260
9071
  content: userContent
8261
9072
  }
8262
9073
  ];
8263
- const summaryText = this.generateCompactSummary(state, compactInstructions);
9074
+ const replayArtifactReferences = this.extractArtifactReferencesFromMessages(replayHistoryMessages);
9075
+ const summaryText = this.generateCompactSummary(
9076
+ state,
9077
+ compactInstructions,
9078
+ replayArtifactReferences
9079
+ );
8264
9080
  const breakdown = this.buildContextBudgetBreakdown({
8265
9081
  historyMessages: replayHistoryMessages,
9082
+ sourceHistoryMessages: sourceReplayHistoryMessages,
8266
9083
  currentTurnContent: userContent,
8267
9084
  localTools: compactionOptions?.localTools,
8268
9085
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8288,7 +9105,8 @@ Do NOT redo any of the above work.`
8288
9105
  reservedOutputTokens: breakdown.reservedOutputTokens,
8289
9106
  breakdown,
8290
9107
  onContextCompaction: compactionOptions?.onContextCompaction,
8291
- compactInstructions
9108
+ compactInstructions,
9109
+ artifactReferences: replayArtifactReferences
8292
9110
  }
8293
9111
  ),
8294
9112
  requestContextManagement
@@ -8312,7 +9130,8 @@ Do NOT redo any of the above work.`
8312
9130
  reservedOutputTokens: breakdown.reservedOutputTokens,
8313
9131
  breakdown,
8314
9132
  onContextCompaction: compactionOptions?.onContextCompaction,
8315
- compactInstructions
9133
+ compactInstructions,
9134
+ artifactReferences: replayArtifactReferences
8316
9135
  }
8317
9136
  ),
8318
9137
  requestContextManagement
@@ -8347,6 +9166,7 @@ Do NOT redo any of the above work.`
8347
9166
  const recoveryMessage2 = this.buildStuckTurnRecoveryMessage(state, wf);
8348
9167
  const rejectionNotice = state.lastCompletionRejectionReason ? `TASK_COMPLETE was rejected because: ${state.lastCompletionRejectionReason}. Address this before signaling completion again.` : void 0;
8349
9168
  const continuationContent = [
9169
+ ...steeringLines,
8350
9170
  "Continue the task.",
8351
9171
  phaseBlock,
8352
9172
  toolsBlock,
@@ -8362,23 +9182,45 @@ Do NOT redo any of the above work.`
8362
9182
  "Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
8363
9183
  ].join("\n");
8364
9184
  const MAX_HISTORY_MESSAGES = 60;
8365
- let historyMessages = this.sanitizeReplayHistoryMessages(state.messages);
8366
- if (historyMessages.length > MAX_HISTORY_MESSAGES) {
8367
- const trimmedHistory = this.trimReplayHistoryMessages(historyMessages, MAX_HISTORY_MESSAGES);
8368
- 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;
8369
9198
  if (trimmedHistory.trimmedCount > 0) {
8370
- historyMessages = [
9199
+ sourceHistoryMessages = [
8371
9200
  {
8372
9201
  role: "system",
8373
9202
  content: `[${trimmedHistory.trimmedCount} earlier messages trimmed to stay within context limits. Original task: ${(state.originalMessage || originalMessage).slice(0, 500)}]`
8374
9203
  },
8375
- ...historyMessages
9204
+ ...sourceHistoryMessages
8376
9205
  ];
8377
9206
  }
8378
9207
  }
8379
- 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
+ );
8380
9221
  const breakdown = this.buildContextBudgetBreakdown({
8381
9222
  historyMessages,
9223
+ sourceHistoryMessages,
8382
9224
  currentTurnContent: continuationContent,
8383
9225
  localTools: compactionOptions?.localTools,
8384
9226
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8405,7 +9247,8 @@ Do NOT redo any of the above work.`
8405
9247
  reservedOutputTokens: breakdown.reservedOutputTokens,
8406
9248
  breakdown,
8407
9249
  onContextCompaction: compactionOptions?.onContextCompaction,
8408
- compactInstructions
9250
+ compactInstructions,
9251
+ artifactReferences: historyArtifactReferences
8409
9252
  }
8410
9253
  ),
8411
9254
  requestContextManagement
@@ -8420,6 +9263,7 @@ Do NOT redo any of the above work.`
8420
9263
  const recoveryMessage = this.buildStuckTurnRecoveryMessage(state, wf);
8421
9264
  const fallbackRejectionNotice = state.lastCompletionRejectionReason ? `TASK_COMPLETE was rejected because: ${state.lastCompletionRejectionReason}. Address this before signaling completion again.` : void 0;
8422
9265
  const content = [
9266
+ ...steeringLines,
8423
9267
  originalMessage,
8424
9268
  phaseBlock,
8425
9269
  toolsBlock,
@@ -8878,6 +9722,52 @@ var BillingEndpoint = class {
8878
9722
  return this.client.get("/billing/spend-analytics", params);
8879
9723
  }
8880
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
+ };
8881
9771
 
8882
9772
  // src/client.ts
8883
9773
  function isObjectRecord(value) {
@@ -8920,6 +9810,7 @@ var RuntypeClient2 = class {
8920
9810
  this.clientTokens = new ClientTokensEndpoint(this);
8921
9811
  this.agents = new AgentsEndpoint(this);
8922
9812
  this.secrets = new SecretsEndpoint(this);
9813
+ this.apps = new AppsEndpoint(this);
8923
9814
  this.schedules = new SchedulesEndpoint(this);
8924
9815
  this.surfaces = new SurfacesEndpoint(this);
8925
9816
  this.conversations = new ConversationsEndpoint(this);
@@ -8966,7 +9857,7 @@ var RuntypeClient2 = class {
8966
9857
  clearApiKey() {
8967
9858
  delete this.headers.Authorization;
8968
9859
  }
8969
- async runWithLocalTools(request, localTools, arg3, arg4) {
9860
+ async runWithLocalTools(request2, localTools, arg3, arg4) {
8970
9861
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
8971
9862
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
8972
9863
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -8980,12 +9871,12 @@ var RuntypeClient2 = class {
8980
9871
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
8981
9872
  })) : [];
8982
9873
  const modifiedRequest = {
8983
- ...request,
9874
+ ...request2,
8984
9875
  ...derivedClientTools.length > 0 ? {
8985
- clientTools: [...request.clientTools ?? [], ...derivedClientTools]
9876
+ clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
8986
9877
  } : {},
8987
9878
  options: {
8988
- ...request.options || {},
9879
+ ...request2.options || {},
8989
9880
  streamResponse: isStreaming
8990
9881
  }
8991
9882
  };
@@ -9001,13 +9892,12 @@ var RuntypeClient2 = class {
9001
9892
  onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
9002
9893
  onError: (error) => callbacks?.onError?.(error)
9003
9894
  };
9004
- const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
9005
9895
  const summary = {
9006
9896
  results: /* @__PURE__ */ new Map(),
9007
9897
  success: true
9008
9898
  };
9009
9899
  try {
9010
- for await (const event of streamEvents2(response)) {
9900
+ for await (const event of streamEvents(response)) {
9011
9901
  collectLocalToolAwait(pausedTools, event);
9012
9902
  switch (event.type) {
9013
9903
  case "flow_start":
@@ -9017,10 +9907,10 @@ var RuntypeClient2 = class {
9017
9907
  wrappedCallbacks.onStepStart?.(event);
9018
9908
  break;
9019
9909
  case "step_delta":
9020
- wrappedCallbacks.onStepDelta?.(stepDeltaText2(event), event);
9910
+ wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
9021
9911
  break;
9022
9912
  case "step_complete": {
9023
- summary.results?.set(stepDisplayName2(event), event.result);
9913
+ summary.results?.set(stepDisplayName(event), event.result);
9024
9914
  wrappedCallbacks.onStepComplete?.(event.result, event);
9025
9915
  break;
9026
9916
  }
@@ -9028,7 +9918,7 @@ var RuntypeClient2 = class {
9028
9918
  wrappedCallbacks.onFlowComplete?.(event);
9029
9919
  break;
9030
9920
  case "flow_error":
9031
- wrappedCallbacks.onError?.(new Error(flowErrorMessage2(event)));
9921
+ wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
9032
9922
  break;
9033
9923
  }
9034
9924
  }
@@ -9107,7 +9997,8 @@ var RuntypeClient2 = class {
9107
9997
  return [toolName, await handler(parameters)];
9108
9998
  } catch (error) {
9109
9999
  throw new Error(
9110
- `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 }
9111
10002
  );
9112
10003
  }
9113
10004
  })
@@ -9171,6 +10062,21 @@ var RuntypeClient2 = class {
9171
10062
  });
9172
10063
  return transformResponse(response);
9173
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
+ }
9174
10080
  /**
9175
10081
  * Generic request that returns raw Response for streaming
9176
10082
  */
@@ -9271,7 +10177,7 @@ var RuntypeClient2 = class {
9271
10177
  } catch (error) {
9272
10178
  if (timeoutId) clearTimeout(timeoutId);
9273
10179
  if (timeoutId && error instanceof Error && error.name === "AbortError") {
9274
- throw new Error(`Request timeout after ${this.timeout}ms`);
10180
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
9275
10181
  }
9276
10182
  throw error;
9277
10183
  }
@@ -9280,8 +10186,18 @@ var RuntypeClient2 = class {
9280
10186
  * Make HTTP request that returns raw Response (for streaming)
9281
10187
  */
9282
10188
  async makeRawRequest(url, options) {
9283
- const controller = this.timeout === null ? null : new AbortController();
10189
+ const callerSignal = options.signal ?? null;
10190
+ const controller = this.timeout === null && !callerSignal ? null : new AbortController();
9284
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
+ }
9285
10201
  try {
9286
10202
  const response = await fetch(url, {
9287
10203
  ...options,
@@ -9294,8 +10210,8 @@ var RuntypeClient2 = class {
9294
10210
  return response;
9295
10211
  } catch (error) {
9296
10212
  if (timeoutId) clearTimeout(timeoutId);
9297
- if (timeoutId && error instanceof Error && error.name === "AbortError") {
9298
- 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 });
9299
10215
  }
9300
10216
  throw error;
9301
10217
  }
@@ -9341,9 +10257,6 @@ function createClient(config) {
9341
10257
  return new RuntypeClient2(config);
9342
10258
  }
9343
10259
 
9344
- // src/index.ts
9345
- init_stream_utils();
9346
-
9347
10260
  // src/batch-builder.ts
9348
10261
  var BatchBuilder = class {
9349
10262
  constructor() {
@@ -9401,20 +10314,20 @@ var BatchBuilder = class {
9401
10314
  if (!this.recordType) {
9402
10315
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
9403
10316
  }
9404
- const request = {
10317
+ const request2 = {
9405
10318
  flowId: this.flowId,
9406
10319
  recordType: this.recordType
9407
10320
  };
9408
10321
  if (Object.keys(this.batchOptions).length > 0) {
9409
- request.options = this.batchOptions;
10322
+ request2.options = this.batchOptions;
9410
10323
  }
9411
10324
  if (this.filterConfig) {
9412
- request.filter = this.filterConfig;
10325
+ request2.filter = this.filterConfig;
9413
10326
  }
9414
10327
  if (this.limitConfig !== void 0) {
9415
- request.limit = this.limitConfig;
10328
+ request2.limit = this.limitConfig;
9416
10329
  }
9417
- return request;
10330
+ return request2;
9418
10331
  }
9419
10332
  /**
9420
10333
  * Execute the batch operation
@@ -9571,32 +10484,32 @@ var EvalBuilder = class {
9571
10484
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
9572
10485
  );
9573
10486
  }
9574
- const request = {};
10487
+ const request2 = {};
9575
10488
  if (this.flowId) {
9576
- request.flowId = this.flowId;
10489
+ request2.flowId = this.flowId;
9577
10490
  } else if (this.virtualFlow) {
9578
- request.flow = this.virtualFlow;
10491
+ request2.flow = this.virtualFlow;
9579
10492
  }
9580
10493
  if (this.recordType) {
9581
- request.recordType = this.recordType;
10494
+ request2.recordType = this.recordType;
9582
10495
  } else if (this.inlineRecords) {
9583
- request.records = this.inlineRecords;
10496
+ request2.records = this.inlineRecords;
9584
10497
  }
9585
10498
  if (this.modelOverrides) {
9586
- request.modelOverrides = this.modelOverrides;
10499
+ request2.modelOverrides = this.modelOverrides;
9587
10500
  } else if (this.modelConfigs) {
9588
- request.modelConfigs = this.modelConfigs;
10501
+ request2.modelConfigs = this.modelConfigs;
9589
10502
  }
9590
10503
  if (Object.keys(this.evalOptions).length > 0) {
9591
- request.options = this.evalOptions;
10504
+ request2.options = this.evalOptions;
9592
10505
  }
9593
10506
  if (this.filterConfig) {
9594
- request.filter = this.filterConfig;
10507
+ request2.filter = this.filterConfig;
9595
10508
  }
9596
10509
  if (this.limitConfig !== void 0) {
9597
- request.limit = this.limitConfig;
10510
+ request2.limit = this.limitConfig;
9598
10511
  }
9599
- return request;
10512
+ return request2;
9600
10513
  }
9601
10514
  /**
9602
10515
  * Execute the evaluation
@@ -10062,10 +10975,14 @@ var STEP_TYPE_TO_METHOD = {
10062
10975
  "memory-summary": "memorySummary"
10063
10976
  };
10064
10977
  export {
10978
+ AgentDriftError,
10979
+ AgentEnsureConflictError,
10065
10980
  AgentVersionsEndpoint,
10066
10981
  AgentsEndpoint,
10982
+ AgentsNamespace,
10067
10983
  AnalyticsEndpoint,
10068
10984
  ApiKeysEndpoint,
10985
+ AppsEndpoint,
10069
10986
  BatchBuilder,
10070
10987
  BatchesNamespace,
10071
10988
  BillingEndpoint,
@@ -10082,12 +10999,15 @@ export {
10082
10999
  EvalRunner,
10083
11000
  EvalsNamespace,
10084
11001
  FlowBuilder,
11002
+ FlowDriftError,
11003
+ FlowEnsureConflictError,
10085
11004
  FlowResult,
10086
11005
  FlowStepsEndpoint,
10087
11006
  FlowVersionsEndpoint,
10088
11007
  FlowsEndpoint,
10089
11008
  FlowsNamespace,
10090
11009
  IntegrationsEndpoint,
11010
+ LEDGER_ARTIFACT_LINE_PREFIX,
10091
11011
  LogsEndpoint,
10092
11012
  ModelConfigsEndpoint,
10093
11013
  PromptRunner,
@@ -10111,19 +11031,29 @@ export {
10111
11031
  applyGeneratedRuntimeToolProposalToDispatchRequest,
10112
11032
  attachRuntimeToolsToDispatchRequest,
10113
11033
  buildGeneratedRuntimeToolGateOutput,
11034
+ buildLedgerOffloadReference,
11035
+ buildSendViewOffloadMarker,
11036
+ computeAgentContentHash,
11037
+ computeFlowContentHash,
10114
11038
  createClient,
10115
11039
  createExternalTool,
10116
11040
  defaultWorkflow,
11041
+ defineAgent,
11042
+ defineFlow,
10117
11043
  deployWorkflow,
10118
11044
  evaluateGeneratedRuntimeToolProposal,
11045
+ extractDeclaredToolResultChars,
10119
11046
  gameWorkflow,
10120
11047
  getDefaultPlanPath,
10121
11048
  getLikelySupportingCandidatePaths,
10122
11049
  isDiscoveryToolName,
10123
11050
  isMarathonArtifactPath,
10124
11051
  isPreservationSensitiveTask,
11052
+ normalizeAgentDefinition,
10125
11053
  normalizeCandidatePath,
10126
11054
  parseFinalBuffer,
11055
+ parseLedgerArtifactRelativePath,
11056
+ parseOffloadedOutputId,
10127
11057
  parseSSEChunk,
10128
11058
  processStream,
10129
11059
  sanitizeTaskSlug,