@runtypelabs/sdk 4.11.0 → 4.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1094,20 +1094,20 @@ var FlowBuilder = class {
1094
1094
  */
1095
1095
  build() {
1096
1096
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
1097
- const request2 = { flow };
1097
+ const request3 = { flow };
1098
1098
  if (this.recordConfig) {
1099
- request2.record = this.recordConfig;
1099
+ request3.record = this.recordConfig;
1100
1100
  }
1101
1101
  if (this.messagesConfig) {
1102
- request2.messages = this.messagesConfig;
1102
+ request3.messages = this.messagesConfig;
1103
1103
  }
1104
1104
  if (this.inputsConfig) {
1105
- request2.inputs = this.inputsConfig;
1105
+ request3.inputs = this.inputsConfig;
1106
1106
  }
1107
1107
  if (Object.keys(this.optionsConfig).length > 0) {
1108
- request2.options = this.optionsConfig;
1108
+ request3.options = this.optionsConfig;
1109
1109
  }
1110
- return request2;
1110
+ return request3;
1111
1111
  }
1112
1112
  /**
1113
1113
  * Validate this prospective flow against the public validation endpoint
@@ -1404,6 +1404,7 @@ function collectStepNonPortableToolRefs(config, path) {
1404
1404
  const found = [];
1405
1405
  const tools = config.tools;
1406
1406
  const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
1407
+ const isRawId = (ref, prefix) => typeof ref === "string" && ref.startsWith(prefix);
1407
1408
  const scanArray = (value, subPath) => {
1408
1409
  if (!Array.isArray(value)) return;
1409
1410
  value.forEach((ref, i) => {
@@ -1429,6 +1430,24 @@ function collectStepNonPortableToolRefs(config, path) {
1429
1430
  if (isPlainObject(tools.codeModeConfig)) {
1430
1431
  scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
1431
1432
  }
1433
+ if (Array.isArray(tools.runtimeTools)) {
1434
+ tools.runtimeTools.forEach((runtimeTool, i) => {
1435
+ if (!isPlainObject(runtimeTool) || !isPlainObject(runtimeTool.config)) return;
1436
+ const base = `${path}.tools.runtimeTools[${i}].config`;
1437
+ const rtConfig = runtimeTool.config;
1438
+ if (runtimeTool.toolType === "subagent" && isRawId(rtConfig.agentId, "agent_")) {
1439
+ found.push(`${base}.agentId`);
1440
+ } else if (runtimeTool.toolType === "flow" && isRawId(rtConfig.flowId, "flow_")) {
1441
+ found.push(`${base}.flowId`);
1442
+ }
1443
+ });
1444
+ }
1445
+ }
1446
+ if (isAccountScoped(config.toolId)) {
1447
+ found.push(`${path}.toolId`);
1448
+ }
1449
+ if (isRawId(config.agentId, "agent_")) {
1450
+ found.push(`${path}.agentId`);
1432
1451
  }
1433
1452
  for (const branch of ["trueSteps", "falseSteps"]) {
1434
1453
  const nested = config[branch];
@@ -1483,7 +1502,7 @@ function defineFlow(input) {
1483
1502
  const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
1484
1503
  if (nonPortable.length > 0) {
1485
1504
  throw new Error(
1486
- `defineFlow: account-scoped tool reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references instead. Name-based resolution of saved tools is a planned follow-up.`
1505
+ `defineFlow: account-scoped reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026/agent_\u2026/flow_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references, or reference a saved resource by name \u2014 tool:<name>, agent:<name>, or flow:<name> instead.`
1487
1506
  );
1488
1507
  }
1489
1508
  }
@@ -2482,15 +2501,15 @@ var RuntypeFlowBuilder = class {
2482
2501
  build() {
2483
2502
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2484
2503
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2485
- const request2 = { flow };
2504
+ const request3 = { flow };
2486
2505
  if (this.recordConfig) {
2487
- request2.record = this.recordConfig;
2506
+ request3.record = this.recordConfig;
2488
2507
  }
2489
2508
  if (this.messagesConfig) {
2490
- request2.messages = this.messagesConfig;
2509
+ request3.messages = this.messagesConfig;
2491
2510
  }
2492
2511
  if (this.inputsConfig) {
2493
- request2.inputs = this.inputsConfig;
2512
+ request3.inputs = this.inputsConfig;
2494
2513
  }
2495
2514
  const options = {
2496
2515
  flowMode,
@@ -2508,8 +2527,8 @@ var RuntypeFlowBuilder = class {
2508
2527
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2509
2528
  options.upsertOptions = this.upsertOptions;
2510
2529
  }
2511
- request2.options = options;
2512
- return request2;
2530
+ request3.options = options;
2531
+ return request3;
2513
2532
  }
2514
2533
  /**
2515
2534
  * Validate this prospective flow against the public validation endpoint
@@ -3376,6 +3395,18 @@ function collectNonPortableToolRefs(config) {
3376
3395
  if (isPlainObject2(tools.codeModeConfig)) {
3377
3396
  scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
3378
3397
  }
3398
+ if (Array.isArray(tools.runtimeTools)) {
3399
+ tools.runtimeTools.forEach((runtimeTool, i) => {
3400
+ if (!isPlainObject2(runtimeTool) || !isPlainObject2(runtimeTool.config)) return;
3401
+ const base = `tools.runtimeTools[${i}].config`;
3402
+ const rtConfig = runtimeTool.config;
3403
+ if (runtimeTool.toolType === "subagent" && typeof rtConfig.agentId === "string" && rtConfig.agentId.startsWith("agent_")) {
3404
+ found.push(`${base}.agentId`);
3405
+ } else if (runtimeTool.toolType === "flow" && typeof rtConfig.flowId === "string" && rtConfig.flowId.startsWith("flow_")) {
3406
+ found.push(`${base}.flowId`);
3407
+ }
3408
+ });
3409
+ }
3379
3410
  return found;
3380
3411
  }
3381
3412
  function defineAgent(input) {
@@ -3399,7 +3430,7 @@ function defineAgent(input) {
3399
3430
  const nonPortable = collectNonPortableToolRefs(config);
3400
3431
  if (nonPortable.length > 0) {
3401
3432
  throw new Error(
3402
- `defineAgent: account-scoped tool reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references instead. Name-based resolution of saved tools is a planned follow-up.`
3433
+ `defineAgent: account-scoped reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable \u2014 tool_\u2026/agent_\u2026/flow_\u2026 IDs belong to one account/environment. Use builtin:/platform:/mcp: references, or reference a saved resource by name \u2014 tool:<name>, agent:<name>, or flow:<name> instead.`
3403
3434
  );
3404
3435
  }
3405
3436
  return {
@@ -3541,6 +3572,242 @@ var AgentsNamespace = class {
3541
3572
  }
3542
3573
  };
3543
3574
 
3575
+ // src/tools-ensure.ts
3576
+ function isPlainObject3(value) {
3577
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3578
+ }
3579
+ function normalizeValue2(value) {
3580
+ if (Array.isArray(value)) {
3581
+ return value.map((item) => normalizeValue2(item));
3582
+ }
3583
+ if (isPlainObject3(value)) {
3584
+ const normalized = {};
3585
+ for (const key of Object.keys(value).sort()) {
3586
+ const entry = value[key];
3587
+ if (entry === void 0 || entry === null) continue;
3588
+ normalized[key] = normalizeValue2(entry);
3589
+ }
3590
+ return normalized;
3591
+ }
3592
+ return value;
3593
+ }
3594
+ function normalizeToolDefinition(definition) {
3595
+ const parametersSchema = isPlainObject3(definition.parametersSchema) ? normalizeValue2(definition.parametersSchema) : {};
3596
+ const config = isPlainObject3(definition.config) ? normalizeValue2(definition.config) : {};
3597
+ return {
3598
+ toolType: definition.toolType,
3599
+ ...definition.description ? { description: definition.description } : {},
3600
+ parametersSchema,
3601
+ config
3602
+ };
3603
+ }
3604
+ async function computeToolContentHash(definition) {
3605
+ const serialized = JSON.stringify(normalizeToolDefinition(definition));
3606
+ const encoded = new TextEncoder().encode(serialized);
3607
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3608
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3609
+ }
3610
+ var DEFINE_TOOL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3611
+ "name",
3612
+ "description",
3613
+ "toolType",
3614
+ "parametersSchema",
3615
+ "config"
3616
+ ]);
3617
+ var TOOL_DEFINITION_TYPES = /* @__PURE__ */ new Set([
3618
+ "flow",
3619
+ "custom",
3620
+ "external",
3621
+ "graphql",
3622
+ "mcp",
3623
+ "local",
3624
+ "subagent"
3625
+ ]);
3626
+ function defineTool(input) {
3627
+ if (!input || typeof input !== "object") {
3628
+ throw new Error("defineTool requires a definition object");
3629
+ }
3630
+ if (typeof input.name !== "string" || input.name.length === 0) {
3631
+ throw new Error('defineTool requires a non-empty string "name"');
3632
+ }
3633
+ if (typeof input.description !== "string" || input.description.length === 0) {
3634
+ throw new Error('defineTool requires a non-empty string "description"');
3635
+ }
3636
+ if (typeof input.toolType !== "string" || !TOOL_DEFINITION_TYPES.has(input.toolType)) {
3637
+ throw new Error(
3638
+ `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
3639
+ );
3640
+ }
3641
+ if (!isPlainObject3(input.parametersSchema)) {
3642
+ throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
3643
+ }
3644
+ if (!isPlainObject3(input.config)) {
3645
+ throw new Error('defineTool requires a "config" object');
3646
+ }
3647
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
3648
+ if (unknownKeys.length > 0) {
3649
+ throw new Error(
3650
+ `defineTool: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, toolType, parametersSchema, config.`
3651
+ );
3652
+ }
3653
+ return {
3654
+ name: input.name,
3655
+ description: input.description,
3656
+ toolType: input.toolType,
3657
+ parametersSchema: input.parametersSchema,
3658
+ config: input.config
3659
+ };
3660
+ }
3661
+ var ToolEnsureConflictError = class extends Error {
3662
+ constructor(body) {
3663
+ super(body.error ?? `Tool ensure conflict: ${body.code}`);
3664
+ this.name = "ToolEnsureConflictError";
3665
+ this.code = body.code;
3666
+ this.lastModifiedSource = body.lastModifiedSource;
3667
+ this.modifiedAt = body.modifiedAt;
3668
+ this.currentHash = body.currentHash;
3669
+ }
3670
+ };
3671
+ var ToolDriftError = class extends Error {
3672
+ constructor(plan) {
3673
+ super(
3674
+ `Tool "${plan.toolId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.tools.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
3675
+ );
3676
+ this.name = "ToolDriftError";
3677
+ this.plan = plan;
3678
+ }
3679
+ };
3680
+ function parseRequestError3(err) {
3681
+ if (!(err instanceof Error)) return { status: null, body: null };
3682
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3683
+ if (!match) return { status: null, body: null };
3684
+ try {
3685
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3686
+ } catch {
3687
+ return { status: Number(match[1]), body: null };
3688
+ }
3689
+ }
3690
+ function toConflictError3(err) {
3691
+ const { status, body } = parseRequestError3(err);
3692
+ if (status !== 409 || !isPlainObject3(body)) return null;
3693
+ const code = body.code;
3694
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3695
+ return new ToolEnsureConflictError(
3696
+ body
3697
+ );
3698
+ }
3699
+ var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3700
+ function memoFor3(client) {
3701
+ let memo = serverHashMemo3.get(client);
3702
+ if (!memo) {
3703
+ memo = /* @__PURE__ */ new Map();
3704
+ serverHashMemo3.set(client, memo);
3705
+ }
3706
+ return memo;
3707
+ }
3708
+ function memoize2(memo, memoKey, result) {
3709
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3710
+ }
3711
+ async function request2(client, body) {
3712
+ try {
3713
+ return await client.post(
3714
+ "/tools/ensure",
3715
+ body
3716
+ );
3717
+ } catch (err) {
3718
+ const conflict = toConflictError3(err);
3719
+ if (conflict) throw conflict;
3720
+ throw err;
3721
+ }
3722
+ }
3723
+ async function ensureTool(client, definition, options = {}) {
3724
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
3725
+ const passthrough = {
3726
+ ...onConflict ? { onConflict } : {},
3727
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3728
+ };
3729
+ if (dryRun || expectNoChanges) {
3730
+ const plan = await request2(client, {
3731
+ name: definition.name,
3732
+ definition,
3733
+ dryRun: true,
3734
+ ...passthrough
3735
+ });
3736
+ if (plan.result !== "plan") {
3737
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3738
+ }
3739
+ if (expectNoChanges && plan.changes !== "none") {
3740
+ throw new ToolDriftError(plan);
3741
+ }
3742
+ return plan;
3743
+ }
3744
+ const memo = memoFor3(client);
3745
+ const localHash = await computeToolContentHash(definition);
3746
+ const memoKey = `${definition.name} ${localHash}`;
3747
+ const contentHash = memo.get(memoKey) ?? localHash;
3748
+ const probe = await request2(client, {
3749
+ name: definition.name,
3750
+ contentHash,
3751
+ ...passthrough
3752
+ });
3753
+ if (probe.result !== "definitionRequired") {
3754
+ memoize2(memo, memoKey, probe);
3755
+ return probe;
3756
+ }
3757
+ const converged = await request2(client, {
3758
+ name: definition.name,
3759
+ definition,
3760
+ ...passthrough
3761
+ });
3762
+ if (converged.result === "definitionRequired") {
3763
+ throw new Error("Server reported definitionRequired for a full-definition request");
3764
+ }
3765
+ memoize2(memo, memoKey, converged);
3766
+ return converged;
3767
+ }
3768
+ async function pullTool(client, name) {
3769
+ return client.get("/tools/pull", { name });
3770
+ }
3771
+
3772
+ // src/tools-namespace.ts
3773
+ var ToolsNamespace = class {
3774
+ constructor(getClient) {
3775
+ this.getClient = getClient;
3776
+ }
3777
+ /**
3778
+ * Idempotently converge a `defineTool` definition onto the platform.
3779
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
3780
+ * the saved tool; never deletes. Identity is name + account scope.
3781
+ *
3782
+ * @example
3783
+ * ```typescript
3784
+ * const weather = defineTool({
3785
+ * name: 'Weather Lookup',
3786
+ * description: 'Fetch the current weather for a city',
3787
+ * toolType: 'external',
3788
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
3789
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
3790
+ * })
3791
+ *
3792
+ * // Converge (CI/deploy).
3793
+ * const result = await Runtype.tools.ensure(weather)
3794
+ *
3795
+ * // PR drift gate.
3796
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
3797
+ * ```
3798
+ */
3799
+ async ensure(definition, options = {}) {
3800
+ return ensureTool(this.getClient(), definition, options);
3801
+ }
3802
+ /**
3803
+ * Pull the canonical definition + provenance for a tool by name — the
3804
+ * absorb-drift direction of the ensure protocol.
3805
+ */
3806
+ async pull(name) {
3807
+ return pullTool(this.getClient(), name);
3808
+ }
3809
+ };
3810
+
3544
3811
  // src/transform.ts
3545
3812
  function transformResponse(data) {
3546
3813
  return data;
@@ -3929,6 +4196,34 @@ var Runtype = class {
3929
4196
  static get agents() {
3930
4197
  return new AgentsNamespace(() => this.getClient());
3931
4198
  }
4199
+ /**
4200
+ * Tools namespace - Tool config-as-code (define / ensure / pull)
4201
+ *
4202
+ * @example
4203
+ * ```typescript
4204
+ * import { defineTool, Runtype } from '@runtypelabs/sdk'
4205
+ *
4206
+ * const weather = defineTool({
4207
+ * name: 'Weather Lookup',
4208
+ * description: 'Fetch the current weather for a city',
4209
+ * toolType: 'external',
4210
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
4211
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
4212
+ * })
4213
+ *
4214
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
4215
+ * await Runtype.tools.ensure(weather)
4216
+ *
4217
+ * // CI drift gate
4218
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
4219
+ *
4220
+ * // Absorb a dashboard edit back into the repo
4221
+ * const { definition } = await Runtype.tools.pull('Weather Lookup')
4222
+ * ```
4223
+ */
4224
+ static get tools() {
4225
+ return new ToolsNamespace(() => this.getClient());
4226
+ }
3932
4227
  };
3933
4228
 
3934
4229
  // src/generated-tool-gate.ts
@@ -4151,8 +4446,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
4151
4446
  ...decision.tool ? { tool: decision.tool } : {}
4152
4447
  };
4153
4448
  }
