@runtypelabs/sdk 4.15.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.mjs CHANGED
@@ -1094,20 +1094,20 @@ var FlowBuilder = class {
1094
1094
  */
1095
1095
  build() {
1096
1096
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
1097
- const request4 = { flow };
1097
+ const request6 = { flow };
1098
1098
  if (this.recordConfig) {
1099
- request4.record = this.recordConfig;
1099
+ request6.record = this.recordConfig;
1100
1100
  }
1101
1101
  if (this.messagesConfig) {
1102
- request4.messages = this.messagesConfig;
1102
+ request6.messages = this.messagesConfig;
1103
1103
  }
1104
1104
  if (this.inputsConfig) {
1105
- request4.inputs = this.inputsConfig;
1105
+ request6.inputs = this.inputsConfig;
1106
1106
  }
1107
1107
  if (Object.keys(this.optionsConfig).length > 0) {
1108
- request4.options = this.optionsConfig;
1108
+ request6.options = this.optionsConfig;
1109
1109
  }
1110
- return request4;
1110
+ return request6;
1111
1111
  }
1112
1112
  /**
1113
1113
  * Validate this prospective flow against the public validation endpoint
@@ -2501,15 +2501,15 @@ var RuntypeFlowBuilder = class {
2501
2501
  build() {
2502
2502
  const flowMode = this.mode === "existing" ? "existing" : this.mode;
2503
2503
  const flow = this.existingFlowId ? { id: this.existingFlowId } : { name: this.flowConfig.name, steps: this.steps };
2504
- const request4 = { flow };
2504
+ const request6 = { flow };
2505
2505
  if (this.recordConfig) {
2506
- request4.record = this.recordConfig;
2506
+ request6.record = this.recordConfig;
2507
2507
  }
2508
2508
  if (this.messagesConfig) {
2509
- request4.messages = this.messagesConfig;
2509
+ request6.messages = this.messagesConfig;
2510
2510
  }
2511
2511
  if (this.inputsConfig) {
2512
- request4.inputs = this.inputsConfig;
2512
+ request6.inputs = this.inputsConfig;
2513
2513
  }
2514
2514
  const options = {
2515
2515
  flowMode,
@@ -2527,8 +2527,8 @@ var RuntypeFlowBuilder = class {
2527
2527
  if (this.mode === "upsert" && Object.keys(this.upsertOptions).length > 0) {
2528
2528
  options.upsertOptions = this.upsertOptions;
2529
2529
  }
2530
- request4.options = options;
2531
- return request4;
2530
+ request6.options = options;
2531
+ return request6;
2532
2532
  }
2533
2533
  /**
2534
2534
  * Validate this prospective flow against the public validation endpoint
@@ -3122,6 +3122,192 @@ var PromptsNamespace = class {
3122
3122
  }
3123
3123
  };
3124
3124
 
3125
+ // src/skills-ensure.ts
3126
+ function isPlainObject2(value) {
3127
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3128
+ }
3129
+ function normalizeValue(value) {
3130
+ if (Array.isArray(value)) {
3131
+ return value.map((item) => normalizeValue(item));
3132
+ }
3133
+ if (isPlainObject2(value)) {
3134
+ const normalized = {};
3135
+ for (const key of Object.keys(value).sort()) {
3136
+ const entry = value[key];
3137
+ if (entry === void 0 || entry === null) continue;
3138
+ normalized[key] = normalizeValue(entry);
3139
+ }
3140
+ return normalized;
3141
+ }
3142
+ return value;
3143
+ }
3144
+ function normalizeSkillDefinition(definition) {
3145
+ const manifest = isPlainObject2(definition.manifest) ? definition.manifest : {};
3146
+ const rawFrontmatter = isPlainObject2(manifest.frontmatter) ? manifest.frontmatter : {};
3147
+ const frontmatterWithoutName = {};
3148
+ for (const key of Object.keys(rawFrontmatter)) {
3149
+ if (key === "name") continue;
3150
+ frontmatterWithoutName[key] = rawFrontmatter[key];
3151
+ }
3152
+ const frontmatter = normalizeValue(frontmatterWithoutName);
3153
+ const runtype = isPlainObject2(manifest.runtype) ? normalizeValue(manifest.runtype) : {};
3154
+ const body = typeof manifest.body === "string" ? manifest.body : "";
3155
+ return { frontmatter, runtype, body };
3156
+ }
3157
+ async function computeSkillContentHash(definition) {
3158
+ const serialized = JSON.stringify(normalizeSkillDefinition(definition));
3159
+ const encoded = new TextEncoder().encode(serialized);
3160
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
3161
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
3162
+ }
3163
+ var DEFINE_SKILL_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "manifest"]);
3164
+ function defineSkill(input) {
3165
+ if (!input || typeof input !== "object") {
3166
+ throw new Error("defineSkill requires a definition object");
3167
+ }
3168
+ if (typeof input.name !== "string" || input.name.length === 0) {
3169
+ throw new Error('defineSkill requires a non-empty string "name"');
3170
+ }
3171
+ if (!isPlainObject2(input.manifest)) {
3172
+ throw new Error('defineSkill requires a "manifest" object ({ frontmatter, runtype, body })');
3173
+ }
3174
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_SKILL_TOP_LEVEL_KEYS.has(key));
3175
+ if (unknownKeys.length > 0) {
3176
+ throw new Error(
3177
+ `defineSkill: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name and manifest.`
3178
+ );
3179
+ }
3180
+ const frontmatter = input.manifest.frontmatter;
3181
+ if (!isPlainObject2(frontmatter) || typeof frontmatter.name !== "string") {
3182
+ throw new Error("defineSkill: manifest.frontmatter.name is required");
3183
+ }
3184
+ if (frontmatter.name !== input.name) {
3185
+ throw new Error(
3186
+ `defineSkill: manifest.frontmatter.name ("${frontmatter.name}") must match the identity name ("${input.name}").`
3187
+ );
3188
+ }
3189
+ return { name: input.name, manifest: input.manifest };
3190
+ }
3191
+ var SkillEnsureConflictError = class extends Error {
3192
+ constructor(body) {
3193
+ super(body.error ?? `Skill ensure conflict: ${body.code}`);
3194
+ this.name = "SkillEnsureConflictError";
3195
+ this.code = body.code;
3196
+ this.lastModifiedSource = body.lastModifiedSource;
3197
+ this.modifiedAt = body.modifiedAt;
3198
+ this.currentHash = body.currentHash;
3199
+ }
3200
+ };
3201
+ var SkillDriftError = class extends Error {
3202
+ constructor(plan) {
3203
+ super(
3204
+ `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.`
3205
+ );
3206
+ this.name = "SkillDriftError";
3207
+ this.plan = plan;
3208
+ }
3209
+ };
3210
+ function parseRequestError2(err) {
3211
+ if (!(err instanceof Error)) return { status: null, body: null };
3212
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3213
+ if (!match) return { status: null, body: null };
3214
+ try {
3215
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
3216
+ } catch {
3217
+ return { status: Number(match[1]), body: null };
3218
+ }
3219
+ }
3220
+ function toConflictError2(err) {
3221
+ const { status, body } = parseRequestError2(err);
3222
+ if (status !== 409 || !isPlainObject2(body)) return null;
3223
+ const code = body.code;
3224
+ if (code !== "external_modification" && code !== "remote_changed") return null;
3225
+ return new SkillEnsureConflictError(
3226
+ body
3227
+ );
3228
+ }
3229
+ var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
3230
+ function memoFor2(client) {
3231
+ let memo = serverHashMemo2.get(client);
3232
+ if (!memo) {
3233
+ memo = /* @__PURE__ */ new Map();
3234
+ serverHashMemo2.set(client, memo);
3235
+ }
3236
+ return memo;
3237
+ }
3238
+ function memoize2(memo, memoKey, result) {
3239
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3240
+ }
3241
+ async function request2(client, body) {
3242
+ try {
3243
+ return await client.post(
3244
+ "/skills/ensure",
3245
+ body
3246
+ );
3247
+ } catch (err) {
3248
+ const conflict = toConflictError2(err);
3249
+ if (conflict) throw conflict;
3250
+ throw err;
3251
+ }
3252
+ }
3253
+ async function ensureSkill(client, definition, options = {}) {
3254
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
3255
+ const passthrough = {
3256
+ ...onConflict ? { onConflict } : {},
3257
+ ...release ? { release } : {},
3258
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3259
+ };
3260
+ if (dryRun || expectNoChanges) {
3261
+ const plan = await request2(client, {
3262
+ name: definition.name,
3263
+ definition: manifestToWire(definition),
3264
+ dryRun: true,
3265
+ ...passthrough
3266
+ });
3267
+ if (plan.result !== "plan") {
3268
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3269
+ }
3270
+ if (expectNoChanges && plan.changes !== "none") {
3271
+ throw new SkillDriftError(plan);
3272
+ }
3273
+ return plan;
3274
+ }
3275
+ const memo = memoFor2(client);
3276
+ const localHash = await computeSkillContentHash(definition);
3277
+ const memoKey = `${definition.name} ${localHash}`;
3278
+ const contentHash = memo.get(memoKey) ?? localHash;
3279
+ const probe = await request2(client, {
3280
+ name: definition.name,
3281
+ contentHash,
3282
+ ...passthrough
3283
+ });
3284
+ if (probe.result !== "definitionRequired") {
3285
+ memoize2(memo, memoKey, probe);
3286
+ return probe;
3287
+ }
3288
+ const converged = await request2(client, {
3289
+ name: definition.name,
3290
+ definition: manifestToWire(definition),
3291
+ ...passthrough
3292
+ });
3293
+ if (converged.result === "definitionRequired") {
3294
+ throw new Error("Server reported definitionRequired for a full-definition request");
3295
+ }
3296
+ memoize2(memo, memoKey, converged);
3297
+ return converged;
3298
+ }
3299
+ function manifestToWire(definition) {
3300
+ const manifest = definition.manifest;
3301
+ const frontmatter = { ...manifest.frontmatter };
3302
+ if (manifest.runtype && Object.keys(manifest.runtype).length > 0) {
3303
+ frontmatter.runtype = manifest.runtype;
3304
+ }
3305
+ return { frontmatter, body: manifest.body ?? "" };
3306
+ }
3307
+ async function pullSkill(client, name) {
3308
+ return client.get("/skills/pull", { name });
3309
+ }
3310
+
3125
3311
  // src/skills-namespace.ts
