@runtypelabs/sdk 4.14.0 → 4.16.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
@@ -57,6 +57,9 @@ __export(index_exports, {
57
57
  LEDGER_ARTIFACT_LINE_PREFIX: () => LEDGER_ARTIFACT_LINE_PREFIX,
58
58
  LogsEndpoint: () => LogsEndpoint,
59
59
  ModelConfigsEndpoint: () => ModelConfigsEndpoint,
60
+ ProductDriftError: () => ProductDriftError,
61
+ ProductEnsureConflictError: () => ProductEnsureConflictError,
62
+ ProductsNamespace: () => ProductsNamespace,
60
63
  PromptRunner: () => PromptRunner,
61
64
  PromptsEndpoint: () => PromptsEndpoint,
62
65
  PromptsNamespace: () => PromptsNamespace,
@@ -70,9 +73,15 @@ __export(index_exports, {
70
73
  STEP_TYPE_TO_METHOD: () => STEP_TYPE_TO_METHOD,
71
74
  SchedulesEndpoint: () => SchedulesEndpoint,
72
75
  SecretsEndpoint: () => SecretsEndpoint,
76
+ SkillDriftError: () => SkillDriftError,
77
+ SkillEnsureConflictError: () => SkillEnsureConflictError,
73
78
  SkillProposalsNamespace: () => SkillProposalsNamespace,
74
79
  SkillsNamespace: () => SkillsNamespace,
80
+ SurfaceDriftError: () => SurfaceDriftError,
81
+ SurfaceEnsureConflictError: () => SurfaceEnsureConflictError,
75
82
  SurfacesEndpoint: () => SurfacesEndpoint,
83
+ SurfacesNamespace: () => SurfacesNamespace,
84
+ ToolApprovalGrantsEndpoint: () => ToolApprovalGrantsEndpoint,
76
85
  ToolDriftError: () => ToolDriftError,
77
86
  ToolEnsureConflictError: () => ToolEnsureConflictError,
78
87
  ToolsEndpoint: () => ToolsEndpoint,
@@ -88,6 +97,9 @@ __export(index_exports, {
88
97
  compileWorkflowConfig: () => compileWorkflowConfig,
89
98
  computeAgentContentHash: () => computeAgentContentHash,
90
99
  computeFlowContentHash: () => computeFlowContentHash,
100
+ computeProductContentHash: () => computeProductContentHash,
101
+ computeSkillContentHash: () => computeSkillContentHash,
102
+ computeSurfaceContentHash: () => computeSurfaceContentHash,
91
103
  computeToolContentHash: () => computeToolContentHash,
92
104
  createClient: () => createClient,
93
105
  createExternalTool: () => createExternalTool,
@@ -96,6 +108,9 @@ __export(index_exports, {
96
108
  defineAgent: () => defineAgent,
97
109
  defineFlow: () => defineFlow,
98
110
  definePlaybook: () => definePlaybook,
111
+ defineProduct: () => defineProduct,
112
+ defineSkill: () => defineSkill,
113
+ defineSurface: () => defineSurface,
99
114
  defineTool: () => defineTool,
100
115
  deployWorkflow: () => deployWorkflow,
101
116
  ensureDefaultWorkflowHooks: () => ensureDefaultWorkflowHooks,
@@ -112,6 +127,9 @@ __export(index_exports, {
112
127
  listWorkflowHooks: () => listWorkflowHooks,
113
128
  normalizeAgentDefinition: () => normalizeAgentDefinition,
114
129
  normalizeCandidatePath: () => normalizeCandidatePath,
130
+ normalizeProductDefinition: () => normalizeProductDefinition,
131
+ normalizeSkillDefinition: () => normalizeSkillDefinition,
132
+ normalizeSurfaceDefinition: () => normalizeSurfaceDefinition,
115
133
  normalizeToolDefinition: () => normalizeToolDefinition,
116
134
  parseFinalBuffer: () => parseFinalBuffer,
117
135
  parseLedgerArtifactRelativePath: () => parseLedgerArtifactRelativePath,
@@ -1225,20 +1243,20 @@ var FlowBuilder = class {
1225
1243
  */
1226
1244
  build() {
1227
1245
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
1228
- const request3 = { flow };
1246
+ const request6 = { flow };
1229
1247
  if (this.recordConfig) {
1230
- request3.record = this.recordConfig;
1248
+ request6.record = this.recordConfig;
1231
1249
  }
1232
1250
  if (this.messagesConfig) {
1233
- request3.messages = this.messagesConfig;
1251
+ request6.messages = this.messagesConfig;
1234
1252
  }
1235
1253
  if (this.inputsConfig) {
1236
- request3.inputs = this.inputsConfig;
1254
+ request6.inputs = this.inputsConfig;
1237
1255
  }
1238
1256
  if (Object.keys(this.optionsConfig).length > 0) {
1239
- request3.options = this.optionsConfig;
1257
+ request6.options = this.optionsConfig;
1240
1258
  }
1241
- return request3;
1259
+ return request6;
1242
1260
  }
1243
1261
  /**
1244
1262
  * Validate this prospective flow against the public validation endpoint
@@ -2632,15 +2650,15 @@ var RuntypeFlowBuilder = class {
2632
2650
  build() {
2633
2651
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2634
2652
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2635
- const request3 = { flow };
2653
+ const request6 = { flow };
2636
2654
  if (this.recordConfig) {
2637
- request3.record = this.recordConfig;
2655
+ request6.record = this.recordConfig;
2638
2656
  }
2639
2657
  if (this.messagesConfig) {
2640
- request3.messages = this.messagesConfig;
2658
+ request6.messages = this.messagesConfig;
2641
2659
  }
2642
2660
  if (this.inputsConfig) {
2643
- request3.inputs = this.inputsConfig;
2661
+ request6.inputs = this.inputsConfig;
2644
2662
  }
2645
2663
  const options = {
2646
2664
  flowMode,
@@ -2658,8 +2676,8 @@ var RuntypeFlowBuilder = class {
2658
2676
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2659
2677
  options.upsertOptions = this.upsertOptions;
2660
2678
  }
2661
- request3.options = options;
2662
- return request3;
2679
+ request6.options = options;
2680
+ return request6;
2663
2681
  }
2664
2682
  /**
2665
2683
  * Validate this prospective flow against the public validation endpoint
@@ -3253,6 +3271,192 @@ var PromptsNamespace = class {
3253
3271
  }
3254
3272
  };
3255
3273
 
3274
+ // src/skills-ensure.ts
3275
+ function isPlainObject2(value) {
3276
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3277
+ }
3278
+ function normalizeValue(value) {
3279
+ if (Array.isArray(value)) {
3280
+ return value.map((item) => normalizeValue(item));
3281
+ }
3282
+ if (isPlainObject2(value)) {
3283
+ const normalized = {};
3284
+ for (const key of Object.keys(value).sort()) {
3285
+ const entry = value[key];
3286
+ if (entry === void 0 || entry === null) continue;
3287
+ normalized[key] = normalizeValue(entry);
3288
+ }
3289
+ return normalized;
3290
+ }
3291
+ return value;
3292
+ }
3293
+ function normalizeSkillDefinition(definition) {
3294
+ const manifest = isPlainObject2(definition.manifest) ? definition.manifest : {};
3295
+ const rawFrontmatter = isPlainObject2(manifest.frontmatter) ? manifest.frontmatter : {};
3296
+ const frontmatterWithoutName = {};
3297
+ for (const key of Object.keys(rawFrontmatter)) {
3298
+ if (key === "name") continue;
3299
+ frontmatterWithoutName[key] = rawFrontmatter[key];
3300
+ }
3301
+ const frontmatter = normalizeValue(frontmatterWithoutName);
3302
+ const runtype = isPlainObject2(manifest.runtype) ? normalizeValue(manifest.runtype) : {};
3303
+ const body = typeof manifest.body === "string" ? manifest.body : "";
3304
+ return { frontmatter, runtype, body };
3305
+ }
3306
+ async function computeSkillContentHash(definition) {
3307
+ const serialized = JSON.stringify(normalizeSkillDefinition(definition));
3308
+ const encoded = new TextEncoder().encode(serialized);
3309
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3310
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3311
+ }
3312
+ var DEFINE_SKILL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "manifest"]);
3313
+ function defineSkill(input) {
3314
+ if (!input || typeof input !== "object") {
3315
+ throw new Error("defineSkill requires a definition object");
3316
+ }
3317
+ if (typeof input.name !== "string" || input.name.length === 0) {
3318
+ throw new Error('defineSkill requires a non-empty string "name"');
3319
+ }
3320
+ if (!isPlainObject2(input.manifest)) {
3321
+ throw new Error('defineSkill requires a "manifest" object ({ frontmatter, runtype, body })');
3322
+ }
3323
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_SKILL_TOP_LEVEL_KEYS.has(key));
3324
+ if (unknownKeys.length > 0) {
3325
+ throw new Error(
3326
+ `defineSkill: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name and manifest.`
3327
+ );
3328
+ }
3329
+ const frontmatter = input.manifest.frontmatter;
3330
+ if (!isPlainObject2(frontmatter) || typeof frontmatter.name !== "string") {
3331
+ throw new Error("defineSkill: manifest.frontmatter.name is required");
3332
+ }
3333
+ if (frontmatter.name !== input.name) {
3334
+ throw new Error(
3335
+ `defineSkill: manifest.frontmatter.name ("${frontmatter.name}") must match the identity name ("${input.name}").`
3336
+ );
3337
+ }
3338
+ return { name: input.name, manifest: input.manifest };
3339
+ }
3340
+ var SkillEnsureConflictError = class extends Error {
3341
+ constructor(body) {
3342
+ super(body.error ?? `Skill ensure conflict: ${body.code}`);
3343
+ this.name = "SkillEnsureConflictError";
3344
+ this.code = body.code;
3345
+ this.lastModifiedSource = body.lastModifiedSource;
3346
+ this.modifiedAt = body.modifiedAt;
3347
+ this.currentHash = body.currentHash;
3348
+ }
3349
+ };
3350
+ var SkillDriftError = class extends Error {
3351
+ constructor(plan) {
3352
+ super(
3353
+ `Skill "${plan.skillId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.skills.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
3354
+ );
3355
+ this.name = "SkillDriftError";
3356
+ this.plan = plan;
3357
+ }
3358
+ };
3359
+ function parseRequestError2(err) {
3360
+ if (!(err instanceof Error)) return { status: null, body: null };
3361
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3362
+ if (!match) return { status: null, body: null };
3363
+ try {
3364
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3365
+ } catch {
3366
+ return { status: Number(match[1]), body: null };
3367
+ }
3368
+ }
3369
+ function toConflictError2(err) {
3370
+ const { status, body } = parseRequestError2(err);
3371
+ if (status !== 409 || !isPlainObject2(body)) return null;
3372
+ const code = body.code;
3373
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3374
+ return new SkillEnsureConflictError(
3375
+ body
3376
+ );
3377
+ }
3378
+ var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
3379
+ function memoFor2(client) {
3380
+ let memo = serverHashMemo2.get(client);
3381
+ if (!memo) {
3382
+ memo = /* @__PURE__ */ new Map();
3383
+ serverHashMemo2.set(client, memo);
3384
+ }
3385
+ return memo;
3386
+ }
3387
+ function memoize2(memo, memoKey, result) {
3388
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3389
+ }
3390
+ async function request2(client, body) {
3391
+ try {
3392
+ return await client.post(
3393
+ "/skills/ensure",
3394
+ body
3395
+ );
3396
+ } catch (err) {
3397
+ const conflict = toConflictError2(err);
3398
+ if (conflict) throw conflict;
3399
+ throw err;
3400
+ }
3401
+ }
3402
+ async function ensureSkill(client, definition, options = {}) {
3403
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
3404
+ const passthrough = {
3405
+ ...onConflict ? { onConflict } : {},
3406
+ ...release ? { release } : {},
3407
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3408
+ };
3409
+ if (dryRun || expectNoChanges) {
3410
+ const plan = await request2(client, {
3411
+ name: definition.name,
3412
+ definition: manifestToWire(definition),
3413
+ dryRun: true,
3414
+ ...passthrough
3415
+ });
3416
+ if (plan.result !== "plan") {
3417
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3418
+ }
3419
+ if (expectNoChanges && plan.changes !== "none") {
3420
+ throw new SkillDriftError(plan);
3421
+ }
3422
+ return plan;
3423
+ }
3424
+ const memo = memoFor2(client);
3425
+ const localHash = await computeSkillContentHash(definition);
3426
+ const memoKey = `${definition.name} ${localHash}`;
3427
+ const contentHash = memo.get(memoKey) ?? localHash;
3428
+ const probe = await request2(client, {
3429
+ name: definition.name,
3430
+ contentHash,
3431
+ ...passthrough
3432
+ });
3433
+ if (probe.result !== "definitionRequired") {
3434
+ memoize2(memo, memoKey, probe);
3435
+ return probe;
3436
+ }
3437
+ const converged = await request2(client, {
3438
+ name: definition.name,
3439
+ definition: manifestToWire(definition),
3440
+ ...passthrough
3441
+ });
3442
+ if (converged.result === "definitionRequired") {
3443
+ throw new Error("Server reported definitionRequired for a full-definition request");
3444
+ }
3445
+ memoize2(memo, memoKey, converged);
3446
+ return converged;
3447
+ }
3448
+ function manifestToWire(definition) {
3449
+ const manifest = definition.manifest;
3450
+ const frontmatter = { ...manifest.frontmatter };
3451
+ if (manifest.runtype && Object.keys(manifest.runtype).length > 0) {
3452
+ frontmatter.runtype = manifest.runtype;
3453
+ }
3454
+ return { frontmatter, body: manifest.body ?? "" };
3455
+ }
3456
+ async function pullSkill(client, name) {
3457
+ return client.get("/skills/pull", { name });
3458
+ }
3459
+
3256
3460
  // src/skills-namespace.ts
3257
3461
  var SkillProposalsNamespace = class {
3258
3462
  constructor(getClient) {
@@ -3393,6 +3597,22 @@ var SkillsNamespace = class {
3393
3597
  const client = this.getClient();
3394
3598
  await client.post(`/skills/${skillId}/versions/${versionId}/publish`);
3395
3599
  }
3600
+ /**
3601
+ * Statically scan a SKILL.md document for malicious patterns and return a
3602
+ * two-tier verdict — `warning` when a skill appears suspicious (low–medium
3603
+ * confidence), `error` when high-confidence malicious. Does not persist or
3604
+ * gate; use it as a "scan before save" affordance.
3605
+ *
3606
+ * @example
3607
+ * ```typescript
3608
+ * const { verdict } = await Runtype.skills.scan(skillMarkdown)
3609
+ * if (verdict.tier === 'error') console.warn(verdict.summary)
3610
+ * ```
3611
+ */
3612
+ async scan(markdown) {
3613
+ const client = this.getClient();
3614
+ return client.post("/skills/scan", { markdown });
3615
+ }
3396
3616
  /**
3397
3617
  * Import a single SKILL.md document. The imported skill lands with
3398
3618
  * `trustLevel: 'imported'` and a draft version.
@@ -3435,6 +3655,40 @@ var SkillsNamespace = class {
3435
3655
  const res = await client.get("/skills/bindings", { agentId });
3436
3656
  return res.data;
3437
3657
  }
3658
+ /**
3659
+ * Idempotently converge a `defineSkill` definition onto the platform.
3660
+ * Hash-first: the steady state is one tiny probe request. Creates or appends a
3661
+ * new version; never deletes. Identity is name + account scope. Pass
3662
+ * `release: 'publish'` to publish the converged version.
3663
+ *
3664
+ * @example
3665
+ * ```typescript
3666
+ * const reviewer = defineSkill({
3667
+ * name: 'code_reviewer',
3668
+ * manifest: {
3669
+ * frontmatter: { name: 'code_reviewer', description: 'Reviews pull requests' },
3670
+ * runtype: { trustLevel: 'org' },
3671
+ * body: '# Code Reviewer\n\nReview the diff...',
3672
+ * },
3673
+ * })
3674
+ *
3675
+ * // Converge + publish (CI/deploy).
3676
+ * const result = await Runtype.skills.ensure(reviewer, { release: 'publish' })
3677
+ *
3678
+ * // PR drift gate.
3679
+ * await Runtype.skills.ensure(reviewer, { expectNoChanges: true })
3680
+ * ```
3681
+ */
3682
+ async ensure(definition, options = {}) {
3683
+ return ensureSkill(this.getClient(), definition, options);
3684
+ }
3685
+ /**
3686
+ * Pull the canonical definition + provenance for a skill by name — the
3687
+ * absorb-drift direction of the ensure protocol.
3688
+ */
3689
+ async pull(name) {
3690
+ return pullSkill(this.getClient(), name);
3691
+ }
3438
3692
  };
3439
3693
 
3440
3694
  // src/agents-namespace.ts
@@ -3460,19 +3714,19 @@ var AGENT_CONFIG_KEYS = [
3460
3714
  "memory"
3461
3715
  ];
3462
3716
  var AGENT_CONFIG_KEY_LIST = [...AGENT_CONFIG_KEYS].sort();
3463
- function isPlainObject2(value) {
3717
+ function isPlainObject3(value) {
3464
3718
  return value !== null && typeof value === "object" && !Array.isArray(value);
3465
3719
  }
3466
- function normalizeValue(value) {
3720
+ function normalizeValue2(value) {
3467
3721
  if (Array.isArray(value)) {
3468
- return value.map((item) => normalizeValue(item));
3722
+ return value.map((item) => normalizeValue2(item));
3469
3723
  }
3470
- if (isPlainObject2(value)) {
3724
+ if (isPlainObject3(value)) {
3471
3725
  const normalized = {};
3472
3726
  for (const key of Object.keys(value).sort()) {
3473
3727
  const entry = value[key];
3474
3728
  if (entry === void 0 || entry === null) continue;
3475
- normalized[key] = normalizeValue(entry);
3729
+ normalized[key] = normalizeValue2(entry);
3476
3730
  }
3477
3731
  return normalized;
3478
3732
  }
@@ -3480,11 +3734,11 @@ function normalizeValue(value) {
3480
3734
  }
3481
3735
  function normalizeAgentDefinition(definition) {
3482
3736
  const config = {};
3483
- const rawConfig = isPlainObject2(definition.config) ? definition.config : {};
3737
+ const rawConfig = isPlainObject3(definition.config) ? definition.config : {};
3484
3738
  for (const key of AGENT_CONFIG_KEY_LIST) {
3485
3739
  const value = rawConfig[key];
3486
3740
  if (value === void 0 || value === null) continue;
3487
- config[key] = normalizeValue(value);
3741
+ config[key] = normalizeValue2(value);
3488
3742
  }
3489
3743
  return {
3490
3744
  name: definition.name,
@@ -3502,7 +3756,7 @@ async function computeAgentContentHash(definition) {
3502
3756
  var DEFINE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "description", "icon", ...AGENT_CONFIG_KEYS]);
3503
3757
  function collectNonPortableToolRefs(config) {
3504
3758
  const tools = config.tools;
3505
- if (!isPlainObject2(tools)) return [];
3759
+ if (!isPlainObject3(tools)) return [];
3506
3760
  const found = [];
3507
3761
  const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
3508
3762
  const scanArray = (value, path) => {
@@ -3512,7 +3766,7 @@ function collectNonPortableToolRefs(config) {
3512
3766
  });
3513
3767
  };
3514
3768
  const scanKeys = (value, path) => {
3515
- if (!isPlainObject2(value)) return;
3769
+ if (!isPlainObject3(value)) return;
3516
3770
  for (const key of Object.keys(value)) {
3517
3771
  if (isAccountScoped(key)) found.push(`${path}.${key}`);
3518
3772
  }
@@ -3520,16 +3774,16 @@ function collectNonPortableToolRefs(config) {
3520
3774
  scanArray(tools.toolIds, "tools.toolIds");
3521
3775
  scanKeys(tools.toolConfigs, "tools.toolConfigs");
3522
3776
  scanKeys(tools.perToolLimits, "tools.perToolLimits");
3523
- if (isPlainObject2(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
3524
- if (isPlainObject2(tools.subagentConfig)) {
3777
+ if (isPlainObject3(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
3778
+ if (isPlainObject3(tools.subagentConfig)) {
3525
3779
  scanArray(tools.subagentConfig.toolPool, "tools.subagentConfig.toolPool");
3526
3780
  }
3527
- if (isPlainObject2(tools.codeModeConfig)) {
3781
+ if (isPlainObject3(tools.codeModeConfig)) {
3528
3782
  scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
3529
3783
  }
3530
3784
  if (Array.isArray(tools.runtimeTools)) {
3531
3785
  tools.runtimeTools.forEach((runtimeTool, i) => {
3532
- if (!isPlainObject2(runtimeTool) || !isPlainObject2(runtimeTool.config)) return;
3786
+ if (!isPlainObject3(runtimeTool) || !isPlainObject3(runtimeTool.config)) return;
3533
3787
  const base = `tools.runtimeTools[${i}].config`;
3534
3788
  const rtConfig = runtimeTool.config;
3535
3789
  if (runtimeTool.toolType === "subagent" && typeof rtConfig.agentId === "string" && rtConfig.agentId.startsWith("agent_")) {
@@ -3590,226 +3844,693 @@ var AgentDriftError = class extends Error {
3590
3844
  this.name = "AgentDriftError";
3591
3845
  this.plan = plan;
3592
3846
  }
3593
- };
3594
- function parseRequestError2(err) {
3595
- if (!(err instanceof Error)) return { status: null, body: null };
3596
- const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3597
- if (!match) return { status: null, body: null };
3598
- try {
3599
- return { status: Number(match[1]), body: JSON.parse(match[2]) };
3600
- } catch {
3601
- return { status: Number(match[1]), body: null };
3847
+ };
3848
+ function parseRequestError3(err) {
3849
+ if (!(err instanceof Error)) return { status: null, body: null };
3850
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3851
+ if (!match) return { status: null, body: null };
3852
+ try {
3853
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3854
+ } catch {
3855
+ return { status: Number(match[1]), body: null };
3856
+ }
3857
+ }
3858
+ function toConflictError3(err) {
3859
+ const { status, body } = parseRequestError3(err);
3860
+ if (status !== 409 || !isPlainObject3(body)) return null;
3861
+ const code = body.code;
3862
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3863
+ return new AgentEnsureConflictError(
3864
+ body
3865
+ );
3866
+ }
3867
+ var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3868
+ function memoFor3(client) {
3869
+ let memo = serverHashMemo3.get(client);
3870
+ if (!memo) {
3871
+ memo = /* @__PURE__ */ new Map();
3872
+ serverHashMemo3.set(client, memo);
3873
+ }
3874
+ return memo;
3875
+ }
3876
+ var AgentsNamespace = class {
3877
+ constructor(getClient) {
3878
+ this.getClient = getClient;
3879
+ }
3880
+ /**
3881
+ * Idempotently converge a definition onto the platform. Hash-first: probes
3882
+ * with a content hash, and only ships the full definition when the server
3883
+ * reports a miss (`definitionRequired`). Creates an immutable version
3884
+ * snapshot on every change; never deletes.
3885
+ */
3886
+ async ensure(definition, options = {}) {
3887
+ const client = this.getClient();
3888
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
3889
+ const passthrough = {
3890
+ ...onConflict ? { onConflict } : {},
3891
+ ...release ? { release } : {},
3892
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3893
+ };
3894
+ if (dryRun || expectNoChanges) {
3895
+ const plan = await this.request(client, {
3896
+ name: definition.name,
3897
+ definition,
3898
+ dryRun: true,
3899
+ ...passthrough
3900
+ });
3901
+ if (plan.result !== "plan") {
3902
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3903
+ }
3904
+ if (expectNoChanges && plan.changes !== "none") {
3905
+ throw new AgentDriftError(plan);
3906
+ }
3907
+ return plan;
3908
+ }
3909
+ const memo = memoFor3(client);
3910
+ const localHash = await computeAgentContentHash({
3911
+ ...definition,
3912
+ config: definition.config
3913
+ });
3914
+ const memoKey = `${definition.name}\0${localHash}`;
3915
+ const contentHash = memo.get(memoKey) ?? localHash;
3916
+ const probe = await this.request(client, {
3917
+ name: definition.name,
3918
+ contentHash,
3919
+ ...passthrough
3920
+ });
3921
+ if (probe.result !== "definitionRequired") {
3922
+ this.memoize(memo, memoKey, probe);
3923
+ return probe;
3924
+ }
3925
+ const converged = await this.request(client, {
3926
+ name: definition.name,
3927
+ definition,
3928
+ ...passthrough
3929
+ });
3930
+ if (converged.result === "definitionRequired") {
3931
+ throw new Error("Server reported definitionRequired for a full-definition request");
3932
+ }
3933
+ this.memoize(memo, memoKey, converged);
3934
+ return converged;
3935
+ }
3936
+ /**
3937
+ * Pull the canonical definition + provenance for an agent by name — the
3938
+ * absorb-drift direction. The contentHash reflects the live agent state.
3939
+ */
3940
+ async pull(name) {
3941
+ const client = this.getClient();
3942
+ return client.get("/agents/pull", { name });
3943
+ }
3944
+ memoize(memo, memoKey, result) {
3945
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3946
+ }
3947
+ async request(client, body) {
3948
+ try {
3949
+ return await client.post(
3950
+ "/agents/ensure",
3951
+ body
3952
+ );
3953
+ } catch (err) {
3954
+ const conflict = toConflictError3(err);
3955
+ if (conflict) throw conflict;
3956
+ throw err;
3957
+ }
3958
+ }
3959
+ };
3960
+
3961
+ // src/tools-ensure.ts
3962
+ function isPlainObject4(value) {
3963
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3964
+ }
3965
+ function normalizeValue3(value) {
3966
+ if (Array.isArray(value)) {
3967
+ return value.map((item) => normalizeValue3(item));
3968
+ }
3969
+ if (isPlainObject4(value)) {
3970
+ const normalized = {};
3971
+ for (const key of Object.keys(value).sort()) {
3972
+ const entry = value[key];
3973
+ if (entry === void 0 || entry === null) continue;
3974
+ normalized[key] = normalizeValue3(entry);
3975
+ }
3976
+ return normalized;
3977
+ }
3978
+ return value;
3979
+ }
3980
+ function normalizeToolDefinition(definition) {
3981
+ const parametersSchema = isPlainObject4(definition.parametersSchema) ? normalizeValue3(definition.parametersSchema) : {};
3982
+ const config = isPlainObject4(definition.config) ? normalizeValue3(definition.config) : {};
3983
+ return {
3984
+ toolType: definition.toolType,
3985
+ ...definition.description ? { description: definition.description } : {},
3986
+ parametersSchema,
3987
+ config
3988
+ };
3989
+ }
3990
+ async function computeToolContentHash(definition) {
3991
+ const serialized = JSON.stringify(normalizeToolDefinition(definition));
3992
+ const encoded = new TextEncoder().encode(serialized);
3993
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3994
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3995
+ }
3996
+ var DEFINE_TOOL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3997
+ "name",
3998
+ "description",
3999
+ "toolType",
4000
+ "parametersSchema",
4001
+ "config"
4002
+ ]);
4003
+ var TOOL_DEFINITION_TYPES = /* @__PURE__ */ new Set([
4004
+ "flow",
4005
+ "custom",
4006
+ "external",
4007
+ "graphql",
4008
+ "mcp",
4009
+ "local",
4010
+ "subagent"
4011
+ ]);
4012
+ function defineTool(input) {
4013
+ if (!input || typeof input !== "object") {
4014
+ throw new Error("defineTool requires a definition object");
4015
+ }
4016
+ if (typeof input.name !== "string" || input.name.length === 0) {
4017
+ throw new Error('defineTool requires a non-empty string "name"');
4018
+ }
4019
+ if (typeof input.description !== "string" || input.description.length === 0) {
4020
+ throw new Error('defineTool requires a non-empty string "description"');
4021
+ }
4022
+ if (typeof input.toolType !== "string" || !TOOL_DEFINITION_TYPES.has(input.toolType)) {
4023
+ throw new Error(
4024
+ `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
4025
+ );
4026
+ }
4027
+ if (!isPlainObject4(input.parametersSchema)) {
4028
+ throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
4029
+ }
4030
+ if (!isPlainObject4(input.config)) {
4031
+ throw new Error('defineTool requires a "config" object');
4032
+ }
4033
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
4034
+ if (unknownKeys.length > 0) {
4035
+ throw new Error(
4036
+ `defineTool: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, toolType, parametersSchema, config.`
4037
+ );
4038
+ }
4039
+ return {
4040
+ name: input.name,
4041
+ description: input.description,
4042
+ toolType: input.toolType,
4043
+ parametersSchema: input.parametersSchema,
4044
+ config: input.config
4045
+ };
4046
+ }
4047
+ var ToolEnsureConflictError = class extends Error {
4048
+ constructor(body) {
4049
+ super(body.error ?? `Tool ensure conflict: ${body.code}`);
4050
+ this.name = "ToolEnsureConflictError";
4051
+ this.code = body.code;
4052
+ this.lastModifiedSource = body.lastModifiedSource;
4053
+ this.modifiedAt = body.modifiedAt;
4054
+ this.currentHash = body.currentHash;
4055
+ }
4056
+ };
4057
+ var ToolDriftError = class extends Error {
4058
+ constructor(plan) {
4059
+ super(
4060
+ `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.`
4061
+ );
4062
+ this.name = "ToolDriftError";
4063
+ this.plan = plan;
4064
+ }
4065
+ };
4066
+ function parseRequestError4(err) {
4067
+ if (!(err instanceof Error)) return { status: null, body: null };
4068
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
4069
+ if (!match) return { status: null, body: null };
4070
+ try {
4071
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
4072
+ } catch {
4073
+ return { status: Number(match[1]), body: null };
4074
+ }
4075
+ }
4076
+ function toConflictError4(err) {
4077
+ const { status, body } = parseRequestError4(err);
4078
+ if (status !== 409 || !isPlainObject4(body)) return null;
4079
+ const code = body.code;
4080
+ if (code !== "external_modification" && code !== "remote_changed") return null;
4081
+ return new ToolEnsureConflictError(
4082
+ body
4083
+ );
4084
+ }
4085
+ var serverHashMemo4 = /* @__PURE__ */ new WeakMap();
4086
+ function memoFor4(client) {
4087
+ let memo = serverHashMemo4.get(client);
4088
+ if (!memo) {
4089
+ memo = /* @__PURE__ */ new Map();
4090
+ serverHashMemo4.set(client, memo);
4091
+ }
4092
+ return memo;
4093
+ }
4094
+ function memoize3(memo, memoKey, result) {
4095
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
4096
+ }
4097
+ async function request3(client, body) {
4098
+ try {
4099
+ return await client.post(
4100
+ "/tools/ensure",
4101
+ body
4102
+ );
4103
+ } catch (err) {
4104
+ const conflict = toConflictError4(err);
4105
+ if (conflict) throw conflict;
4106
+ throw err;
4107
+ }
4108
+ }
4109
+ async function ensureTool(client, definition, options = {}) {
4110
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
4111
+ const passthrough = {
4112
+ ...onConflict ? { onConflict } : {},
4113
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
4114
+ };
4115
+ if (dryRun || expectNoChanges) {
4116
+ const plan = await request3(client, {
4117
+ name: definition.name,
4118
+ definition,
4119
+ dryRun: true,
4120
+ ...passthrough
4121
+ });
4122
+ if (plan.result !== "plan") {
4123
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
4124
+ }
4125
+ if (expectNoChanges && plan.changes !== "none") {
4126
+ throw new ToolDriftError(plan);
4127
+ }
4128
+ return plan;
4129
+ }
4130
+ const memo = memoFor4(client);
4131
+ const localHash = await computeToolContentHash(definition);
4132
+ const memoKey = `${definition.name} ${localHash}`;
4133
+ const contentHash = memo.get(memoKey) ?? localHash;
4134
+ const probe = await request3(client, {
4135
+ name: definition.name,
4136
+ contentHash,
4137
+ ...passthrough
4138
+ });
4139
+ if (probe.result !== "definitionRequired") {
4140
+ memoize3(memo, memoKey, probe);
4141
+ return probe;
4142
+ }
4143
+ const converged = await request3(client, {
4144
+ name: definition.name,
4145
+ definition,
4146
+ ...passthrough
4147
+ });
4148
+ if (converged.result === "definitionRequired") {
4149
+ throw new Error("Server reported definitionRequired for a full-definition request");
4150
+ }
4151
+ memoize3(memo, memoKey, converged);
4152
+ return converged;
4153
+ }
4154
+ async function pullTool(client, name) {
4155
+ return client.get("/tools/pull", { name });
4156
+ }
4157
+
4158
+ // src/tools-namespace.ts
4159
+ var ToolsNamespace = class {
4160
+ constructor(getClient) {
4161
+ this.getClient = getClient;
4162
+ }
4163
+ /**
4164
+ * Idempotently converge a `defineTool` definition onto the platform.
4165
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
4166
+ * the saved tool; never deletes. Identity is name + account scope.
4167
+ *
4168
+ * @example
4169
+ * ```typescript
4170
+ * const weather = defineTool({
4171
+ * name: 'Weather Lookup',
4172
+ * description: 'Fetch the current weather for a city',
4173
+ * toolType: 'external',
4174
+ * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
4175
+ * config: { url: 'https://api.example.com/weather', method: 'GET' },
4176
+ * })
4177
+ *
4178
+ * // Converge (CI/deploy).
4179
+ * const result = await Runtype.tools.ensure(weather)
4180
+ *
4181
+ * // PR drift gate.
4182
+ * await Runtype.tools.ensure(weather, { expectNoChanges: true })
4183
+ * ```
4184
+ */
4185
+ async ensure(definition, options = {}) {
4186
+ return ensureTool(this.getClient(), definition, options);
4187
+ }
4188
+ /**
4189
+ * Pull the canonical definition + provenance for a tool by name — the
4190
+ * absorb-drift direction of the ensure protocol.
4191
+ */
4192
+ async pull(name) {
4193
+ return pullTool(this.getClient(), name);
4194
+ }
4195
+ };
4196
+
4197
+ // src/products-ensure.ts
4198
+ function isPlainObject5(value) {
4199
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4200
+ }
4201
+ function normalizeValue4(value) {
4202
+ if (Array.isArray(value)) {
4203
+ return value.map((item) => normalizeValue4(item));
4204
+ }
4205
+ if (isPlainObject5(value)) {
4206
+ const normalized = {};
4207
+ for (const key of Object.keys(value).sort()) {
4208
+ const entry = value[key];
4209
+ if (entry === void 0 || entry === null) continue;
4210
+ normalized[key] = normalizeValue4(entry);
4211
+ }
4212
+ return normalized;
4213
+ }
4214
+ return value;
4215
+ }
4216
+ function normalizeProductDefinition(definition) {
4217
+ const spec = isPlainObject5(definition.spec) ? normalizeValue4(definition.spec) : {};
4218
+ return {
4219
+ ...definition.description ? { description: definition.description } : {},
4220
+ ...definition.icon ? { icon: definition.icon } : {},
4221
+ spec
4222
+ };
4223
+ }
4224
+ async function computeProductContentHash(definition) {
4225
+ const serialized = JSON.stringify(normalizeProductDefinition(definition));
4226
+ const encoded = new TextEncoder().encode(serialized);
4227
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
4228
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
4229
+ }
4230
+ var DEFINE_PRODUCT_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "description", "icon", "spec"]);
4231
+ function defineProduct(input) {
4232
+ if (!input || typeof input !== "object") {
4233
+ throw new Error("defineProduct requires a definition object");
4234
+ }
4235
+ if (typeof input.name !== "string" || input.name.length === 0) {
4236
+ throw new Error('defineProduct requires a non-empty string "name"');
4237
+ }
4238
+ if (input.description != null && typeof input.description !== "string") {
4239
+ throw new Error('defineProduct "description" must be a string when provided');
4240
+ }
4241
+ if (input.icon != null && typeof input.icon !== "string") {
4242
+ throw new Error('defineProduct "icon" must be a string when provided');
4243
+ }
4244
+ if (input.spec != null && !isPlainObject5(input.spec)) {
4245
+ throw new Error('defineProduct "spec" must be an object when provided');
4246
+ }
4247
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_PRODUCT_TOP_LEVEL_KEYS.has(key));
4248
+ if (unknownKeys.length > 0) {
4249
+ throw new Error(
4250
+ `defineProduct: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, icon, spec. (canvas / nested capabilities and surfaces are not converged by ensure.)`
4251
+ );
4252
+ }
4253
+ return {
4254
+ name: input.name,
4255
+ ...input.description !== void 0 ? { description: input.description } : {},
4256
+ ...input.icon !== void 0 ? { icon: input.icon } : {},
4257
+ ...input.spec !== void 0 ? { spec: input.spec } : {}
4258
+ };
4259
+ }
4260
+ var ProductEnsureConflictError = class extends Error {
4261
+ constructor(body) {
4262
+ super(body.error ?? `Product ensure conflict: ${body.code}`);
4263
+ this.name = "ProductEnsureConflictError";
4264
+ this.code = body.code;
4265
+ this.lastModifiedSource = body.lastModifiedSource;
4266
+ this.modifiedAt = body.modifiedAt;
4267
+ this.currentHash = body.currentHash;
4268
+ }
4269
+ };
4270
+ var ProductDriftError = class extends Error {
4271
+ constructor(plan) {
4272
+ super(
4273
+ `Product "${plan.productId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.products.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`
4274
+ );
4275
+ this.name = "ProductDriftError";
4276
+ this.plan = plan;
4277
+ }
4278
+ };
4279
+ function parseRequestError5(err) {
4280
+ if (!(err instanceof Error)) return { status: null, body: null };
4281
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
4282
+ if (!match) return { status: null, body: null };
4283
+ try {
4284
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
4285
+ } catch {
4286
+ return { status: Number(match[1]), body: null };
4287
+ }
4288
+ }
4289
+ function toConflictError5(err) {
4290
+ const { status, body } = parseRequestError5(err);
4291
+ if (status !== 409 || !isPlainObject5(body)) return null;
4292
+ const code = body.code;
4293
+ if (code !== "external_modification" && code !== "remote_changed") return null;
4294
+ return new ProductEnsureConflictError(
4295
+ body
4296
+ );
4297
+ }
4298
+ var serverHashMemo5 = /* @__PURE__ */ new WeakMap();
4299
+ function memoFor5(client) {
4300
+ let memo = serverHashMemo5.get(client);
4301
+ if (!memo) {
4302
+ memo = /* @__PURE__ */ new Map();
4303
+ serverHashMemo5.set(client, memo);
4304
+ }
4305
+ return memo;
4306
+ }
4307
+ function memoize4(memo, memoKey, result) {
4308
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
4309
+ }
4310
+ async function request4(client, body) {
4311
+ try {
4312
+ return await client.post(
4313
+ "/products/ensure",
4314
+ body
4315
+ );
4316
+ } catch (err) {
4317
+ const conflict = toConflictError5(err);
4318
+ if (conflict) throw conflict;
4319
+ throw err;
4320
+ }
4321
+ }
4322
+ async function ensureProduct(client, definition, options = {}) {
4323
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
4324
+ const passthrough = {
4325
+ ...onConflict ? { onConflict } : {},
4326
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
4327
+ };
4328
+ if (dryRun || expectNoChanges) {
4329
+ const plan = await request4(client, {
4330
+ name: definition.name,
4331
+ definition,
4332
+ dryRun: true,
4333
+ ...passthrough
4334
+ });
4335
+ if (plan.result !== "plan") {
4336
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
4337
+ }
4338
+ if (expectNoChanges && plan.changes !== "none") {
4339
+ throw new ProductDriftError(plan);
4340
+ }
4341
+ return plan;
4342
+ }
4343
+ const memo = memoFor5(client);
4344
+ const localHash = await computeProductContentHash(definition);
4345
+ const memoKey = `${definition.name} ${localHash}`;
4346
+ const contentHash = memo.get(memoKey) ?? localHash;
4347
+ const probe = await request4(client, {
4348
+ name: definition.name,
4349
+ contentHash,
4350
+ ...passthrough
4351
+ });
4352
+ if (probe.result !== "definitionRequired") {
4353
+ memoize4(memo, memoKey, probe);
4354
+ return probe;
4355
+ }
4356
+ const converged = await request4(client, {
4357
+ name: definition.name,
4358
+ definition,
4359
+ ...passthrough
4360
+ });
4361
+ if (converged.result === "definitionRequired") {
4362
+ throw new Error("Server reported definitionRequired for a full-definition request");
3602
4363
  }
4364
+ memoize4(memo, memoKey, converged);
4365
+ return converged;
3603
4366
  }
3604
- function toConflictError2(err) {
3605
- const { status, body } = parseRequestError2(err);
3606
- if (status !== 409 || !isPlainObject2(body)) return null;
3607
- const code = body.code;
3608
- if (code !== "external_modification" && code !== "remote_changed") return null;
3609
- return new AgentEnsureConflictError(
3610
- body
3611
- );
3612
- }
3613
- var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
3614
- function memoFor2(client) {
3615
- let memo = serverHashMemo2.get(client);
3616
- if (!memo) {
3617
- memo = /* @__PURE__ */ new Map();
3618
- serverHashMemo2.set(client, memo);
3619
- }
3620
- return memo;
4367
+ async function pullProduct(client, name) {
4368
+ return client.get("/products/pull", { name });
3621
4369
  }
3622
- var AgentsNamespace = class {
4370
+
4371
+ // src/products-namespace.ts
4372
+ var ProductsNamespace = class {
3623
4373
  constructor(getClient) {
3624
4374
  this.getClient = getClient;
3625
4375
  }
3626
4376
  /**
3627
- * Idempotently converge a definition onto the platform. Hash-first: probes
3628
- * with a content hash, and only ships the full definition when the server
3629
- * reports a miss (`definitionRequired`). Creates an immutable version
3630
- * snapshot on every change; never deletes.
4377
+ * Idempotently converge a `defineProduct` definition onto the platform.
4378
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
4379
+ * the top-level product record; never deletes. Identity is name + account
4380
+ * scope.
4381
+ *
4382
+ * @example
4383
+ * ```typescript
4384
+ * const product = defineProduct({
4385
+ * name: 'Support Copilot',
4386
+ * description: 'An AI support assistant',
4387
+ * icon: '🤖',
4388
+ * spec: { productGoal: 'Deflect tier-1 tickets', productStage: 'beta' },
4389
+ * })
4390
+ *
4391
+ * // Converge (CI/deploy).
4392
+ * const result = await Runtype.products.ensure(product)
4393
+ *
4394
+ * // PR drift gate.
4395
+ * await Runtype.products.ensure(product, { expectNoChanges: true })
4396
+ * ```
3631
4397
  */
3632
4398
  async ensure(definition, options = {}) {
3633
- const client = this.getClient();
3634
- const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
3635
- const passthrough = {
3636
- ...onConflict ? { onConflict } : {},
3637
- ...release ? { release } : {},
3638
- ...expectedRemoteHash ? { expectedRemoteHash } : {}
3639
- };
3640
- if (dryRun || expectNoChanges) {
3641
- const plan = await this.request(client, {
3642
- name: definition.name,
3643
- definition,
3644
- dryRun: true,
3645
- ...passthrough
3646
- });
3647
- if (plan.result !== "plan") {
3648
- throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3649
- }
3650
- if (expectNoChanges && plan.changes !== "none") {
3651
- throw new AgentDriftError(plan);
3652
- }
3653
- return plan;
3654
- }
3655
- const memo = memoFor2(client);
3656
- const localHash = await computeAgentContentHash({
3657
- ...definition,
3658
- config: definition.config
3659
- });
3660
- const memoKey = `${definition.name}\0${localHash}`;
3661
- const contentHash = memo.get(memoKey) ?? localHash;
3662
- const probe = await this.request(client, {
3663
- name: definition.name,
3664
- contentHash,
3665
- ...passthrough
3666
- });
3667
- if (probe.result !== "definitionRequired") {
3668
- this.memoize(memo, memoKey, probe);
3669
- return probe;
3670
- }
3671
- const converged = await this.request(client, {
3672
- name: definition.name,
3673
- definition,
3674
- ...passthrough
3675
- });
3676
- if (converged.result === "definitionRequired") {
3677
- throw new Error("Server reported definitionRequired for a full-definition request");
3678
- }
3679
- this.memoize(memo, memoKey, converged);
3680
- return converged;
4399
+ return ensureProduct(this.getClient(), definition, options);
3681
4400
  }
3682
4401
  /**
3683
- * Pull the canonical definition + provenance for an agent by name — the
3684
- * absorb-drift direction. The contentHash reflects the live agent state.
4402
+ * Pull the canonical definition + provenance for a product by name — the
4403
+ * absorb-drift direction of the ensure protocol.
3685
4404
  */
3686
4405
  async pull(name) {
3687
- const client = this.getClient();
3688
- return client.get("/agents/pull", { name });
3689
- }
3690
- memoize(memo, memoKey, result) {
3691
- if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3692
- }
3693
- async request(client, body) {
3694
- try {
3695
- return await client.post(
3696
- "/agents/ensure",
3697
- body
3698
- );
3699
- } catch (err) {
3700
- const conflict = toConflictError2(err);
3701
- if (conflict) throw conflict;
3702
- throw err;
3703
- }
4406
+ return pullProduct(this.getClient(), name);
3704
4407
  }
3705
4408
  };
3706
4409
 
3707
- // src/tools-ensure.ts
3708
- function isPlainObject3(value) {
4410
+ // src/surfaces-ensure.ts
4411
+ function isPlainObject6(value) {
3709
4412
  return value !== null && typeof value === "object" && !Array.isArray(value);
3710
4413
  }
3711
- function normalizeValue2(value) {
4414
+ function normalizeValue5(value) {
3712
4415
  if (Array.isArray(value)) {
3713
- return value.map((item) => normalizeValue2(item));
4416
+ return value.map((item) => normalizeValue5(item));
3714
4417
  }
3715
- if (isPlainObject3(value)) {
4418
+ if (isPlainObject6(value)) {
3716
4419
  const normalized = {};
3717
4420
  for (const key of Object.keys(value).sort()) {
3718
4421
  const entry = value[key];
3719
4422
  if (entry === void 0 || entry === null) continue;
3720
- normalized[key] = normalizeValue2(entry);
4423
+ normalized[key] = normalizeValue5(entry);
3721
4424
  }
3722
4425
  return normalized;
3723
4426
  }
3724
4427
  return value;
3725
4428
  }
3726
- function normalizeToolDefinition(definition) {
3727
- const parametersSchema = isPlainObject3(definition.parametersSchema) ? normalizeValue2(definition.parametersSchema) : {};
3728
- const config = isPlainObject3(definition.config) ? normalizeValue2(definition.config) : {};
4429
+ function normalizeSurfaceDefinition(definition) {
4430
+ const behavior = isPlainObject6(definition.behavior) ? normalizeValue5(definition.behavior) : { type: definition.type };
3729
4431
  return {
3730
- toolType: definition.toolType,
3731
- ...definition.description ? { description: definition.description } : {},
3732
- parametersSchema,
3733
- config
4432
+ type: definition.type,
4433
+ behavior,
4434
+ status: definition.status || "draft",
4435
+ environment: definition.environment || "development"
3734
4436
  };
3735
4437
  }
3736
- async function computeToolContentHash(definition) {
3737
- const serialized = JSON.stringify(normalizeToolDefinition(definition));
4438
+ async function computeSurfaceContentHash(definition) {
4439
+ const serialized = JSON.stringify(normalizeSurfaceDefinition(definition));
3738
4440
  const encoded = new TextEncoder().encode(serialized);
3739
4441
  const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3740
4442
  return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3741
4443
  }
3742
- var DEFINE_TOOL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
4444
+ var DEFINE_SURFACE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
3743
4445
  "name",
3744
- "description",
3745
- "toolType",
3746
- "parametersSchema",
3747
- "config"
4446
+ "type",
4447
+ "behavior",
4448
+ "inbound",
4449
+ "outbound",
4450
+ "status",
4451
+ "environment"
3748
4452
  ]);
3749
- var TOOL_DEFINITION_TYPES = /* @__PURE__ */ new Set([
3750
- "flow",
3751
- "custom",
3752
- "external",
3753
- "graphql",
4453
+ var SURFACE_DEFINITION_TYPES = /* @__PURE__ */ new Set([
4454
+ "chat",
3754
4455
  "mcp",
3755
- "local",
3756
- "subagent"
4456
+ "mcp_code",
4457
+ "api",
4458
+ "webhook",
4459
+ "schedule",
4460
+ "a2a",
4461
+ "email",
4462
+ "slack",
4463
+ "sms",
4464
+ "imessage",
4465
+ "discord",
4466
+ "whatsapp",
4467
+ "telegram",
4468
+ "hosted-page",
4469
+ "chrome_extension"
3757
4470
  ]);
3758
- function defineTool(input) {
4471
+ function defineSurface(input) {
3759
4472
  if (!input || typeof input !== "object") {
3760
- throw new Error("defineTool requires a definition object");
4473
+ throw new Error("defineSurface requires a definition object");
3761
4474
  }
3762
4475
  if (typeof input.name !== "string" || input.name.length === 0) {
3763
- throw new Error('defineTool requires a non-empty string "name"');
4476
+ throw new Error('defineSurface requires a non-empty string "name"');
3764
4477
  }
3765
- if (typeof input.description !== "string" || input.description.length === 0) {
3766
- throw new Error('defineTool requires a non-empty string "description"');
3767
- }
3768
- if (typeof input.toolType !== "string" || !TOOL_DEFINITION_TYPES.has(input.toolType)) {
4478
+ if (typeof input.type !== "string" || !SURFACE_DEFINITION_TYPES.has(input.type)) {
3769
4479
  throw new Error(
3770
- `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
4480
+ `defineSurface requires "type" to be one of: ${[...SURFACE_DEFINITION_TYPES].join(", ")}`
3771
4481
  );
3772
4482
  }
3773
- if (!isPlainObject3(input.parametersSchema)) {
3774
- throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
4483
+ if (input.behavior !== void 0 && !isPlainObject6(input.behavior)) {
4484
+ throw new Error('defineSurface "behavior" must be an object when provided');
3775
4485
  }
3776
- if (!isPlainObject3(input.config)) {
3777
- throw new Error('defineTool requires a "config" object');
4486
+ if (input.inbound !== void 0 && !isPlainObject6(input.inbound)) {
4487
+ throw new Error('defineSurface "inbound" must be an object when provided');
3778
4488
  }
3779
- const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
4489
+ if (input.outbound !== void 0 && !isPlainObject6(input.outbound)) {
4490
+ throw new Error('defineSurface "outbound" must be an object when provided');
4491
+ }
4492
+ if (input.status !== void 0 && !["draft", "active", "paused"].includes(input.status)) {
4493
+ throw new Error('defineSurface "status" must be one of: draft, active, paused');
4494
+ }
4495
+ if (input.environment !== void 0 && !["production", "development"].includes(input.environment)) {
4496
+ throw new Error('defineSurface "environment" must be one of: production, development');
4497
+ }
4498
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_SURFACE_TOP_LEVEL_KEYS.has(key));
3780
4499
  if (unknownKeys.length > 0) {
3781
4500
  throw new Error(
3782
- `defineTool: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, description, toolType, parametersSchema, config.`
4501
+ `defineSurface: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, type, behavior, inbound, outbound, status, environment.`
3783
4502
  );
3784
4503
  }
3785
4504
  return {
3786
4505
  name: input.name,
3787
- description: input.description,
3788
- toolType: input.toolType,
3789
- parametersSchema: input.parametersSchema,
3790
- config: input.config
4506
+ type: input.type,
4507
+ ...input.behavior !== void 0 ? { behavior: input.behavior } : {},
4508
+ ...input.inbound !== void 0 ? { inbound: input.inbound } : {},
4509
+ ...input.outbound !== void 0 ? { outbound: input.outbound } : {},
4510
+ ...input.status !== void 0 ? { status: input.status } : {},
4511
+ ...input.environment !== void 0 ? { environment: input.environment } : {}
3791
4512
  };
3792
4513
  }
3793
- var ToolEnsureConflictError = class extends Error {
4514
+ var SurfaceEnsureConflictError = class extends Error {
3794
4515
  constructor(body) {
3795
- super(body.error ?? `Tool ensure conflict: ${body.code}`);
3796
- this.name = "ToolEnsureConflictError";
4516
+ super(body.error ?? `Surface ensure conflict: ${body.code}`);
4517
+ this.name = "SurfaceEnsureConflictError";
3797
4518
  this.code = body.code;
3798
4519
  this.lastModifiedSource = body.lastModifiedSource;
3799
4520
  this.modifiedAt = body.modifiedAt;
3800
4521
  this.currentHash = body.currentHash;
3801
4522
  }
3802
4523
  };
3803
- var ToolDriftError = class extends Error {
4524
+ var SurfaceDriftError = class extends Error {
3804
4525
  constructor(plan) {
3805
4526
  super(
3806
- `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.`
4527
+ `Surface "${plan.surfaceId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.surfaces.pull(productId, name) to absorb the remote edit into your repo, or re-run ensure to converge.`
3807
4528
  );
3808
- this.name = "ToolDriftError";
4529
+ this.name = "SurfaceDriftError";
3809
4530
  this.plan = plan;
3810
4531
  }
3811
4532
  };
3812
- function parseRequestError3(err) {
4533
+ function parseRequestError6(err) {
3813
4534
  if (!(err instanceof Error)) return { status: null, body: null };
3814
4535
  const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3815
4536
  if (!match) return { status: null, body: null };
@@ -3819,47 +4540,47 @@ function parseRequestError3(err) {
3819
4540
  return { status: Number(match[1]), body: null };
3820
4541
  }
3821
4542
  }
3822
- function toConflictError3(err) {
3823
- const { status, body } = parseRequestError3(err);
3824
- if (status !== 409 || !isPlainObject3(body)) return null;
4543
+ function toConflictError6(err) {
4544
+ const { status, body } = parseRequestError6(err);
4545
+ if (status !== 409 || !isPlainObject6(body)) return null;
3825
4546
  const code = body.code;
3826
4547
  if (code !== "external_modification" && code !== "remote_changed") return null;
3827
- return new ToolEnsureConflictError(
4548
+ return new SurfaceEnsureConflictError(
3828
4549
  body
3829
4550
  );
3830
4551
  }
3831
- var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3832
- function memoFor3(client) {
3833
- let memo = serverHashMemo3.get(client);
4552
+ var serverHashMemo6 = /* @__PURE__ */ new WeakMap();
4553
+ function memoFor6(client) {
4554
+ let memo = serverHashMemo6.get(client);
3834
4555
  if (!memo) {
3835
4556
  memo = /* @__PURE__ */ new Map();
3836
- serverHashMemo3.set(client, memo);
4557
+ serverHashMemo6.set(client, memo);
3837
4558
  }
3838
4559
  return memo;
3839
4560
  }
3840
- function memoize2(memo, memoKey, result) {
4561
+ function memoize5(memo, memoKey, result) {
3841
4562
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3842
4563
  }
3843
- async function request2(client, body) {
4564
+ async function request5(client, productId, body) {
3844
4565
  try {
3845
4566
  return await client.post(
3846
- "/tools/ensure",
4567
+ `/products/${encodeURIComponent(productId)}/surfaces/ensure`,
3847
4568
  body
3848
4569
  );
3849
4570
  } catch (err) {
3850
- const conflict = toConflictError3(err);
4571
+ const conflict = toConflictError6(err);
3851
4572
  if (conflict) throw conflict;
3852
4573
  throw err;
3853
4574
  }
3854
4575
  }
3855
- async function ensureTool(client, definition, options = {}) {
4576
+ async function ensureSurface(client, productId, definition, options = {}) {
3856
4577
  const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
3857
4578
  const passthrough = {
3858
4579
  ...onConflict ? { onConflict } : {},
3859
4580
  ...expectedRemoteHash ? { expectedRemoteHash } : {}
3860
4581
  };
3861
4582
  if (dryRun || expectNoChanges) {
3862
- const plan = await request2(client, {
4583
+ const plan = await request5(client, productId, {
3863
4584
  name: definition.name,
3864
4585
  definition,
3865
4586
  dryRun: true,
@@ -3869,24 +4590,24 @@ async function ensureTool(client, definition, options = {}) {
3869
4590
  throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3870
4591
  }
3871
4592
  if (expectNoChanges && plan.changes !== "none") {
3872
- throw new ToolDriftError(plan);
4593
+ throw new SurfaceDriftError(plan);
3873
4594
  }
3874
4595
  return plan;
3875
4596
  }
3876
- const memo = memoFor3(client);
3877
- const localHash = await computeToolContentHash(definition);
3878
- const memoKey = `${definition.name} ${localHash}`;
4597
+ const memo = memoFor6(client);
4598
+ const localHash = await computeSurfaceContentHash(definition);
4599
+ const memoKey = `${productId} ${definition.name} ${localHash}`;
3879
4600
  const contentHash = memo.get(memoKey) ?? localHash;
3880
- const probe = await request2(client, {
4601
+ const probe = await request5(client, productId, {
3881
4602
  name: definition.name,
3882
4603
  contentHash,
3883
4604
  ...passthrough
3884
4605
  });
3885
4606
  if (probe.result !== "definitionRequired") {
3886
- memoize2(memo, memoKey, probe);
4607
+ memoize5(memo, memoKey, probe);
3887
4608
  return probe;
3888
4609
  }
3889
- const converged = await request2(client, {
4610
+ const converged = await request5(client, productId, {
3890
4611
  name: definition.name,
3891
4612
  definition,
3892
4613
  ...passthrough
@@ -3894,49 +4615,51 @@ async function ensureTool(client, definition, options = {}) {
3894
4615
  if (converged.result === "definitionRequired") {
3895
4616
  throw new Error("Server reported definitionRequired for a full-definition request");
3896
4617
  }
3897
- memoize2(memo, memoKey, converged);
4618
+ memoize5(memo, memoKey, converged);
3898
4619
  return converged;
3899
4620
  }
3900
- async function pullTool(client, name) {
3901
- return client.get("/tools/pull", { name });
4621
+ async function pullSurface(client, productId, name) {
4622
+ return client.get(
4623
+ `/products/${encodeURIComponent(productId)}/surfaces/pull`,
4624
+ { name }
4625
+ );
3902
4626
  }
3903
4627
 
3904
- // src/tools-namespace.ts
3905
- var ToolsNamespace = class {
4628
+ // src/surfaces-namespace.ts
4629
+ var SurfacesNamespace = class {
3906
4630
  constructor(getClient) {
3907
4631
  this.getClient = getClient;
3908
4632
  }
3909
4633
  /**
3910
- * Idempotently converge a `defineTool` definition onto the platform.
4634
+ * Idempotently converge a `defineSurface` definition onto a product.
3911
4635
  * Hash-first: the steady state is one tiny probe request. Creates or updates
3912
- * the saved tool; never deletes. Identity is name + account scope.
4636
+ * the surface; never deletes. Identity is name + product.
3913
4637
  *
3914
4638
  * @example
3915
4639
  * ```typescript
3916
- * const weather = defineTool({
3917
- * name: 'Weather Lookup',
3918
- * description: 'Fetch the current weather for a city',
3919
- * toolType: 'external',
3920
- * parametersSchema: { type: 'object', properties: { city: { type: 'string' } } },
3921
- * config: { url: 'https://api.example.com/weather', method: 'GET' },
4640
+ * const chat = defineSurface({
4641
+ * name: 'Support Chat',
4642
+ * type: 'chat',
4643
+ * behavior: { type: 'chat', greeting: 'Hi there!' },
4644
+ * status: 'active',
3922
4645
  * })
3923
4646
  *
3924
4647
  * // Converge (CI/deploy).
3925
- * const result = await Runtype.tools.ensure(weather)
4648
+ * const result = await Runtype.surfaces.ensure('product_abc', chat)
3926
4649
  *
3927
4650
  * // PR drift gate.
3928
- * await Runtype.tools.ensure(weather, { expectNoChanges: true })
4651
+ * await Runtype.surfaces.ensure('product_abc', chat, { expectNoChanges: true })
3929
4652
  * ```
3930
4653
  */
3931
- async ensure(definition, options = {}) {
3932
- return ensureTool(this.getClient(), definition, options);
4654
+ async ensure(productId, definition, options = {}) {
4655
+ return ensureSurface(this.getClient(), productId, definition, options);
3933
4656
  }
3934
4657
  /**
3935
- * Pull the canonical definition + provenance for a tool by name the
3936
- * absorb-drift direction of the ensure protocol.
4658
+ * Pull the canonical definition + provenance for a surface by name within a
4659
+ * product — the absorb-drift direction of the ensure protocol.
3937
4660
  */
3938
- async pull(name) {
3939
- return pullTool(this.getClient(), name);
4661
+ async pull(productId, name) {
4662
+ return pullSurface(this.getClient(), productId, name);
3940
4663
  }
3941
4664
  };
3942
4665
 
@@ -4356,6 +5079,66 @@ var Runtype = class {
4356
5079
  static get tools() {
4357
5080
  return new ToolsNamespace(() => this.getClient());
4358
5081
  }
5082
+ /**
5083
+ * Products namespace - Product config-as-code (define / ensure / pull)
5084
+ *
5085
+ * Converges the top-level product record (description, icon, spec). Nested
5086
+ * capabilities/surfaces/tools and the canvas UI layout state are not
5087
+ * converged by ensure.
5088
+ *
5089
+ * @example
5090
+ * ```typescript
5091
+ * import { defineProduct, Runtype } from '@runtypelabs/sdk'
5092
+ *
5093
+ * const product = defineProduct({
5094
+ * name: 'Support Copilot',
5095
+ * description: 'An AI support assistant',
5096
+ * icon: '🤖',
5097
+ * spec: { productGoal: 'Deflect tier-1 tickets', productStage: 'beta' },
5098
+ * })
5099
+ *
5100
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
5101
+ * await Runtype.products.ensure(product)
5102
+ *
5103
+ * // CI drift gate
5104
+ * await Runtype.products.ensure(product, { expectNoChanges: true })
5105
+ *
5106
+ * // Absorb a dashboard edit back into the repo
5107
+ * const { definition } = await Runtype.products.pull('Support Copilot')
5108
+ * ```
5109
+ */
5110
+ static get products() {
5111
+ return new ProductsNamespace(() => this.getClient());
5112
+ }
5113
+ /**
5114
+ * Config-as-code operations for product surfaces. `surfaces.ensure` is the
5115
+ * deploy-time, non-executing converge (create-or-update a surface by name
5116
+ * within a product); `surfaces.pull` is the absorb-drift direction.
5117
+ *
5118
+ * @example
5119
+ * ```typescript
5120
+ * import { Runtype, defineSurface } from '@runtypelabs/sdk'
5121
+ *
5122
+ * const chat = defineSurface({
5123
+ * name: 'Support Chat',
5124
+ * type: 'chat',
5125
+ * behavior: { type: 'chat', greeting: 'Hi there!' },
5126
+ * status: 'active',
5127
+ * })
5128
+ *
5129
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
5130
+ * await Runtype.surfaces.ensure('product_abc', chat)
5131
+ *
5132
+ * // CI drift gate
5133
+ * await Runtype.surfaces.ensure('product_abc', chat, { expectNoChanges: true })
5134
+ *
5135
+ * // Absorb a dashboard edit back into the repo
5136
+ * const { definition } = await Runtype.surfaces.pull('product_abc', 'Support Chat')
5137
+ * ```
5138
+ */
5139
+ static get surfaces() {
5140
+ return new SurfacesNamespace(() => this.getClient());
5141
+ }
4359
5142
  };
4360
5143
 
4361
5144
  // src/generated-tool-gate.ts
@@ -4578,8 +5361,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
4578
5361
  ...decision.tool ? { tool: decision.tool } : {}
4579
5362
  };
4580
5363
  }
4581
- function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {}) {
4582
- const stepList = request3.flow.steps;
5364
+ function attachRuntimeToolsToDispatchRequest(request6, runtimeTools, options = {}) {
5365
+ const stepList = request6.flow.steps;
4583
5366
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
4584
5367
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
4585
5368
  }
@@ -4622,9 +5405,9 @@ function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {
4622
5405
  }
4623
5406
  };
4624
5407
  return {
4625
- ...request3,
5408
+ ...request6,
4626
5409
  flow: {
4627
- ...request3.flow,
5410
+ ...request6.flow,
4628
5411
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
4629
5412
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
4630
5413
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -4634,12 +5417,12 @@ function attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options = {
4634
5417
  }
4635
5418
  };
4636
5419
  }
4637
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options = {}) {
5420
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request6, proposal, options = {}) {
4638
5421
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
4639
5422
  if (!decision.approved || !decision.tool) {
4640
- return { decision, request: request3 };
5423
+ return { decision, request: request6 };
4641
5424
  }
4642
- const nextRequest = attachRuntimeToolsToDispatchRequest(request3, [decision.tool], options.attach);
5425
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request6, [decision.tool], options.attach);
4643
5426
  return {
4644
5427
  decision,
4645
5428
  request: nextRequest
@@ -6889,15 +7672,15 @@ var DispatchEndpoint = class {
6889
7672
  * Attach approved runtime tools to a prompt step in a redispatch request.
6890
7673
  * Returns a new request object and does not mutate the original.
6891
7674
  */
6892
- attachApprovedRuntimeTools(request3, runtimeTools, options) {
6893
- return attachRuntimeToolsToDispatchRequest(request3, runtimeTools, options);
7675
+ attachApprovedRuntimeTools(request6, runtimeTools, options) {
7676
+ return attachRuntimeToolsToDispatchRequest(request6, runtimeTools, options);
6894
7677
  }
6895
7678
  /**
6896
7679
  * Validate a generated runtime tool proposal and attach it to the redispatch
6897
7680
  * request if approved, in one call.
6898
7681
  */
6899
- applyGeneratedRuntimeToolProposal(request3, proposal, options) {
6900
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request3, proposal, options);
7682
+ applyGeneratedRuntimeToolProposal(request6, proposal, options) {
7683
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request6, proposal, options);
6901
7684
  }
6902
7685
  };
6903
7686
  var ChatEndpoint = class {
@@ -7449,8 +8232,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
7449
8232
  },
7450
8233
  required: ["name", "description", "toolType", "parametersSchema", "config"]
7451
8234
  };
7452
- function appendRuntimeToolsToAgentRequest(request3, runtimeTools) {
7453
- const existing = request3.tools?.runtimeTools || [];
8235
+ function appendRuntimeToolsToAgentRequest(request6, runtimeTools) {
8236
+ const existing = request6.tools?.runtimeTools || [];
7454
8237
  const existingNames = new Set(existing.map((tool) => tool.name));
7455
8238
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
7456
8239
  name: tool.name,
@@ -7460,9 +8243,9 @@ function appendRuntimeToolsToAgentRequest(request3, runtimeTools) {
7460
8243
  ...tool.config ? { config: tool.config } : {}
7461
8244
  }));
7462
8245
  return {
7463
- ...request3,
8246
+ ...request6,
7464
8247
  tools: {
7465
- ...request3.tools,
8248
+ ...request6.tools,
7466
8249
  runtimeTools: [...existing, ...converted]
7467
8250
  }
7468
8251
  };
@@ -7538,21 +8321,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7538
8321
  * Attach approved runtime tools to an agent execute request.
7539
8322
  * Returns a new request object and does not mutate the original.
7540
8323
  */
7541
- attachApprovedRuntimeTools(request3, runtimeTools) {
7542
- return appendRuntimeToolsToAgentRequest(request3, runtimeTools);
8324
+ attachApprovedRuntimeTools(request6, runtimeTools) {
8325
+ return appendRuntimeToolsToAgentRequest(request6, runtimeTools);
7543
8326
  }
7544
8327
  /**
7545
8328
  * Validate a generated runtime tool proposal and append it to an agent execute
7546
8329
  * request if approved, in one call.
7547
8330
  */
7548
- applyGeneratedRuntimeToolProposal(request3, proposal, options) {
8331
+ applyGeneratedRuntimeToolProposal(request6, proposal, options) {
7549
8332
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
7550
8333
  if (!decision.approved || !decision.tool) {
7551
- return { decision, request: request3 };
8334
+ return { decision, request: request6 };
7552
8335
  }
7553
8336
  return {
7554
8337
  decision,
7555
- request: appendRuntimeToolsToAgentRequest(request3, [decision.tool])
8338
+ request: appendRuntimeToolsToAgentRequest(request6, [decision.tool])
7556
8339
  };
7557
8340
  }
7558
8341
  /**
@@ -10653,6 +11436,29 @@ var BillingEndpoint = class {
10653
11436
  return this.client.get("/billing/spend-analytics", params);
10654
11437
  }
10655
11438
  };
11439
+ var ToolApprovalGrantsEndpoint = class {
11440
+ constructor(client) {
11441
+ this.client = client;
11442
+ }
11443
+ /**
11444
+ * List active remembered tool-approval grants for the authenticated owner,
11445
+ * optionally filtered to a single agent.
11446
+ */
11447
+ async list(agentId) {
11448
+ const query = agentId ? `?agentId=${encodeURIComponent(agentId)}` : "";
11449
+ const response = await this.client.get(
11450
+ `/tool-approval-grants${query}`
11451
+ );
11452
+ return response.data;
11453
+ }
11454
+ /**
11455
+ * Revoke (soft-delete) a remembered grant so the tool prompts for approval
11456
+ * again on future dispatches.
11457
+ */
11458
+ async revoke(id) {
11459
+ return this.client.delete(`/tool-approval-grants/${id}`);
11460
+ }
11461
+ };
10656
11462
  var AppsEndpoint = class {
10657
11463
  constructor(client) {
10658
11464
  this.client = client;
@@ -10750,6 +11556,7 @@ var RuntypeClient2 = class {
10750
11556
  this.flowVersions = new FlowVersionsEndpoint(this);
10751
11557
  this.integrations = new IntegrationsEndpoint(this);
10752
11558
  this.billing = new BillingEndpoint(this);
11559
+ this.toolApprovalGrants = new ToolApprovalGrantsEndpoint(this);
10753
11560
  }
10754
11561
  /**
10755
11562
  * Set the API key for authentication
@@ -10788,7 +11595,7 @@ var RuntypeClient2 = class {
10788
11595
  clearApiKey() {
10789
11596
  delete this.headers.Authorization;
10790
11597
  }
10791
- async runWithLocalTools(request3, localTools, arg3, arg4) {
11598
+ async runWithLocalTools(request6, localTools, arg3, arg4) {
10792
11599
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
10793
11600
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
10794
11601
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -10802,12 +11609,12 @@ var RuntypeClient2 = class {
10802
11609
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
10803
11610
  })) : [];
10804
11611
  const modifiedRequest = {
10805
- ...request3,
11612
+ ...request6,
10806
11613
  ...derivedClientTools.length > 0 ? {
10807
- clientTools: [...request3.clientTools ?? [], ...derivedClientTools]
11614
+ clientTools: [...request6.clientTools ?? [], ...derivedClientTools]
10808
11615
  } : {},
10809
11616
  options: {
10810
- ...request3.options || {},
11617
+ ...request6.options || {},
10811
11618
  streamResponse: isStreaming
10812
11619
  }
10813
11620
  };
@@ -11245,20 +12052,20 @@ var BatchBuilder = class {
11245
12052
  if (!this.recordType) {
11246
12053
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
11247
12054
  }
11248
- const request3 = {
12055
+ const request6 = {
11249
12056
  flowId: this.flowId,
11250
12057
  recordType: this.recordType
11251
12058
  };
11252
12059
  if (Object.keys(this.batchOptions).length > 0) {
11253
- request3.options = this.batchOptions;
12060
+ request6.options = this.batchOptions;
11254
12061
  }
11255
12062
  if (this.filterConfig) {
11256
- request3.filter = this.filterConfig;
12063
+ request6.filter = this.filterConfig;
11257
12064
  }
11258
12065
  if (this.limitConfig !== void 0) {
11259
- request3.limit = this.limitConfig;
12066
+ request6.limit = this.limitConfig;
11260
12067
  }
11261
- return request3;
12068
+ return request6;
11262
12069
  }
11263
12070
  /**
11264
12071
  * Execute the batch operation
@@ -11415,32 +12222,32 @@ var EvalBuilder = class {
11415
12222
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
11416
12223
  );
11417
12224
  }
11418
- const request3 = {};
12225
+ const request6 = {};
11419
12226
  if (this.flowId) {
11420
- request3.flowId = this.flowId;
12227
+ request6.flowId = this.flowId;
11421
12228
  } else if (this.virtualFlow) {
11422
- request3.flow = this.virtualFlow;
12229
+ request6.flow = this.virtualFlow;
11423
12230
  }
11424
12231
  if (this.recordType) {
11425
- request3.recordType = this.recordType;
12232
+ request6.recordType = this.recordType;
11426
12233
  } else if (this.inlineRecords) {
11427
- request3.records = this.inlineRecords;
12234
+ request6.records = this.inlineRecords;
11428
12235
  }
11429
12236
  if (this.modelOverrides) {
11430
- request3.modelOverrides = this.modelOverrides;
12237
+ request6.modelOverrides = this.modelOverrides;
11431
12238
  } else if (this.modelConfigs) {
11432
- request3.modelConfigs = this.modelConfigs;
12239
+ request6.modelConfigs = this.modelConfigs;
11433
12240
  }
11434
12241
  if (Object.keys(this.evalOptions).length > 0) {
11435
- request3.options = this.evalOptions;
12242
+ request6.options = this.evalOptions;
11436
12243
  }
11437
12244
  if (this.filterConfig) {
11438
- request3.filter = this.filterConfig;
12245
+ request6.filter = this.filterConfig;
11439
12246
  }
11440
12247
  if (this.limitConfig !== void 0) {
11441
- request3.limit = this.limitConfig;
12248
+ request6.limit = this.limitConfig;
11442
12249
  }
11443
- return request3;
12250
+ return request6;
11444
12251
  }
11445
12252
  /**
11446
12253
  * Execute the evaluation
@@ -11944,6 +12751,9 @@ var STEP_TYPE_TO_METHOD = {
11944
12751
  LEDGER_ARTIFACT_LINE_PREFIX,
11945
12752
  LogsEndpoint,
11946
12753
  ModelConfigsEndpoint,
12754
+ ProductDriftError,
12755
+ ProductEnsureConflictError,
12756
+ ProductsNamespace,
11947
12757
  PromptRunner,
11948
12758
  PromptsEndpoint,
11949
12759
  PromptsNamespace,
@@ -11957,9 +12767,15 @@ var STEP_TYPE_TO_METHOD = {
11957
12767
  STEP_TYPE_TO_METHOD,
11958
12768
  SchedulesEndpoint,
11959
12769
  SecretsEndpoint,
12770
+ SkillDriftError,
12771
+ SkillEnsureConflictError,
11960
12772
  SkillProposalsNamespace,
11961
12773
  SkillsNamespace,
12774
+ SurfaceDriftError,
12775
+ SurfaceEnsureConflictError,
11962
12776
  SurfacesEndpoint,
12777
+ SurfacesNamespace,
12778
+ ToolApprovalGrantsEndpoint,
11963
12779
  ToolDriftError,
11964
12780
  ToolEnsureConflictError,
11965
12781
  ToolsEndpoint,
@@ -11975,6 +12791,9 @@ var STEP_TYPE_TO_METHOD = {
11975
12791
  compileWorkflowConfig,
11976
12792
  computeAgentContentHash,
11977
12793
  computeFlowContentHash,
12794
+ computeProductContentHash,
12795
+ computeSkillContentHash,
12796
+ computeSurfaceContentHash,
11978
12797
  computeToolContentHash,
11979
12798
  createClient,
11980
12799
  createExternalTool,
@@ -11983,6 +12802,9 @@ var STEP_TYPE_TO_METHOD = {
11983
12802
  defineAgent,
11984
12803
  defineFlow,
11985
12804
  definePlaybook,
12805
+ defineProduct,
12806
+ defineSkill,
12807
+ defineSurface,
11986
12808
  defineTool,
11987
12809
  deployWorkflow,
11988
12810
  ensureDefaultWorkflowHooks,
@@ -11999,6 +12821,9 @@ var STEP_TYPE_TO_METHOD = {
11999
12821
  listWorkflowHooks,
12000
12822
  normalizeAgentDefinition,
12001
12823
  normalizeCandidatePath,
12824
+ normalizeProductDefinition,
12825
+ normalizeSkillDefinition,
12826
+ normalizeSurfaceDefinition,
12002
12827
  normalizeToolDefinition,
12003
12828
  parseFinalBuffer,
12004
12829
  parseLedgerArtifactRelativePath,