4154
- function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {}) {
4155
- const stepList = request2.flow.steps;
4449
+ function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {}) {
4450
+ const stepList = request3.flow.steps;
4156
4451
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
4157
4452
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
4158
4453
  }
@@ -4195,9 +4490,9 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4195
4490
  }
4196
4491
  };
4197
4492
  return {
4198
- ...request2,
4493
+ ...request3,
4199
4494
  flow: {
4200
- ...request2.flow,
4495
+ ...request3.flow,
4201
4496
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
4202
4497
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
4203
4498
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -4207,12 +4502,12 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4207
4502
  }
4208
4503
  };
4209
4504
  }
4210
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options = {}) {
4505
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options = {}) {
4211
4506
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
4212
4507
  if (!decision.approved || !decision.tool) {
4213
- return { decision, request: request2 };
4508
+ return { decision, request: request3 };
4214
4509
  }
4215
- const nextRequest = attachRuntimeToolsToDispatchRequest(request2, [decision.tool], options.attach);
4510
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request3, [decision.tool], options.attach);
4216
4511
  return {
4217
4512
  decision,
4218
4513
  request: nextRequest
@@ -6121,6 +6416,19 @@ var RecordsEndpoint = class {
6121
6416
  async getResults(id, params) {
6122
6417
  return this.client.get(`/records/${id}/results`, params);
6123
6418
  }
6419
+ /**
6420
+ * Get unified step-level execution results for a record, with filtering and
6421
+ * pagination.
6422
+ */
6423
+ async getStepResults(id, params) {
6424
+ return this.client.get(`/records/${id}/step-results`, params);
6425
+ }
6426
+ /**
6427
+ * Get the aggregated cost breakdown (by model) for a record's executions.
6428
+ */
6429
+ async getCosts(id) {
6430
+ return this.client.get(`/records/${id}/costs`);
6431
+ }
6124
6432
  /**
6125
6433
  * Delete a specific result for a record
6126
6434
  */
@@ -6449,15 +6757,15 @@ var DispatchEndpoint = class {
6449
6757
  * Attach approved runtime tools to a prompt step in a redispatch request.
6450
6758
  * Returns a new request object and does not mutate the original.
6451
6759
  */
6452
- attachApprovedRuntimeTools(request2, runtimeTools, options) {
6453
- return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
6760
+ attachApprovedRuntimeTools(request3, runtimeTools, options) {
6761
+ return attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options);
6454
6762
  }
6455
6763
  /**
6456
6764
  * Validate a generated runtime tool proposal and attach it to the redispatch
6457
6765
  * request if approved, in one call.
6458
6766
  */
6459
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6460
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
6767
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
6768
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options);
6461
6769
  }
6462
6770
  };
