@runtypelabs/sdk 4.9.0 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,9 +3,6 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __esm = (fn, res) => function __init() {
7
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
- };
9
6
  var __export = (target, all) => {
10
7
  for (var name in all)
11
8
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -20,17 +17,96 @@ var __copyProps = (to, from, except, desc) => {
20
17
  };
21
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
19
 
23
- // src/stream-utils.ts
24
- var stream_utils_exports = {};
25
- __export(stream_utils_exports, {
26
- flowErrorMessage: () => flowErrorMessage,
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AgentDriftError: () => AgentDriftError,
24
+ AgentEnsureConflictError: () => AgentEnsureConflictError,
25
+ AgentVersionsEndpoint: () => AgentVersionsEndpoint,
26
+ AgentsEndpoint: () => AgentsEndpoint,
27
+ AgentsNamespace: () => AgentsNamespace,
28
+ AnalyticsEndpoint: () => AnalyticsEndpoint,
29
+ ApiKeysEndpoint: () => ApiKeysEndpoint,
30
+ AppsEndpoint: () => AppsEndpoint,
31
+ BatchBuilder: () => BatchBuilder,
32
+ BatchesNamespace: () => BatchesNamespace,
33
+ BillingEndpoint: () => BillingEndpoint,
34
+ ChatEndpoint: () => ChatEndpoint,
35
+ ClientBatchBuilder: () => ClientBatchBuilder,
36
+ ClientEvalBuilder: () => ClientEvalBuilder,
37
+ ClientFlowBuilder: () => ClientFlowBuilder,
38
+ ClientTokensEndpoint: () => ClientTokensEndpoint,
39
+ ContextTemplatesEndpoint: () => ContextTemplatesEndpoint,
40
+ ConversationsEndpoint: () => ConversationsEndpoint,
41
+ DispatchEndpoint: () => DispatchEndpoint,
42
+ EvalBuilder: () => EvalBuilder,
43
+ EvalEndpoint: () => EvalEndpoint,
44
+ EvalRunner: () => EvalRunner,
45
+ EvalsNamespace: () => EvalsNamespace,
46
+ FlowBuilder: () => FlowBuilder,
47
+ FlowDriftError: () => FlowDriftError,
48
+ FlowEnsureConflictError: () => FlowEnsureConflictError,
49
+ FlowResult: () => FlowResult,
50
+ FlowStepsEndpoint: () => FlowStepsEndpoint,
51
+ FlowVersionsEndpoint: () => FlowVersionsEndpoint,
52
+ FlowsEndpoint: () => FlowsEndpoint,
53
+ FlowsNamespace: () => FlowsNamespace,
54
+ IntegrationsEndpoint: () => IntegrationsEndpoint,
55
+ LEDGER_ARTIFACT_LINE_PREFIX: () => LEDGER_ARTIFACT_LINE_PREFIX,
56
+ LogsEndpoint: () => LogsEndpoint,
57
+ ModelConfigsEndpoint: () => ModelConfigsEndpoint,
58
+ PromptRunner: () => PromptRunner,
59
+ PromptsEndpoint: () => PromptsEndpoint,
60
+ PromptsNamespace: () => PromptsNamespace,
61
+ ProviderKeysEndpoint: () => ProviderKeysEndpoint,
62
+ RecordsEndpoint: () => RecordsEndpoint,
63
+ Runtype: () => Runtype,
64
+ RuntypeApiError: () => RuntypeApiError,
65
+ RuntypeClient: () => RuntypeClient2,
66
+ RuntypeFlowBuilder: () => RuntypeFlowBuilder,
67
+ STEP_FIELD_REGISTRY: () => STEP_FIELD_REGISTRY,
68
+ STEP_TYPE_TO_METHOD: () => STEP_TYPE_TO_METHOD,
69
+ SchedulesEndpoint: () => SchedulesEndpoint,
70
+ SecretsEndpoint: () => SecretsEndpoint,
71
+ SkillProposalsNamespace: () => SkillProposalsNamespace,
72
+ SkillsNamespace: () => SkillsNamespace,
73
+ SurfacesEndpoint: () => SurfacesEndpoint,
74
+ ToolsEndpoint: () => ToolsEndpoint,
75
+ UsersEndpoint: () => UsersEndpoint,
76
+ applyGeneratedRuntimeToolProposalToDispatchRequest: () => applyGeneratedRuntimeToolProposalToDispatchRequest,
77
+ attachRuntimeToolsToDispatchRequest: () => attachRuntimeToolsToDispatchRequest,
78
+ buildGeneratedRuntimeToolGateOutput: () => buildGeneratedRuntimeToolGateOutput,
79
+ buildLedgerOffloadReference: () => buildLedgerOffloadReference,
80
+ buildSendViewOffloadMarker: () => buildSendViewOffloadMarker,
81
+ computeAgentContentHash: () => computeAgentContentHash,
82
+ computeFlowContentHash: () => computeFlowContentHash,
83
+ createClient: () => createClient,
84
+ createExternalTool: () => createExternalTool,
85
+ defaultWorkflow: () => defaultWorkflow,
86
+ defineAgent: () => defineAgent,
87
+ defineFlow: () => defineFlow,
88
+ deployWorkflow: () => deployWorkflow,
89
+ evaluateGeneratedRuntimeToolProposal: () => evaluateGeneratedRuntimeToolProposal,
90
+ extractDeclaredToolResultChars: () => extractDeclaredToolResultChars,
91
+ gameWorkflow: () => gameWorkflow,
92
+ getDefaultPlanPath: () => getDefaultPlanPath,
93
+ getLikelySupportingCandidatePaths: () => getLikelySupportingCandidatePaths,
94
+ isDiscoveryToolName: () => isDiscoveryToolName,
95
+ isMarathonArtifactPath: () => isMarathonArtifactPath,
96
+ isPreservationSensitiveTask: () => isPreservationSensitiveTask,
97
+ normalizeAgentDefinition: () => normalizeAgentDefinition,
98
+ normalizeCandidatePath: () => normalizeCandidatePath,
27
99
  parseFinalBuffer: () => parseFinalBuffer,
100
+ parseLedgerArtifactRelativePath: () => parseLedgerArtifactRelativePath,
101
+ parseOffloadedOutputId: () => parseOffloadedOutputId,
28
102
  parseSSEChunk: () => parseSSEChunk,
29
103
  processStream: () => processStream,
30
- stepDeltaText: () => stepDeltaText,
31
- stepDisplayName: () => stepDisplayName,
104
+ sanitizeTaskSlug: () => sanitizeTaskSlug,
32
105
  streamEvents: () => streamEvents
33
106
  });
107
+ module.exports = __toCommonJS(index_exports);
108
+
109
+ // src/stream-utils.ts
34
110
  function parseSSEChunk(chunk, buffer) {
35
111
  buffer += chunk;
36
112
  const lines = buffer.split("\n");
@@ -280,86 +356,8 @@ async function* streamEvents(response) {
280
356
  reader.releaseLock();
281
357
  }
282
358
  }
283
- var init_stream_utils = __esm({
284
- "src/stream-utils.ts"() {
285
- "use strict";
286
- }
287
- });
288
-
289
- // src/index.ts
290
- var index_exports = {};
291
- __export(index_exports, {
292
- AgentVersionsEndpoint: () => AgentVersionsEndpoint,
293
- AgentsEndpoint: () => AgentsEndpoint,
294
- AnalyticsEndpoint: () => AnalyticsEndpoint,
295
- ApiKeysEndpoint: () => ApiKeysEndpoint,
296
- BatchBuilder: () => BatchBuilder,
297
- BatchesNamespace: () => BatchesNamespace,
298
- BillingEndpoint: () => BillingEndpoint,
299
- ChatEndpoint: () => ChatEndpoint,
300
- ClientBatchBuilder: () => ClientBatchBuilder,
301
- ClientEvalBuilder: () => ClientEvalBuilder,
302
- ClientFlowBuilder: () => ClientFlowBuilder,
303
- ClientTokensEndpoint: () => ClientTokensEndpoint,
304
- ContextTemplatesEndpoint: () => ContextTemplatesEndpoint,
305
- ConversationsEndpoint: () => ConversationsEndpoint,
306
- DispatchEndpoint: () => DispatchEndpoint,
307
- EvalBuilder: () => EvalBuilder,
308
- EvalEndpoint: () => EvalEndpoint,
309
- EvalRunner: () => EvalRunner,
310
- EvalsNamespace: () => EvalsNamespace,
311
- FlowBuilder: () => FlowBuilder,
312
- FlowResult: () => FlowResult,
313
- FlowStepsEndpoint: () => FlowStepsEndpoint,
314
- FlowVersionsEndpoint: () => FlowVersionsEndpoint,
315
- FlowsEndpoint: () => FlowsEndpoint,
316
- FlowsNamespace: () => FlowsNamespace,
317
- IntegrationsEndpoint: () => IntegrationsEndpoint,
318
- LogsEndpoint: () => LogsEndpoint,
319
- ModelConfigsEndpoint: () => ModelConfigsEndpoint,
320
- PromptRunner: () => PromptRunner,
321
- PromptsEndpoint: () => PromptsEndpoint,
322
- PromptsNamespace: () => PromptsNamespace,
323
- ProviderKeysEndpoint: () => ProviderKeysEndpoint,
324
- RecordsEndpoint: () => RecordsEndpoint,
325
- Runtype: () => Runtype,
326
- RuntypeApiError: () => RuntypeApiError,
327
- RuntypeClient: () => RuntypeClient2,
328
- RuntypeFlowBuilder: () => RuntypeFlowBuilder,
329
- STEP_FIELD_REGISTRY: () => STEP_FIELD_REGISTRY,
330
- STEP_TYPE_TO_METHOD: () => STEP_TYPE_TO_METHOD,
331
- SchedulesEndpoint: () => SchedulesEndpoint,
332
- SecretsEndpoint: () => SecretsEndpoint,
333
- SkillProposalsNamespace: () => SkillProposalsNamespace,
334
- SkillsNamespace: () => SkillsNamespace,
335
- SurfacesEndpoint: () => SurfacesEndpoint,
336
- ToolsEndpoint: () => ToolsEndpoint,
337
- UsersEndpoint: () => UsersEndpoint,
338
- applyGeneratedRuntimeToolProposalToDispatchRequest: () => applyGeneratedRuntimeToolProposalToDispatchRequest,
339
- attachRuntimeToolsToDispatchRequest: () => attachRuntimeToolsToDispatchRequest,
340
- buildGeneratedRuntimeToolGateOutput: () => buildGeneratedRuntimeToolGateOutput,
341
- createClient: () => createClient,
342
- createExternalTool: () => createExternalTool,
343
- defaultWorkflow: () => defaultWorkflow,
344
- deployWorkflow: () => deployWorkflow,
345
- evaluateGeneratedRuntimeToolProposal: () => evaluateGeneratedRuntimeToolProposal,
346
- gameWorkflow: () => gameWorkflow,
347
- getDefaultPlanPath: () => getDefaultPlanPath,
348
- getLikelySupportingCandidatePaths: () => getLikelySupportingCandidatePaths,
349
- isDiscoveryToolName: () => isDiscoveryToolName,
350
- isMarathonArtifactPath: () => isMarathonArtifactPath,
351
- isPreservationSensitiveTask: () => isPreservationSensitiveTask,
352
- normalizeCandidatePath: () => normalizeCandidatePath,
353
- parseFinalBuffer: () => parseFinalBuffer,
354
- parseSSEChunk: () => parseSSEChunk,
355
- processStream: () => processStream,
356
- sanitizeTaskSlug: () => sanitizeTaskSlug,
357
- streamEvents: () => streamEvents
358
- });
359
- module.exports = __toCommonJS(index_exports);
360
359
 
361
360
  // src/flow-result.ts
362
- init_stream_utils();
363
361
  var FlowResult = class {
364
362
  constructor(response, summary) {
365
363
  this.consumed = false;
@@ -495,7 +493,6 @@ var FlowResult = class {
495
493
  };
496
494
 
497
495
  // src/flow-builder.ts
498
- init_stream_utils();
499
496
  async function validateInlineFlow(client, args, savedFlowHint) {
500
497
  if (args.existingFlowId) {
501
498
  throw new Error(
@@ -1205,20 +1202,20 @@ var FlowBuilder = class {
1205
1202
  */
1206
1203
  build() {
1207
1204
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
1208
- const request = { flow };
1205
+ const request2 = { flow };
1209
1206
  if (this.recordConfig) {
1210
- request.record = this.recordConfig;
1207
+ request2.record = this.recordConfig;
1211
1208
  }
1212
1209
  if (this.messagesConfig) {
1213
- request.messages = this.messagesConfig;
1210
+ request2.messages = this.messagesConfig;
1214
1211
  }
1215
1212
  if (this.inputsConfig) {
1216
- request.inputs = this.inputsConfig;
1213
+ request2.inputs = this.inputsConfig;
1217
1214
  }
1218
1215
  if (Object.keys(this.optionsConfig).length > 0) {
1219
- request.options = this.optionsConfig;
1216
+ request2.options = this.optionsConfig;
1220
1217
  }
1221
- return request;
1218
+ return request2;
1222
1219
  }
1223
1220
  /**
1224
1221
  * Validate this prospective flow against the public validation endpoint
@@ -1455,22 +1452,22 @@ function resolveBatchExecutionId(pausedTools) {
1455
1452
  return "";
1456
1453
  }
1457
1454
 
1458
- // src/flows-namespace.ts
1459
- function isRecord(value) {
1455
+ // src/flows-ensure.ts
1456
+ function isPlainObject(value) {
1460
1457
  return value !== null && typeof value === "object" && !Array.isArray(value);
1461
1458
  }
1462
- function normalizeConfig(config) {
1463
- if (!isRecord(config)) return {};
1459
+ function normalizeConfigForHash(config) {
1460
+ if (!isPlainObject(config)) return {};
1464
1461
  const normalized = {};
1465
1462
  for (const key of Object.keys(config).sort()) {
1466
1463
  const value = config[key];
1467
1464
  if (value === void 0) continue;
1468
1465
  if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1469
- normalized[key] = normalizeConfig(value);
1466
+ normalized[key] = normalizeConfigForHash(value);
1470
1467
  } else if (Array.isArray(value)) {
1471
1468
  normalized[key] = value.map((item) => {
1472
1469
  if (item !== null && typeof item === "object" && !Array.isArray(item)) {
1473
- return normalizeConfig(item);
1470
+ return normalizeConfigForHash(item);
1474
1471
  }
1475
1472
  return item;
1476
1473
  });
@@ -1481,28 +1478,249 @@ function normalizeConfig(config) {
1481
1478
  return normalized;
1482
1479
  }
1483
1480
  function normalizeStepForHash(step) {
1484
- const stepObj = isRecord(step) ? step : {};
1481
+ const stepObj = isPlainObject(step) ? step : {};
1485
1482
  return {
1486
1483
  type: typeof stepObj.type === "string" ? stepObj.type : "",
1487
1484
  name: typeof stepObj.name === "string" ? stepObj.name : "",
1488
1485
  enabled: stepObj.enabled !== false,
1489
1486
  ...typeof stepObj.when === "string" ? { when: stepObj.when } : {},
1490
- config: normalizeConfig(stepObj.config),
1487
+ config: normalizeConfigForHash(stepObj.config),
1491
1488
  order: typeof stepObj.order === "number" ? stepObj.order : 0
1492
1489
  };
1493
1490
  }
1494
1491
  async function computeFlowContentHash(steps) {
1495
1492
  const normalized = [...steps].sort((a, b) => {
1496
- const orderA = isRecord(a) && typeof a.order === "number" ? a.order : 0;
1497
- const orderB = isRecord(b) && typeof b.order === "number" ? b.order : 0;
1493
+ const orderA = isPlainObject(a) && typeof a.order === "number" ? a.order : 0;
1494
+ const orderB = isPlainObject(b) && typeof b.order === "number" ? b.order : 0;
1498
1495
  return orderA - orderB;
1499
1496
  }).map(normalizeStepForHash);
1500
1497
  const serialized = JSON.stringify(normalized);
1501
1498
  const encoded = new TextEncoder().encode(serialized);
1502
1499
  const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
1503
- const hashArray = new Uint8Array(hashBuffer);
1504
- return Array.from(hashArray).map((b) => b.toString(16).padStart(2, "0")).join("");
1500
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
1505
1501
  }
1502
+ var DEFINE_FLOW_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "steps"]);
1503
+ var DEFINE_FLOW_STEP_KEYS = /* @__PURE__ */ new Set([
1504
+ "type",
1505
+ "name",
1506
+ "order",
1507
+ "enabled",
1508
+ "when",
1509
+ "config"
1510
+ ]);
1511
+ function collectStepNonPortableToolRefs(config, path) {
1512
+ const found = [];
1513
+ const tools = config.tools;
1514
+ const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
1515
+ const scanArray = (value, subPath) => {
1516
+ if (!Array.isArray(value)) return;
1517
+ value.forEach((ref, i) => {
1518
+ if (isAccountScoped(ref)) found.push(`${subPath}[${i}]`);
1519
+ });
1520
+ };
1521
+ const scanKeys = (value, subPath) => {
1522
+ if (!isPlainObject(value)) return;
1523
+ for (const key of Object.keys(value)) {
1524
+ if (isAccountScoped(key)) found.push(`${subPath}.${key}`);
1525
+ }
1526
+ };
1527
+ if (isPlainObject(tools)) {
1528
+ scanArray(tools.toolIds, `${path}.tools.toolIds`);
1529
+ scanKeys(tools.toolConfigs, `${path}.tools.toolConfigs`);
1530
+ scanKeys(tools.perToolLimits, `${path}.tools.perToolLimits`);
1531
+ if (isPlainObject(tools.approval)) {
1532
+ scanArray(tools.approval.require, `${path}.tools.approval.require`);
1533
+ }
1534
+ if (isPlainObject(tools.subagentConfig)) {
1535
+ scanArray(tools.subagentConfig.toolPool, `${path}.tools.subagentConfig.toolPool`);
1536
+ }
1537
+ if (isPlainObject(tools.codeModeConfig)) {
1538
+ scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
1539
+ }
1540
+ }
1541
+ for (const branch of ["trueSteps", "falseSteps"]) {
1542
+ const nested = config[branch];
1543
+ if (!Array.isArray(nested)) continue;
1544
+ nested.forEach((nestedStep, i) => {
1545
+ if (isPlainObject(nestedStep) && isPlainObject(nestedStep.config)) {
1546
+ found.push(
1547
+ ...collectStepNonPortableToolRefs(
1548
+ nestedStep.config,
1549
+ `${path}.${branch}[${i}].config`
1550
+ )
1551
+ );
1552
+ }
1553
+ });
1554
+ }
1555
+ return found;
1556
+ }
1557
+ function defineFlow(input) {
1558
+ if (!input || typeof input !== "object") {
1559
+ throw new Error("defineFlow requires a definition object");
1560
+ }
1561
+ if (typeof input.name !== "string" || input.name.length === 0) {
1562
+ throw new Error('defineFlow requires a non-empty string "name"');
1563
+ }
1564
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_FLOW_TOP_LEVEL_KEYS.has(key));
1565
+ if (unknownKeys.length > 0) {
1566
+ throw new Error(
1567
+ `defineFlow: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name and steps. (Description is not part of the v1 ensure surface.)`
1568
+ );
1569
+ }
1570
+ if (!Array.isArray(input.steps) || input.steps.length === 0) {
1571
+ throw new Error('defineFlow requires a non-empty "steps" array');
1572
+ }
1573
+ const steps = input.steps.map((step, index) => {
1574
+ if (!isPlainObject(step)) {
1575
+ throw new Error(`defineFlow: steps[${index}] must be an object`);
1576
+ }
1577
+ if (typeof step.type !== "string" || step.type.length === 0) {
1578
+ throw new Error(`defineFlow: steps[${index}] requires a non-empty string "type"`);
1579
+ }
1580
+ if (typeof step.name !== "string" || step.name.length === 0) {
1581
+ throw new Error(`defineFlow: steps[${index}] requires a non-empty string "name"`);
1582
+ }
1583
+ const unknownStepKeys = Object.keys(step).filter((key) => !DEFINE_FLOW_STEP_KEYS.has(key));
1584
+ if (unknownStepKeys.length > 0) {
1585
+ throw new Error(
1586
+ `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.)`
1587
+ );
1588
+ }
1589
+ const config = isPlainObject(step.config) ? step.config : void 0;
1590
+ if (config) {
1591
+ const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
1592
+ if (nonPortable.length > 0) {
1593
+ throw new Error(
1594
+ `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.`
1595
+ );
1596
+ }
1597
+ }
1598
+ return {
1599
+ type: step.type,
1600
+ name: step.name,
1601
+ // Explicit 1-based order (the flow builder's convention) so the local
1602
+ // probe hash agrees with the server's persisted step order.
1603
+ order: typeof step.order === "number" ? step.order : index + 1,
1604
+ ...step.enabled !== void 0 ? { enabled: step.enabled } : {},
1605
+ ...typeof step.when === "string" ? { when: step.when } : {},
1606
+ ...config ? { config } : {}
1607
+ };
1608
+ });
1609
+ return { name: input.name, steps };
1610
+ }
1611
+ var FlowEnsureConflictError = class extends Error {
1612
+ constructor(body) {
1613
+ super(body.error ?? `Flow ensure conflict: ${body.code}`);
1614
+ this.name = "FlowEnsureConflictError";
1615
+ this.code = body.code;
1616
+ this.lastModifiedSource = body.lastModifiedSource;
1617
+ this.modifiedAt = body.modifiedAt;
1618
+ this.currentHash = body.currentHash;
1619
+ }
1620
+ };
1621
+ var FlowDriftError = class extends Error {
1622
+ constructor(plan) {
1623
+ super(
1624
+ `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.`
1625
+ );
1626
+ this.name = "FlowDriftError";
1627
+ this.plan = plan;
1628
+ }
1629
+ };
1630
+ function parseRequestError(err) {
1631
+ if (!(err instanceof Error)) return { status: null, body: null };
1632
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
1633
+ if (!match) return { status: null, body: null };
1634
+ try {
1635
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
1636
+ } catch {
1637
+ return { status: Number(match[1]), body: null };
1638
+ }
1639
+ }
1640
+ function toConflictError(err) {
1641
+ const { status, body } = parseRequestError(err);
1642
+ if (status !== 409 || !isPlainObject(body)) return null;
1643
+ const code = body.code;
1644
+ if (code !== "external_modification" && code !== "remote_changed") return null;
1645
+ return new FlowEnsureConflictError(
1646
+ body
1647
+ );
1648
+ }
1649
+ var serverHashMemo = /* @__PURE__ */ new WeakMap();
1650
+ function memoFor(client) {
1651
+ let memo = serverHashMemo.get(client);
1652
+ if (!memo) {
1653
+ memo = /* @__PURE__ */ new Map();
1654
+ serverHashMemo.set(client, memo);
1655
+ }
1656
+ return memo;
1657
+ }
1658
+ function memoize(memo, memoKey, result) {
1659
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
1660
+ }
1661
+ async function request(client, body) {
1662
+ try {
1663
+ return await client.post(
1664
+ "/flows/ensure",
1665
+ body
1666
+ );
1667
+ } catch (err) {
1668
+ const conflict = toConflictError(err);
1669
+ if (conflict) throw conflict;
1670
+ throw err;
1671
+ }
1672
+ }
1673
+ async function ensureFlow(client, definition, options = {}) {
1674
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
1675
+ const passthrough = {
1676
+ ...onConflict ? { onConflict } : {},
1677
+ ...release ? { release } : {},
1678
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
1679
+ };
1680
+ if (dryRun || expectNoChanges) {
1681
+ const plan = await request(client, {
1682
+ name: definition.name,
1683
+ definition,
1684
+ dryRun: true,
1685
+ ...passthrough
1686
+ });
1687
+ if (plan.result !== "plan") {
1688
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
1689
+ }
1690
+ if (expectNoChanges && plan.changes !== "none") {
1691
+ throw new FlowDriftError(plan);
1692
+ }
1693
+ return plan;
1694
+ }
1695
+ const memo = memoFor(client);
1696
+ const localHash = await computeFlowContentHash(definition.steps);
1697
+ const memoKey = `${definition.name} ${localHash}`;
1698
+ const contentHash = memo.get(memoKey) ?? localHash;
1699
+ const probe = await request(client, {
1700
+ name: definition.name,
1701
+ contentHash,
1702
+ ...passthrough
1703
+ });
1704
+ if (probe.result !== "definitionRequired") {
1705
+ memoize(memo, memoKey, probe);
1706
+ return probe;
1707
+ }
1708
+ const converged = await request(client, {
1709
+ name: definition.name,
1710
+ definition,
1711
+ ...passthrough
1712
+ });
1713
+ if (converged.result === "definitionRequired") {
1714
+ throw new Error("Server reported definitionRequired for a full-definition request");
1715
+ }
1716
+ memoize(memo, memoKey, converged);
1717
+ return converged;
1718
+ }
1719
+ async function pullFlow(client, name) {
1720
+ return client.get("/flows/pull", { name });
1721
+ }
1722
+
1723
+ // src/flows-namespace.ts
1506
1724
  var FlowsNamespace = class {
1507
1725
  constructor(getClient) {
1508
1726
  this.getClient = getClient;
@@ -1510,8 +1728,11 @@ var FlowsNamespace = class {
1510
1728
  /**
1511
1729
  * Create or update a flow by name (upsert mode)
1512
1730
  *
1513
- * The recommended pattern for code-first flow management.
1514
- * Creates the flow if it doesn't exist, updates if steps changed.
1731
+ * The recommended pattern for code-first flow management when you want to
1732
+ * save AND run in one dispatch. For a deploy-time, non-executing converge
1733
+ * (CI/CD config-as-code), use {@link ensure} instead — upsert and ensure
1734
+ * are siblings, not versions of each other: upsert is the runtime verb
1735
+ * (save-and-run), ensure is the deploy verb (converge only).
1515
1736
  *
1516
1737
  * @example
1517
1738
  * ```typescript
@@ -1526,6 +1747,33 @@ var FlowsNamespace = class {
1526
1747
  upsert(config) {
1527
1748
  return new RuntypeFlowBuilder(this.getClient, "upsert", config);
1528
1749
  }
1750
+ /**
1751
+ * Idempotently converge a `defineFlow` definition onto the platform —
1752
+ * the deploy-time, non-executing sibling of {@link upsert}. Hash-first:
1753
+ * the steady state is one tiny probe request. Creates an immutable version
1754
+ * snapshot on every change; never deletes; never executes the flow.
1755
+ *
1756
+ * @example
1757
+ * ```typescript
1758
+ * const def = defineFlow({ name: 'Onboarding Digest', steps: [...] })
1759
+ *
1760
+ * // Converge (CI/deploy).
1761
+ * const result = await Runtype.flows.ensure(def)
1762
+ *
1763
+ * // PR drift gate.
1764
+ * await Runtype.flows.ensure(def, { expectNoChanges: true })
1765
+ * ```
1766
+ */
1767
+ async ensure(definition, options = {}) {
1768
+ return ensureFlow(this.getClient(), definition, options);
1769
+ }
1770
+ /**
1771
+ * Pull the canonical definition + provenance for a flow by name — the
1772
+ * absorb-drift direction of the ensure protocol.
1773
+ */
1774
+ async pull(name) {
1775
+ return pullFlow(this.getClient(), name);
1776
+ }
1529
1777
  /**
1530
1778
  * Create a virtual flow (one-off, not saved)
1531
1779
  *
@@ -2211,9 +2459,8 @@ var RuntypeFlowBuilder = class {
2211
2459
  onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
2212
2460
  onError: (error) => callbacks?.onError?.(error)
2213
2461
  };
2214
- const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
2215
2462
  try {
2216
- for await (const event of streamEvents2(response)) {
2463
+ for await (const event of streamEvents(response)) {
2217
2464
  collectLocalToolAwait(pausedTools, event);
2218
2465
  switch (event.type) {
2219
2466
  case "flow_start":
@@ -2223,10 +2470,10 @@ var RuntypeFlowBuilder = class {
2223
2470
  wrappedCallbacks.onStepStart?.(event);
2224
2471
  break;
2225
2472
  case "step_delta":
2226
- wrappedCallbacks.onStepDelta?.(stepDeltaText2(event), event);
2473
+ wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
2227
2474
  break;
2228
2475
  case "step_complete": {
2229
- accumulatedSummary.results?.set(stepDisplayName2(event), event.result);
2476
+ accumulatedSummary.results?.set(stepDisplayName(event), event.result);
2230
2477
  wrappedCallbacks.onStepComplete?.(event.result, event);
2231
2478
  break;
2232
2479
  }
@@ -2234,7 +2481,7 @@ var RuntypeFlowBuilder = class {
2234
2481
  wrappedCallbacks.onFlowComplete?.(event);
2235
2482
  break;
2236
2483
  case "flow_error":
2237
- wrappedCallbacks.onError?.(new Error(flowErrorMessage2(event)));
2484
+ wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
2238
2485
  break;
2239
2486
  }
2240
2487
  }
@@ -2309,7 +2556,8 @@ var RuntypeFlowBuilder = class {
2309
2556
  return [toolName, await localTools[toolName](parameters)];
2310
2557
  } catch (error) {
2311
2558
  throw new Error(
2312
- `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`
2559
+ `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`,
2560
+ { cause: error }
2313
2561
  );
2314
2562
  }
2315
2563
  })
@@ -2342,15 +2590,15 @@ var RuntypeFlowBuilder = class {
2342
2590
  build() {
2343
2591
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2344
2592
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2345
- const request = { flow };
2593
+ const request2 = { flow };
2346
2594
  if (this.recordConfig) {
2347
- request.record = this.recordConfig;
2595
+ request2.record = this.recordConfig;
2348
2596
  }
2349
2597
  if (this.messagesConfig) {
2350
- request.messages = this.messagesConfig;
2598
+ request2.messages = this.messagesConfig;
2351
2599
  }
2352
2600
  if (this.inputsConfig) {
2353
- request.inputs = this.inputsConfig;
2601
+ request2.inputs = this.inputsConfig;
2354
2602
  }
2355
2603
  const options = {
2356
2604
  flowMode,
@@ -2368,8 +2616,8 @@ var RuntypeFlowBuilder = class {
2368
2616
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2369
2617
  options.upsertOptions = this.upsertOptions;
2370
2618
  }
2371
- request.options = options;
2372
- return request;
2619
+ request2.options = options;
2620
+ return request2;
2373
2621
  }
2374
2622
  /**
2375
2623
  * Validate this prospective flow against the public validation endpoint
@@ -3029,6 +3277,8 @@ var SkillsNamespace = class {
3029
3277
  }
3030
3278
  /**
3031
3279
  * List skills for the authenticated owner, optionally filtered by status.
3280
+ * Returns just the rows (one page); pass `cursor`/`limit` to page, or use
3281
+ * {@link listPage} when you need the pagination envelope.
3032
3282
  *
3033
3283
  * @example
3034
3284
  * ```typescript
@@ -3036,10 +3286,23 @@ var SkillsNamespace = class {
3036
3286
  * ```
3037
3287
  */
3038
3288
  async list(params) {
3039
- const client = this.getClient();
3040
- const res = await client.get("/skills", params);
3289
+ const res = await this.listPage(params);
3041
3290
  return res.data;
3042
3291
  }
3292
+ /**
3293
+ * List skills with the cursor-pagination envelope (mirrors the tools list
3294
+ * shape: `{ data, pagination }`).
3295
+ *
3296
+ * @example
3297
+ * ```typescript
3298
+ * const page1 = await Runtype.skills.listPage({ limit: 50, includeCount: true })
3299
+ * const page2 = await Runtype.skills.listPage({ limit: 50, cursor: page1.pagination?.nextCursor ?? undefined })
3300
+ * ```
3301
+ */
3302
+ async listPage(params) {
3303
+ const client = this.getClient();
3304
+ return client.get("/skills", params);
3305
+ }
3043
3306
  /**
3044
3307
  * Get a skill and its full version history.
3045
3308
  *
@@ -3132,6 +3395,260 @@ var SkillsNamespace = class {
3132
3395
  }
3133
3396
  };
3134
3397
 
3398
+ // src/agents-namespace.ts
3399
+ var AGENT_CONFIG_KEYS = [
3400
+ "model",
3401
+ "systemPrompt",
3402
+ "temperature",
3403
+ "topP",
3404
+ "topK",
3405
+ "frequencyPenalty",
3406
+ "presencePenalty",
3407
+ "seed",
3408
+ "tools",
3409
+ "reasoning",
3410
+ "advisor",
3411
+ "loopConfig",
3412
+ "voice",
3413
+ "errorHandling",
3414
+ "artifacts",
3415
+ "loggingPolicy",
3416
+ "temporal",
3417
+ "memory"
3418
+ ];
3419
+ var AGENT_CONFIG_KEY_LIST = [...AGENT_CONFIG_KEYS].sort();
3420
+ function isPlainObject2(value) {
3421
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3422
+ }
3423
+ function normalizeValue(value) {
3424
+ if (Array.isArray(value)) {
3425
+ return value.map((item) => normalizeValue(item));
3426
+ }
3427
+ if (isPlainObject2(value)) {
3428
+ const normalized = {};
3429
+ for (const key of Object.keys(value).sort()) {
3430
+ const entry = value[key];
3431
+ if (entry === void 0 || entry === null) continue;
3432
+ normalized[key] = normalizeValue(entry);
3433
+ }
3434
+ return normalized;
3435
+ }
3436
+ return value;
3437
+ }
3438
+ function normalizeAgentDefinition(definition) {
3439
+ const config = {};
3440
+ const rawConfig = isPlainObject2(definition.config) ? definition.config : {};
3441
+ for (const key of AGENT_CONFIG_KEY_LIST) {
3442
+ const value = rawConfig[key];
3443
+ if (value === void 0 || value === null) continue;
3444
+ config[key] = normalizeValue(value);
3445
+ }
3446
+ return {
3447
+ name: definition.name,
3448
+ ...definition.description ? { description: definition.description } : {},
3449
+ ...definition.icon ? { icon: definition.icon } : {},
3450
+ config
3451
+ };
3452
+ }
3453
+ async function computeAgentContentHash(definition) {
3454
+ const serialized = JSON.stringify(normalizeAgentDefinition(definition));
3455
+ const encoded = new TextEncoder().encode(serialized);
3456
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3457
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3458
+ }
3459
+ var DEFINE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "description", "icon", ...AGENT_CONFIG_KEYS]);
3460
+ function collectNonPortableToolRefs(config) {
3461
+ const tools = config.tools;
3462
+ if (!isPlainObject2(tools)) return [];
3463
+ const found = [];
3464
+ const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
3465
+ const scanArray = (value, path) => {
3466
+ if (!Array.isArray(value)) return;
3467
+ value.forEach((ref, i) => {
3468
+ if (isAccountScoped(ref)) found.push(`${path}[${i}]`);
3469
+ });
3470
+ };
3471
+ const scanKeys = (value, path) => {
3472
+ if (!isPlainObject2(value)) return;
3473
+ for (const key of Object.keys(value)) {
3474
+ if (isAccountScoped(key)) found.push(`${path}.${key}`);
3475
+ }
3476
+ };
3477
+ scanArray(tools.toolIds, "tools.toolIds");
3478
+ scanKeys(tools.toolConfigs, "tools.toolConfigs");
3479
+ scanKeys(tools.perToolLimits, "tools.perToolLimits");
3480
+ if (isPlainObject2(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
3481
+ if (isPlainObject2(tools.subagentConfig)) {
3482
+ scanArray(tools.subagentConfig.toolPool, "tools.subagentConfig.toolPool");
3483
+ }
3484
+ if (isPlainObject2(tools.codeModeConfig)) {
3485
+ scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
3486
+ }
3487
+ return found;
3488
+ }
3489
+ function defineAgent(input) {
3490
+ if (!input || typeof input !== "object") {
3491
+ throw new Error("defineAgent requires a definition object");
3492
+ }
3493
+ if (typeof input.name !== "string" || input.name.length === 0) {
3494
+ throw new Error('defineAgent requires a non-empty string "name"');
3495
+ }
3496
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOP_LEVEL_KEYS.has(key));
3497
+ if (unknownKeys.length > 0) {
3498
+ throw new Error(
3499
+ `defineAgent: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, icon, and the agent runtime config surface (${AGENT_CONFIG_KEY_LIST.join(", ")}).`
3500
+ );
3501
+ }
3502
+ const config = {};
3503
+ for (const key of AGENT_CONFIG_KEYS) {
3504
+ const value = input[key];
3505
+ if (value !== void 0) config[key] = value;
3506
+ }
3507
+ const nonPortable = collectNonPortableToolRefs(config);
3508
+ if (nonPortable.length > 0) {
3509
+ throw new Error(
3510
+ `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.`
3511
+ );
3512
+ }
3513
+ return {
3514
+ name: input.name,
3515
+ ...input.description !== void 0 ? { description: input.description } : {},
3516
+ ...input.icon !== void 0 ? { icon: input.icon } : {},
3517
+ config
3518
+ };
3519
+ }
3520
+ var AgentEnsureConflictError = class extends Error {
3521
+ constructor(body) {
3522
+ super(body.error ?? `Agent ensure conflict: ${body.code}`);
3523
+ this.name = "AgentEnsureConflictError";
3524
+ this.code = body.code;
3525
+ this.lastModifiedSource = body.lastModifiedSource;
3526
+ this.modifiedAt = body.modifiedAt;
3527
+ this.currentHash = body.currentHash;
3528
+ }
3529
+ };
3530
+ var AgentDriftError = class extends Error {
3531
+ constructor(plan) {
3532
+ super(
3533
+ `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.`
3534
+ );
3535
+ this.name = "AgentDriftError";
3536
+ this.plan = plan;
3537
+ }
3538
+ };
3539
+ function parseRequestError2(err) {
3540
+ if (!(err instanceof Error)) return { status: null, body: null };
3541
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3542
+ if (!match) return { status: null, body: null };
3543
+ try {
3544
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3545
+ } catch {
3546
+ return { status: Number(match[1]), body: null };
3547
+ }
3548
+ }
3549
+ function toConflictError2(err) {
3550
+ const { status, body } = parseRequestError2(err);
3551
+ if (status !== 409 || !isPlainObject2(body)) return null;
3552
+ const code = body.code;
3553
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3554
+ return new AgentEnsureConflictError(
3555
+ body
3556
+ );
3557
+ }
3558
+ var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
3559
+ function memoFor2(client) {
3560
+ let memo = serverHashMemo2.get(client);
3561
+ if (!memo) {
3562
+ memo = /* @__PURE__ */ new Map();
3563
+ serverHashMemo2.set(client, memo);
3564
+ }
3565
+ return memo;
3566
+ }
3567
+ var AgentsNamespace = class {
3568
+ constructor(getClient) {
3569
+ this.getClient = getClient;
3570
+ }
3571
+ /**
3572
+ * Idempotently converge a definition onto the platform. Hash-first: probes
3573
+ * with a content hash, and only ships the full definition when the server
3574
+ * reports a miss (`definitionRequired`). Creates an immutable version
3575
+ * snapshot on every change; never deletes.
3576
+ */
3577
+ async ensure(definition, options = {}) {
3578
+ const client = this.getClient();
3579
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
3580
+ const passthrough = {
3581
+ ...onConflict ? { onConflict } : {},
3582
+ ...release ? { release } : {},
3583
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3584
+ };
3585
+ if (dryRun || expectNoChanges) {
3586
+ const plan = await this.request(client, {
3587
+ name: definition.name,
3588
+ definition,
3589
+ dryRun: true,
3590
+ ...passthrough
3591
+ });
3592
+ if (plan.result !== "plan") {
3593
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3594
+ }
3595
+ if (expectNoChanges && plan.changes !== "none") {
3596
+ throw new AgentDriftError(plan);
3597
+ }
3598
+ return plan;
3599
+ }
3600
+ const memo = memoFor2(client);
3601
+ const localHash = await computeAgentContentHash({
3602
+ ...definition,
3603
+ config: definition.config
3604
+ });
3605
+ const memoKey = `${definition.name}\0${localHash}`;
3606
+ const contentHash = memo.get(memoKey) ?? localHash;
3607
+ const probe = await this.request(client, {
3608
+ name: definition.name,
3609
+ contentHash,
3610
+ ...passthrough
3611
+ });
3612
+ if (probe.result !== "definitionRequired") {
3613
+ this.memoize(memo, memoKey, probe);
3614
+ return probe;
3615
+ }
3616
+ const converged = await this.request(client, {
3617
+ name: definition.name,
3618
+ definition,
3619
+ ...passthrough
3620
+ });
3621
+ if (converged.result === "definitionRequired") {
3622
+ throw new Error("Server reported definitionRequired for a full-definition request");
3623
+ }
3624
+ this.memoize(memo, memoKey, converged);
3625
+ return converged;
3626
+ }
3627
+ /**
3628
+ * Pull the canonical definition + provenance for an agent by name — the
3629
+ * absorb-drift direction. The contentHash reflects the live agent state.
3630
+ */
3631
+ async pull(name) {
3632
+ const client = this.getClient();
3633
+ return client.get("/agents/pull", { name });
3634
+ }
3635
+ memoize(memo, memoKey, result) {
3636
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3637
+ }
3638
+ async request(client, body) {
3639
+ try {
3640
+ return await client.post(
3641
+ "/agents/ensure",
3642
+ body
3643
+ );
3644
+ } catch (err) {
3645
+ const conflict = toConflictError2(err);
3646
+ if (conflict) throw conflict;
3647
+ throw err;
3648
+ }
3649
+ }
3650
+ };
3651
+
3135
3652
  // src/transform.ts
3136
3653
  function transformResponse(data) {
3137
3654
  return data;
@@ -3292,7 +3809,7 @@ var RuntypeClient = class {
3292
3809
  } catch (error) {
3293
3810
  clearTimeout(timeoutId);
3294
3811
  if (error instanceof Error && error.name === "AbortError") {
3295
- throw new Error(`Request timeout after ${this.timeout}ms`);
3812
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
3296
3813
  }
3297
3814
  throw error;
3298
3815
  }
@@ -3319,7 +3836,7 @@ var RuntypeClient = class {
3319
3836
  } catch (error) {
3320
3837
  clearTimeout(timeoutId);
3321
3838
  if (error instanceof Error && error.name === "AbortError") {
3322
- throw new Error(`Request timeout after ${this.timeout}ms`);
3839
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
3323
3840
  }
3324
3841
  throw error;
3325
3842
  }
@@ -3494,6 +4011,32 @@ var Runtype = class {
3494
4011
  static get skills() {
3495
4012
  return new SkillsNamespace(() => this.getClient());
3496
4013
  }
4014
+ /**
4015
+ * Agents namespace - Agent config-as-code (define / ensure / pull)
4016
+ *
4017
+ * @example
4018
+ * ```typescript
4019
+ * import { defineAgent, Runtype } from '@runtypelabs/sdk'
4020
+ *
4021
+ * const assistant = defineAgent({
4022
+ * name: 'Pricing Assistant',
4023
+ * model: 'claude-sonnet-4-6',
4024
+ * systemPrompt: renderPrompt(pricingData),
4025
+ * })
4026
+ *
4027
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
4028
+ * await Runtype.agents.ensure(assistant)
4029
+ *
4030
+ * // CI drift gate
4031
+ * await Runtype.agents.ensure(assistant, { expectNoChanges: true })
4032
+ *
4033
+ * // Absorb a dashboard edit back into the repo
4034
+ * const { definition } = await Runtype.agents.pull('Pricing Assistant')
4035
+ * ```
4036
+ */
4037
+ static get agents() {
4038
+ return new AgentsNamespace(() => this.getClient());
4039
+ }
3497
4040
  };
3498
4041
 
3499
4042
  // src/generated-tool-gate.ts
@@ -3716,8 +4259,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
3716
4259
  ...decision.tool ? { tool: decision.tool } : {}
3717
4260
  };
3718
4261
  }
3719
- function attachRuntimeToolsToDispatchRequest(request, runtimeTools, options = {}) {
3720
- const stepList = request.flow.steps;
4262
+ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {}) {
4263
+ const stepList = request2.flow.steps;
3721
4264
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
3722
4265
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
3723
4266
  }
@@ -3760,9 +4303,9 @@ function attachRuntimeToolsToDispatchRequest(request, runtimeTools, options = {}
3760
4303
  }
3761
4304
  };
3762
4305
  return {
3763
- ...request,
4306
+ ...request2,
3764
4307
  flow: {
3765
- ...request.flow,
4308
+ ...request2.flow,
3766
4309
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
3767
4310
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
3768
4311
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -3772,18 +4315,56 @@ function attachRuntimeToolsToDispatchRequest(request, runtimeTools, options = {}
3772
4315
  }
3773
4316
  };
3774
4317
  }
3775
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request, proposal, options = {}) {
4318
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options = {}) {
3776
4319
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
3777
4320
  if (!decision.approved || !decision.tool) {
3778
- return { decision, request };
4321
+ return { decision, request: request2 };
3779
4322
  }
3780
- const nextRequest = attachRuntimeToolsToDispatchRequest(request, [decision.tool], options.attach);
4323
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request2, [decision.tool], options.attach);
3781
4324
  return {
3782
4325
  decision,
3783
4326
  request: nextRequest
3784
4327
  };
3785
4328
  }
3786
4329
 
4330
+ // src/offload-markers.ts
4331
+ var LEDGER_ARTIFACT_LINE_PREFIX = "Ledger artifact: ";
4332
+ function formatChars(charLength) {
4333
+ return charLength.toLocaleString("en-US");
4334
+ }
4335
+ function buildSendViewOffloadMarker(details) {
4336
+ return `[${details.toolName} output (${formatChars(details.charLength)} chars) saved to ${details.filePath} \u2014 use read_file to retrieve if needed]`;
4337
+ }
4338
+ function buildLedgerOffloadReference(details) {
4339
+ return [
4340
+ `[Output offloaded as ${details.outputId} \u2014 ${formatChars(details.charLength)} chars stored in the marathon context ledger]`,
4341
+ `${LEDGER_ARTIFACT_LINE_PREFIX}${details.relativePath}`,
4342
+ `Preview: ${details.preview}${details.truncated ? "..." : ""}`,
4343
+ "",
4344
+ `Use read_offloaded_output with id "${details.outputId}" to retrieve the full output if needed.`
4345
+ ].join("\n");
4346
+ }
4347
+ var DECLARED_CHARS_PATTERNS = [
4348
+ /—\s*([\d,]+)\s+chars?\s+(?:stored|saved)/i,
4349
+ /\(([\d,]+)\s+chars?\)\s+saved/i
4350
+ ];
4351
+ function extractDeclaredToolResultChars(value) {
4352
+ if (typeof value !== "string") return void 0;
4353
+ for (const pattern of DECLARED_CHARS_PATTERNS) {
4354
+ const match = pattern.exec(value);
4355
+ if (!match?.[1]) continue;
4356
+ const parsed = Number.parseInt(match[1].replace(/,/g, ""), 10);
4357
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
4358
+ }
4359
+ return void 0;
4360
+ }
4361
+ function parseOffloadedOutputId(value) {
4362
+ 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;
4363
+ }
4364
+ function parseLedgerArtifactRelativePath(value) {
4365
+ return value.split("\n").find((line) => line.startsWith(LEDGER_ARTIFACT_LINE_PREFIX))?.slice(LEDGER_ARTIFACT_LINE_PREFIX.length).trim();
4366
+ }
4367
+
3787
4368
  // src/workflow-utils.ts
3788
4369
  function normalizeCandidatePath(candidatePath) {
3789
4370
  return candidatePath.trim().replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/+/g, "/");
@@ -5515,15 +6096,15 @@ var DispatchEndpoint = class {
5515
6096
  * Attach approved runtime tools to a prompt step in a redispatch request.
5516
6097
  * Returns a new request object and does not mutate the original.
5517
6098
  */
5518
- attachApprovedRuntimeTools(request, runtimeTools, options) {
5519
- return attachRuntimeToolsToDispatchRequest(request, runtimeTools, options);
6099
+ attachApprovedRuntimeTools(request2, runtimeTools, options) {
6100
+ return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
5520
6101
  }
5521
6102
  /**
5522
6103
  * Validate a generated runtime tool proposal and attach it to the redispatch
5523
6104
  * request if approved, in one call.
5524
6105
  */
5525
- applyGeneratedRuntimeToolProposal(request, proposal, options) {
5526
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request, proposal, options);
6106
+ applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6107
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
5527
6108
  }
5528
6109
  };
5529
6110
  var ChatEndpoint = class {
@@ -6028,6 +6609,22 @@ async function processAgentStream(body, callbacks) {
6028
6609
  reader.releaseLock();
6029
6610
  }
6030
6611
  }
6612
+ function sleepWithAbort(delayMs, signal) {
6613
+ return new Promise((resolve) => {
6614
+ const onAbort = () => {
6615
+ clearTimeout(timer);
6616
+ resolve();
6617
+ };
6618
+ const timer = setTimeout(() => {
6619
+ signal?.removeEventListener("abort", onAbort);
6620
+ resolve();
6621
+ }, delayMs);
6622
+ if (signal) {
6623
+ if (signal.aborted) onAbort();
6624
+ else signal.addEventListener("abort", onAbort, { once: true });
6625
+ }
6626
+ });
6627
+ }
6031
6628
  var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
6032
6629
  type: "object",
6033
6630
  properties: {
@@ -6059,8 +6656,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
6059
6656
  },
6060
6657
  required: ["name", "description", "toolType", "parametersSchema", "config"]
6061
6658
  };
6062
- function appendRuntimeToolsToAgentRequest(request, runtimeTools) {
6063
- const existing = request.tools?.runtimeTools || [];
6659
+ function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
6660
+ const existing = request2.tools?.runtimeTools || [];
6064
6661
  const existingNames = new Set(existing.map((tool) => tool.name));
6065
6662
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
6066
6663
  name: tool.name,
@@ -6070,9 +6667,9 @@ function appendRuntimeToolsToAgentRequest(request, runtimeTools) {
6070
6667
  ...tool.config ? { config: tool.config } : {}
6071
6668
  }));
6072
6669
  return {
6073
- ...request,
6670
+ ...request2,
6074
6671
  tools: {
6075
- ...request.tools,
6672
+ ...request2.tools,
6076
6673
  runtimeTools: [...existing, ...converted]
6077
6674
  }
6078
6675
  };
@@ -6148,21 +6745,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6148
6745
  * Attach approved runtime tools to an agent execute request.
6149
6746
  * Returns a new request object and does not mutate the original.
6150
6747
  */
6151
- attachApprovedRuntimeTools(request, runtimeTools) {
6152
- return appendRuntimeToolsToAgentRequest(request, runtimeTools);
6748
+ attachApprovedRuntimeTools(request2, runtimeTools) {
6749
+ return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
6153
6750
  }
6154
6751
  /**
6155
6752
  * Validate a generated runtime tool proposal and append it to an agent execute
6156
6753
  * request if approved, in one call.
6157
6754
  */
6158
- applyGeneratedRuntimeToolProposal(request, proposal, options) {
6755
+ applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6159
6756
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
6160
6757
  if (!decision.approved || !decision.tool) {
6161
- return { decision, request };
6758
+ return { decision, request: request2 };
6162
6759
  }
6163
6760
  return {
6164
6761
  decision,
6165
- request: appendRuntimeToolsToAgentRequest(request, [decision.tool])
6762
+ request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
6166
6763
  };
6167
6764
  }
6168
6765
  /**
@@ -6192,13 +6789,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6192
6789
  * // ...
6193
6790
  * ```
6194
6791
  */
6195
- async executeStream(id, data) {
6792
+ async executeStream(id, data, init) {
6196
6793
  return this.client.requestStream(`/agents/${id}/execute`, {
6197
6794
  method: "POST",
6198
6795
  body: JSON.stringify({
6199
6796
  ...data,
6200
6797
  streamResponse: true
6201
- })
6798
+ }),
6799
+ ...init?.signal ? { signal: init.signal } : {}
6202
6800
  });
6203
6801
  }
6204
6802
  /**
@@ -6294,56 +6892,94 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6294
6892
  runtimeTools: [...data.tools?.runtimeTools || [], ...runtimeTools]
6295
6893
  }
6296
6894
  };
6297
- const response = await this.executeStream(id, requestData);
6298
- if (!response.ok) {
6299
- const error = await response.json().catch(() => ({ error: "Unknown error" }));
6300
- throw new Error(error.error || `HTTP ${response.status}`);
6301
- }
6302
- let currentBody = response.body;
6895
+ const abortSignal = options?.abortSignal;
6303
6896
  let accumulatedOutput = "";
6304
6897
  let lastKnownCost = 0;
6305
6898
  let lastKnownTokens;
6899
+ let lastSeenExecutionId = "";
6306
6900
  let pauseCount = 0;
6307
6901
  let discoveryPauseCount = 0;
6308
6902
  let consecutiveDiscoveryPauseCount = 0;
6309
6903
  const toolNameCounts = {};
6310
6904
  let recentActionKeys = [];
6311
6905
  const toolMessages = [];
6906
+ const finishAborted = (executionId) => {
6907
+ const abortCompleteEvent = {
6908
+ type: "agent_complete",
6909
+ executionId,
6910
+ seq: 0,
6911
+ agentId: id,
6912
+ success: true,
6913
+ iterations: 1,
6914
+ stopReason: "end_turn",
6915
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
6916
+ totalCost: lastKnownCost,
6917
+ ...lastKnownTokens ? { totalTokens: lastKnownTokens } : {},
6918
+ finalOutput: [accumulatedOutput.trim(), "Session aborted by user request."].filter(Boolean).join("\n\n"),
6919
+ duration: 0
6920
+ };
6921
+ callbacks?.onAgentComplete?.(abortCompleteEvent);
6922
+ return { completeEvent: abortCompleteEvent, toolMessages };
6923
+ };
6924
+ let response;
6925
+ try {
6926
+ response = await this.executeStream(id, requestData, {
6927
+ ...abortSignal ? { signal: abortSignal } : {}
6928
+ });
6929
+ } catch (error) {
6930
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6931
+ throw error;
6932
+ }
6933
+ if (!response.ok) {
6934
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
6935
+ throw new Error(error.error || `HTTP ${response.status}`);
6936
+ }
6937
+ let currentBody = response.body;
6312
6938
  while (true) {
6313
6939
  let pausedEvent = null;
6314
6940
  let completeEvent = null;
6315
- await processAgentStream(currentBody, {
6316
- ...callbacks,
6317
- onTurnDelta: (event) => {
6318
- if (event.contentType === "text") {
6319
- accumulatedOutput += event.delta;
6320
- }
6321
- callbacks?.onTurnDelta?.(event);
6322
- },
6323
- onTurnComplete: (event) => {
6324
- if (typeof event.cost === "number") {
6325
- lastKnownCost = event.cost;
6326
- }
6327
- if (event.tokens) {
6328
- lastKnownTokens = event.tokens;
6329
- }
6330
- callbacks?.onTurnComplete?.(event);
6331
- },
6332
- onAgentPaused: (event) => {
6333
- pausedEvent = event;
6334
- callbacks?.onAgentPaused?.(event);
6335
- },
6336
- onAgentComplete: (event) => {
6337
- if (!event.finalOutput && accumulatedOutput) {
6338
- event.finalOutput = accumulatedOutput;
6941
+ try {
6942
+ await processAgentStream(currentBody, {
6943
+ ...callbacks,
6944
+ onAgentStart: (event) => {
6945
+ lastSeenExecutionId = event.executionId;
6946
+ callbacks?.onAgentStart?.(event);
6947
+ },
6948
+ onTurnDelta: (event) => {
6949
+ if (event.contentType === "text") {
6950
+ accumulatedOutput += event.delta;
6951
+ }
6952
+ callbacks?.onTurnDelta?.(event);
6953
+ },
6954
+ onTurnComplete: (event) => {
6955
+ if (typeof event.cost === "number") {
6956
+ lastKnownCost = event.cost;
6957
+ }
6958
+ if (event.tokens) {
6959
+ lastKnownTokens = event.tokens;
6960
+ }
6961
+ callbacks?.onTurnComplete?.(event);
6962
+ },
6963
+ onAgentPaused: (event) => {
6964
+ pausedEvent = event;
6965
+ callbacks?.onAgentPaused?.(event);
6966
+ },
6967
+ onAgentComplete: (event) => {
6968
+ if (!event.finalOutput && accumulatedOutput) {
6969
+ event.finalOutput = accumulatedOutput;
6970
+ }
6971
+ completeEvent = event;
6972
+ callbacks?.onAgentComplete?.(event);
6339
6973
  }
6340
- completeEvent = event;
6341
- callbacks?.onAgentComplete?.(event);
6342
- }
6343
- });
6974
+ });
6975
+ } catch (error) {
6976
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6977
+ throw error;
6978
+ }
6344
6979
  if (completeEvent) return { completeEvent, toolMessages };
6345
6980
  if (pausedEvent) {
6346
6981
  const { toolName, toolId, parameters, executionId } = pausedEvent;
6982
+ lastSeenExecutionId = executionId;
6347
6983
  const toolDef = localTools[toolName];
6348
6984
  if (!toolDef) {
6349
6985
  throw new Error(`Local tool "${toolName}" required but not provided`);
@@ -6458,6 +7094,19 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6458
7094
  callbacks?.onAgentComplete?.(forcedCompleteEvent);
6459
7095
  return { completeEvent: forcedCompleteEvent, toolMessages };
6460
7096
  }
7097
+ if (abortSignal?.aborted) {
7098
+ callbacks?.onLocalToolExecutionComplete?.({
7099
+ executionId,
7100
+ toolCallId: toolId,
7101
+ toolName,
7102
+ parameters: parsedParams,
7103
+ result: toolResult,
7104
+ success: true,
7105
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
7106
+ durationMs: Date.now() - localExecutionStartedAtMs
7107
+ });
7108
+ return finishAborted(executionId);
7109
+ }
6461
7110
  if (options?.shouldInterrupt?.()) {
6462
7111
  callbacks?.onLocalToolExecutionComplete?.({
6463
7112
  executionId,
@@ -6491,15 +7140,22 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6491
7140
  callbacks?.onAgentComplete?.(interruptCompleteEvent);
6492
7141
  return { completeEvent: interruptCompleteEvent, toolMessages };
6493
7142
  }
6494
- const resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
6495
- method: "POST",
6496
- body: JSON.stringify({
6497
- executionId,
6498
- toolOutputs: { [toolName]: toolResult },
6499
- streamResponse: true,
6500
- debugMode: data.debugMode
6501
- })
6502
- });
7143
+ let resumeResponse;
7144
+ try {
7145
+ resumeResponse = await this.client.requestStream(`/agents/${id}/resume`, {
7146
+ method: "POST",
7147
+ body: JSON.stringify({
7148
+ executionId,
7149
+ toolOutputs: { [toolName]: toolResult },
7150
+ streamResponse: true,
7151
+ debugMode: data.debugMode
7152
+ }),
7153
+ ...abortSignal ? { signal: abortSignal } : {}
7154
+ });
7155
+ } catch (error) {
7156
+ if (abortSignal?.aborted) return finishAborted(executionId);
7157
+ throw error;
7158
+ }
6503
7159
  if (!resumeResponse.ok) {
6504
7160
  const error = await resumeResponse.json().catch(() => ({ error: "Unknown error" }));
6505
7161
  throw new Error(error.error || `HTTP ${resumeResponse.status}`);
@@ -6517,6 +7173,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6517
7173
  currentBody = resumeResponse.body;
6518
7174
  continue;
6519
7175
  }
7176
+ if (abortSignal?.aborted) return finishAborted(lastSeenExecutionId);
6520
7177
  return null;
6521
7178
  }
6522
7179
  }
@@ -6788,7 +7445,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6788
7445
  reasons.push("Best candidate file has not been verified (read back after writing)");
6789
7446
  }
6790
7447
  if (state.verificationRequired && !state.lastVerificationPassed && !trace.verificationPassed) {
6791
- reasons.push("Verification has not passed \u2014 run a verification command (run_check) before completing");
7448
+ reasons.push(
7449
+ "Verification has not passed \u2014 run a verification command (run_check) before completing"
7450
+ );
6792
7451
  }
6793
7452
  return reasons.length > 0 ? reasons.join("; ") : "Completion gates not satisfied for the current workflow phase";
6794
7453
  }
@@ -6831,32 +7490,71 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6831
7490
  );
6832
7491
  }
6833
7492
  /**
6834
- * Compact old tool results based on context mode and window setting.
6835
- * Modifies messages in-place.
7493
+ * Resolve the replay window: the base index sliced off the full history and
7494
+ * the durable summary message that stands in for everything before it.
7495
+ * The base is honored ONLY when the latest summary actually exists —
7496
+ * slicing history with no summary substitute would silently drop prior
7497
+ * context (defensive against states that carried a base but lost their
7498
+ * summaries). The base is also clamped so a stale pointer (fork truncation,
7499
+ * trimmed legacy state) never slices past the end of the array.
6836
7500
  */
6837
- compactToolResults(messages, newToolMessages, taskName, mode, window) {
6838
- if (mode === "full-inline") return;
7501
+ resolveWindowReplay(state, messageCount) {
7502
+ const base = state.contextWindowBaseIndex ?? 0;
7503
+ if (!Number.isFinite(base) || base <= 0) {
7504
+ return { windowBase: 0, windowSummaryMessages: [] };
7505
+ }
7506
+ const summaries = state.contextCompactionSummaries;
7507
+ const latest = summaries?.[summaries.length - 1];
7508
+ if (!latest) {
7509
+ return { windowBase: 0, windowSummaryMessages: [] };
7510
+ }
7511
+ return {
7512
+ windowBase: Math.min(Math.floor(base), messageCount),
7513
+ windowSummaryMessages: [{ role: "system", content: latest.content }]
7514
+ };
7515
+ }
7516
+ /**
7517
+ * Derive the message view sent to the model based on context mode and window
7518
+ * setting. This never mutates persisted marathon history; masking/offloading
7519
+ * is a send-time view over the full-fidelity ledger/history.
7520
+ */
7521
+ deriveToolContextMessages(messages, taskName, mode, window) {
7522
+ if (mode === "full-inline") return [...messages];
7523
+ const maskMessage = (msg) => ({
7524
+ ...msg,
7525
+ toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
7526
+ });
7527
+ const view = [...messages];
6839
7528
  if (window === "session") {
6840
- for (const msg of messages) {
7529
+ const lastUserIndex = view.reduce(
7530
+ (lastIndex, message, index) => message.role === "user" ? index : lastIndex,
7531
+ -1
7532
+ );
7533
+ for (let index = 0; index < view.length; index++) {
7534
+ if (lastUserIndex >= 0 && index > lastUserIndex) continue;
7535
+ const msg = view[index];
7536
+ if (!msg) continue;
6841
7537
  if (msg.role === "tool" && msg.toolResults) {
6842
- msg.toolResults = msg.toolResults.map((tr) => this.compactOneResult(tr, taskName, mode));
7538
+ view[index] = maskMessage(msg);
6843
7539
  }
6844
7540
  }
6845
7541
  } else {
6846
- const newToolResultCount = newToolMessages.filter((m) => m.role === "tool").length;
6847
- const keepInlineFromExisting = Math.max(0, window - newToolResultCount);
6848
7542
  const toolResultIndices = [];
6849
- for (let i = 0; i < messages.length; i++) {
6850
- if (messages[i].role === "tool" && messages[i].toolResults) {
7543
+ for (let i = 0; i < view.length; i++) {
7544
+ const message = view[i];
7545
+ if (message?.role === "tool" && message.toolResults) {
6851
7546
  toolResultIndices.push(i);
6852
7547
  }
6853
7548
  }
6854
- const compactUpTo = toolResultIndices.length - keepInlineFromExisting;
7549
+ const compactUpTo = toolResultIndices.length - window;
6855
7550
  for (let j = 0; j < compactUpTo && j < toolResultIndices.length; j++) {
6856
- const msg = messages[toolResultIndices[j]];
6857
- msg.toolResults = msg.toolResults.map((tr) => this.compactOneResult(tr, taskName, mode));
7551
+ const index = toolResultIndices[j];
7552
+ const msg = index === void 0 ? void 0 : view[index];
7553
+ if (!msg) continue;
7554
+ view[index] = maskMessage(msg);
6858
7555
  }
6859
7556
  }
7557
+ return view;
6860
7558
  }
6861
7559
  compactOneResult(tr, taskName, mode) {
6862
7560
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
@@ -6892,10 +7590,20 @@ var _AgentsEndpoint = class _AgentsEndpoint {
6892
7590
  }
6893
7591
  const slug = this.sanitizeTaskSlug(taskName || "task");
6894
7592
  const dir = `.runtype/marathons/${slug}/tool-outputs`;
6895
- fs.mkdirSync(dir, { recursive: true });
6896
7593
  const filePath = `${dir}/${toolCallId}.txt`;
6897
- fs.writeFileSync(filePath, resultStr, "utf-8");
6898
- return `[${toolName} output (${resultStr.length} chars) saved to ${filePath} \u2014 use read_file to retrieve if needed]`;
7594
+ try {
7595
+ if (!fs.existsSync(filePath)) {
7596
+ fs.mkdirSync(dir, { recursive: true });
7597
+ fs.writeFileSync(filePath, resultStr, "utf-8");
7598
+ }
7599
+ } catch {
7600
+ return result;
7601
+ }
7602
+ return buildSendViewOffloadMarker({
7603
+ toolName,
7604
+ charLength: resultStr.length,
7605
+ filePath
7606
+ });
6899
7607
  }
6900
7608
  getDefaultPlanPath(taskName) {
6901
7609
  return getDefaultPlanPath(taskName);
@@ -7017,6 +7725,7 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7017
7725
  const migratedPlanPath = workflowVariant === "external" && planPath === this.getDefaultPlanPath(taskName) ? this.getDefaultExternalReportPath(taskName) : planPath;
7018
7726
  const candidatePaths = this.dedupeNormalizedCandidatePaths(resumeState.candidatePaths);
7019
7727
  const recentReadPaths = this.dedupeNormalizedCandidatePaths(resumeState.recentReadPaths);
7728
+ const contextCompactionSummaries = (resumeState.contextCompactionSummaries ?? []).slice(-20);
7020
7729
  const normalizedBestCandidatePath = typeof resumeState.bestCandidatePath === "string" && resumeState.bestCandidatePath.trim() ? this.normalizeCandidatePath(resumeState.bestCandidatePath) : void 0;
7021
7730
  const bestCandidatePath = normalizedBestCandidatePath && !this.isMarathonArtifactPath(normalizedBestCandidatePath) ? normalizedBestCandidatePath : [...candidatePaths, ...recentReadPaths].sort(
7022
7731
  (left, right) => this.scoreCandidatePath(right) - this.scoreCandidatePath(left)
@@ -7036,10 +7745,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7036
7745
  bestCandidateVerified: Boolean(resumeState.bestCandidateVerified),
7037
7746
  ...resumeState.verificationRequired !== void 0 ? { verificationRequired: resumeState.verificationRequired } : {},
7038
7747
  lastVerificationPassed: Boolean(resumeState.lastVerificationPassed),
7039
- ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? { consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions } : {},
7748
+ ...resumeState.consecutiveBlockedVerificationSessions !== void 0 ? {
7749
+ consecutiveBlockedVerificationSessions: resumeState.consecutiveBlockedVerificationSessions
7750
+ } : {},
7040
7751
  ...resumeState.isCreationTask !== void 0 ? { isCreationTask: resumeState.isCreationTask } : {},
7041
7752
  ...resumeState.workflowVariant !== void 0 ? { workflowVariant: resumeState.workflowVariant } : {},
7042
7753
  ...resumeState.workflowState !== void 0 ? { workflowState: resumeState.workflowState } : {},
7754
+ ...contextCompactionSummaries.length ? { contextCompactionSummaries } : {},
7755
+ ...typeof resumeState.contextWindowBaseIndex === "number" && Number.isFinite(resumeState.contextWindowBaseIndex) && resumeState.contextWindowBaseIndex > 0 ? { contextWindowBaseIndex: Math.floor(resumeState.contextWindowBaseIndex) } : {},
7043
7756
  ...typeof resumeState.outputRoot === "string" && resumeState.outputRoot.trim() ? { outputRoot: resumeState.outputRoot.trim().replace(/\\/g, "/").replace(/\/+/g, "/") } : {}
7044
7757
  };
7045
7758
  }
@@ -7505,8 +8218,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7505
8218
  } : {},
7506
8219
  ...seededResumeState?.candidatePaths ? { candidatePaths: seededResumeState.candidatePaths } : {},
7507
8220
  ...seededResumeState?.recentReadPaths ? { recentReadPaths: seededResumeState.recentReadPaths } : {},
7508
- ...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {}
8221
+ ...seededResumeState?.recentActionKeys ? { recentActionKeys: seededResumeState.recentActionKeys } : {},
8222
+ ...seededResumeState?.contextCompactionSummaries?.length ? { contextCompactionSummaries: seededResumeState.contextCompactionSummaries } : {},
8223
+ ...typeof seededResumeState?.contextWindowBaseIndex === "number" ? { contextWindowBaseIndex: seededResumeState.contextWindowBaseIndex } : {}
7509
8224
  };
8225
+ if (options.previousMessages && options.previousMessages.length > 0) {
8226
+ state.messages = options.previousMessages.map((message) => structuredClone(message));
8227
+ }
7510
8228
  state.workflowVariant = classifiedVariant;
7511
8229
  state.isCreationTask = seededResumeState?.isCreationTask ?? state.workflowVariant === "create";
7512
8230
  state.outputRoot = seededResumeState?.outputRoot ?? (state.isCreationTask ? "public/" : void 0);
@@ -7539,6 +8257,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7539
8257
  }
7540
8258
  }
7541
8259
  for (let session = 0; session < maxSessions; session++) {
8260
+ if (options.abortSignal?.aborted) {
8261
+ state.status = "paused";
8262
+ break;
8263
+ }
7542
8264
  const phaseAtSessionStart = state.workflowPhase;
7543
8265
  const sessionTrace = this.createEmptyToolTrace();
7544
8266
  const sessionLocalTools = this.wrapLocalToolsForTrace(
@@ -7575,7 +8297,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7575
8297
  localTools: options.localTools,
7576
8298
  builtinToolSchemas,
7577
8299
  onContextCompaction: options.onContextCompaction,
7578
- onContextNotice: options.onContextNotice
8300
+ onContextNotice: options.onContextNotice,
8301
+ toolContextMode: options.toolContextMode || "hot-tail",
8302
+ toolWindow: options.toolWindow ?? "session"
7579
8303
  },
7580
8304
  queuedSteeringMessages
7581
8305
  );
@@ -7605,7 +8329,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7605
8329
  sessionCallbacks,
7606
8330
  {
7607
8331
  onLocalToolResult: this.createLocalToolLoopGuard(state, sessionTrace, workflow),
7608
- shouldInterrupt: options.hasQueuedUserMessages
8332
+ shouldInterrupt: options.hasQueuedUserMessages,
8333
+ ...options.abortSignal ? { abortSignal: options.abortSignal } : {}
7609
8334
  },
7610
8335
  state.taskName
7611
8336
  );