3126
3312
  var SkillProposalsNamespace = class {
3127
3313
  constructor(getClient) {
@@ -3262,6 +3448,22 @@ var SkillsNamespace = class {
3262
3448
  const client = this.getClient();
3263
3449
  await client.post(`/skills/${skillId}/versions/${versionId}/publish`);
3264
3450
  }
3451
+ /**
3452
+ * Statically scan a SKILL.md document for malicious patterns and return a
3453
+ * two-tier verdict — `warning` when a skill appears suspicious (low–medium
3454
+ * confidence), `error` when high-confidence malicious. Does not persist or
3455
+ * gate; use it as a "scan before save" affordance.
3456
+ *
3457
+ * @example
3458
+ * ```typescript
3459
+ * const { verdict } = await Runtype.skills.scan(skillMarkdown)
3460
+ * if (verdict.tier === 'error') console.warn(verdict.summary)
3461
+ * ```
3462
+ */
3463
+ async scan(markdown) {
3464
+ const client = this.getClient();
3465
+ return client.post("/skills/scan", { markdown });
3466
+ }
3265
3467
  /**
3266
3468
  * Import a single SKILL.md document. The imported skill lands with
3267
3469
  * `trustLevel: 'imported'` and a draft version.
@@ -3304,6 +3506,40 @@ var SkillsNamespace = class {
3304
3506
  const res = await client.get("/skills/bindings", { agentId });
3305
3507
  return res.data;
3306
3508
  }
3509
+ /**
3510
+ * Idempotently converge a `defineSkill` definition onto the platform.
3511
+ * Hash-first: the steady state is one tiny probe request. Creates or appends a
3512
+ * new version; never deletes. Identity is name + account scope. Pass
3513
+ * `release: 'publish'` to publish the converged version.
3514
+ *
3515
+ * @example
3516
+ * ```typescript
3517
+ * const reviewer = defineSkill({
3518
+ * name: 'code_reviewer',
3519
+ * manifest: {
3520
+ * frontmatter: { name: 'code_reviewer', description: 'Reviews pull requests' },
3521
+ * runtype: { trustLevel: 'org' },
3522
+ * body: '# Code Reviewer\n\nReview the diff...',
3523
+ * },
3524
+ * })
3525
+ *
3526
+ * // Converge + publish (CI/deploy).
3527
+ * const result = await Runtype.skills.ensure(reviewer, { release: 'publish' })
3528
+ *
3529
+ * // PR drift gate.
3530
+ * await Runtype.skills.ensure(reviewer, { expectNoChanges: true })
3531
+ * ```
3532
+ */
3533
+ async ensure(definition, options = {}) {
3534
+ return ensureSkill(this.getClient(), definition, options);
3535
+ }
3536
+ /**
3537
+ * Pull the canonical definition + provenance for a skill by name — the
3538
+ * absorb-drift direction of the ensure protocol.
3539
+ */
3540
+ async pull(name) {
3541
+ return pullSkill(this.getClient(), name);
3542
+ }
3307
3543
  };
3308
3544
 
3309
3545
  // src/agents-namespace.ts
@@ -3329,19 +3565,19 @@ var AGENT_CONFIG_KEYS = [
3329
3565
  "memory"
3330
3566
  ];
3331
3567
  var AGENT_CONFIG_KEY_LIST = [...AGENT_CONFIG_KEYS].sort();
3332
- function isPlainObject2(value) {
3568
+ function isPlainObject3(value) {
3333
3569
  return value !== null && typeof value === "object" && !Array.isArray(value);
3334
3570
  }
3335
- function normalizeValue(value) {
3571
+ function normalizeValue2(value) {
3336
3572
  if (Array.isArray(value)) {
3337
- return value.map((item) => normalizeValue(item));
3573
+ return value.map((item) => normalizeValue2(item));
3338
3574
  }
3339
- if (isPlainObject2(value)) {
3575
+ if (isPlainObject3(value)) {
3340
3576
  const normalized = {};
3341
3577
  for (const key of Object.keys(value).sort()) {
3342
3578
  const entry = value[key];
3343
3579
  if (entry === void 0 || entry === null) continue;
3344
- normalized[key] = normalizeValue(entry);
3580
+ normalized[key] = normalizeValue2(entry);
3345
3581
  }
3346
3582
  return normalized;
3347
3583
  }
@@ -3349,11 +3585,11 @@ function normalizeValue(value) {
3349
3585
  }
3350
3586
  function normalizeAgentDefinition(definition) {
3351
3587
  const config = {};
3352
- const rawConfig = isPlainObject2(definition.config) ? definition.config : {};
3588
+ const rawConfig = isPlainObject3(definition.config) ? definition.config : {};
3353
3589
  for (const key of AGENT_CONFIG_KEY_LIST) {
3354
3590
  const value = rawConfig[key];
3355
3591
  if (value === void 0 || value === null) continue;
3356
- config[key] = normalizeValue(value);
3592
+ config[key] = normalizeValue2(value);
3357
3593
  }
3358
3594
  return {
3359
3595
  name: definition.name,
@@ -3371,7 +3607,7 @@ async function computeAgentContentHash(definition) {
3371
3607
  var DEFINE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set(["name", "description", "icon", ...AGENT_CONFIG_KEYS]);
3372
3608
  function collectNonPortableToolRefs(config) {
3373
3609
  const tools = config.tools;
3374
- if (!isPlainObject2(tools)) return [];
3610
+ if (!isPlainObject3(tools)) return [];
3375
3611
  const found = [];
3376
3612
  const isAccountScoped = (ref) => typeof ref === "string" && ref.startsWith("tool_");
3377
3613
  const scanArray = (value, path) => {
@@ -3381,7 +3617,7 @@ function collectNonPortableToolRefs(config) {
3381
3617
  });
3382
3618
  };
3383
3619
  const scanKeys = (value, path) => {
3384
- if (!isPlainObject2(value)) return;
3620
+ if (!isPlainObject3(value)) return;
3385
3621
  for (const key of Object.keys(value)) {
3386
3622
  if (isAccountScoped(key)) found.push(`${path}.${key}`);
3387
3623
  }
@@ -3389,16 +3625,16 @@ function collectNonPortableToolRefs(config) {
3389
3625
  scanArray(tools.toolIds, "tools.toolIds");
3390
3626
  scanKeys(tools.toolConfigs, "tools.toolConfigs");
3391
3627
  scanKeys(tools.perToolLimits, "tools.perToolLimits");
3392
- if (isPlainObject2(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
3393
- if (isPlainObject2(tools.subagentConfig)) {
3628
+ if (isPlainObject3(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
3629
+ if (isPlainObject3(tools.subagentConfig)) {
3394
3630
  scanArray(tools.subagentConfig.toolPool, "tools.subagentConfig.toolPool");
3395
3631
  }
3396
- if (isPlainObject2(tools.codeModeConfig)) {
3632
+ if (isPlainObject3(tools.codeModeConfig)) {
3397
3633
  scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
3398
3634
  }
3399
3635
  if (Array.isArray(tools.runtimeTools)) {
3400
3636
  tools.runtimeTools.forEach((runtimeTool, i) => {
3401
- if (!isPlainObject2(runtimeTool) || !isPlainObject2(runtimeTool.config)) return;
3637
+ if (!isPlainObject3(runtimeTool) || !isPlainObject3(runtimeTool.config)) return;
3402
3638
  const base = `tools.runtimeTools[${i}].config`;
3403
3639
  const rtConfig = runtimeTool.config;
3404
3640
  if (runtimeTool.toolType === "subagent" && typeof rtConfig.agentId === "string" && rtConfig.agentId.startsWith("agent_")) {
@@ -3460,7 +3696,7 @@ var AgentDriftError = class extends Error {
3460
3696
  this.plan = plan;
3461
3697
  }
3462
3698
  };
3463
- function parseRequestError2(err) {
3699
+ function parseRequestError3(err) {
3464
3700
  if (!(err instanceof Error)) return { status: null, body: null };
3465
3701
  const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3466
3702
  if (!match) return { status: null, body: null };
@@ -3470,21 +3706,21 @@ function parseRequestError2(err) {
3470
3706
  return { status: Number(match[1]), body: null };
3471
3707
  }
3472
3708
  }
3473
- function toConflictError2(err) {
3474
- const { status, body } = parseRequestError2(err);
3475
- if (status !== 409 || !isPlainObject2(body)) return null;
3709
+ function toConflictError3(err) {
3710
+ const { status, body } = parseRequestError3(err);
3711
+ if (status !== 409 || !isPlainObject3(body)) return null;
3476
3712
  const code = body.code;
3477
3713
  if (code !== "external_modification" && code !== "remote_changed") return null;
3478
3714
  return new AgentEnsureConflictError(
3479
3715
  body
3480
3716
  );
3481
3717
  }
3482
- var serverHashMemo2 = /* @__PURE__ */ new WeakMap();
3483
- function memoFor2(client) {
3484
- let memo = serverHashMemo2.get(client);
3718
+ var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3719
+ function memoFor3(client) {
3720
+ let memo = serverHashMemo3.get(client);
3485
3721
  if (!memo) {
3486
3722
  memo = /* @__PURE__ */ new Map();
3487
- serverHashMemo2.set(client, memo);
3723
+ serverHashMemo3.set(client, memo);
3488
3724
  }
3489
3725
  return memo;
3490
3726
  }
@@ -3521,7 +3757,7 @@ var AgentsNamespace = class {
3521
3757
  }
3522
3758
  return plan;
3523
3759
  }
3524
- const memo = memoFor2(client);
3760
+ const memo = memoFor3(client);
3525
3761
  const localHash = await computeAgentContentHash({
3526
3762
  ...definition,
3527
3763
  config: definition.config
@@ -3566,7 +3802,7 @@ var AgentsNamespace = class {
3566
3802
  body
3567
3803
  );
3568
3804
  } catch (err) {
3569
- const conflict = toConflictError2(err);
3805
+ const conflict = toConflictError3(err);
3570
3806
  if (conflict) throw conflict;
3571
3807
  throw err;
3572
3808
  }
@@ -3574,27 +3810,27 @@ var AgentsNamespace = class {
3574
3810
  };
3575
3811
 
3576
3812
  // src/tools-ensure.ts
3577
- function isPlainObject3(value) {
3813
+ function isPlainObject4(value) {
3578
3814
  return value !== null && typeof value === "object" && !Array.isArray(value);
3579
3815
  }
3580
- function normalizeValue2(value) {
3816
+ function normalizeValue3(value) {
3581
3817
  if (Array.isArray(value)) {
3582
- return value.map((item) => normalizeValue2(item));
3818
+ return value.map((item) => normalizeValue3(item));
3583
3819
  }
3584
- if (isPlainObject3(value)) {
3820
+ if (isPlainObject4(value)) {
3585
3821
  const normalized = {};
3586
3822
  for (const key of Object.keys(value).sort()) {
3587
3823
  const entry = value[key];
3588
3824
  if (entry === void 0 || entry === null) continue;
3589
- normalized[key] = normalizeValue2(entry);
3825
+ normalized[key] = normalizeValue3(entry);
3590
3826
  }
3591
3827
  return normalized;
3592
3828
  }
3593
3829
  return value;
3594
3830
  }
3595
3831
  function normalizeToolDefinition(definition) {
3596
- const parametersSchema = isPlainObject3(definition.parametersSchema) ? normalizeValue2(definition.parametersSchema) : {};
3597
- const config = isPlainObject3(definition.config) ? normalizeValue2(definition.config) : {};
3832
+ const parametersSchema = isPlainObject4(definition.parametersSchema) ? normalizeValue3(definition.parametersSchema) : {};
3833
+ const config = isPlainObject4(definition.config) ? normalizeValue3(definition.config) : {};
3598
3834
  return {
3599
3835
  toolType: definition.toolType,
3600
3836
  ...definition.description ? { description: definition.description } : {},
@@ -3639,10 +3875,10 @@ function defineTool(input) {
3639
3875
  `defineTool requires "toolType" to be one of: ${[...TOOL_DEFINITION_TYPES].join(", ")}`
3640
3876
  );
3641
3877
  }
3642
- if (!isPlainObject3(input.parametersSchema)) {
3878
+ if (!isPlainObject4(input.parametersSchema)) {
3643
3879
  throw new Error('defineTool requires a "parametersSchema" object (a JSON Schema)');
3644
3880
  }
3645
- if (!isPlainObject3(input.config)) {
3881
+ if (!isPlainObject4(input.config)) {
3646
3882
  throw new Error('defineTool requires a "config" object');
3647
3883
  }
3648
3884
  const unknownKeys = Object.keys(input).filter((key) => !DEFINE_TOOL_TOP_LEVEL_KEYS.has(key));
@@ -3678,7 +3914,7 @@ var ToolDriftError = class extends Error {
3678
3914
  this.plan = plan;
3679
3915
  }
3680
3916
  };
3681
- function parseRequestError3(err) {
3917
+ function parseRequestError4(err) {
3682
3918
  if (!(err instanceof Error)) return { status: null, body: null };
3683
3919
  const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3684
3920
  if (!match) return { status: null, body: null };
@@ -3688,35 +3924,35 @@ function parseRequestError3(err) {
3688
3924
  return { status: Number(match[1]), body: null };
3689
3925
  }
3690
3926
  }
3691
- function toConflictError3(err) {
3692
- const { status, body } = parseRequestError3(err);
3693
- if (status !== 409 || !isPlainObject3(body)) return null;
3927
+ function toConflictError4(err) {
3928
+ const { status, body } = parseRequestError4(err);
3929
+ if (status !== 409 || !isPlainObject4(body)) return null;
3694
3930
  const code = body.code;
3695
3931
  if (code !== "external_modification" && code !== "remote_changed") return null;
3696
3932
  return new ToolEnsureConflictError(
3697
3933
  body
3698
3934
  );
3699
3935
  }
3700
- var serverHashMemo3 = /* @__PURE__ */ new WeakMap();
3701
- function memoFor3(client) {
3702
- let memo = serverHashMemo3.get(client);
3936
+ var serverHashMemo4 = /* @__PURE__ */ new WeakMap();
3937
+ function memoFor4(client) {
3938
+ let memo = serverHashMemo4.get(client);
3703
3939
  if (!memo) {
3704
3940
  memo = /* @__PURE__ */ new Map();
3705
- serverHashMemo3.set(client, memo);
3941
+ serverHashMemo4.set(client, memo);
3706
3942
  }
3707
3943
  return memo;
3708
3944
  }
3709
- function memoize2(memo, memoKey, result) {
3945
+ function memoize3(memo, memoKey, result) {
3710
3946
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3711
3947
  }
3712
- async function request2(client, body) {
3948
+ async function request3(client, body) {
3713
3949
  try {
3714
3950
  return await client.post(
3715
3951
  "/tools/ensure",
3716
3952
  body
3717
3953
  );
3718
3954
  } catch (err) {
3719
- const conflict = toConflictError3(err);
3955
+ const conflict = toConflictError4(err);
3720
3956
  if (conflict) throw conflict;
3721
3957
  throw err;
3722
3958
  }
@@ -3728,7 +3964,7 @@ async function ensureTool(client, definition, options = {}) {
3728
3964
  ...expectedRemoteHash ? { expectedRemoteHash } : {}
3729
3965
  };
3730
3966
  if (dryRun || expectNoChanges) {
3731
- const plan = await request2(client, {
3967
+ const plan = await request3(client, {
3732
3968
  name: definition.name,
3733
3969
  definition,
3734
3970
  dryRun: true,
@@ -3742,20 +3978,20 @@ async function ensureTool(client, definition, options = {}) {
3742
3978
  }
3743
3979
  return plan;
3744
3980
  }
3745
- const memo = memoFor3(client);
3981
+ const memo = memoFor4(client);
3746
3982
  const localHash = await computeToolContentHash(definition);
3747
3983
  const memoKey = `${definition.name} ${localHash}`;
3748
3984
  const contentHash = memo.get(memoKey) ?? localHash;
3749
- const probe = await request2(client, {
3985
+ const probe = await request3(client, {
3750
3986
  name: definition.name,
3751
3987
  contentHash,
3752
3988
  ...passthrough
3753
3989
  });
3754
3990
  if (probe.result !== "definitionRequired") {
3755
- memoize2(memo, memoKey, probe);
3991
+ memoize3(memo, memoKey, probe);
3756
3992
  return probe;
3757
3993
  }
3758
- const converged = await request2(client, {
3994
+ const converged = await request3(client, {
3759
3995
  name: definition.name,
3760
3996
  definition,
3761
3997
  ...passthrough
@@ -3763,7 +3999,7 @@ async function ensureTool(client, definition, options = {}) {
3763
3999
  if (converged.result === "definitionRequired") {
3764
4000
  throw new Error("Server reported definitionRequired for a full-definition request");
3765
4001
  }
3766
- memoize2(memo, memoKey, converged);
4002
+ memoize3(memo, memoKey, converged);
3767
4003
  return converged;
3768
4004
  }
3769
4005
  async function pullTool(client, name) {
@@ -3810,26 +4046,26 @@ var ToolsNamespace = class {
3810
4046
  };
3811
4047
 
3812
4048
  // src/products-ensure.ts
3813
- function isPlainObject4(value) {
4049
+ function isPlainObject5(value) {
3814
4050
  return value !== null && typeof value === "object" && !Array.isArray(value);
3815
4051
  }
3816
- function normalizeValue3(value) {
4052
+ function normalizeValue4(value) {
3817
4053
  if (Array.isArray(value)) {
3818
- return value.map((item) => normalizeValue3(item));
4054
+ return value.map((item) => normalizeValue4(item));
3819
4055
  }
3820
- if (isPlainObject4(value)) {
4056
+ if (isPlainObject5(value)) {
3821
4057
  const normalized = {};
3822
4058
  for (const key of Object.keys(value).sort()) {
3823
4059
  const entry = value[key];
3824
4060
  if (entry === void 0 || entry === null) continue;
3825
- normalized[key] = normalizeValue3(entry);
4061
+ normalized[key] = normalizeValue4(entry);
3826
4062
  }
3827
4063
  return normalized;
3828
4064
  }
3829
4065
  return value;
3830
4066
  }
3831
4067
  function normalizeProductDefinition(definition) {
3832
- const spec = isPlainObject4(definition.spec) ? normalizeValue3(definition.spec) : {};
4068
+ const spec = isPlainObject5(definition.spec) ? normalizeValue4(definition.spec) : {};
3833
4069
  return {
3834
4070
  ...definition.description ? { description: definition.description } : {},
3835
4071
  ...definition.icon ? { icon: definition.icon } : {},
@@ -3856,7 +4092,7 @@ function defineProduct(input) {
3856
4092
  if (input.icon != null && typeof input.icon !== "string") {
3857
4093
  throw new Error('defineProduct "icon" must be a string when provided');
3858
4094
  }
3859
- if (input.spec != null && !isPlainObject4(input.spec)) {
4095
+ if (input.spec != null && !isPlainObject5(input.spec)) {
3860
4096
  throw new Error('defineProduct "spec" must be an object when provided');
3861
4097
  }
3862
4098
  const unknownKeys = Object.keys(input).filter((key) => !DEFINE_PRODUCT_TOP_LEVEL_KEYS.has(key));
@@ -3891,7 +4127,7 @@ var ProductDriftError = class extends Error {
3891
4127
  this.plan = plan;
3892
4128
  }
3893
4129
  };
3894
- function parseRequestError4(err) {
4130
+ function parseRequestError5(err) {
3895
4131
  if (!(err instanceof Error)) return { status: null, body: null };
3896
4132
  const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
3897
4133
  if (!match) return { status: null, body: null };
@@ -3901,35 +4137,35 @@ function parseRequestError4(err) {
3901
4137
  return { status: Number(match[1]), body: null };
3902
4138
  }
3903
4139
  }
3904
- function toConflictError4(err) {
3905
- const { status, body } = parseRequestError4(err);
3906
- if (status !== 409 || !isPlainObject4(body)) return null;
4140
+ function toConflictError5(err) {
4141
+ const { status, body } = parseRequestError5(err);
4142
+ if (status !== 409 || !isPlainObject5(body)) return null;
3907
4143
  const code = body.code;
3908
4144
  if (code !== "external_modification" && code !== "remote_changed") return null;
3909
4145
  return new ProductEnsureConflictError(
3910
4146
  body
3911
4147
  );
3912
4148
  }
3913
- var serverHashMemo4 = /* @__PURE__ */ new WeakMap();
3914
- function memoFor4(client) {
3915
- let memo = serverHashMemo4.get(client);
4149
+ var serverHashMemo5 = /* @__PURE__ */ new WeakMap();
4150
+ function memoFor5(client) {
4151
+ let memo = serverHashMemo5.get(client);
3916
4152
  if (!memo) {
3917
4153
  memo = /* @__PURE__ */ new Map();
3918
- serverHashMemo4.set(client, memo);
4154
+ serverHashMemo5.set(client, memo);
3919
4155
  }
3920
4156
  return memo;
3921
4157
  }
3922
- function memoize3(memo, memoKey, result) {
4158
+ function memoize4(memo, memoKey, result) {
3923
4159
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
3924
4160
  }
3925
- async function request3(client, body) {
4161
+ async function request4(client, body) {
3926
4162
  try {
3927
4163
  return await client.post(
3928
4164
  "/products/ensure",
3929
4165
  body
3930
4166
  );
3931
4167
  } catch (err) {
3932
- const conflict = toConflictError4(err);
4168
+ const conflict = toConflictError5(err);
3933
4169
  if (conflict) throw conflict;
3934
4170
  throw err;
3935
4171
  }
@@ -3941,7 +4177,7 @@ async function ensureProduct(client, definition, options = {}) {
3941
4177
  ...expectedRemoteHash ? { expectedRemoteHash } : {}
3942
4178
  };
3943
4179
  if (dryRun || expectNoChanges) {
3944
- const plan = await request3(client, {
4180
+ const plan = await request4(client, {
3945
4181
  name: definition.name,
3946
4182
  definition,
3947
4183
  dryRun: true,
@@ -3955,20 +4191,20 @@ async function ensureProduct(client, definition, options = {}) {
3955
4191
  }
3956
4192
  return plan;
3957
4193
  }
3958
- const memo = memoFor4(client);
4194
+ const memo = memoFor5(client);
3959
4195
  const localHash = await computeProductContentHash(definition);
3960
4196
  const memoKey = `${definition.name} ${localHash}`;
3961
4197
  const contentHash = memo.get(memoKey) ?? localHash;
3962
- const probe = await request3(client, {
4198
+ const probe = await request4(client, {
3963
4199
  name: definition.name,
3964
4200
  contentHash,
3965
4201
  ...passthrough
3966
4202
  });
3967
4203
  if (probe.result !== "definitionRequired") {
3968
- memoize3(memo, memoKey, probe);
4204
+ memoize4(memo, memoKey, probe);
3969
4205
  return probe;
3970
4206
  }
3971
- const converged = await request3(client, {
4207
+ const converged = await request4(client, {
3972
4208
  name: definition.name,
3973
4209
  definition,
3974
4210
  ...passthrough
@@ -3976,7 +4212,7 @@ async function ensureProduct(client, definition, options = {}) {
3976
4212
  if (converged.result === "definitionRequired") {
3977
4213
  throw new Error("Server reported definitionRequired for a full-definition request");
3978
4214
  }
3979
- memoize3(memo, memoKey, converged);
4215
+ memoize4(memo, memoKey, converged);
3980
4216
  return converged;
3981
4217
  }
3982
4218
  async function pullProduct(client, name) {
@@ -4022,6 +4258,262 @@ var ProductsNamespace = class {
4022
4258
  }
4023
4259
  };
4024
4260
 
4261
+ // src/surfaces-ensure.ts
4262
+ function isPlainObject6(value) {
4263
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4264
+ }
4265
+ function normalizeValue5(value) {
4266
+ if (Array.isArray(value)) {
4267
+ return value.map((item) => normalizeValue5(item));
4268
+ }
4269
+ if (isPlainObject6(value)) {
4270
+ const normalized = {};
4271
+ for (const key of Object.keys(value).sort()) {
4272
+ const entry = value[key];
4273
+ if (entry === void 0 || entry === null) continue;
4274
+ normalized[key] = normalizeValue5(entry);
4275
+ }
4276
+ return normalized;
4277
+ }
4278
+ return value;
4279
+ }
4280
+ function normalizeSurfaceDefinition(definition) {
4281
+ const behavior = isPlainObject6(definition.behavior) ? normalizeValue5(definition.behavior) : { type: definition.type };
4282
+ return {
4283
+ type: definition.type,
4284
+ behavior,
4285
+ status: definition.status || "draft",
4286
+ environment: definition.environment || "development"
4287
+ };
4288
+ }
4289
+ async function computeSurfaceContentHash(definition) {
4290
+ const serialized = JSON.stringify(normalizeSurfaceDefinition(definition));
4291
+ const encoded = new TextEncoder().encode(serialized);
4292
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
4293
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
4294
+ }
4295
+ var DEFINE_SURFACE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
4296
+ "name",
4297
+ "type",
4298
+ "behavior",
4299
+ "inbound",
4300
+ "outbound",
4301
+ "status",
4302
+ "environment"
4303
+ ]);
4304
+ var SURFACE_DEFINITION_TYPES = /* @__PURE__ */ new Set([
4305
+ "chat",
4306
+ "mcp",
4307
+ "mcp_code",
4308
+ "api",
4309
+ "webhook",
4310
+ "schedule",
4311
+ "a2a",
4312
+ "email",
4313
+ "slack",
4314
+ "sms",
4315
+ "imessage",
4316
+ "discord",
4317
+ "whatsapp",
4318
+ "telegram",
4319
+ "hosted-page",
4320
+ "chrome_extension"
4321
+ ]);
4322
+ function defineSurface(input) {
4323
+ if (!input || typeof input !== "object") {
4324
+ throw new Error("defineSurface requires a definition object");
4325
+ }
4326
+ if (typeof input.name !== "string" || input.name.length === 0) {
4327
+ throw new Error('defineSurface requires a non-empty string "name"');
4328
+ }
4329
+ if (typeof input.type !== "string" || !SURFACE_DEFINITION_TYPES.has(input.type)) {
4330
+ throw new Error(
4331
+ `defineSurface requires "type" to be one of: ${[...SURFACE_DEFINITION_TYPES].join(", ")}`
4332
+ );
4333
+ }
4334
+ if (input.behavior !== void 0 && !isPlainObject6(input.behavior)) {
4335
+ throw new Error('defineSurface "behavior" must be an object when provided');
4336
+ }
4337
+ if (input.inbound !== void 0 && !isPlainObject6(input.inbound)) {
4338
+ throw new Error('defineSurface "inbound" must be an object when provided');
4339
+ }
4340
+ if (input.outbound !== void 0 && !isPlainObject6(input.outbound)) {
4341
+ throw new Error('defineSurface "outbound" must be an object when provided');
4342
+ }
4343
+ if (input.status !== void 0 && !["draft", "active", "paused"].includes(input.status)) {
4344
+ throw new Error('defineSurface "status" must be one of: draft, active, paused');
4345
+ }
4346
+ if (input.environment !== void 0 && !["production", "development"].includes(input.environment)) {
4347
+ throw new Error('defineSurface "environment" must be one of: production, development');
4348
+ }
4349
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_SURFACE_TOP_LEVEL_KEYS.has(key));
4350
+ if (unknownKeys.length > 0) {
4351
+ throw new Error(
4352
+ `defineSurface: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are name, type, behavior, inbound, outbound, status, environment.`
4353
+ );
4354
+ }
4355
+ return {
4356
+ name: input.name,
4357
+ type: input.type,
4358
+ ...input.behavior !== void 0 ? { behavior: input.behavior } : {},
4359
+ ...input.inbound !== void 0 ? { inbound: input.inbound } : {},
4360
+ ...input.outbound !== void 0 ? { outbound: input.outbound } : {},
4361
+ ...input.status !== void 0 ? { status: input.status } : {},
4362
+ ...input.environment !== void 0 ? { environment: input.environment } : {}
4363
+ };
4364
+ }
4365
+ var SurfaceEnsureConflictError = class extends Error {
4366
+ constructor(body) {
4367
+ super(body.error ?? `Surface ensure conflict: ${body.code}`);
4368
+ this.name = "SurfaceEnsureConflictError";
4369
+ this.code = body.code;
4370
+ this.lastModifiedSource = body.lastModifiedSource;
4371
+ this.modifiedAt = body.modifiedAt;
4372
+ this.currentHash = body.currentHash;
4373
+ }
4374
+ };
4375
+ var SurfaceDriftError = class extends Error {
4376
+ constructor(plan) {
4377
+ super(
4378
+ `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.`
4379
+ );
4380
+ this.name = "SurfaceDriftError";
4381
+ this.plan = plan;
4382
+ }
4383
+ };
4384
+ function parseRequestError6(err) {
4385
+ if (!(err instanceof Error)) return { status: null, body: null };
4386
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
4387
+ if (!match) return { status: null, body: null };
4388
+ try {
4389
+ return { status: Number(match[1]), body: JSON.parse(match[2]) };
4390
+ } catch {
4391
+ return { status: Number(match[1]), body: null };
4392
+ }
4393
+ }
4394
+ function toConflictError6(err) {
4395
+ const { status, body } = parseRequestError6(err);
4396
+ if (status !== 409 || !isPlainObject6(body)) return null;
4397
+ const code = body.code;
4398
+ if (code !== "external_modification" && code !== "remote_changed") return null;
4399
+ return new SurfaceEnsureConflictError(
4400
+ body
4401
+ );
4402
+ }
4403
+ var serverHashMemo6 = /* @__PURE__ */ new WeakMap();
4404
+ function memoFor6(client) {
4405
+ let memo = serverHashMemo6.get(client);
4406
+ if (!memo) {
4407
+ memo = /* @__PURE__ */ new Map();
4408
+ serverHashMemo6.set(client, memo);
4409
+ }
4410
+ return memo;
4411
+ }
4412
+ function memoize5(memo, memoKey, result) {
4413
+ if (result.result !== "plan") memo.set(memoKey, result.contentHash);
4414
+ }
4415
+ async function request5(client, productId, body) {
4416
+ try {
4417
+ return await client.post(
4418
+ `/products/${encodeURIComponent(productId)}/surfaces/ensure`,
4419
+ body
4420
+ );
4421
+ } catch (err) {
4422
+ const conflict = toConflictError6(err);
4423
+ if (conflict) throw conflict;
4424
+ throw err;
4425
+ }
4426
+ }
4427
+ async function ensureSurface(client, productId, definition, options = {}) {
4428
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
4429
+ const passthrough = {
4430
+ ...onConflict ? { onConflict } : {},
4431
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
4432
+ };
4433
+ if (dryRun || expectNoChanges) {
4434
+ const plan = await request5(client, productId, {
4435
+ name: definition.name,
4436
+ definition,
4437
+ dryRun: true,
4438
+ ...passthrough
4439
+ });
4440
+ if (plan.result !== "plan") {
4441
+ throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
4442
+ }
4443
+ if (expectNoChanges && plan.changes !== "none") {
4444
+ throw new SurfaceDriftError(plan);
4445
+ }
4446
+ return plan;
4447
+ }
4448
+ const memo = memoFor6(client);
4449
+ const localHash = await computeSurfaceContentHash(definition);
4450
+ const memoKey = `${productId} ${definition.name} ${localHash}`;
4451
+ const contentHash = memo.get(memoKey) ?? localHash;
4452
+ const probe = await request5(client, productId, {
4453
+ name: definition.name,
4454
+ contentHash,
4455
+ ...passthrough
4456
+ });
4457
+ if (probe.result !== "definitionRequired") {
4458
+ memoize5(memo, memoKey, probe);
4459
+ return probe;
4460
+ }
4461
+ const converged = await request5(client, productId, {
4462
+ name: definition.name,
4463
+ definition,
4464
+ ...passthrough
4465
+ });
4466
+ if (converged.result === "definitionRequired") {
4467
+ throw new Error("Server reported definitionRequired for a full-definition request");
4468
+ }
4469
+ memoize5(memo, memoKey, converged);
4470
+ return converged;
4471
+ }
4472
+ async function pullSurface(client, productId, name) {
4473
+ return client.get(
4474
+ `/products/${encodeURIComponent(productId)}/surfaces/pull`,
4475
+ { name }
4476
+ );
4477
+ }
4478
+
4479
+ // src/surfaces-namespace.ts
4480
+ var SurfacesNamespace = class {
4481
+ constructor(getClient) {
4482
+ this.getClient = getClient;
4483
+ }
4484
+ /**
4485
+ * Idempotently converge a `defineSurface` definition onto a product.
4486
+ * Hash-first: the steady state is one tiny probe request. Creates or updates
4487
+ * the surface; never deletes. Identity is name + product.
4488
+ *
4489
+ * @example
4490
+ * ```typescript
4491
+ * const chat = defineSurface({
4492
+ * name: 'Support Chat',
4493
+ * type: 'chat',
4494
+ * behavior: { type: 'chat', greeting: 'Hi there!' },
4495
+ * status: 'active',
4496
+ * })
4497
+ *
4498
+ * // Converge (CI/deploy).
4499
+ * const result = await Runtype.surfaces.ensure('product_abc', chat)
4500
+ *
4501
+ * // PR drift gate.
4502
+ * await Runtype.surfaces.ensure('product_abc', chat, { expectNoChanges: true })
4503
+ * ```
4504
+ */
4505
+ async ensure(productId, definition, options = {}) {
4506
+ return ensureSurface(this.getClient(), productId, definition, options);
4507
+ }
4508
+ /**
4509
+ * Pull the canonical definition + provenance for a surface by name within a
4510
+ * product — the absorb-drift direction of the ensure protocol.
4511
+ */
4512
+ async pull(productId, name) {
4513
+ return pullSurface(this.getClient(), productId, name);
4514
+ }
4515
+ };
4516
+
4025
4517
  // src/transform.ts
4026
4518
  function transformResponse(data) {
4027
4519
  return data;
@@ -4469,6 +4961,35 @@ var Runtype = class {
4469
4961
  static get products() {
4470
4962
  return new ProductsNamespace(() => this.getClient());
4471
4963
  }
4964
+ /**
4965
+ * Config-as-code operations for product surfaces. `surfaces.ensure` is the
4966
+ * deploy-time, non-executing converge (create-or-update a surface by name
4967
+ * within a product); `surfaces.pull` is the absorb-drift direction.
4968
+ *
4969
+ * @example
4970
+ * ```typescript
4971
+ * import { Runtype, defineSurface } from '@runtypelabs/sdk'
4972
+ *
4973
+ * const chat = defineSurface({
4974
+ * name: 'Support Chat',
4975
+ * type: 'chat',
4976
+ * behavior: { type: 'chat', greeting: 'Hi there!' },
4977
+ * status: 'active',
4978
+ * })
4979
+ *
4980
+ * // Converge at deploy time (idempotent; one tiny probe in steady state)
4981
+ * await Runtype.surfaces.ensure('product_abc', chat)
4982
+ *
4983
+ * // CI drift gate
4984
+ * await Runtype.surfaces.ensure('product_abc', chat, { expectNoChanges: true })
4985
+ *
4986
+ * // Absorb a dashboard edit back into the repo
4987
+ * const { definition } = await Runtype.surfaces.pull('product_abc', 'Support Chat')
4988
+ * ```
4989
+ */
4990
+ static get surfaces() {
4991
+ return new SurfacesNamespace(() => this.getClient());
4992
+ }
4472
4993
  };
4473
4994
 
4474
4995
  // src/generated-tool-gate.ts
@@ -4691,8 +5212,8 @@ function buildGeneratedRuntimeToolGateOutput(proposal, options = {}) {
4691
5212
  ...decision.tool ? { tool: decision.tool } : {}
4692
5213
  };
4693
5214
  }
4694
- function attachRuntimeToolsToDispatchRequest(request4, runtimeTools, options = {}) {
4695
- const stepList = request4.flow.steps;
5215
+ function attachRuntimeToolsToDispatchRequest(request6, runtimeTools, options = {}) {
5216
+ const stepList = request6.flow.steps;
4696
5217
  if (!stepList || !Array.isArray(stepList) || stepList.length === 0) {
4697
5218
  throw new Error("Cannot attach runtime tools: dispatch request must include flow.steps");
4698
5219
  }
@@ -4735,9 +5256,9 @@ function attachRuntimeToolsToDispatchRequest(request4, runtimeTools, options = {
4735
5256
  }
4736
5257
  };
4737
5258
  return {
4738
- ...request4,
5259
+ ...request6,
4739
5260
  flow: {
4740
- ...request4.flow,
5261
+ ...request6.flow,
4741
5262
  // `clonedSteps` is a structural clone of `request.flow.steps` (already
4742
5263
  // `FlowStepDefinition[]`); only the prompt step's `config.tools` was
4743
5264
  // merged, so every step's `type` discriminant is preserved. The clone is
@@ -4747,12 +5268,12 @@ function attachRuntimeToolsToDispatchRequest(request4, runtimeTools, options = {
4747
5268
  }
4748
5269
  };
4749
5270
  }
4750
- function applyGeneratedRuntimeToolProposalToDispatchRequest(request4, proposal, options = {}) {
5271
+ function applyGeneratedRuntimeToolProposalToDispatchRequest(request6, proposal, options = {}) {
4751
5272
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options.gate);
4752
5273
  if (!decision.approved || !decision.tool) {
4753
- return { decision, request: request4 };
5274
+ return { decision, request: request6 };
4754
5275
  }
4755
- const nextRequest = attachRuntimeToolsToDispatchRequest(request4, [decision.tool], options.attach);
5276
+ const nextRequest = attachRuntimeToolsToDispatchRequest(request6, [decision.tool], options.attach);
4756
5277
  return {
4757
5278
  decision,
4758
5279
  request: nextRequest
@@ -7002,15 +7523,15 @@ var DispatchEndpoint = class {
7002
7523
  * Attach approved runtime tools to a prompt step in a redispatch request.
7003
7524
  * Returns a new request object and does not mutate the original.
7004
7525
  */
7005
- attachApprovedRuntimeTools(request4, runtimeTools, options) {
7006
- return attachRuntimeToolsToDispatchRequest(request4, runtimeTools, options);
7526
+ attachApprovedRuntimeTools(request6, runtimeTools, options) {
7527
+ return attachRuntimeToolsToDispatchRequest(request6, runtimeTools, options);
7007
7528
  }
7008
7529
  /**
7009
7530
  * Validate a generated runtime tool proposal and attach it to the redispatch
7010
7531
  * request if approved, in one call.
7011
7532
  */
7012
- applyGeneratedRuntimeToolProposal(request4, proposal, options) {
7013
- return applyGeneratedRuntimeToolProposalToDispatchRequest(request4, proposal, options);
7533
+ applyGeneratedRuntimeToolProposal(request6, proposal, options) {
7534
+ return applyGeneratedRuntimeToolProposalToDispatchRequest(request6, proposal, options);
7014
7535
  }
7015
7536
  };
7016
7537
  var ChatEndpoint = class {
@@ -7562,8 +8083,8 @@ var GENERATED_RUNTIME_TOOL_PROPOSAL_SCHEMA = {
7562
8083
  },
7563
8084
  required: ["name", "description", "toolType", "parametersSchema", "config"]
7564
8085
  };
7565
- function appendRuntimeToolsToAgentRequest(request4, runtimeTools) {
7566
- const existing = request4.tools?.runtimeTools || [];
8086
+ function appendRuntimeToolsToAgentRequest(request6, runtimeTools) {
8087
+ const existing = request6.tools?.runtimeTools || [];
7567
8088
  const existingNames = new Set(existing.map((tool) => tool.name));
7568
8089
  const converted = runtimeTools.filter((tool) => !existingNames.has(tool.name)).map((tool) => ({
7569
8090
  name: tool.name,
@@ -7573,9 +8094,9 @@ function appendRuntimeToolsToAgentRequest(request4, runtimeTools) {
7573
8094
  ...tool.config ? { config: tool.config } : {}
7574
8095
  }));
7575
8096
  return {
7576
- ...request4,
8097
+ ...request6,
7577
8098
  tools: {
7578
- ...request4.tools,
8099
+ ...request6.tools,
7579
8100
  runtimeTools: [...existing, ...converted]
7580
8101
  }
7581
8102
  };
@@ -7651,21 +8172,21 @@ var _AgentsEndpoint = class _AgentsEndpoint {
7651
8172
  * Attach approved runtime tools to an agent execute request.
7652
8173
  * Returns a new request object and does not mutate the original.
7653
8174
  */
7654
- attachApprovedRuntimeTools(request4, runtimeTools) {
7655
- return appendRuntimeToolsToAgentRequest(request4, runtimeTools);
8175
+ attachApprovedRuntimeTools(request6, runtimeTools) {
8176
+ return appendRuntimeToolsToAgentRequest(request6, runtimeTools);
7656
8177
  }
7657
8178
  /**
7658
8179
  * Validate a generated runtime tool proposal and append it to an agent execute
7659
8180
  * request if approved, in one call.
7660
8181
  */
7661
- applyGeneratedRuntimeToolProposal(request4, proposal, options) {
8182
+ applyGeneratedRuntimeToolProposal(request6, proposal, options) {
7662
8183
  const decision = evaluateGeneratedRuntimeToolProposal(proposal, options);
7663
8184
  if (!decision.approved || !decision.tool) {
7664
- return { decision, request: request4 };
8185
+ return { decision, request: request6 };
7665
8186
  }
7666
8187
  return {
7667
8188
  decision,
7668
- request: appendRuntimeToolsToAgentRequest(request4, [decision.tool])
8189
+ request: appendRuntimeToolsToAgentRequest(request6, [decision.tool])
7669
8190
  };
7670
8191
  }
7671
8192
  /**
@@ -10766,6 +11287,29 @@ var BillingEndpoint = class {
10766
11287
  return this.client.get("/billing/spend-analytics", params);
10767
11288
  }
10768
11289
  };
11290
+ var ToolApprovalGrantsEndpoint = class {
11291
+ constructor(client) {
11292
+ this.client = client;
11293
+ }
11294
+ /**
11295
+ * List active remembered tool-approval grants for the authenticated owner,
11296
+ * optionally filtered to a single agent.
11297
+ */
11298
+ async list(agentId) {
11299
+ const query = agentId ? `?agentId=${encodeURIComponent(agentId)}` : "";
11300
+ const response = await this.client.get(
11301
+ `/tool-approval-grants${query}`
11302
+ );
11303
+ return response.data;
11304
+ }
11305
+ /**
11306
+ * Revoke (soft-delete) a remembered grant so the tool prompts for approval
11307
+ * again on future dispatches.
11308
+ */
11309
+ async revoke(id) {
11310
+ return this.client.delete(`/tool-approval-grants/${id}`);
11311
+ }
11312
+ };
10769
11313
  var AppsEndpoint = class {
10770
11314
  constructor(client) {
10771
11315
  this.client = client;
@@ -10863,6 +11407,7 @@ var RuntypeClient2 = class {
10863
11407
  this.flowVersions = new FlowVersionsEndpoint(this);
10864
11408
  this.integrations = new IntegrationsEndpoint(this);
10865
11409
  this.billing = new BillingEndpoint(this);
11410
+ this.toolApprovalGrants = new ToolApprovalGrantsEndpoint(this);
10866
11411
  }
10867
11412
  /**
10868
11413
  * Set the API key for authentication
@@ -10901,7 +11446,7 @@ var RuntypeClient2 = class {
10901
11446
  clearApiKey() {
10902
11447
  delete this.headers.Authorization;
10903
11448
  }
10904
- async runWithLocalTools(request4, localTools, arg3, arg4) {
11449
+ async runWithLocalTools(request6, localTools, arg3, arg4) {
10905
11450
  const isOptionsObject = (val) => typeof val === "object" && val !== null && "scope" in val;
10906
11451
  const callbacks = isOptionsObject(arg3) ? void 0 : arg3;
10907
11452
  const options = (isOptionsObject(arg3) ? arg3 : arg4) ?? {};
@@ -10915,12 +11460,12 @@ var RuntypeClient2 = class {
10915
11460
  ...entry.pageOrigin ? { pageOrigin: entry.pageOrigin } : {}
10916
11461
  })) : [];
10917
11462
  const modifiedRequest = {
10918
- ...request4,
11463
+ ...request6,
10919
11464
  ...derivedClientTools.length > 0 ? {
10920
- clientTools: [...request4.clientTools ?? [], ...derivedClientTools]
11465
+ clientTools: [...request6.clientTools ?? [], ...derivedClientTools]
10921
11466
  } : {},
10922
11467
  options: {
10923
- ...request4.options || {},
11468
+ ...request6.options || {},
10924
11469
  streamResponse: isStreaming
10925
11470
  }
10926
11471
  };
@@ -11358,20 +11903,20 @@ var BatchBuilder = class {
11358
11903
  if (!this.recordType) {
11359
11904
  throw new Error("BatchBuilder: recordType is required. Call .forRecordType(type) first.");
11360
11905
  }
11361
- const request4 = {
11906
+ const request6 = {
11362
11907
  flowId: this.flowId,
11363
11908
  recordType: this.recordType
11364
11909
  };
11365
11910
  if (Object.keys(this.batchOptions).length > 0) {
11366
- request4.options = this.batchOptions;
11911
+ request6.options = this.batchOptions;
11367
11912
  }
11368
11913
  if (this.filterConfig) {
11369
- request4.filter = this.filterConfig;
11914
+ request6.filter = this.filterConfig;
11370
11915
  }
11371
11916
  if (this.limitConfig !== void 0) {
11372
- request4.limit = this.limitConfig;
11917
+ request6.limit = this.limitConfig;
11373
11918
  }
11374
- return request4;
11919
+ return request6;
11375
11920
  }
11376
11921
  /**
11377
11922
  * Execute the batch operation
@@ -11528,32 +12073,32 @@ var EvalBuilder = class {
11528
12073
  "EvalBuilder: records are required. Call .forRecordType(type) or .withRecords([...]) first."
11529
12074
  );
11530
12075
  }
11531
- const request4 = {};
12076
+ const request6 = {};
11532
12077
  if (this.flowId) {
11533
- request4.flowId = this.flowId;
12078
+ request6.flowId = this.flowId;
11534
12079
  } else if (this.virtualFlow) {
11535
- request4.flow = this.virtualFlow;
12080
+ request6.flow = this.virtualFlow;
11536
12081
  }
11537
12082
  if (this.recordType) {
11538
- request4.recordType = this.recordType;
12083
+ request6.recordType = this.recordType;
11539
12084
  } else if (this.inlineRecords) {
11540
- request4.records = this.inlineRecords;
12085
+ request6.records = this.inlineRecords;
11541
12086
  }
11542
12087
  if (this.modelOverrides) {
11543
- request4.modelOverrides = this.modelOverrides;
12088
+ request6.modelOverrides = this.modelOverrides;
11544
12089
  } else if (this.modelConfigs) {
11545
- request4.modelConfigs = this.modelConfigs;
12090
+ request6.modelConfigs = this.modelConfigs;
11546
12091
  }
11547
12092
  if (Object.keys(this.evalOptions).length > 0) {
11548
- request4.options = this.evalOptions;
12093
+ request6.options = this.evalOptions;
11549
12094
  }
11550
12095
  if (this.filterConfig) {
11551
- request4.filter = this.filterConfig;
12096
+ request6.filter = this.filterConfig;
11552
12097
  }
11553
12098
  if (this.limitConfig !== void 0) {
11554
- request4.limit = this.limitConfig;
12099
+ request6.limit = this.limitConfig;
11555
12100
  }
11556
- return request4;
12101
+ return request6;
11557
12102
  }
11558
12103
  /**
11559
12104
  * Execute the evaluation
@@ -12072,9 +12617,15 @@ export {
12072
12617
  STEP_TYPE_TO_METHOD,
12073
12618
  SchedulesEndpoint,
12074
12619
  SecretsEndpoint,
12620
+ SkillDriftError,
12621
+ SkillEnsureConflictError,
12075
12622
  SkillProposalsNamespace,
12076
12623
  SkillsNamespace,
12624
+ SurfaceDriftError,
12625
+ SurfaceEnsureConflictError,
12077
12626
  SurfacesEndpoint,
12627
+ SurfacesNamespace,
12628
+ ToolApprovalGrantsEndpoint,
12078
12629
  ToolDriftError,
12079
12630
  ToolEnsureConflictError,
12080
12631
  ToolsEndpoint,
@@ -12091,6 +12642,8 @@ export {
12091
12642
  computeAgentContentHash,
12092
12643
  computeFlowContentHash,
12093
12644
  computeProductContentHash,
12645
+ computeSkillContentHash,
12646
+ computeSurfaceContentHash,
12094
12647
  computeToolContentHash,
12095
12648
  createClient,
12096
12649
  createExternalTool,
@@ -12100,6 +12653,8 @@ export {
12100
12653
  defineFlow,
12101
12654
  definePlaybook,
12102
12655
  defineProduct,
12656
+ defineSkill,
12657
+ defineSurface,
12103
12658
  defineTool,
12104
12659
  deployWorkflow,
12105
12660
  ensureDefaultWorkflowHooks,
@@ -12117,6 +12672,8 @@ export {
12117
12672
  normalizeAgentDefinition,
12118
12673
  normalizeCandidatePath,
12119
12674
  normalizeProductDefinition,
12675
+ normalizeSkillDefinition,
12676
+ normalizeSurfaceDefinition,
12120
12677
  normalizeToolDefinition,
12121
12678
  parseFinalBuffer,
12122
12679
  parseLedgerArtifactRelativePath,