6463
6771
  var ChatEndpoint = class {
@@ -7009,8 +7317,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
7009
7317
  },
7010
7318
  required: ["name", "description", "toolType", "parametersSchema", "config"]
7011
7319
  };
7012
- function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7013
- const existing = request2.tools?.runtimeTools || [];
7320
+ function appendRuntimeToolsToAgentRequest(request3, runtimeTools) {
7321
+ const existing = request3.tools?.runtimeTools || [];
7014
7322
  const existingNames = new Set(existing.map((tool) => tool.name));
7015
7323
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
7016
7324
  name: tool.name,
@@ -7020,9 +7328,9 @@ function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7020
7328
  ...tool.config ? { config: tool.config } : {}
7021
7329
  }));
7022
7330
  return {
7023
- ...request2,
7331
+ ...request3,
7024
7332
  tools: {
7025
- ...request2.tools,
7333
+ ...request3.tools,
7026
7334
  runtimeTools: [...existing, ...converted]
7027
7335
  }
7028
7336
  };
@@ -7098,21 +7406,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7098
7406
  * Attach approved runtime tools to an agent execute request.
7099
7407
  * Returns a new request object and does not mutate the original.
7100
7408
  */
7101
- attachApprovedRuntimeTools(request2, runtimeTools) {
7102
- return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
7409
+ attachApprovedRuntimeTools(request3, runtimeTools) {
7410
+ return appendRuntimeToolsToAgentRequest(request3, runtimeTools);
7103
7411
  }
7104
7412
  /**
7105
7413
  * Validate a generated runtime tool proposal and append it to an agent execute
7106
7414
  * request if approved, in one call.
7107
7415
  */
7108
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
7416
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
7109
7417
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
7110
7418
  if (!decision.approved || !decision.tool) {
7111
- return { decision, request: request2 };
7419
+ return { decision, request: request3 };
7112
7420
  }
7113
7421
  return {
7114
7422
  decision,
7115
- request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
7423
+ request: appendRuntimeToolsToAgentRequest(request3, [decision.tool])
7116
7424
  };
7117
7425
  }