@@ -7754,22 +8479,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7754
8479
  }
7755
8480
  }
7756
8481
  if (!state.messages) state.messages = [];
7757
- if (this.isCompactHistoryMessageSet(messages)) {
7758
- state.messages = [...messages];
7759
- } else if (state.messages.length > 0 && messages.length > state.messages.length) {
7760
- const newMessages = messages.slice(state.messages.length);
7761
- state.messages.push(...newMessages);
7762
- } else {
8482
+ const sentUserMessage = messages[messages.length - 1];
8483
+ if (sentUserMessage?.role === "user") {
8484
+ state.messages.push(sentUserMessage);
8485
+ } else if (state.messages.length === 0) {
7763
8486
  state.messages.push(...messages);
7764
8487
  }
7765
8488
  if (sessionToolMessages.length > 0) {
7766
- this.compactToolResults(
7767
- state.messages,
7768
- sessionToolMessages,
7769
- state.taskName,
7770
- options.toolContextMode || "hot-tail",
7771
- options.toolWindow ?? "session"
7772
- );
7773
8489
  state.messages.push(...sessionToolMessages);
7774
8490
  }
7775
8491
  const assistantContent = effectiveSessionOutput || `[Session ${session + 1} completed (${sessionResult.stopReason}). No text output captured.]`;
@@ -7789,7 +8505,10 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7789
8505
  workflow
7790
8506
  );
