@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.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
@@ -1529,6 +1535,7 @@ function collectStepNonPortableToolRefs(config, path) {
1529
1535
  const found = [];
1530
1536
  const tools = config.tools;
1531
1537
  const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
1538
+ const isRawId = (ref, prefix) => typeof ref === "string" && ref.startsWith(prefix);
1532
1539
  const scanArray = (value, subPath) => {
1533
1540
  if (!Array.isArray(value)) return;
1534
1541
  value.forEach((ref, i) => {
@@ -1554,6 +1561,24 @@ function collectStepNonPortableToolRefs(config, path) {
1554
1561
  if (isPlainObject(tools.codeModeConfig)) {
1555
1562
  scanArray(tools.codeModeConfig.toolPool, `${path}.tools.codeModeConfig.toolPool`);
1556
1563
  }
1564
+ if (Array.isArray(tools.runtimeTools)) {
1565
+ tools.runtimeTools.forEach((runtimeTool, i) => {
1566
+ if (!isPlainObject(runtimeTool) || !isPlainObject(runtimeTool.config)) return;
1567
+ const base = `${path}.tools.runtimeTools[${i}].config`;
1568
+ const rtConfig = runtimeTool.config;
1569
+ if (runtimeTool.toolType === "subagent" && isRawId(rtConfig.agentId, "agent_")) {
1570
+ found.push(`${base}.agentId`);
1571
+ } else if (runtimeTool.toolType === "flow" && isRawId(rtConfig.flowId, "flow_")) {
1572
+ found.push(`${base}.flowId`);
1573
+ }
1574
+ });
1575
+ }
1576
+ }
1577
+ if (isAccountScoped(config.toolId)) {
1578
+ found.push(`${path}.toolId`);
1579
+ }
1580
+ if (isRawId(config.agentId, "agent_")) {
1581
+ found.push(`${path}.agentId`);
1557
1582
  }
1558
1583
  for (const branch of ["trueSteps", "falseSteps"]) {
1559
1584
  const nested = config[branch];
@@ -1608,7 +1633,7 @@ function defineFlow(input) {
1608
1633
  const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
1609
1634
  if (nonPortable.length > 0) {
1610
1635
  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.`
1636
+ `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.`
1612
1637
  );
1613
1638
  }
1614
1639
  }
@@ -2607,15 +2632,15 @@ var RuntypeFlowBuilder = class {
2607
2632
  build() {
2608
2633
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2609
2634
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2610
- const request2 = { flow };
2635
+ const request3 = { flow };
2611
2636
  if (this.recordConfig) {
2612
- request2.record = this.recordConfig;
2637
+ request3.record = this.recordConfig;
2613
2638
  }
2614
2639
  if (this.messagesConfig) {
2615
- request2.messages = this.messagesConfig;
2640
+ request3.messages = this.messagesConfig;
2616
2641
  }
2617
2642
  if (this.inputsConfig) {
2618
- request2.inputs = this.inputsConfig;
2643
+ request3.inputs = this.inputsConfig;
2619
2644
  }
2620
2645
  const options = {
2621
2646
  flowMode,
@@ -2633,8 +2658,8 @@ var RuntypeFlowBuilder = class {
2633
2658
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2634
2659
  options.upsertOptions = this.upsertOptions;
2635
2660
  }
2636
- request2.options = options;
2637
- return request2;
2661
+ request3.options = options;
2662
+ return request3;
2638
2663
  }
2639
2664
  /**
2640
2665
  * Validate this prospective flow against the public validation endpoint
@@ -3501,6 +3526,18 @@ function collectNonPortableToolRefs(config) {
3501
3526
  if (isPlainObject2(tools.codeModeConfig)) {
3502
3527
  scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
3503
3528
  }
3529
+ if (Array.isArray(tools.runtimeTools)) {
3530
+ tools.runtimeTools.forEach((runtimeTool, i) => {
3531
+ if (!isPlainObject2(runtimeTool) || !isPlainObject2(runtimeTool.config)) return;
3532
+ const base = `tools.runtimeTools[${i}].config`;
3533
+ const rtConfig = runtimeTool.config;
3534
+ if (runtimeTool.toolType === "subagent" && typeof rtConfig.agentId === "string" && rtConfig.agentId.startsWith("agent_")) {
3535
+ found.push(`${base}.agentId`);
3536
+ } else if (runtimeTool.toolType === "flow" && typeof rtConfig.flowId === "string" && rtConfig.flowId.startsWith("flow_")) {
3537
+ found.push(`${base}.flowId`);
3538
+ }
3539
+ });
3540
+ }
3504
3541
  return found;
3505
3542
  }
3506
3543
  function defineAgent(input) {
@@ -3524,7 +3561,7 @@ function defineAgent(input) {
3524
3561
  const nonPortable = collectNonPortableToolRefs(config);
3525
3562
  if (nonPortable.length > 0) {
3526
3563
  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.`
3564
+ `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.`
3528
3565
  );
3529
3566
  }
3530
3567
  return {
@@ -3666,6 +3703,242 @@ var AgentsNamespace = class {
3666
3703
  }
3667
3704
  };
3668
3705
 
3706
+ // src/tools-ensure.ts
3707
+ function isPlainObject3(value) {
3708
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3709
+ }
3710
+ function normalizeValue2(value) {
3711
+ if (Array.isArray(value)) {
3712
+ return value.map((item) => normalizeValue2(item));
3713
+ }
3714
+ if (isPlainObject3(value)) {
3715
+ const normalized = {};
3716
+ for (const key of Object.keys(value).sort()) {
3717
+ const entry = value[key];
3718
+ if (entry === void 0 || entry === null) continue;
3719
+ normalized[key] = normalizeValue2(entry);
3720
+ }
3721
+ return normalized;
3722
+ }
3723
+ return value;
3724
+ }
3725
+ function normalizeToolDefinition(definition) {
3726
+ const parametersSchema = isPlainObject3(definition.parametersSchema) ? normalizeValue2(definition.parametersSchema) : {};
3727
+ const config = isPlainObject3(definition.config) ? normalizeValue2(definition.config) : {};
3728
+ return {
3729
+ toolType: definition.toolType,
3730
+ ...definition.description ? { description: definition.description } : {},
3731
+ parametersSchema,
3732
+ config
3733
+ };
3734
+ }
3735
+ async function computeToolContentHash(definition) {
3736
+ const serialized = JSON.stringify(normalizeToolDefinition(definition));
3737
+ const encoded = new TextEncoder().encode(serialized);
3738
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3739
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3740
+ }
3741
+ var DEFINE_TOOL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3742
+ "name",
3743
+ "description",
3744
+ "toolType",
3745
+ "parametersSchema",
3746
+ "config"
3747
+ ]);
3748
+ var TOOL_DEFINITION_TYPES = /* @__PURE__ */ new Set([
3749
+ "flow",
3750
+ "custom",
3751
+ "external",
3752
+ "graphql",
3753
+ "mcp",
3754
+ "local",
3755
+ "subagent"
3756
+ ]);
3757
+ function defineTool(input) {
3758
+ if (!input || typeof input !== "object") {
3759
+ throw new Error("defineTool requires a definition object");
3760
+ }
3761
+ if (typeof input.name !== "string" || input.name.length === 0) {
3762
+ throw new Error('defineTool requires a non-empty string "name"');
3763
+ }
3764
+ if (typeof input.description !== "string" || input.description.length === 0) {
3765
+ throw new Error('defineTool requires a non-empty string "description"');
3766
+ }
3767
+ if (typeof input.toolType !== "string" || !TOOL_DEFINITION_TYPES.has(input.toolType)) {
3768
+ throw new Error(
3769
+ `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
3770
+ );
3771
+ }
3772
+ if (!isPlainObject3(input.parametersSchema)) {
3773
+ throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
3774
+ }
3775
+ if (!isPlainObject3(input.config)) {
3776
+ throw new Error('defineTool requires a "config" object');
3777
+ }
3778
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
3779
+ if (unknownKeys.length > 0) {
3780
+ throw new Error(
3781
+ `defineTool: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, toolType, parametersSchema, config.`
3782
+ );
3783
+ }
3784
+ return {
3785
+ name: input.name,
3786
+ description: input.description,
3787
+ toolType: input.toolType,
3788
+ parametersSchema: input.parametersSchema,
3789
+ config: input.config
3790
+ };
3791
+ }
3792
+ var ToolEnsureConflictError = class extends Error {
3793
+ constructor(body) {
3794
+ super(body.error ?? `Tool ensure conflict: ${body.code}`);
3795
+ this.name = "ToolEnsureConflictError";
3796
+ this.code = body.code;
3797
+ this.lastModifiedSource = body.lastModifiedSource;
3798
+ this.modifiedAt = body.modifiedAt;
3799
+ this.currentHash = body.currentHash;
3800
+ }
3801
+ };
3802
+ var ToolDriftError = class extends Error {
3803
+ constructor(plan) {
3804
+ super(
3805
+ `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.`
3806
+ );
3807
+ this.name = "ToolDriftError";
3808
+ this.plan = plan;
3809
+ }
3810
+ };
3811
+ function parseRequestError3(err) {
3812
+ if (!(err instanceof Error)) return { status: null, body: null };
3813
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3814
+ if (!match) return { status: null, body: null };
3815
+ try {
3816
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3817
+ } catch {
3818
+ return { status: Number(match[1]), body: null };
3819
+ }
3820
+ }
3821
+ function toConflictError3(err) {
3822
+ const { status, body } = parseRequestError3(err);
3823
+ if (status !== 409 || !isPlainObject3(body)) return null;
3824
+ const code = body.code;
3825
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3826
+ return new ToolEnsureConflictError(
3827
+ body
3828
+ );
3829
+ }
3830
+ var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3831
+ function memoFor3(client) {
3832
+ let memo = serverHashMemo3.get(client);
3833
+ if (!memo) {
3834
+ memo = /* @__PURE__ */ new Map();
3835
+ serverHashMemo3.set(client, memo);
3836
+ }
3837
+ return memo;
3838
+ }
3839
+ function memoize2(memo, memoKey, result) {
3840
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3841
+ }
3842
+ async function request2(client, body) {
3843
+ try {
3844
+ return await client.post(
3845
+ "/tools/ensure",
3846
+ body
3847
+ );
3848
+ } catch (err) {
3849
+ const conflict = toConflictError3(err);
3850
+ if (conflict) throw conflict;
3851
+ throw err;
3852
+ }
3853
+ }
3854
+ async function ensureTool(client, definition, options = {}) {
3855
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
3856
+ const passthrough = {
3857
+ ...onConflict ? { onConflict } : {},
3858
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3859
+ };
3860
+ if (dryRun || expectNoChanges) {
3861
+ const plan = await request2(client, {
3862
+ name: definition.name,
3863
+ definition,
3864
+ dryRun: true,
3865
+ ...passthrough
3866
+ });
3867
+ if (plan.result !== "plan") {
3868
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3869
+ }
3870
+ if (expectNoChanges && plan.changes !== "none") {
3871
+ throw new ToolDriftError(plan);
3872
+ }
3873
+ return plan;
3874
+ }
3875
+ const memo = memoFor3(client);
3876
+ const localHash = await computeToolContentHash(definition);
3877
+ const memoKey = `${definition.name} ${localHash}`;
3878
+ const contentHash = memo.get(memoKey) ?? localHash;
3879
+ const probe = await request2(client, {
3880
+ name: definition.name,
3881
+ contentHash,
3882
+ ...passthrough
3883
+ });
3884
+ if (probe.result !== "definitionRequired") {
3885
+ memoize2(memo, memoKey, probe);
3886
+ return probe;
3887
+ }
3888
+ const converged = await request2(client, {
3889
+ name: definition.name,
3890
+ definition,
3891
+ ...passthrough
3892
+ });
3893
+ if (converged.result === "definitionRequired") {
3894
+ throw new Error("Server reported definitionRequired for a full-definition request");
3895
+ }
3896
+ memoize2(memo, memoKey, converged);
3897
+ return converged;
3898
+ }
3899
+ async function pullTool(client, name) {
3900
+ return client.get("/tools/pull", { name });
3901
+ }
3902
+
3903
+ // src/tools-namespace.ts
3904
+ var ToolsNamespace = class {
3905
+ constructor(getClient) {
3906
+ this.getClient = getClient;
3907
+ }
3908
+ /**
3909
+ * Idempotently converge a `defineTool` definition onto the platform.
3910
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
3911
+ * the saved tool; never deletes. Identity is name + account scope.
3912
+ *
3913
+ * @example
3914
+ * ```typescript
3915
+ * const weather = defineTool({
3916
+ * name: 'Weather Lookup',
3917
+ * description: 'Fetch the current weather for a city',
3918
+ * toolType: 'external',
3919
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
3920
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
3921
+ * })
3922
+ *
3923
+ * // Converge (CI/deploy).
3924
+ * const result = await Runtype.tools.ensure(weather)
3925
+ *
3926
+ * // PR drift gate.
3927
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
3928
+ * ```
3929
+ */
3930
+ async ensure(definition, options = {}) {
3931
+ return ensureTool(this.getClient(), definition, options);
3932
+ }
3933
+ /**
3934
+ * Pull the canonical definition + provenance for a tool by name — the
3935
+ * absorb-drift direction of the ensure protocol.
3936
+ */
3937
+ async pull(name) {
3938
+ return pullTool(this.getClient(), name);
3939
+ }
3940
+ };
3941
+
3669
3942
  // src/transform.ts
3670
3943
  function transformResponse(data) {
3671
3944
  return data;
@@ -4054,6 +4327,34 @@ var Runtype = class {
4054
4327
  static get agents() {
4055
4328
  return new AgentsNamespace(() => this.getClient());
4056
4329
  }
4330
+ /**
4331
+ * Tools namespace - Tool config-as-code (define / ensure / pull)
4332
+ *
4333
+ * @example
4334
+ * ```typescript
4335
+ * import { defineTool, Runtype } from '@runtypelabs/sdk'
4336
+ *
4337
+ * const weather = defineTool({
4338
+ * name: 'Weather Lookup',
4339
+ * description: 'Fetch the current weather for a city',
4340
+ * toolType: 'external',
4341
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
4342
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
4343
+ * })
4344
+ *
4345
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
4346
+ * await Runtype.tools.ensure(weather)
4347
+ *
4348
+ * // CI drift gate
4349
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
4350
+ *
4351
+ * // Absorb a dashboard edit back into the repo
4352
+ * const { definition } = await Runtype.tools.pull('Weather Lookup')
4353
+ * ```
4354
+ */
4355
+ static get tools() {
4356
+ return new ToolsNamespace(() => this.getClient());
4357
+ }
4057
4358
  };
4058
4359
 
4059
4360
  // src/generated-tool-gate.ts
@@ -4276,8 +4577,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
4276
4577
  ...decision.tool ? { tool: decision.tool } : {}
4277
4578
  };
4278
4579
  }
4279
- function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {}) {
4280
- const stepList = request2.flow.steps;
4580
+ function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {}) {
4581
+ const stepList = request3.flow.steps;
4281
4582
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
4282
4583
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
4283
4584
  }
@@ -4320,9 +4621,9 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4320
4621
  }
4321
4622
  };
4322
4623
  return {
4323
- ...request2,
4624
+ ...request3,
4324
4625
  flow: {
4325
- ...request2.flow,
4626
+ ...request3.flow,
4326
4627
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
4327
4628
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
4328
4629
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -4332,12 +4633,12 @@ function attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options = {
4332
4633
  }
4333
4634
  };
4334
4635
  }
4335
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options = {}) {
4636
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options = {}) {
4336
4637
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
4337
4638
  if (!decision.approved || !decision.tool) {
4338
- return { decision, request: request2 };
4639
+ return { decision, request: request3 };
4339
4640
  }
4340
- const nextRequest = attachRuntimeToolsToDispatchRequest(request2, [decision.tool], options.attach);
4641
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request3, [decision.tool], options.attach);
4341
4642
  return {
4342
4643
  decision,
4343
4644
  request: nextRequest
@@ -6246,6 +6547,19 @@ var RecordsEndpoint = class {
6246
6547
  async getResults(id, params) {
6247
6548
  return this.client.get(`/records/${id}/results`, params);
6248
6549
  }
6550
+ /**
6551
+ * Get unified step-level execution results for a record, with filtering and
6552
+ * pagination.
6553
+ */
6554
+ async getStepResults(id, params) {
6555
+ return this.client.get(`/records/${id}/step-results`, params);
6556
+ }
6557
+ /**
6558
+ * Get the aggregated cost breakdown (by model) for a record's executions.
6559
+ */
6560
+ async getCosts(id) {
6561
+ return this.client.get(`/records/${id}/costs`);
6562
+ }
6249
6563
  /**
6250
6564
  * Delete a specific result for a record
6251
6565
  */
@@ -6574,15 +6888,15 @@ var DispatchEndpoint = class {
6574
6888
  * Attach approved runtime tools to a prompt step in a redispatch request.
6575
6889
  * Returns a new request object and does not mutate the original.
6576
6890
  */
6577
- attachApprovedRuntimeTools(request2, runtimeTools, options) {
6578
- return attachRuntimeToolsToDispatchRequest(request2, runtimeTools, options);
6891
+ attachApprovedRuntimeTools(request3, runtimeTools, options) {
6892
+ return attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options);
6579
6893
  }
6580
6894
  /**
6581
6895
  * Validate a generated runtime tool proposal and attach it to the redispatch
6582
6896
  * request if approved, in one call.
6583
6897
  */
6584
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
6585
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request2, proposal, options);
6898
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
6899
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options);
6586
6900
  }
6587
6901
  };
6588
6902
  var ChatEndpoint = class {
@@ -7134,8 +7448,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
7134
7448
  },
7135
7449
  required: ["name", "description", "toolType", "parametersSchema", "config"]
7136
7450
  };
7137
- function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7138
- const existing = request2.tools?.runtimeTools || [];
7451
+ function appendRuntimeToolsToAgentRequest(request3, runtimeTools) {
7452
+ const existing = request3.tools?.runtimeTools || [];
7139
7453
  const existingNames = new Set(existing.map((tool) => tool.name));
7140
7454
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
7141
7455
  name: tool.name,
@@ -7145,9 +7459,9 @@ function appendRuntimeToolsToAgentRequest(request2, runtimeTools) {
7145
7459
  ...tool.config ? { config: tool.config } : {}
7146
7460
  }));
7147
7461
  return {
7148
- ...request2,
7462
+ ...request3,
7149
7463
  tools: {
7150
- ...request2.tools,
7464
+ ...request3.tools,
7151
7465
  runtimeTools: [...existing, ...converted]
7152
7466
  }
7153
7467
  };
@@ -7223,21 +7537,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7223
7537
  * Attach approved runtime tools to an agent execute request.
7224
7538
  * Returns a new request object and does not mutate the original.
7225
7539
  */
7226
- attachApprovedRuntimeTools(request2, runtimeTools) {
7227
- return appendRuntimeToolsToAgentRequest(request2, runtimeTools);
7540
+ attachApprovedRuntimeTools(request3, runtimeTools) {
7541
+ return appendRuntimeToolsToAgentRequest(request3, runtimeTools);
7228
7542
  }
7229
7543
  /**
7230
7544
  * Validate a generated runtime tool proposal and append it to an agent execute
7231
7545
  * request if approved, in one call.
7232
7546
  */
7233
- applyGeneratedRuntimeToolProposal(request2, proposal, options) {
7547
+ applyGeneratedRuntimeToolProposal(request3, proposal, options) {
7234
7548
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
7235
7549
  if (!decision.approved || !decision.tool) {
7236
- return { decision, request: request2 };
7550
+ return { decision, request: request3 };
7237
7551
  }
7238
7552
  return {
7239
7553
  decision,
7240
- request: appendRuntimeToolsToAgentRequest(request2, [decision.tool])
7554
+ request: appendRuntimeToolsToAgentRequest(request3, [decision.tool])
7241
7555
  };
7242
7556
  }
7243
7557
  /**
@@ -7996,11 +8310,13 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7996
8310
  * setting. This never mutates persisted marathon history; masking/offloading
7997
8311
  * is a send-time view over the full-fidelity ledger/history.
7998
8312
  */
7999
- deriveToolContextMessages(messages, taskName, mode, window) {
8313
+ deriveToolContextMessages(messages, taskName, mode, window, offloadRecorder) {
8000
8314
  if (mode === "full-inline") return [...messages];
8001
8315
  const maskMessage = (msg) => ({
8002
8316
  ...msg,
8003
- toolResults: (msg.toolResults ?? []).map((tr) => this.compactOneResult(tr, taskName, mode))
8317
+ toolResults: (msg.toolResults ?? []).map(
8318
+ (tr) => this.compactOneResult(tr, taskName, mode, offloadRecorder)
8319
+ )
8004
8320
  });
8005
8321
  const view = [...messages];
8006
8322
  if (window === "session") {
@@ -8034,12 +8350,18 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8034
8350
  }
8035
8351
  return view;
8036
8352
  }
8037
- compactOneResult(tr, taskName, mode) {
8353
+ compactOneResult(tr, taskName, mode, offloadRecorder) {
8038
8354
  if (typeof tr.result === "string" && tr.result.startsWith("[")) return tr;
8039
8355
  if (mode === "hot-tail") {
8040
8356
  return {
8041
8357
  ...tr,
8042
- result: this.offloadToolResult(taskName, tr.toolCallId, tr.toolName, tr.result)
8358
+ result: this.offloadToolResult(
8359
+ taskName,
8360
+ tr.toolCallId,
8361
+ tr.toolName,
8362
+ tr.result,
8363
+ offloadRecorder
8364
+ )
8043
8365
  };
8044
8366
  }
8045
8367
  return { ...tr, result: `[Output from ${tr.toolName} masked \u2014 re-run the tool if needed]` };
@@ -8055,9 +8377,16 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8055
8377
  return taskName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
8056
8378
  }
8057
8379
  // chars
8058
- offloadToolResult(taskName, toolCallId, toolName, result) {
8380
+ offloadToolResult(taskName, toolCallId, toolName, result, offloadRecorder) {
8059
8381
  const resultStr = typeof result === "string" ? result : JSON.stringify(result, null, 2);
8060
8382
  if (resultStr.length <= this.TOOL_OUTPUT_INLINE_THRESHOLD) return result;
8383
+ if (offloadRecorder) {
8384
+ try {
8385
+ const recorded = offloadRecorder({ toolCallId, toolName, content: resultStr });
8386
+ if (recorded?.reference) return recorded.reference;
8387
+ } catch {
8388
+ }
8389
+ }
8061
8390
  let fs;
8062
8391
  try {
8063
8392
  const dynamicRequire = (0, eval)('typeof require !== "undefined" ? require : undefined');
@@ -8784,7 +9113,8 @@ var _AgentsEndpoint = class _AgentsEndpoint {
8784
9113
  onContextCompaction: options.onContextCompaction,
8785
9114
  onContextNotice: options.onContextNotice,
8786
9115
  toolContextMode: options.toolContextMode || "hot-tail",
8787
- toolWindow: options.toolWindow ?? "session"
9116
+ toolWindow: options.toolWindow ?? "session",
9117
+ offloadRecorder: options.offloadRecorder
8788
9118
  },
8789
9119
  queuedSteeringMessages
8790
9120
  );
@@ -9575,7 +9905,8 @@ Do NOT redo any of the above work.`
9575
9905
  });
9576
9906
  };
9577
9907
  const buildNativeCompactionEvent = (mode, breakdown) => {
9578
- if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || breakdown.estimatedInputTokens < compactionOptions.autoCompactTokenThreshold) {
9908
+ const gateTokens = breakdown.sendEstimatedInputTokens ?? breakdown.estimatedInputTokens;
9909
+ if (resolvedStrategy !== "provider_native" || typeof compactionOptions?.autoCompactTokenThreshold !== "number" || compactionOptions.autoCompactTokenThreshold <= 0 || gateTokens < compactionOptions.autoCompactTokenThreshold) {
9579
9910
  return void 0;
9580
9911
  }
9581
9912
  return {
@@ -9644,7 +9975,8 @@ Do NOT redo any of the above work.`
9644
9975
  sourceReplayHistoryMessages,
9645
9976
  state.taskName,
9646
9977
  compactionOptions?.toolContextMode || "hot-tail",
9647
- compactionOptions?.toolWindow ?? "session"
9978
+ compactionOptions?.toolWindow ?? "session",
9979
+ compactionOptions?.offloadRecorder
9648
9980
  );
9649
9981
  const continuationGuardrail = this.buildContinuationGuardrail(state);
9650
9982
  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 +10139,8 @@ Do NOT redo any of the above work.`
9807
10139
  sourceHistoryMessages,
9808
10140
  state.taskName,
9809
10141
  compactionOptions?.toolContextMode || "hot-tail",
9810
- compactionOptions?.toolWindow ?? "session"
10142
+ compactionOptions?.toolWindow ?? "session",
10143
+ compactionOptions?.offloadRecorder
9811
10144
  );
9812
10145
  const historyArtifactReferences = this.extractArtifactReferencesFromMessages(historyMessages);
9813
10146
  const summaryText = this.generateCompactSummary(
@@ -10454,7 +10787,7 @@ var RuntypeClient2 = class {
10454
10787
  clearApiKey() {
10455
10788
  delete this.headers.Authorization;
10456
10789
  }
10457
- async runWithLocalTools(request2, localTools, arg3, arg4) {
10790
+ async runWithLocalTools(request3, localTools, arg3, arg4) {
10458
10791
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
10459
10792
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
10460
10793
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -10468,12 +10801,12 @@ var RuntypeClient2 = class {
10468
10801
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
10469
10802
  })) : [];
10470
10803
  const modifiedRequest = {
10471
- ...request2,
10804
+ ...request3,
10472
10805
  ...derivedClientTools.length > 0 ? {
10473
- clientTools: [...request2.clientTools ?? [], ...derivedClientTools]
10806
+ clientTools: [...request3.clientTools ?? [], ...derivedClientTools]
10474
10807
  } : {},
10475
10808
  options: {
10476
- ...request2.options || {},
10809
+ ...request3.options || {},
10477
10810
  streamResponse: isStreaming
10478
10811
  }
10479
10812
  };
@@ -10911,20 +11244,20 @@ var BatchBuilder = class {
10911
11244
  if (!this.recordType) {
10912
11245
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
10913
11246
  }
10914
- const request2 = {
11247
+ const request3 = {
10915
11248
  flowId: this.flowId,
10916
11249
  recordType: this.recordType
10917
11250
  };
10918
11251
  if (Object.keys(this.batchOptions).length > 0) {
10919
- request2.options = this.batchOptions;
11252
+ request3.options = this.batchOptions;
10920
11253
  }
10921
11254
  if (this.filterConfig) {
10922
- request2.filter = this.filterConfig;
11255
+ request3.filter = this.filterConfig;
10923
11256
  }
10924
11257
  if (this.limitConfig !== void 0) {
10925
- request2.limit = this.limitConfig;
11258
+ request3.limit = this.limitConfig;
10926
11259
  }
10927
- return request2;
11260
+ return request3;
10928
11261
  }
10929
11262
  /**
10930
11263
  * Execute the batch operation
@@ -11081,32 +11414,32 @@ var EvalBuilder = class {
11081
11414
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
11082
11415
  );
11083
11416
  }
11084
- const request2 = {};
11417
+ const request3 = {};
11085
11418
  if (this.flowId) {
11086
- request2.flowId = this.flowId;
11419
+ request3.flowId = this.flowId;
11087
11420
  } else if (this.virtualFlow) {
11088
- request2.flow = this.virtualFlow;
11421
+ request3.flow = this.virtualFlow;
11089
11422
  }
11090
11423
  if (this.recordType) {
11091
- request2.recordType = this.recordType;
11424
+ request3.recordType = this.recordType;
11092
11425
  } else if (this.inlineRecords) {
11093
- request2.records = this.inlineRecords;
11426
+ request3.records = this.inlineRecords;
11094
11427
  }
11095
11428
  if (this.modelOverrides) {
11096
- request2.modelOverrides = this.modelOverrides;
11429
+ request3.modelOverrides = this.modelOverrides;
11097
11430
  } else if (this.modelConfigs) {
11098
- request2.modelConfigs = this.modelConfigs;
11431
+ request3.modelConfigs = this.modelConfigs;
11099
11432
  }
11100
11433
  if (Object.keys(this.evalOptions).length > 0) {
11101
- request2.options = this.evalOptions;
11434
+ request3.options = this.evalOptions;
11102
11435
  }
11103
11436
  if (this.filterConfig) {
11104
- request2.filter = this.filterConfig;
11437
+ request3.filter = this.filterConfig;
11105
11438
  }
11106
11439
  if (this.limitConfig !== void 0) {
11107
- request2.limit = this.limitConfig;
11440
+ request3.limit = this.limitConfig;
11108
11441
  }
11109
- return request2;
11442
+ return request3;
11110
11443
  }
11111
11444
  /**
11112
11445
  * Execute the evaluation
@@ -11626,7 +11959,10 @@ var STEP_TYPE_TO_METHOD = {
11626
11959
  SkillProposalsNamespace,
11627
11960
  SkillsNamespace,
11628
11961
  SurfacesEndpoint,
11962
+ ToolDriftError,
11963
+ ToolEnsureConflictError,
11629
11964
  ToolsEndpoint,
11965
+ ToolsNamespace,
11630
11966
  UsersEndpoint,
11631
11967
  applyGeneratedRuntimeToolProposalToDispatchRequest,
11632
11968
  attachRuntimeToolsToDispatchRequest,
@@ -11638,6 +11974,7 @@ var STEP_TYPE_TO_METHOD = {
11638
11974
  compileWorkflowConfig,
11639
11975
  computeAgentContentHash,
11640
11976
  computeFlowContentHash,
11977
+ computeToolContentHash,
11641
11978
  createClient,
11642
11979
  createExternalTool,
11643
11980
  defaultWorkflow,
@@ -11645,6 +11982,7 @@ var STEP_TYPE_TO_METHOD = {
11645
11982
  defineAgent,
11646
11983
  defineFlow,
11647
11984
  definePlaybook,
11985
+ defineTool,
11648
11986
  deployWorkflow,
11649
11987
  ensureDefaultWorkflowHooks,
11650
11988
  evaluateGeneratedRuntimeToolProposal,
@@ -11660,6 +11998,7 @@ var STEP_TYPE_TO_METHOD = {
11660
11998
  listWorkflowHooks,
11661
11999
  normalizeAgentDefinition,
11662
12000
  normalizeCandidatePath,
12001
+ normalizeToolDefinition,
11663
12002
  parseFinalBuffer,
11664
12003
  parseLedgerArtifactRelativePath,
11665
12004
  parseOffloadedOutputId,