@runtypelabs/sdk 4.11.0 → 4.12.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
@@ -73,7 +73,10 @@ __export(index_exports, {
73
73
  SkillProposalsNamespace: () => SkillProposalsNamespace,
74
74
  SkillsNamespace: () => SkillsNamespace,
75
75
  SurfacesEndpoint: () => SurfacesEndpoint,
76
+ ToolDriftError: () => ToolDriftError,
77
+ ToolEnsureConflictError: () => ToolEnsureConflictError,
76
78
  ToolsEndpoint: () => ToolsEndpoint,
79
+ ToolsNamespace: () => ToolsNamespace,
77
80
  UsersEndpoint: () => UsersEndpoint,
78
81
  applyGeneratedRuntimeToolProposalToDispatchRequest: () => applyGeneratedRuntimeToolProposalToDispatchRequest,
79
82
  attachRuntimeToolsToDispatchRequest: () => attachRuntimeToolsToDispatchRequest,
@@ -85,6 +88,7 @@ __export(index_exports, {
85
88
  compileWorkflowConfig: () => compileWorkflowConfig,
86
89
  computeAgentContentHash: () => computeAgentContentHash,
87
90
  computeFlowContentHash: () => computeFlowContentHash,
91
+ computeToolContentHash: () => computeToolContentHash,
88
92
  createClient: () => createClient,
89
93
  createExternalTool: () => createExternalTool,
90
94
  defaultWorkflow: () => defaultWorkflow,
@@ -92,6 +96,7 @@ __export(index_exports, {
92
96
  defineAgent: () => defineAgent,
93
97
  defineFlow: () => defineFlow,
94
98
  definePlaybook: () => definePlaybook,
99
+ defineTool: () => defineTool,
95
100
  deployWorkflow: () => deployWorkflow,
96
101
  ensureDefaultWorkflowHooks: () => ensureDefaultWorkflowHooks,
97
102
  evaluateGeneratedRuntimeToolProposal: () => evaluateGeneratedRuntimeToolProposal,
@@ -107,6 +112,7 @@ __export(index_exports, {
107
112
  listWorkflowHooks: () => listWorkflowHooks,
108
113
  normalizeAgentDefinition: () => normalizeAgentDefinition,
109
114
  normalizeCandidatePath: () => normalizeCandidatePath,
115
+ normalizeToolDefinition: () => normalizeToolDefinition,
110
116
  parseFinalBuffer: () => parseFinalBuffer,
111
117
  parseLedgerArtifactRelativePath: () => parseLedgerArtifactRelativePath,
112
118
  parseOffloadedOutputId: () => parseOffloadedOutputId,
@@ -1219,20 +1225,20 @@ var FlowBuilder = class {
1219
1225
  */
1220
1226
  build() {
1221
1227
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
1222
- const request2 = { flow };
1228
+ const request3 = { flow };
1223
1229
  if (this.recordConfig) {
1224
- request2.record = this.recordConfig;
1230
+ request3.record = this.recordConfig;
1225
1231
  }
1226
1232
  if (this.messagesConfig) {
1227
- request2.messages = this.messagesConfig;
1233
+ request3.messages = this.messagesConfig;
1228
1234
  }
1229
1235
  if (this.inputsConfig) {
1230
- request2.inputs = this.inputsConfig;
1236
+ request3.inputs = this.inputsConfig;
1231
1237
  }
1232
1238
  if (Object.keys(this.optionsConfig).length > 0) {
1233
- request2.options = this.optionsConfig;
1239
+ request3.options = this.optionsConfig;
1234
1240
  }
1235
- return request2;
1241
+ return request3;
1236
1242
  }
1237
1243
  /**
1238
1244
  * Validate this prospective flow against the public validation endpoint
@@ -1555,6 +1561,9 @@ function collectStepNonPortableToolRefs(config, path) {
1555
1561
  scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
1556
1562
  }
1557
1563
  }
1564
+ if (isAccountScoped(config.toolId)) {
1565
+ found.push(`${path}.toolId`);
1566
+ }
1558
1567
  for (const branch of ["trueSteps", "falseSteps"]) {
1559
1568
  const nested = config[branch];
1560
1569
  if (!Array.isArray(nested)) continue;
@@ -1608,7 +1617,7 @@ function defineFlow(input) {
1608
1617
  const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
1609
1618
  if (nonPortable.length > 0) {
1610
1619
  throw new Error(
1611
- `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.`
1620
+ `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, or reference a saved tool by name with tool:<name> instead.`
1612
1621
  );
1613
1622
  }
1614
1623
  }
@@ -2607,15 +2616,15 @@ var RuntypeFlowBuilder = class {
2607
2616
  build() {
2608
2617
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2609
2618
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2610
- const request2 = { flow };
2619
+ const request3 = { flow };
2611
2620
  if (this.recordConfig) {
2612
- request2.record = this.recordConfig;
2621
+ request3.record = this.recordConfig;
2613
2622
  }
2614
2623
  if (this.messagesConfig) {
2615
- request2.messages = this.messagesConfig;
2624
+ request3.messages = this.messagesConfig;
2616
2625
  }
2617
2626
  if (this.inputsConfig) {
2618
- request2.inputs = this.inputsConfig;
2627
+ request3.inputs = this.inputsConfig;
2619
2628
  }
2620
2629
  const options = {
2621
2630
  flowMode,
@@ -2633,8 +2642,8 @@ var RuntypeFlowBuilder = class {
2633
2642
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2634
2643
  options.upsertOptions = this.upsertOptions;
2635
2644
  }
2636
- request2.options = options;
2637
- return request2;
2645
+ request3.options = options;
2646
+ return request3;
2638
2647
  }
2639
2648
  /**
2640
2649
  * Validate this prospective flow against the public validation endpoint
@@ -3524,7 +3533,7 @@ function defineAgent(input) {
3524
3533
  const nonPortable = collectNonPortableToolRefs(config);
3525
3534
  if (nonPortable.length > 0) {
3526
3535
  throw new Error(
3527
- `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.`
3536
+ `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, or reference a saved tool by name with tool:<name> instead.`
3528
3537
  );
3529
3538
  }
3530
3539
  return {
@@ -3666,6 +3675,242 @@ var AgentsNamespace = class {
3666
3675
  }
3667
3676
  };
3668
3677
 
3678
+ // src/tools-ensure.ts
3679
+ function isPlainObject3(value) {
3680
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3681
+ }
3682
+ function normalizeValue2(value) {
3683
+ if (Array.isArray(value)) {
3684
+ return value.map((item) => normalizeValue2(item));
3685
+ }
3686
+ if (isPlainObject3(value)) {
3687
+ const normalized = {};
3688
+ for (const key of Object.keys(value).sort()) {
3689
+ const entry = value[key];
3690
+ if (entry === void 0 || entry === null) continue;
3691
+ normalized[key] = normalizeValue2(entry);
3692
+ }
3693
+ return normalized;
3694
+ }
3695
+ return value;
3696
+ }
3697
+ function normalizeToolDefinition(definition) {
3698
+ const parametersSchema = isPlainObject3(definition.parametersSchema) ? normalizeValue2(definition.parametersSchema) : {};
3699
+ const config = isPlainObject3(definition.config) ? normalizeValue2(definition.config) : {};
3700
+ return {
3701
+ toolType: definition.toolType,
3702
+ ...definition.description ? { description: definition.description } : {},
3703
+ parametersSchema,
3704
+ config
3705
+ };
3706
+ }
3707
+ async function computeToolContentHash(definition) {
3708
+ const serialized = JSON.stringify(normalizeToolDefinition(definition));
3709
+ const encoded = new TextEncoder().encode(serialized);
3710
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3711
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3712
+ }
3713
+ var DEFINE_TOOL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3714
+ "name",
3715
+ "description",
3716
+ "toolType",
3717
+ "parametersSchema",
3718
+ "config"
3719
+ ]);
3720
+ var TOOL_DEFINITION_TYPES = /* @__PURE__ */ new Set([
3721
+ "flow",
3722
+ "custom",
3723
+ "external",
3724
+ "graphql",
3725
+ "mcp",
3726
+ "local",
3727
+ "subagent"
3728
+ ]);
3729
+ function defineTool(input) {
3730
+ if (!input || typeof input !== "object") {
3731
+ throw new Error("defineTool requires a definition object");
3732
+ }
3733
+ if (typeof input.name !== "string" || input.name.length === 0) {
3734
+ throw new Error('defineTool requires a non-empty string "name"');
3735
+ }
3736
+ if (typeof input.description !== "string" || input.description.length === 0) {
3737
+ throw new Error('defineTool requires a non-empty string "description"');
3738
+ }
3739
+ if (typeof input.toolType !== "string" || !TOOL_DEFINITION_TYPES.has(input.toolType)) {
3740
+ throw new Error(
3741
+ `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
3742
+ );
3743
+ }
3744
+ if (!isPlainObject3(input.parametersSchema)) {
3745
+ throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
3746
+ }
3747
+ if (!isPlainObject3(input.config)) {
3748
+ throw new Error('defineTool requires a "config" object');
3749
+ }
3750
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
3751
+ if (unknownKeys.length > 0) {
3752
+ throw new Error(
3753
+ `defineTool: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, toolType, parametersSchema, config.`
3754
+ );
3755
+ }
3756
+ return {
3757
+ name: input.name,
3758
+ description: input.description,
3759
+ toolType: input.toolType,
3760
+ parametersSchema: input.parametersSchema,
3761
+ config: input.config
3762
+ };
3763
+ }
3764
+ var ToolEnsureConflictError = class extends Error {
3765
+ constructor(body) {
3766
+ super(body.error ?? `Tool ensure conflict: ${body.code}`);
3767
+ this.name = "ToolEnsureConflictError";
3768
+ this.code = body.code;
3769
+ this.lastModifiedSource = body.lastModifiedSource;
3770
+ this.modifiedAt = body.modifiedAt;
3771
+ this.currentHash = body.currentHash;
3772
+ }
3773
+ };
3774
+ var ToolDriftError = class extends Error {
3775
+ constructor(plan) {
3776
+ super(
3777
+ `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.`
3778
+ );
3779
+ this.name = "ToolDriftError";
3780
+ this.plan = plan;
3781
+ }
3782
+ };
3783
+ function parseRequestError3(err) {
3784
+ if (!(err instanceof Error)) return { status: null, body: null };
3785
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3786
+ if (!match) return { status: null, body: null };
3787
+ try {
3788
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3789
+ } catch {
3790
+ return { status: Number(match[1]), body: null };
3791
+ }
3792
+ }
3793
+ function toConflictError3(err) {
3794
+ const { status, body } = parseRequestError3(err);
3795
+ if (status !== 409 || !isPlainObject3(body)) return null;
3796
+ const code = body.code;
3797
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3798
+ return new ToolEnsureConflictError(
3799
+ body
3800
+ );
3801
+ }
3802
+ var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3803
+ function memoFor3(client) {
3804
+ let memo = serverHashMemo3.get(client);
3805
+ if (!memo) {
3806
+ memo = /* @__PURE__ */ new Map();
3807
+ serverHashMemo3.set(client, memo);
3808
+ }
3809
+ return memo;
3810
+ }
3811
+ function memoize2(memo, memoKey, result) {
3812
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3813
+ }
3814
+ async function request2(client, body) {
3815
+ try {
3816
+ return await client.post(
3817
+ "/tools/ensure",
3818
+ body
3819
+ );
3820
+ } catch (err) {
3821
+ const conflict = toConflictError3(err);
3822
+ if (conflict) throw conflict;
3823
+ throw err;
3824
+ }
3825
+ }
3826
+ async function ensureTool(client, definition, options = {}) {
3827
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
3828
+ const passthrough = {
3829
+ ...onConflict ? { onConflict } : {},
3830
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3831
+ };
3832
+ if (dryRun || expectNoChanges) {
3833
+ const plan = await request2(client, {
3834
+ name: definition.name,
3835
+ definition,
3836
+ dryRun: true,
3837
+ ...passthrough
3838
+ });
3839
+ if (plan.result !== "plan") {
3840
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3841
+ }
3842
+ if (expectNoChanges && plan.changes !== "none") {
3843
+ throw new ToolDriftError(plan);
3844
+ }
3845
+ return plan;
3846
+ }
3847
+ const memo = memoFor3(client);
3848
+ const localHash = await computeToolContentHash(definition);
3849
+ const memoKey = `${definition.name} ${localHash}`;
3850
+ const contentHash = memo.get(memoKey) ?? localHash;
3851
+ const probe = await request2(client, {
3852
+ name: definition.name,
3853
+ contentHash,
3854
+ ...passthrough
3855
+ });
3856
+ if (probe.result !== "definitionRequired") {
3857
+ memoize2(memo, memoKey, probe);
3858
+ return probe;
3859
+ }
3860
+ const converged = await request2(client, {
3861
+ name: definition.name,
3862
+ definition,
3863
+ ...passthrough
3864
+ });
3865
+ if (converged.result === "definitionRequired") {
3866
+ throw new Error("Server reported definitionRequired for a full-definition request");
3867
+ }
3868
+ memoize2(memo, memoKey, converged);
3869
+ return converged;
3870
+ }
3871
+ async function pullTool(client, name) {
3872
+ return client.get("/tools/pull", { name });
3873
+ }
3874
+
3875
+ // src/tools-namespace.ts
3876
+ var ToolsNamespace = class {
3877
+ constructor(getClient) {
3878
+ this.getClient = getClient;
3879
+ }
3880
+ /**
3881
+ * Idempotently converge a `defineTool` definition onto the platform.
3882
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
3883
+ * the saved tool; never deletes. Identity is name + account scope.
3884
+ *
3885
+ * @example
3886
+ * ```typescript
3887
+ * const weather = defineTool({
3888
+ * name: 'Weather Lookup',
3889
+ * description: 'Fetch the current weather for a city',
3890
+ * toolType: 'external',
3891
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
3892
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
3893
+ * })
3894
+ *
3895
+ * // Converge (CI/deploy).
3896
+ * const result = await Runtype.tools.ensure(weather)
3897
+ *
3898
+ * // PR drift gate.
3899
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
3900
+ * ```
3901
+ */
3902
+ async ensure(definition, options = {}) {
3903
+ return ensureTool(this.getClient(), definition, options);
3904
+ }
3905
+ /**
3906
+ * Pull the canonical definition + provenance for a tool by name — the
3907
+ * absorb-drift direction of the ensure protocol.
3908
+ */
3909
+ async pull(name) {
3910
+ return pullTool(this.getClient(), name);
3911
+ }
3912
+ };
3913
+
3669
3914
  // src/transform.ts
3670
3915
  function transformResponse(data) {
3671
3916
  return data;
@@ -4054,6 +4299,34 @@ var Runtype = class {
4054
4299
  static get agents() {
4055
4300
  return new AgentsNamespace(() => this.getClient());
4056
4301
  }
4302
+ /**
4303
+ * Tools namespace - Tool config-as-code (define / ensure / pull)
4304
+ *
4305
+ * @example
4306
+ * ```typescript
4307
+ * import { defineTool, Runtype } from '@runtypelabs/sdk'
4308
+ *
4309
+ * const weather = defineTool({
4310
+ * name: 'Weather Lookup',
4311
+ * description: 'Fetch the current weather for a city',
4312
+ * toolType: 'external',
4313
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
4314
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
4315
+ * })
4316
+ *
4317
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
4318
+ * await Runtype.tools.ensure(weather)
4319
+ *
4320
+ * // CI drift gate
4321
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
4322
+ *
4323
+ * // Absorb a dashboard edit back into the repo
4324
+ * const { definition } = await Runtype.tools.pull('Weather Lookup')
4325
+ * ```
4326
+ */
4327
+ static get tools() {
4328
+ return new ToolsNamespace(() => this.getClient());
4329
+ }
4057
4330
  };
4058
4331
 
4059
4332
  // src/generated-tool-gate.ts
@@ -4276,8 +4549,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
4276
4549
  ...decision.tool ? { tool: decision.tool } : {}
4277
4550
  };
4278
4551
  }
4279
- function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {}) {
4280
- const stepList = request2.flow.steps;
4552
+ function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {}) {
4553
+ const stepList = request3.flow.steps;
4281
4554
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
4282
4555
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
4283
4556
  }
@@ -4320,9 +4593,9 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4320
4593
  }
4321
4594
  };
4322
4595
  return {
4323
- ...request2,
4596
+ ...request3,
4324
4597
  flow: {
4325
- ...request2.flow,
4598
+ ...request3.flow,
4326
4599
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
4327
4600
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
4328
4601
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -4332,12 +4605,12 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4332
4605
  }
4333
4606
  };
4334
4607
  }
4335
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options = {}) {
4608
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options = {}) {
4336
4609
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
4337
4610
  if (!decision.approved || !decision.tool) {
4338
- return { decision, request: request2 };
4611
+ return { decision, request: request3 };
4339
4612
  }
4340
- const nextRequest = attachRuntimeToolsToDispatchRequest(request2, [decision.tool], options.attach);
4613
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request3, [decision.tool], options.attach);
4341
4614
  return {
4342
4615
  decision,
4343
4616
  request: nextRequest
@@ -6246,6 +6519,19 @@ var RecordsEndpoint = class {
6246
6519
  async getResults(id, params) {
6247
6520
  return this.client.get(`/records/${id}/results`, params);
6248
6521
  }
6522
+ /**
6523
+ * Get unified step-level execution results for a record, with filtering and
6524
+ * pagination.
6525
+ */
6526
+ async getStepResults(id, params) {
6527
+ return this.client.get(`/records/${id}/step-results`, params);
6528
+ }
6529
+ /**
6530
+ * Get the aggregated cost breakdown (by model) for a record's executions.
6531
+ */
6532
+ async getCosts(id) {
6533
+ return this.client.get(`/records/${id}/costs`);
6534
+ }
6249
6535
  /**
6250
6536
  * Delete a specific result for a record
6251
6537
  */
@@ -6574,15 +6860,15 @@ var DispatchEndpoint = class {
6574
6860
  * Attach approved runtime tools to a prompt step in a redispatch request.
6575
6861
  * Returns a new request object and does not mutate the original.
6576
6862
  */
6577
- attachApprovedRuntimeTools(request2, runtimeTools, options) {
6578
- return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
6863
+ attachApprovedRuntimeTools(request3, runtimeTools, options) {
6864
+ return attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options);
6579
6865
  }
6580
6866
  /**
6581
6867
  * Validate a generated runtime tool proposal and attach it to the redispatch
6582
6868
  * request if approved, in one call.
6583
6869
  */
6584
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6585
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
6870
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
6871
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options);
6586
6872
  }
6587
6873
  };
6588
6874
  var ChatEndpoint = class {
@@ -7134,8 +7420,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
7134
7420
  },
7135
7421
  required: ["name", "description", "toolType", "parametersSchema", "config"]
7136
7422
  };
7137
- function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7138
- const existing = request2.tools?.runtimeTools || [];
7423
+ function appendRuntimeToolsToAgentRequest(request3, runtimeTools) {
7424
+ const existing = request3.tools?.runtimeTools || [];
7139
7425
  const existingNames = new Set(existing.map((tool) => tool.name));
7140
7426
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
7141
7427
  name: tool.name,
@@ -7145,9 +7431,9 @@ function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7145
7431
  ...tool.config ? { config: tool.config } : {}
7146
7432
  }));
7147
7433
  return {
7148
- ...request2,
7434
+ ...request3,
7149
7435
  tools: {
7150
- ...request2.tools,
7436
+ ...request3.tools,
7151
7437
  runtimeTools: [...existing, ...converted]
7152
7438
  }
7153
7439
  };
@@ -7223,21 +7509,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7223
7509
  * Attach approved runtime tools to an agent execute request.
7224
7510
  * Returns a new request object and does not mutate the original.
7225
7511
  */
7226
- attachApprovedRuntimeTools(request2, runtimeTools) {
7227
- return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
7512
+ attachApprovedRuntimeTools(request3, runtimeTools) {
7513
+ return appendRuntimeToolsToAgentRequest(request3, runtimeTools);
7228
7514
  }
7229
7515
  /**
7230
7516
  * Validate a generated runtime tool proposal and append it to an agent execute
7231
7517
  * request if approved, in one call.
7232
7518
  */
7233
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
7519
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
7234
7520
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
7235
7521
  if (!decision.approved || !decision.tool) {
7236
- return { decision, request: request2 };
7522
+ return { decision, request: request3 };
7237
7523
  }
7238
7524
  return {
7239
7525
  decision,
7240
- request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
7526
+ request: appendRuntimeToolsToAgentRequest(request3, [decision.tool])
7241
7527
  };
7242
7528
  }
7243
7529
  /**
@@ -7996,11 +8282,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7996
8282
  * setting. This never mutates persisted marathon history; masking/offloading
7997
8283
  * is a send-time view over the full-fidelity ledger/history.
7998
8284
  */
7999
- deriveToolContextMessages(messages, taskName, mode, window) {
8285
+ deriveToolContextMessages(messages, taskName, mode, window, offloadRecorder) {
8000
8286
  if (mode === "full-inline") return [...messages];
8001
8287
  const maskMessage = (msg) => ({
8002
8288
  ...msg,
8003
- toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
8289
+ toolResults: (msg.toolResults ?? []).map(
8290
+ (tr) => this.compactOneResult(tr, taskName, mode, offloadRecorder)
8291
+ )
8004
8292
  });
8005
8293
  const view = [...messages];
8006
8294
  if (window === "session") {
@@ -8034,12 +8322,18 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8034
8322
  }
8035
8323
  return view;
8036
8324
  }
8037
- compactOneResult(tr, taskName, mode) {
8325
+ compactOneResult(tr, taskName, mode, offloadRecorder) {
8038
8326
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
8039
8327
  if (mode === "hot-tail") {
8040
8328
  return {
8041
8329
  ...tr,
8042
- result: this.offloadToolResult(taskName, tr.toolCallId, tr.toolName, tr.result)
8330
+ result: this.offloadToolResult(
8331
+ taskName,
8332
+ tr.toolCallId,
8333
+ tr.toolName,
8334
+ tr.result,
8335
+ offloadRecorder
8336
+ )
8043
8337
  };
8044
8338
  }
8045
8339
  return { ...tr, result: `[Output from ${tr.toolName} masked \u2014 re-run the tool if needed]` };
@@ -8055,9 +8349,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8055
8349
  return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
8056
8350
  }
8057
8351
  // chars
8058
- offloadToolResult(taskName, toolCallId, toolName, result) {
8352
+ offloadToolResult(taskName, toolCallId, toolName, result, offloadRecorder) {
8059
8353
  const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
8060
8354
  if (resultStr.length <= this.TOOL_OUTPUT_INLINE_THRESHOLD) return result;
8355
+ if (offloadRecorder) {
8356
+ try {
8357
+ const recorded = offloadRecorder({ toolCallId, toolName, content: resultStr });
8358
+ if (recorded?.reference) return recorded.reference;
8359
+ } catch {
8360
+ }
8361
+ }
8061
8362
  let fs;
8062
8363
  try {
8063
8364
  const dynamicRequire = (0, eval)('typeof require !== "undefined" ? require : undefined');
@@ -8784,7 +9085,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8784
9085
  onContextCompaction: options.onContextCompaction,
8785
9086
  onContextNotice: options.onContextNotice,
8786
9087
  toolContextMode: options.toolContextMode || "hot-tail",
8787
- toolWindow: options.toolWindow ?? "session"
9088
+ toolWindow: options.toolWindow ?? "session",
9089
+ offloadRecorder: options.offloadRecorder
8788
9090
  },
8789
9091
  queuedSteeringMessages
8790
9092
  );
@@ -9644,7 +9946,8 @@ Do NOT redo any of the above work.`
9644
9946
  sourceReplayHistoryMessages,
9645
9947
  state.taskName,
9646
9948
  compactionOptions?.toolContextMode || "hot-tail",
9647
- compactionOptions?.toolWindow ?? "session"
9949
+ compactionOptions?.toolWindow ?? "session",
9950
+ compactionOptions?.offloadRecorder
9648
9951
  );
9649
9952
  const continuationGuardrail = this.buildContinuationGuardrail(state);
9650
9953
  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.";
@@ -9807,7 +10110,8 @@ Do NOT redo any of the above work.`
9807
10110
  sourceHistoryMessages,
9808
10111
  state.taskName,
9809
10112
  compactionOptions?.toolContextMode || "hot-tail",
9810
- compactionOptions?.toolWindow ?? "session"
10113
+ compactionOptions?.toolWindow ?? "session",
10114
+ compactionOptions?.offloadRecorder
9811
10115
  );
9812
10116
  const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
9813
10117
  const summaryText = this.generateCompactSummary(
@@ -10454,7 +10758,7 @@ var RuntypeClient2 = class {
10454
10758
  clearApiKey() {
10455
10759
  delete this.headers.Authorization;
10456
10760
  }
10457
- async runWithLocalTools(request2, localTools, arg3, arg4) {
10761
+ async runWithLocalTools(request3, localTools, arg3, arg4) {
10458
10762
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
10459
10763
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
10460
10764
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -10468,12 +10772,12 @@ var RuntypeClient2 = class {
10468
10772
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
10469
10773
  })) : [];
10470
10774
  const modifiedRequest = {
10471
- ...request2,
10775
+ ...request3,
10472
10776
  ...derivedClientTools.length > 0 ? {
10473
- clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
10777
+ clientTools: [...request3.clientTools ?? [], ...derivedClientTools]
10474
10778
  } : {},
10475
10779
  options: {
10476
- ...request2.options || {},
10780
+ ...request3.options || {},
10477
10781
  streamResponse: isStreaming
10478
10782
  }
10479
10783
  };
@@ -10911,20 +11215,20 @@ var BatchBuilder = class {
10911
11215
  if (!this.recordType) {
10912
11216
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
10913
11217
  }
10914
- const request2 = {
11218
+ const request3 = {
10915
11219
  flowId: this.flowId,
10916
11220
  recordType: this.recordType
10917
11221
  };
10918
11222
  if (Object.keys(this.batchOptions).length > 0) {
10919
- request2.options = this.batchOptions;
11223
+ request3.options = this.batchOptions;
10920
11224
  }
10921
11225
  if (this.filterConfig) {
10922
- request2.filter = this.filterConfig;
11226
+ request3.filter = this.filterConfig;
10923
11227
  }
10924
11228
  if (this.limitConfig !== void 0) {
10925
- request2.limit = this.limitConfig;
11229
+ request3.limit = this.limitConfig;
10926
11230
  }
10927
- return request2;
11231
+ return request3;
10928
11232
  }
10929
11233
  /**
10930
11234
  * Execute the batch operation
@@ -11081,32 +11385,32 @@ var EvalBuilder = class {
11081
11385
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
11082
11386
  );
11083
11387
  }
11084
- const request2 = {};
11388
+ const request3 = {};
11085
11389
  if (this.flowId) {
11086
- request2.flowId = this.flowId;
11390
+ request3.flowId = this.flowId;
11087
11391
  } else if (this.virtualFlow) {
11088
- request2.flow = this.virtualFlow;
11392
+ request3.flow = this.virtualFlow;
11089
11393
  }
11090
11394
  if (this.recordType) {
11091
- request2.recordType = this.recordType;
11395
+ request3.recordType = this.recordType;
11092
11396
  } else if (this.inlineRecords) {
11093
- request2.records = this.inlineRecords;
11397
+ request3.records = this.inlineRecords;
11094
11398
  }
11095
11399
  if (this.modelOverrides) {
11096
- request2.modelOverrides = this.modelOverrides;
11400
+ request3.modelOverrides = this.modelOverrides;
11097
11401
  } else if (this.modelConfigs) {
11098
- request2.modelConfigs = this.modelConfigs;
11402
+ request3.modelConfigs = this.modelConfigs;
11099
11403
  }
11100
11404
  if (Object.keys(this.evalOptions).length > 0) {
11101
- request2.options = this.evalOptions;
11405
+ request3.options = this.evalOptions;
11102
11406
  }
11103
11407
  if (this.filterConfig) {
11104
- request2.filter = this.filterConfig;
11408
+ request3.filter = this.filterConfig;
11105
11409
  }
11106
11410
  if (this.limitConfig !== void 0) {
11107
- request2.limit = this.limitConfig;
11411
+ request3.limit = this.limitConfig;
11108
11412
  }
11109
- return request2;
11413
+ return request3;
11110
11414
  }
11111
11415
  /**
11112
11416
  * Execute the evaluation
@@ -11626,7 +11930,10 @@ var STEP_TYPE_TO_METHOD = {
11626
11930
  SkillProposalsNamespace,
11627
11931
  SkillsNamespace,
11628
11932
  SurfacesEndpoint,
11933
+ ToolDriftError,
11934
+ ToolEnsureConflictError,
11629
11935
  ToolsEndpoint,
11936
+ ToolsNamespace,
11630
11937
  UsersEndpoint,
11631
11938
  applyGeneratedRuntimeToolProposalToDispatchRequest,
11632
11939
  attachRuntimeToolsToDispatchRequest,
@@ -11638,6 +11945,7 @@ var STEP_TYPE_TO_METHOD = {
11638
11945
  compileWorkflowConfig,
11639
11946
  computeAgentContentHash,
11640
11947
  computeFlowContentHash,
11948
+ computeToolContentHash,
11641
11949
  createClient,
11642
11950
  createExternalTool,
11643
11951
  defaultWorkflow,
@@ -11645,6 +11953,7 @@ var STEP_TYPE_TO_METHOD = {
11645
11953
  defineAgent,
11646
11954
  defineFlow,
11647
11955
  definePlaybook,
11956
+ defineTool,
11648
11957
  deployWorkflow,
11649
11958
  ensureDefaultWorkflowHooks,
11650
11959
  evaluateGeneratedRuntimeToolProposal,
@@ -11660,6 +11969,7 @@ var STEP_TYPE_TO_METHOD = {
11660
11969
  listWorkflowHooks,
11661
11970
  normalizeAgentDefinition,
11662
11971
  normalizeCandidatePath,
11972
+ normalizeToolDefinition,
11663
11973
  parseFinalBuffer,
11664
11974
  parseLedgerArtifactRelativePath,
11665
11975
  parseOffloadedOutputId,