7791
8507
  if (detectedTaskCompletion && !acceptedTaskCompletion) {
7792
- state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(state, sessionTrace);
8508
+ state.lastCompletionRejectionReason = this.computeCompletionRejectionReason(
8509
+ state,
8510
+ sessionTrace
8511
+ );
7793
8512
  if (state.verificationRequired && !state.lastVerificationPassed && !sessionTrace.verificationPassed && !sessionTrace.verificationAttempted) {
7794
8513
  state.consecutiveBlockedVerificationSessions = (state.consecutiveBlockedVerificationSessions || 0) + 1;
7795
8514
  if ((state.consecutiveBlockedVerificationSessions || 0) >= 2) {
@@ -7808,24 +8527,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7808
8527
  }
7809
8528
  if (sessionResult.stopReason === "complete" && !detectedTaskCompletion) {
7810
8529
  const currentPhase = workflow.phases.find((p) => p.name === state.workflowPhase);
7811
- const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(state, sessionTrace) : true;
8530
+ const gatesSatisfied = currentPhase?.canAcceptCompletion ? currentPhase.canAcceptCompletion(
8531
+ state,
8532
+ sessionTrace
8533
+ ) : true;
7812
8534
  if (gatesSatisfied) {
7813
8535
  state.status = "complete";
7814
8536
  }
7815
8537
  } else if (sessionResult.stopReason === "error") {
7816
8538
  if (_AgentsEndpoint.isRetryableSessionError(sessionResult.error) && consecutiveServerNetworkErrors < maxServerNetworkRetries) {
7817
8539
  consecutiveServerNetworkErrors++;
7818
- const delayMs = Math.min(
7819
- 5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1),
7820
- 3e4
7821
- );
8540
+ const delayMs = Math.min(5e3 * Math.pow(2, consecutiveServerNetworkErrors - 1), 3e4);
7822
8541
  const delaySec = Math.round(delayMs / 1e3);
7823
8542
  await this.emitContextNotice(options.onContextNotice, {
7824
8543
  kind: "server_network_retry",
7825
8544
  sessionIndex: session,
7826
8545
  message: `Server network error: ${sessionResult.error}. Retrying in ${delaySec}s (attempt ${consecutiveServerNetworkErrors}/${maxServerNetworkRetries})...`
7827
8546
  });
7828
- await new Promise((resolve) => setTimeout(resolve, delayMs));
8547
+ await sleepWithAbort(delayMs, options.abortSignal);
7829
8548
  } else {
7830
8549
  state.status = "error";
7831
8550
  }
@@ -7840,6 +8559,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7840
8559
  } else if (session + 1 >= maxSessions) {
7841
8560
  state.status = "max_sessions";
7842
8561
  }
8562
+ if (options.abortSignal?.aborted) {
8563
+ state.status = "paused";
8564
+ }
7843
8565
  if (options.trackProgress) {
7844
8566
  recordId = await this.syncProgressRecord(state, recordId);
7845
8567
  }
@@ -7916,6 +8638,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7916
8638
  return 0;
7917
8639
  }