7118
7426
  /**
@@ -7871,11 +8179,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7871
8179
  * setting. This never mutates persisted marathon history; masking/offloading
7872
8180
  * is a send-time view over the full-fidelity ledger/history.
7873
8181
  */
7874
- deriveToolContextMessages(messages, taskName, mode, window) {
8182
+ deriveToolContextMessages(messages, taskName, mode, window, offloadRecorder) {
7875
8183
  if (mode === "full-inline") return [...messages];
7876
8184
  const maskMessage = (msg) => ({
7877
8185
  ...msg,
7878
- toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
8186
+ toolResults: (msg.toolResults ?? []).map(
8187
+ (tr) => this.compactOneResult(tr, taskName, mode, offloadRecorder)
8188
+ )
7879
8189
  });
7880
8190
  const view = [...messages];
7881
8191
  if (window === "session") {
@@ -7909,12 +8219,18 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7909
8219
  }
7910
8220
  return view;
7911
8221
  }
7912
- compactOneResult(tr, taskName, mode) {
8222
+ compactOneResult(tr, taskName, mode, offloadRecorder) {
7913
8223
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
7914
8224
  if (mode === "hot-tail") {
7915
8225
  return {
7916
8226
  ...tr,
7917
- result: this.offloadToolResult(taskName, tr.toolCallId, tr.toolName, tr.result)
8227
+ result: this.offloadToolResult(
8228
+ taskName,
8229
+ tr.toolCallId,
8230
+ tr.toolName,
8231
+ tr.result,
8232
+ offloadRecorder
8233
+ )
7918
8234
  };
7919
8235
  }
7920
8236
  return { ...tr, result: `[Output from ${tr.toolName} masked \u2014 re-run the tool if needed]` };
@@ -7930,9 +8246,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7930
8246
  return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
7931
8247
  }
7932
8248
  // chars
7933
- offloadToolResult(taskName, toolCallId, toolName, result) {
8249
+ offloadToolResult(taskName, toolCallId, toolName, result, offloadRecorder) {
7934
8250
  const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
7935
8251
  if (resultStr.length <= this.TOOL_OUTPUT_INLINE_THRESHOLD) return result;
8252
+ if (offloadRecorder) {
8253
+ try {
8254
+ const recorded = offloadRecorder({ toolCallId, toolName, content: resultStr });
8255
+ if (recorded?.reference) return recorded.reference;
8256
+ } catch {
8257
+ }
8258
+ }
7936
8259
  let fs;
7937
8260
  try {
7938
8261
  const dynamicRequire = (0, eval)('typeof require !== "undefined" ? require : undefined');
@@ -8659,7 +8982,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8659
8982
  onContextCompaction: options.onContextCompaction,
8660
8983
  onContextNotice: options.onContextNotice,
8661
8984
  toolContextMode: options.toolContextMode || "hot-tail",
8662
- toolWindow: options.toolWindow ?? "session"
8985
+ toolWindow: options.toolWindow ?? "session",
8986
+ offloadRecorder: options.offloadRecorder
8663
8987
  },
8664
8988
  queuedSteeringMessages
8665
8989
  );
@@ -9450,7 +9774,8 @@ Do NOT redo any of the above work.`
9450
9774
  });
9451
9775
  };
9452
9776
  const buildNativeCompactionEvent = (mode, breakdown) => {
9453
- if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || breakdown.estimatedInputTokens < compactionOptions.autoCompactTokenThreshold) {
9777
+ const gateTokens = breakdown.sendEstimatedInputTokens ?? breakdown.estimatedInputTokens;
9778
+ if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || gateTokens < compactionOptions.autoCompactTokenThreshold) {
9454
9779
  return void 0;
9455
9780
  }
9456
9781
  return {
@@ -9519,7 +9844,8 @@ Do NOT redo any of the above work.`
9519
9844
  sourceReplayHistoryMessages,
9520
9845
  state.taskName,
9521
9846
  compactionOptions?.toolContextMode || "hot-tail",
9522
- compactionOptions?.toolWindow ?? "session"
9847
+ compactionOptions?.toolWindow ?? "session",
9848
+ compactionOptions?.offloadRecorder
9523
9849
  );
9524
9850
  const continuationGuardrail = this.buildContinuationGuardrail(state);
9525
9851
  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.";
@@ -9682,7 +10008,8 @@ Do NOT redo any of the above work.`
9682
10008
  sourceHistoryMessages,
9683
10009
  state.taskName,
9684
10010
  compactionOptions?.toolContextMode || "hot-tail",
9685
- compactionOptions?.toolWindow ?? "session"
10011
+ compactionOptions?.toolWindow ?? "session",
10012
+ compactionOptions?.offloadRecorder
9686
10013
  );
9687
10014
  const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
9688
10015
  const summaryText = this.generateCompactSummary(
@@ -10329,7 +10656,7 @@ var RuntypeClient2 = class {
10329
10656
  clearApiKey() {
10330
10657
  delete this.headers.Authorization;
10331
10658
  }
10332
- async runWithLocalTools(request2, localTools, arg3, arg4) {
10659
+ async runWithLocalTools(request3, localTools, arg3, arg4) {
10333
10660
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
10334
10661
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
10335
10662
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -10343,12 +10670,12 @@ var RuntypeClient2 = class {
10343
10670
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
10344
10671
  })) : [];
10345
10672
  const modifiedRequest = {
10346
- ...request2,
10673
+ ...request3,
10347
10674
  ...derivedClientTools.length > 0 ? {
10348
- clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
10675
+ clientTools: [...request3.clientTools ?? [], ...derivedClientTools]
10349
10676
  } : {},
10350
10677
  options: {
10351
- ...request2.options || {},
10678
+ ...request3.options || {},
10352
10679
  streamResponse: isStreaming
10353
10680
  }
10354
10681
  };
@@ -10786,20 +11113,20 @@ var BatchBuilder = class {
10786
11113
  if (!this.recordType) {
10787
11114
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
10788
11115
  }
10789
- const request2 = {
11116
+ const request3 = {
10790
11117
  flowId: this.flowId,
10791
11118
  recordType: this.recordType
10792
11119
  };
10793
11120
  if (Object.keys(this.batchOptions).length > 0) {
10794
- request2.options = this.batchOptions;
11121
+ request3.options = this.batchOptions;
10795
11122
  }
10796
11123
  if (this.filterConfig) {
10797
- request2.filter = this.filterConfig;
11124
+ request3.filter = this.filterConfig;
10798
11125
  }
10799
11126
  if (this.limitConfig !== void 0) {
10800
- request2.limit = this.limitConfig;
11127
+ request3.limit = this.limitConfig;
10801
11128
  }
10802
- return request2;
11129
+ return request3;
10803
11130
  }
10804
11131
  /**
10805
11132
  * Execute the batch operation
@@ -10956,32 +11283,32 @@ var EvalBuilder = class {
10956
11283
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
10957
11284
  );
10958
11285
  }
10959
- const request2 = {};
11286
+ const request3 = {};
10960
11287
  if (this.flowId) {
10961
- request2.flowId = this.flowId;
11288
+ request3.flowId = this.flowId;
10962
11289
  } else if (this.virtualFlow) {
10963
- request2.flow = this.virtualFlow;
11290
+ request3.flow = this.virtualFlow;
10964
11291
  }
10965
11292
  if (this.recordType) {
10966
- request2.recordType = this.recordType;
11293
+ request3.recordType = this.recordType;
10967
11294
  } else if (this.inlineRecords) {
10968
- request2.records = this.inlineRecords;
11295
+ request3.records = this.inlineRecords;
10969
11296
  }
10970
11297
  if (this.modelOverrides) {
10971
- request2.modelOverrides = this.modelOverrides;
11298
+ request3.modelOverrides = this.modelOverrides;
10972
11299
  } else if (this.modelConfigs) {
10973
- request2.modelConfigs = this.modelConfigs;
11300
+ request3.modelConfigs = this.modelConfigs;
10974
11301
  }
10975
11302
  if (Object.keys(this.evalOptions).length > 0) {
10976
- request2.options = this.evalOptions;
11303
+ request3.options = this.evalOptions;
10977
11304
  }
10978
11305
  if (this.filterConfig) {
10979
- request2.filter = this.filterConfig;
11306
+ request3.filter = this.filterConfig;
10980
11307
  }
10981
11308
  if (this.limitConfig !== void 0) {
10982
- request2.limit = this.limitConfig;
11309
+ request3.limit = this.limitConfig;
10983
11310
  }
10984
- return request2;
11311
+ return request3;
10985
11312
  }
10986
11313
  /**
10987
11314
  * Execute the evaluation
@@ -11500,7 +11827,10 @@ export {
11500
11827
  SkillProposalsNamespace,
11501
11828
  SkillsNamespace,
11502
11829
  SurfacesEndpoint,
11830
+ ToolDriftError,
11831
+ ToolEnsureConflictError,
11503
11832
  ToolsEndpoint,
11833
+ ToolsNamespace,
11504
11834
  UsersEndpoint,
11505
11835
  applyGeneratedRuntimeToolProposalToDispatchRequest,
11506
11836
  attachRuntimeToolsToDispatchRequest,
@@ -11512,6 +11842,7 @@ export {
11512
11842
  compileWorkflowConfig,
11513
11843
  computeAgentContentHash,
11514
11844
  computeFlowContentHash,
11845
+ computeToolContentHash,
11515
11846
  createClient,
11516
11847
  createExternalTool,
11517
11848
  defaultWorkflow,
@@ -11519,6 +11850,7 @@ export {
11519
11850
  defineAgent,
11520
11851
  defineFlow,
11521
11852
  definePlaybook,
11853
+ defineTool,
11522
11854
  deployWorkflow,
11523
11855
  ensureDefaultWorkflowHooks,
11524
11856
  evaluateGeneratedRuntimeToolProposal,
@@ -11534,6 +11866,7 @@ export {
11534
11866
  listWorkflowHooks,
11535
11867
  normalizeAgentDefinition,
11536
11868
  normalizeCandidatePath,
11869
+ normalizeToolDefinition,
11537
11870
  parseFinalBuffer,
11538
11871
  parseLedgerArtifactRelativePath,
11539
11872
  parseOffloadedOutputId,