7918
8640
  }
8641
+ extractDeclaredToolResultChars(value) {
8642
+ return extractDeclaredToolResultChars(value);
8643
+ }
7919
8644
  estimateMessageContentTokens(content) {
7920
8645
  if (typeof content === "string") return this.estimateTextTokens(content);
7921
8646
  return content.reduce((total, part) => {
@@ -7934,12 +8659,14 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7934
8659
  0
7935
8660
  );
7936
8661
  }
7937
- estimateToolResultTokens(toolResults) {
8662
+ estimateToolResultTokens(toolResults, options) {
7938
8663
  if (!toolResults || toolResults.length === 0) return 0;
7939
- return toolResults.reduce(
7940
- (sum, toolResult) => sum + 12 + this.estimateTextTokens(toolResult.toolName) + this.estimateUnknownTokens(toolResult.result),
7941
- 0
7942
- );
8664
+ return toolResults.reduce((sum, toolResult) => {
8665
+ const resultTokens = this.estimateUnknownTokens(toolResult.result);
8666
+ const declaredChars = options?.useDeclaredSize ? this.extractDeclaredToolResultChars(toolResult.result) : void 0;
8667
+ const declaredTokens = typeof declaredChars === "number" ? Math.ceil(declaredChars / 4) : 0;
8668
+ return sum + 12 + this.estimateTextTokens(toolResult.toolName) + Math.max(resultTokens, declaredTokens);
8669
+ }, 0);
7943
8670
  }
7944
8671
  estimateMessageTokens(message) {
7945
8672
  return 6 + this.estimateMessageContentTokens(message.content) + this.estimateToolCallTokens(message.toolCalls) + this.estimateToolResultTokens(message.toolResults);
@@ -7947,13 +8674,15 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7947
8674
  estimateConversationTokens(messages) {
7948
8675
  return messages.reduce((sum, message) => sum + this.estimateMessageTokens(message), 0);
7949
8676
  }
7950
- estimateConversationBreakdown(messages) {
8677
+ estimateConversationBreakdown(messages, options) {
7951
8678
  let historyTokens = 0;
7952
8679
  let toolOutputTokens = 0;
7953
8680
  for (const message of messages) {
7954
8681
  const contentTokens = this.estimateMessageContentTokens(message.content);
7955
8682
  const toolCallTokens = this.estimateToolCallTokens(message.toolCalls);
7956
- const toolResultTokens = this.estimateToolResultTokens(message.toolResults);
8683
+ const toolResultTokens = this.estimateToolResultTokens(message.toolResults, {
8684
+ useDeclaredSize: options?.useDeclaredToolResultSizes
8685
+ });
7957
8686
  const messageTotal = 6 + contentTokens + toolCallTokens + toolResultTokens;
7958
8687
  if (message.role === "tool") {
7959
8688
  toolOutputTokens += messageTotal;
@@ -8007,13 +8736,24 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8007
8736
  const compactInstructions = config.compactInstructions;
8008
8737
  return typeof compactInstructions === "string" && compactInstructions.trim() ? compactInstructions.trim() : void 0;
8009
8738
  }
8010
- extractArtifactReferences(state) {
8739
+ extractArtifactReferencesFromMessages(messages = []) {
8011
8740
  const references = /* @__PURE__ */ new Set();
8012
8741
  const offloadPrefix = "[Output saved to ";
8013
- for (const message of state.messages ?? []) {
8742
+ const ledgerArtifactPrefix = LEDGER_ARTIFACT_LINE_PREFIX;
8743
+ const savedToPattern = /saved to\s+([^—\]\n]+?)(?:\s+—|\]|\n|$)/gi;
8744
+ for (const message of messages) {
8014
8745
  if (!message.toolResults) continue;
8015
8746
  for (const toolResult of message.toolResults) {
8016
8747
  if (typeof toolResult.result !== "string") continue;
8748
+ for (const line of toolResult.result.split("\n")) {
8749
+ if (line.startsWith(ledgerArtifactPrefix)) {
8750
+ references.add(line.slice(ledgerArtifactPrefix.length).trim());
8751
+ }
8752
+ }
8753
+ for (const match of toolResult.result.matchAll(savedToPattern)) {
8754
+ const pathText = match[1]?.trim();
8755
+ if (pathText) references.add(pathText);
8756
+ }
8017
8757
  let startIndex = 0;
8018
8758
  while (startIndex < toolResult.result.length) {
8019
8759
  const prefixIndex = toolResult.result.indexOf(offloadPrefix, startIndex);
@@ -8031,6 +8771,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8031
8771
  }
8032
8772
  }
8033
8773
  }
8774
+ return Array.from(references);
8775
+ }
8776
+ extractArtifactReferences(state, additionalReferences = []) {
8777
+ const references = /* @__PURE__ */ new Set([
8778
+ ...this.extractArtifactReferencesFromMessages(state.messages ?? []),
8779
+ ...additionalReferences
8780
+ ]);
8034
8781
  if (state.planPath) {
8035
8782
  references.add(state.planPath);
8036
8783
  }
@@ -8038,6 +8785,9 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8038
8785
  }
8039
8786
  buildContextBudgetBreakdown(details) {
8040
8787
  const conversationBreakdown = this.estimateConversationBreakdown(details.historyMessages);
8788
+ const sourceConversationBreakdown = details.sourceHistoryMessages ? this.estimateConversationBreakdown(details.sourceHistoryMessages, {
8789
+ useDeclaredToolResultSizes: true
8790
+ }) : conversationBreakdown;
8041
8791
  const currentTurnTokens = this.estimateTextTokens(details.currentTurnContent);
8042
8792
  const toolDefinitionTokens = this.estimateToolDefinitionTokens(
8043
8793
  details.localTools,
@@ -8054,15 +8804,26 @@ ${details.summaryText}
8054
8804
 
8055
8805
  Do NOT redo any of the above work.`
8056
8806
  ) : void 0;
8807
+ const sendEstimatedInputTokens = conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
8808
+ const estimatedInputTokens = sourceConversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens;
8809
+ const toolOutputReductionTokens = Math.max(
8810
+ 0,
8811
+ sourceConversationBreakdown.toolOutputTokens - conversationBreakdown.toolOutputTokens
8812
+ );
8057
8813
  return {
8058
- historyTokens: conversationBreakdown.historyTokens,
8059
- toolOutputTokens: conversationBreakdown.toolOutputTokens,
8814
+ historyTokens: sourceConversationBreakdown.historyTokens,
8815
+ toolOutputTokens: sourceConversationBreakdown.toolOutputTokens,
8060
8816
  currentTurnTokens,
8061
8817
  toolDefinitionTokens,
8062
8818
  ...summaryTokens ? { summaryTokens } : {},
8063
8819
  ...reservedOutputTokens ? { reservedOutputTokens } : {},
8064
8820
  ...effectiveInputBudgetTokens ? { effectiveInputBudgetTokens } : {},
8065
- estimatedInputTokens: conversationBreakdown.estimatedInputTokens + currentTurnTokens + toolDefinitionTokens
8821
+ ...toolOutputReductionTokens > 0 ? {
8822
+ sendEstimatedInputTokens,
8823
+ sendToolOutputTokens: conversationBreakdown.toolOutputTokens,
8824
+ toolOutputReductionTokens
8825
+ } : {},
8826
+ estimatedInputTokens
8066
8827
  };
8067
8828
  }
8068
8829
  async emitContextCompactionEvent(onContextCompaction, event) {
@@ -8102,16 +8863,33 @@ Do NOT redo any of the above work.`
8102
8863
  state,
8103
8864
  userContent,
8104
8865
  details.compactInstructions,
8105
- details.mode
8866
+ details.mode,
8867
+ details.artifactReferences
8106
8868
  );
8869
+ const summaryMessage = compactMessages[0];
8870
+ if (summaryMessage?.role === "system" && typeof summaryMessage.content === "string") {
8871
+ const existingSummaries = state.contextCompactionSummaries ?? [];
8872
+ state.contextCompactionSummaries = [
8873
+ ...existingSummaries,
8874
+ {
8875
+ id: `ctx_${sessionIndex + 1}_${existingSummaries.length + 1}`,
8876
+ sessionIndex: sessionIndex + 1,
8877
+ mode: details.mode,
8878
+ strategy: details.strategy,
8879
+ content: summaryMessage.content,
8880
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8881
+ }
8882
+ ].slice(-20);
8883
+ }
8884
+ state.contextWindowBaseIndex = state.messages?.length ?? 0;
8107
8885
  await this.emitContextCompactionEvent(details.onContextCompaction, {
8108
8886
  phase: "complete",
8109
8887
  ...baseEvent
8110
8888
  });
8111
8889
  return compactMessages;
8112
8890
  }
8113
- buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto") {
8114
- const summary = this.generateCompactSummary(state, compactInstructions);
8891
+ buildCompactHistoryMessages(state, userContent, compactInstructions, mode = "auto", artifactReferences) {
8892
+ const summary = this.generateCompactSummary(state, compactInstructions, artifactReferences);
8115
8893
  const prefix = mode === "forced" ? this.getForcedCompactionSummaryPrefix(state) : _AgentsEndpoint.AUTO_COMPACT_SUMMARY_PREFIX;
8116
8894
  return [
8117
8895
  {
@@ -8128,16 +8906,11 @@ Do NOT redo any of the above work.`
8128
8906
  }
8129
8907
  ];
8130
8908
  }
8131
- isCompactHistoryMessageSet(messages) {
8132
- if (messages.length === 0) return false;
8133
- const firstMessage = messages[0];
8134
- 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));
8135
- }
8136
8909
  /**
8137
8910
  * Generate a compact summary of prior work for continuation context.
8138
8911
  * Used when compact mode is enabled to keep token usage low.
8139
8912
  */
8140
- generateCompactSummary(state, compactInstructions) {
8913
+ generateCompactSummary(state, compactInstructions, additionalArtifactReferences = []) {
8141
8914
  const recentSessions = (state.sessions ?? []).slice(-5);
8142
8915
  const sessionSummaries = recentSessions.map(
8143
8916
  (session) => `- Session ${session.index}: ${session.stopReason} ($${session.cost.toFixed(4)}) ${session.outputPreview.slice(0, 160)}`
@@ -8151,7 +8924,7 @@ Do NOT redo any of the above work.`
8151
8924
  )
8152
8925
  ).slice(0, 8);
8153
8926
  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.";
8154
- const artifactReferences = this.extractArtifactReferences(state);
8927
+ const artifactReferences = this.extractArtifactReferences(state, additionalArtifactReferences);
8155
8928
  const pendingNextStep = state.lastStopReason === "complete" ? "Confirm nothing else remains before declaring the task complete." : `Continue the ${state.workflowPhase || "research"} phase without redoing prior work.`;
8156
8929
  const instructions = compactInstructions || this.buildDefaultCompactInstructions();
8157
8930
  return [
@@ -8366,14 +9139,23 @@ Do NOT redo any of the above work.`
8366
9139
  const phaseBlock = ["", this.buildPhaseInstructions(state, wf)].join("\n");
8367
9140
  const candidateBlock = wf.buildCandidateBlock?.(state) ?? "";
8368
9141
  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.`;
8369
- const steeringLines = steeringMessages && steeringMessages.length > 0 ? [
8370
- "--- User steering (queued during the previous session) ---",
8371
- ...steeringMessages,
8372
- ""
8373
- ] : [];
9142
+ const steeringLines = steeringMessages && steeringMessages.length > 0 ? ["--- User steering (queued during the previous session) ---", ...steeringMessages, ""] : [];
8374
9143
  if (continuationContext && sessionIndex === 0) {
8375
- const replayHistoryMessages = this.sanitizeReplayHistoryMessages(
8376
- continuationContext.previousMessages
9144
+ const resumeWindow = this.resolveWindowReplay(
9145
+ state,
9146
+ continuationContext.previousMessages.length
9147
+ );
9148
+ const sourceReplayHistoryMessages = [
9149
+ ...resumeWindow.windowSummaryMessages,
9150
+ ...this.sanitizeReplayHistoryMessages(
9151
+ continuationContext.previousMessages.slice(resumeWindow.windowBase)
9152
+ )
9153
+ ];
9154
+ const replayHistoryMessages = this.deriveToolContextMessages(
9155
+ sourceReplayHistoryMessages,
9156
+ state.taskName,
9157
+ compactionOptions?.toolContextMode || "hot-tail",
9158
+ compactionOptions?.toolWindow ?? "session"
8377
9159
  );
8378
9160
  const continuationGuardrail = this.buildContinuationGuardrail(state);
8379
9161
  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.";
@@ -8397,9 +9179,15 @@ Do NOT redo any of the above work.`
8397
9179
  content: userContent
8398
9180
  }
8399
9181
  ];
8400
- const summaryText = this.generateCompactSummary(state, compactInstructions);
9182
+ const replayArtifactReferences = this.extractArtifactReferencesFromMessages(replayHistoryMessages);
9183
+ const summaryText = this.generateCompactSummary(
9184
+ state,
9185
+ compactInstructions,
9186
+ replayArtifactReferences
9187
+ );
8401
9188
  const breakdown = this.buildContextBudgetBreakdown({
8402
9189
  historyMessages: replayHistoryMessages,
9190
+ sourceHistoryMessages: sourceReplayHistoryMessages,
8403
9191
  currentTurnContent: userContent,
8404
9192
  localTools: compactionOptions?.localTools,
8405
9193
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8425,7 +9213,8 @@ Do NOT redo any of the above work.`
8425
9213
  reservedOutputTokens: breakdown.reservedOutputTokens,
8426
9214
  breakdown,
8427
9215
  onContextCompaction: compactionOptions?.onContextCompaction,
8428
- compactInstructions
9216
+ compactInstructions,
9217
+ artifactReferences: replayArtifactReferences
8429
9218
  }
8430
9219
  ),
8431
9220
  requestContextManagement
@@ -8449,7 +9238,8 @@ Do NOT redo any of the above work.`
8449
9238
  reservedOutputTokens: breakdown.reservedOutputTokens,
8450
9239
  breakdown,
8451
9240
  onContextCompaction: compactionOptions?.onContextCompaction,
8452
- compactInstructions
9241
+ compactInstructions,
9242
+ artifactReferences: replayArtifactReferences
8453
9243
  }
8454
9244
  ),
8455
9245
  requestContextManagement
@@ -8500,23 +9290,45 @@ Do NOT redo any of the above work.`
8500
9290
  "Do not redo previous work. If the task is already complete, respond with TASK_COMPLETE."
8501
9291
  ].join("\n");
8502
9292
  const MAX_HISTORY_MESSAGES = 60;
8503
- let historyMessages = this.sanitizeReplayHistoryMessages(state.messages);
8504
- if (historyMessages.length > MAX_HISTORY_MESSAGES) {
8505
- const trimmedHistory = this.trimReplayHistoryMessages(historyMessages, MAX_HISTORY_MESSAGES);
8506
- historyMessages = trimmedHistory.historyMessages;
9293
+ const { windowBase, windowSummaryMessages } = this.resolveWindowReplay(
9294
+ state,
9295
+ state.messages.length
9296
+ );
9297
+ let sourceHistoryMessages = this.sanitizeReplayHistoryMessages(
9298
+ state.messages.slice(windowBase)
9299
+ );
9300
+ if (sourceHistoryMessages.length > MAX_HISTORY_MESSAGES) {
9301
+ const trimmedHistory = this.trimReplayHistoryMessages(
9302
+ sourceHistoryMessages,
9303
+ MAX_HISTORY_MESSAGES
9304
+ );
9305
+ sourceHistoryMessages = trimmedHistory.historyMessages;
8507
9306
  if (trimmedHistory.trimmedCount > 0) {
8508
- historyMessages = [
9307
+ sourceHistoryMessages = [
8509
9308
  {
8510
9309
  role: "system",
8511
9310
  content: `[${trimmedHistory.trimmedCount} earlier messages trimmed to stay within context limits. Original task: ${(state.originalMessage || originalMessage).slice(0, 500)}]`
8512
9311
  },
8513
- ...historyMessages
9312
+ ...sourceHistoryMessages
8514
9313
  ];
8515
9314
  }
8516
9315
  }
8517
- const summaryText = this.generateCompactSummary(state, compactInstructions);
9316
+ sourceHistoryMessages = [...windowSummaryMessages, ...sourceHistoryMessages];
9317
+ const historyMessages = this.deriveToolContextMessages(
9318
+ sourceHistoryMessages,
9319
+ state.taskName,
9320
+ compactionOptions?.toolContextMode || "hot-tail",
9321
+ compactionOptions?.toolWindow ?? "session"
9322
+ );
9323
+ const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
9324
+ const summaryText = this.generateCompactSummary(
9325
+ state,
9326
+ compactInstructions,
9327
+ historyArtifactReferences
9328
+ );
8518
9329
  const breakdown = this.buildContextBudgetBreakdown({
8519
9330
  historyMessages,
9331
+ sourceHistoryMessages,
8520
9332
  currentTurnContent: continuationContent,
8521
9333
  localTools: compactionOptions?.localTools,
8522
9334
  builtinToolSchemas: compactionOptions?.builtinToolSchemas || [],
@@ -8543,7 +9355,8 @@ Do NOT redo any of the above work.`
8543
9355
  reservedOutputTokens: breakdown.reservedOutputTokens,
8544
9356
  breakdown,
8545
9357
  onContextCompaction: compactionOptions?.onContextCompaction,
8546
- compactInstructions
9358
+ compactInstructions,
9359
+ artifactReferences: historyArtifactReferences
8547
9360
  }
8548
9361
  ),
8549
9362
  requestContextManagement
@@ -9017,6 +9830,52 @@ var BillingEndpoint = class {
9017
9830
  return this.client.get("/billing/spend-analytics", params);
9018
9831
  }
9019
9832
  };
9833
+ var AppsEndpoint = class {
9834
+ constructor(client) {
9835
+ this.client = client;
9836
+ }
9837
+ /** List apps for the authenticated owner, newest first. */
9838
+ async list() {
9839
+ return this.client.get("/apps");
9840
+ }
9841
+ /** Get an app by id, including its URL and active version pointer. */
9842
+ async get(id) {
9843
+ return this.client.get(`/apps/${id}`);
9844
+ }
9845
+ /** Create an app. A client token scoped to the app origin is auto-provisioned. */
9846
+ async create(data) {
9847
+ return this.client.post("/apps", data);
9848
+ }
9849
+ /** Update name, description, visibility, or status (suspended serves 410). */
9850
+ async update(id, data) {
9851
+ return this.client.patch(`/apps/${id}`, data);
9852
+ }
9853
+ /** Delete an app, its versions, and its hosting. Irreversible. */
9854
+ async delete(id) {
9855
+ return this.client.delete(`/apps/${id}`);
9856
+ }
9857
+ /** List an app's versions, newest first. */
9858
+ async listVersions(id) {
9859
+ return this.client.get(`/apps/${id}/versions`);
9860
+ }
9861
+ /** Upload a zipped bundle (raw application/zip body) as a new version. */
9862
+ async uploadVersion(id, zipBytes) {
9863
+ return this.client.postBinary(`/apps/${id}/versions`, zipBytes, "application/zip");
9864
+ }
9865
+ /**
9866
+ * Upload a bundle from in-memory file maps (the API zips server-side).
9867
+ * Text files in `files`, binary files base64-encoded in `filesBase64`.
9868
+ */
9869
+ async uploadVersionFiles(id, data) {
9870
+ return this.client.post(`/apps/${id}/versions`, data);
9871
+ }
9872
+ /** Activate an uploaded version (deploy or rollback). */
9873
+ async activate(id, versionId) {
9874
+ return this.client.post(`/apps/${id}/activate`, {
9875
+ versionId
9876
+ });
9877
+ }
9878
+ };
9020
9879
 
9021
9880
  // src/client.ts
9022
9881
  function isObjectRecord(value) {
@@ -9059,6 +9918,7 @@ var RuntypeClient2 = class {
9059
9918
  this.clientTokens = new ClientTokensEndpoint(this);
9060
9919
  this.agents = new AgentsEndpoint(this);
9061
9920
  this.secrets = new SecretsEndpoint(this);
9921
+ this.apps = new AppsEndpoint(this);
9062
9922
  this.schedules = new SchedulesEndpoint(this);
9063
9923
  this.surfaces = new SurfacesEndpoint(this);
9064
9924
  this.conversations = new ConversationsEndpoint(this);
@@ -9105,7 +9965,7 @@ var RuntypeClient2 = class {
9105
9965
  clearApiKey() {
9106
9966
  delete this.headers.Authorization;
9107
9967
  }
9108
- async runWithLocalTools(request, localTools, arg3, arg4) {
9968
+ async runWithLocalTools(request2, localTools, arg3, arg4) {
9109
9969
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
9110
9970
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
9111
9971
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -9119,12 +9979,12 @@ var RuntypeClient2 = class {
9119
9979
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
9120
9980
  })) : [];
9121
9981
  const modifiedRequest = {
9122
- ...request,
9982
+ ...request2,
9123
9983
  ...derivedClientTools.length > 0 ? {
9124
- clientTools: [...request.clientTools ?? [], ...derivedClientTools]
9984
+ clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
9125
9985
  } : {},
9126
9986
  options: {
9127
- ...request.options || {},
9987
+ ...request2.options || {},
9128
9988
  streamResponse: isStreaming
9129
9989
  }
9130
9990
  };
@@ -9140,13 +10000,12 @@ var RuntypeClient2 = class {
9140
10000
  onFlowComplete: (event) => callbacks?.onFlowComplete?.(event),
9141
10001
  onError: (error) => callbacks?.onError?.(error)
9142
10002
  };
9143
- const { streamEvents: streamEvents2, stepDeltaText: stepDeltaText2, stepDisplayName: stepDisplayName2, flowErrorMessage: flowErrorMessage2 } = await Promise.resolve().then(() => (init_stream_utils(), stream_utils_exports));
9144
10003
  const summary = {
9145
10004
  results: /* @__PURE__ */ new Map(),
9146
10005
  success: true
9147
10006
  };
9148
10007
  try {
9149
- for await (const event of streamEvents2(response)) {
10008
+ for await (const event of streamEvents(response)) {
9150
10009
  collectLocalToolAwait(pausedTools, event);
9151
10010
  switch (event.type) {
9152
10011
  case "flow_start":
@@ -9156,10 +10015,10 @@ var RuntypeClient2 = class {
9156
10015
  wrappedCallbacks.onStepStart?.(event);
9157
10016
  break;
9158
10017
  case "step_delta":
9159
- wrappedCallbacks.onStepDelta?.(stepDeltaText2(event), event);
10018
+ wrappedCallbacks.onStepDelta?.(stepDeltaText(event), event);
9160
10019
  break;
9161
10020
  case "step_complete": {
9162
- summary.results?.set(stepDisplayName2(event), event.result);
10021
+ summary.results?.set(stepDisplayName(event), event.result);
9163
10022
  wrappedCallbacks.onStepComplete?.(event.result, event);
9164
10023
  break;
9165
10024
  }
@@ -9167,7 +10026,7 @@ var RuntypeClient2 = class {
9167
10026
  wrappedCallbacks.onFlowComplete?.(event);
9168
10027
  break;
9169
10028
  case "flow_error":
9170
- wrappedCallbacks.onError?.(new Error(flowErrorMessage2(event)));
10029
+ wrappedCallbacks.onError?.(new Error(flowErrorMessage(event)));
9171
10030
  break;
9172
10031
  }
9173
10032
  }
@@ -9246,7 +10105,8 @@ var RuntypeClient2 = class {
9246
10105
  return [toolName, await handler(parameters)];
9247
10106
  } catch (error) {
9248
10107
  throw new Error(
9249
- `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`
10108
+ `Error executing local tool "${toolName}": ${error instanceof Error ? error.message : String(error)}`,
10109
+ { cause: error }
9250
10110
  );
9251
10111
  }
9252
10112
  })
@@ -9310,6 +10170,21 @@ var RuntypeClient2 = class {
9310
10170
  });
9311
10171
  return transformResponse(response);
9312
10172
  }
10173
+ /**
10174
+ * POST request with a raw binary body (e.g. application/zip bundle uploads).
10175
+ */
10176
+ async postBinary(path, body, contentType) {
10177
+ const url = this.buildUrl(path);
10178
+ const headers = { ...this.headers, "Content-Type": contentType };
10179
+ const response = await this.makeRequest(url, {
10180
+ method: "POST",
10181
+ headers,
10182
+ // TS 5.7 types Uint8Array over ArrayBufferLike, which no longer
10183
+ // overlaps DOM BodyInit; the runtime value is a valid fetch body.
10184
+ body
10185
+ });
10186
+ return transformResponse(response);
10187
+ }
9313
10188
  /**
9314
10189
  * Generic request that returns raw Response for streaming
9315
10190
  */
@@ -9410,7 +10285,7 @@ var RuntypeClient2 = class {
9410
10285
  } catch (error) {
9411
10286
  if (timeoutId) clearTimeout(timeoutId);
9412
10287
  if (timeoutId && error instanceof Error && error.name === "AbortError") {
9413
- throw new Error(`Request timeout after ${this.timeout}ms`);
10288
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
9414
10289
  }
9415
10290
  throw error;
9416
10291
  }
@@ -9419,8 +10294,18 @@ var RuntypeClient2 = class {
9419
10294
  * Make HTTP request that returns raw Response (for streaming)
9420
10295
  */
9421
10296
  async makeRawRequest(url, options) {
9422
- const controller = this.timeout === null ? null : new AbortController();
10297
+ const callerSignal = options.signal ?? null;
10298
+ const controller = this.timeout === null && !callerSignal ? null : new AbortController();
9423
10299
  const timeoutId = controller && this.timeout !== null ? setTimeout(() => controller.abort(), this.timeout) : null;
10300
+ if (callerSignal && controller) {
10301
+ if (callerSignal.aborted) {
10302
+ controller.abort(callerSignal.reason);
10303
+ } else {
10304
+ callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), {
10305
+ once: true
10306
+ });
10307
+ }
10308
+ }
9424
10309
  try {
9425
10310
  const response = await fetch(url, {
9426
10311
  ...options,
@@ -9433,8 +10318,8 @@ var RuntypeClient2 = class {
9433
10318
  return response;
9434
10319
  } catch (error) {
9435
10320
  if (timeoutId) clearTimeout(timeoutId);
9436
- if (timeoutId && error instanceof Error && error.name === "AbortError") {
9437
- throw new Error(`Request timeout after ${this.timeout}ms`);
10321
+ if (timeoutId && error instanceof Error && error.name === "AbortError" && !callerSignal?.aborted) {
10322
+ throw new Error(`Request timeout after ${this.timeout}ms`, { cause: error });
9438
10323
  }
9439
10324
  throw error;
9440
10325
  }
@@ -9480,9 +10365,6 @@ function createClient(config) {
9480
10365
  return new RuntypeClient2(config);
9481
10366
  }
9482
10367
 
9483
- // src/index.ts
9484
- init_stream_utils();
9485
-
9486
10368
  // src/batch-builder.ts
9487
10369
  var BatchBuilder = class {
9488
10370
  constructor() {
@@ -9540,20 +10422,20 @@ var BatchBuilder = class {
9540
10422
  if (!this.recordType) {
9541
10423
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
9542
10424
  }
9543
- const request = {
10425
+ const request2 = {
9544
10426
  flowId: this.flowId,
9545
10427
  recordType: this.recordType
9546
10428
  };
9547
10429
  if (Object.keys(this.batchOptions).length > 0) {
9548
- request.options = this.batchOptions;
10430
+ request2.options = this.batchOptions;
9549
10431
  }
9550
10432
  if (this.filterConfig) {
9551
- request.filter = this.filterConfig;
10433
+ request2.filter = this.filterConfig;
9552
10434
  }
9553
10435
  if (this.limitConfig !== void 0) {
9554
- request.limit = this.limitConfig;
10436
+ request2.limit = this.limitConfig;
9555
10437
  }
9556
- return request;
10438
+ return request2;
9557
10439
  }
9558
10440
  /**
9559
10441
  * Execute the batch operation
@@ -9710,32 +10592,32 @@ var EvalBuilder = class {
9710
10592
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
9711
10593
  );
9712
10594
  }
9713
- const request = {};
10595
+ const request2 = {};
9714
10596
  if (this.flowId) {
9715
- request.flowId = this.flowId;
10597
+ request2.flowId = this.flowId;
9716
10598
  } else if (this.virtualFlow) {
9717
- request.flow = this.virtualFlow;
10599
+ request2.flow = this.virtualFlow;
9718
10600
  }
9719
10601
  if (this.recordType) {
9720
- request.recordType = this.recordType;
10602
+ request2.recordType = this.recordType;
9721
10603
  } else if (this.inlineRecords) {
9722
- request.records = this.inlineRecords;
10604
+ request2.records = this.inlineRecords;
9723
10605
  }
9724
10606
  if (this.modelOverrides) {
9725
- request.modelOverrides = this.modelOverrides;
10607
+ request2.modelOverrides = this.modelOverrides;
9726
10608
  } else if (this.modelConfigs) {
9727
- request.modelConfigs = this.modelConfigs;
10609
+ request2.modelConfigs = this.modelConfigs;
9728
10610
  }
9729
10611
  if (Object.keys(this.evalOptions).length > 0) {
9730
- request.options = this.evalOptions;
10612
+ request2.options = this.evalOptions;
9731
10613
  }
9732
10614
  if (this.filterConfig) {
9733
- request.filter = this.filterConfig;
10615
+ request2.filter = this.filterConfig;
9734
10616
  }
9735
10617
  if (this.limitConfig !== void 0) {
9736
- request.limit = this.limitConfig;
10618
+ request2.limit = this.limitConfig;
9737
10619
  }
9738
- return request;
10620
+ return request2;
9739
10621
  }
9740
10622
  /**
9741
10623
  * Execute the evaluation
@@ -10202,10 +11084,14 @@ var STEP_TYPE_TO_METHOD = {
10202
11084
  };
10203
11085
  // Annotate the CommonJS export names for ESM import in node:
10204
11086
  0 && (module.exports = {
11087
+ AgentDriftError,
11088
+ AgentEnsureConflictError,
10205
11089
  AgentVersionsEndpoint,
10206
11090
  AgentsEndpoint,
11091
+ AgentsNamespace,
10207
11092
  AnalyticsEndpoint,
10208
11093
  ApiKeysEndpoint,
11094
+ AppsEndpoint,
10209
11095
  BatchBuilder,
10210
11096
  BatchesNamespace,
10211
11097
  BillingEndpoint,
@@ -10222,12 +11108,15 @@ var STEP_TYPE_TO_METHOD = {
10222
11108
  EvalRunner,
10223
11109
  EvalsNamespace,
10224
11110
  FlowBuilder,
11111
+ FlowDriftError,
11112
+ FlowEnsureConflictError,
10225
11113
  FlowResult,
10226
11114
  FlowStepsEndpoint,
10227
11115
  FlowVersionsEndpoint,
10228
11116
  FlowsEndpoint,
10229
11117
  FlowsNamespace,
10230
11118
  IntegrationsEndpoint,
11119
+ LEDGER_ARTIFACT_LINE_PREFIX,
10231
11120
  LogsEndpoint,
10232
11121
  ModelConfigsEndpoint,
10233
11122
  PromptRunner,
@@ -10251,19 +11140,29 @@ var STEP_TYPE_TO_METHOD = {
10251
11140
  applyGeneratedRuntimeToolProposalToDispatchRequest,
10252
11141
  attachRuntimeToolsToDispatchRequest,
10253
11142
  buildGeneratedRuntimeToolGateOutput,
11143
+ buildLedgerOffloadReference,
11144
+ buildSendViewOffloadMarker,
11145
+ computeAgentContentHash,
11146
+ computeFlowContentHash,
10254
11147
  createClient,
10255
11148
  createExternalTool,
10256
11149
  defaultWorkflow,
11150
+ defineAgent,
11151
+ defineFlow,
10257
11152
  deployWorkflow,
10258
11153
  evaluateGeneratedRuntimeToolProposal,
11154
+ extractDeclaredToolResultChars,
10259
11155
  gameWorkflow,
10260
11156
  getDefaultPlanPath,
10261
11157
  getLikelySupportingCandidatePaths,
10262
11158
  isDiscoveryToolName,
10263
11159
  isMarathonArtifactPath,
10264
11160
  isPreservationSensitiveTask,
11161
+ normalizeAgentDefinition,
10265
11162
  normalizeCandidatePath,
10266
11163
  parseFinalBuffer,
11164
+ parseLedgerArtifactRelativePath,
11165
+ parseOffloadedOutputId,
10267
11166
  parseSSEChunk,
10268
11167
  processStream,
10269
11168
  sanitizeTaskSlug,