@runtypelabs/sdk 10.8.0 → 10.9.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
@@ -2113,7 +2113,7 @@ function asRecord(value) {
2113
2113
  * Reading the structured fields FIRST is what makes the typed alias errors fire
2114
2114
  * for `createClient()` and the CLI, not just for `Runtype.agents`.
2115
2115
  */
2116
- function parseRequestError$6(err) {
2116
+ function parseRequestError$7(err) {
2117
2117
  if (!(err instanceof Error)) return {
2118
2118
  status: null,
2119
2119
  body: null
@@ -2148,7 +2148,7 @@ function asString$1(value, fallback) {
2148
2148
  * failure exactly as the transport threw it.
2149
2149
  */
2150
2150
  function toAliasError(err, agentId, alias) {
2151
- const { status, body } = parseRequestError$6(err);
2151
+ const { status, body } = parseRequestError$7(err);
2152
2152
  if (status === null) return null;
2153
2153
  const error = body && typeof body.error === "string" ? body.error : void 0;
2154
2154
  if (status === 412 && body && typeof body.expected === "number") return new AgentAliasRevisionMismatchError({
@@ -2338,6 +2338,174 @@ var AgentDeploymentsNamespace = class {
2338
2338
  }
2339
2339
  };
2340
2340
  //#endregion
2341
+ //#region src/content-hash.ts
2342
+ /**
2343
+ * Canonical content-hash primitives shared by the `*.ensure()` config-as-code
2344
+ * surfaces (products, tools, skills, surfaces, products-ensure-fpo). Each of
2345
+ * those modules computes a SHA-256 content hash for its hash-first `/ensure`
2346
+ * wire protocol (see docs/adr/0003-agent-config-as-code-ensure.md) — the hash
2347
+ * output for any given definition is a WIRE CONTRACT with the server, so this
2348
+ * module must stay dependency-free (the SDK is published to npm and must not
2349
+ * import `@runtypelabs/shared`, a private package) and must not change its
2350
+ * normalization behavior without a matching server-side change.
2351
+ *
2352
+ * `isPlainObject` and `normalizeValue` here are mirrored from
2353
+ * `packages/shared/src/utils/*-content-hash.ts`. `normalizeValue` sorts object
2354
+ * keys and drops `null`/`undefined` entries recursively — this is the
2355
+ * canonical normalization used by products, tools, skills, surfaces, and the
2356
+ * whole-FPO hash.
2357
+ *
2358
+ * NOT every `*-ensure.ts` module uses this normalization: `flows-ensure.ts`
2359
+ * (`normalizeConfigForHash`/`normalizeStepForHash`) and `evals-ensure.ts`
2360
+ * (`normalizeForHash`) keep `null` values (they drop only `undefined`) and,
2361
+ * in the flow case, use a shallower array-recursion rule. Those are real
2362
+ * semantic divergences from this canonical normalizer, not incidental
2363
+ * duplication — changing either to reuse `normalizeValue` would alter the
2364
+ * content hash for definitions containing explicit `null`s and break the
2365
+ * wire contract with already-converged flows/evals. They stay local by
2366
+ * design; see the divergence comment at each site.
2367
+ */
2368
+ function isPlainObject$1(value) {
2369
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2370
+ }
2371
+ /**
2372
+ * Recursively normalizes a value for canonical hashing: object keys are
2373
+ * sorted, and `null`/`undefined` entries are dropped. Arrays preserve order
2374
+ * and have each item normalized in turn.
2375
+ */
2376
+ function normalizeValue$1(value) {
2377
+ if (Array.isArray(value)) return value.map((item) => normalizeValue$1(item));
2378
+ if (isPlainObject$1(value)) {
2379
+ const normalized = {};
2380
+ for (const key of Object.keys(value).sort()) {
2381
+ const entry = value[key];
2382
+ if (entry === void 0 || entry === null) continue;
2383
+ normalized[key] = normalizeValue$1(entry);
2384
+ }
2385
+ return normalized;
2386
+ }
2387
+ return value;
2388
+ }
2389
+ /** SHA-256 (hex) over an already-serialized (typically `JSON.stringify`'d) string. */
2390
+ async function sha256Hex(serialized) {
2391
+ const encoded = new TextEncoder().encode(serialized);
2392
+ const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
2393
+ return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
2394
+ }
2395
+ //#endregion
2396
+ //#region src/collections-ensure.ts
2397
+ /** Canonical normalized form of a collection definition (slug excluded — identity). */
2398
+ function normalizeRecordCollectionDefinition(definition) {
2399
+ return {
2400
+ displayName: definition.displayName,
2401
+ ...definition.description ? { description: definition.description } : {},
2402
+ ...definition.icon ? { icon: definition.icon } : {},
2403
+ ...isPlainObject$1(definition.schema) ? { schema: normalizeValue$1(definition.schema) } : {},
2404
+ validationMode: definition.validationMode || "off"
2405
+ };
2406
+ }
2407
+ /** SHA-256 (hex) over the canonical normalized collection definition. */
2408
+ async function computeRecordCollectionContentHash(definition) {
2409
+ return sha256Hex(JSON.stringify(normalizeRecordCollectionDefinition(definition)));
2410
+ }
2411
+ /** A 409 from the ensure protocol (external_modification or remote_changed). */
2412
+ var CollectionEnsureConflictError = class extends Error {
2413
+ constructor(body) {
2414
+ super(body.error ?? `Collection ensure conflict: ${body.code}`);
2415
+ this.name = "CollectionEnsureConflictError";
2416
+ this.code = body.code;
2417
+ this.lastModifiedSource = body.lastModifiedSource;
2418
+ this.modifiedAt = body.modifiedAt;
2419
+ this.currentHash = body.currentHash;
2420
+ }
2421
+ };
2422
+ /** Thrown by `ensure(def, { expectNoChanges: true })` when drift is detected. */
2423
+ var CollectionDriftError = class extends Error {
2424
+ constructor(slug, plan) {
2425
+ super(`Collection "${slug}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.collections.pull(slug) to absorb the remote edit into your repo, or re-run ensure to converge.`);
2426
+ this.name = "CollectionDriftError";
2427
+ this.plan = plan;
2428
+ }
2429
+ };
2430
+ function toConflictError$8(err) {
2431
+ if (!(err instanceof Error)) return null;
2432
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
2433
+ if (!match || match[1] !== "409") return null;
2434
+ let body;
2435
+ try {
2436
+ body = JSON.parse(match[2]);
2437
+ } catch {
2438
+ return null;
2439
+ }
2440
+ if (!isPlainObject$1(body)) return null;
2441
+ if (body.code !== "external_modification" && body.code !== "remote_changed") return null;
2442
+ return new CollectionEnsureConflictError(body);
2443
+ }
2444
+ /** Per-transport memo of `{ slug + localHash → server contentHash }`, as in tools.ensure. */
2445
+ const serverHashMemo$9 = /* @__PURE__ */ new WeakMap();
2446
+ function memoFor$9(transport) {
2447
+ let memo = serverHashMemo$9.get(transport);
2448
+ if (!memo) {
2449
+ memo = /* @__PURE__ */ new Map();
2450
+ serverHashMemo$9.set(transport, memo);
2451
+ }
2452
+ return memo;
2453
+ }
2454
+ async function request$7(transport, body) {
2455
+ try {
2456
+ return await transport.post("/collections/ensure", body);
2457
+ } catch (err) {
2458
+ throw toConflictError$8(err) ?? err;
2459
+ }
2460
+ }
2461
+ /**
2462
+ * Idempotently converge a collection definition. Probes with a content hash and
2463
+ * only ships the full definition when the server reports a miss. Never deletes
2464
+ * a collection and never touches records.
2465
+ */
2466
+ async function ensureCollection(transport, definition, options = {}) {
2467
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
2468
+ const passthrough = {
2469
+ ...onConflict ? { onConflict } : {},
2470
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
2471
+ };
2472
+ if (dryRun || expectNoChanges) {
2473
+ const plan = await request$7(transport, {
2474
+ slug: definition.slug,
2475
+ definition,
2476
+ dryRun: true,
2477
+ ...passthrough
2478
+ });
2479
+ if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
2480
+ if (expectNoChanges && plan.changes !== "none") throw new CollectionDriftError(definition.slug, plan);
2481
+ return plan;
2482
+ }
2483
+ const memo = memoFor$9(transport);
2484
+ const localHash = await computeRecordCollectionContentHash(definition);
2485
+ const memoKey = `${definition.slug} ${localHash}`;
2486
+ const probe = await request$7(transport, {
2487
+ slug: definition.slug,
2488
+ contentHash: memo.get(memoKey) ?? localHash,
2489
+ ...passthrough
2490
+ });
2491
+ if (probe.result !== "definitionRequired") {
2492
+ if (probe.result !== "plan") memo.set(memoKey, probe.contentHash);
2493
+ return probe;
2494
+ }
2495
+ const converged = await request$7(transport, {
2496
+ slug: definition.slug,
2497
+ definition,
2498
+ ...passthrough
2499
+ });
2500
+ if (converged.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-definition request");
2501
+ if (converged.result !== "plan") memo.set(memoKey, converged.contentHash);
2502
+ return converged;
2503
+ }
2504
+ /** Pull the canonical definition and provenance for a collection by slug. */
2505
+ async function pullCollection(transport, slug) {
2506
+ return transport.get(`/collections/${encodeURIComponent(slug)}/pull`);
2507
+ }
2508
+ //#endregion
2341
2509
  //#region src/generated-tool-gate.ts
2342
2510
  const TOOL_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_]{1,63}$/;
2343
2511
  const DEFAULT_MAX_CODE_LENGTH = 12e3;
@@ -2666,6 +2834,239 @@ function normalizeDispatchRequest(request) {
2666
2834
  };
2667
2835
  }
2668
2836
  //#endregion
2837
+ //#region src/schedules-ensure.ts
2838
+ function nonEmptyObject(value) {
2839
+ if (!isPlainObject$1(value)) return void 0;
2840
+ const normalized = normalizeValue$1(value);
2841
+ return Object.keys(normalized).length > 0 ? normalized : void 0;
2842
+ }
2843
+ function nonEmptyString(value) {
2844
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2845
+ }
2846
+ function canonicalRunAt(value) {
2847
+ if (value === null || value === void 0 || value === "") return void 0;
2848
+ const date = value instanceof Date ? value : new Date(value);
2849
+ return Number.isNaN(date.getTime()) ? String(value) : date.toISOString();
2850
+ }
2851
+ /** Canonical normalized schedule content (name and target excluded — identity). */
2852
+ function normalizeScheduleContent(definition) {
2853
+ const cron = nonEmptyString(definition.cron);
2854
+ const timezone = nonEmptyString(definition.timezone);
2855
+ const runAt = canonicalRunAt(definition.runAt);
2856
+ const recordIds = Array.isArray(definition.recordIds) && definition.recordIds.length > 0 ? [...definition.recordIds] : void 0;
2857
+ const recordType = nonEmptyString(definition.recordType);
2858
+ const recordFilter = nonEmptyObject(definition.recordFilter);
2859
+ const messages = Array.isArray(definition.messages) && definition.messages.length > 0 ? normalizeValue$1(definition.messages) : void 0;
2860
+ const inputs = nonEmptyObject(definition.inputs);
2861
+ const executionOptions = nonEmptyObject(definition.executionOptions);
2862
+ return {
2863
+ triggerType: definition.triggerType === "one_time" ? "one_time" : "recurring",
2864
+ ...cron !== void 0 ? { cron } : {},
2865
+ ...timezone !== void 0 ? { timezone } : {},
2866
+ ...runAt !== void 0 ? { runAt } : {},
2867
+ ...recordIds !== void 0 ? { recordIds } : {},
2868
+ ...recordType !== void 0 ? { recordType } : {},
2869
+ ...recordFilter !== void 0 ? { recordFilter } : {},
2870
+ ...messages !== void 0 ? { messages } : {},
2871
+ ...inputs !== void 0 ? { inputs } : {},
2872
+ ...executionOptions !== void 0 ? { executionOptions } : {},
2873
+ ...typeof definition.enabled === "boolean" ? { enabled: definition.enabled } : {}
2874
+ };
2875
+ }
2876
+ /** SHA-256 (hex) over canonical schedule content, in the flat content shape. */
2877
+ async function computeScheduleContentHash(definition) {
2878
+ return sha256Hex(JSON.stringify(normalizeScheduleContent(definition)));
2879
+ }
2880
+ /** The content hash of a `defineSchedule` definition. */
2881
+ async function computeScheduleDefinitionHash(definition) {
2882
+ return computeScheduleContentHash({
2883
+ triggerType: definition.trigger.type,
2884
+ cron: definition.trigger.cron,
2885
+ timezone: definition.trigger.timezone,
2886
+ runAt: definition.trigger.runAt,
2887
+ recordIds: definition.recordIds,
2888
+ recordType: definition.recordType,
2889
+ recordFilter: definition.recordFilter,
2890
+ messages: definition.messages,
2891
+ inputs: definition.inputs,
2892
+ executionOptions: definition.executionOptions,
2893
+ enabled: definition.enabled
2894
+ });
2895
+ }
2896
+ const TARGET_KEYS = [
2897
+ "flowId",
2898
+ "agentId",
2899
+ "flowName",
2900
+ "agentName"
2901
+ ];
2902
+ const DEFINE_SCHEDULE_KEYS = /* @__PURE__ */ new Set([
2903
+ "name",
2904
+ "target",
2905
+ "trigger",
2906
+ "recordIds",
2907
+ "recordType",
2908
+ "recordFilter",
2909
+ "messages",
2910
+ "inputs",
2911
+ "executionOptions",
2912
+ "enabled"
2913
+ ]);
2914
+ /**
2915
+ * Pure-local declarative constructor for a standalone schedule. No I/O.
2916
+ * Validates structure; cron, timezone, record filter and plan limits are
2917
+ * checked server-side at ensure time.
2918
+ *
2919
+ * @example
2920
+ * ```typescript
2921
+ * const nightly = defineSchedule({
2922
+ * name: 'Nightly digest',
2923
+ * target: { flowName: 'Digest' },
2924
+ * trigger: { type: 'recurring', cron: '0 2 * * *', timezone: 'UTC' },
2925
+ * })
2926
+ * ```
2927
+ */
2928
+ function defineSchedule(input) {
2929
+ if (!input || typeof input !== "object") throw new Error("defineSchedule requires a definition object");
2930
+ if (typeof input.name !== "string" || input.name.length === 0) throw new Error("defineSchedule requires a non-empty string \"name\"");
2931
+ if (!isPlainObject$1(input.target)) throw new Error("defineSchedule requires a \"target\" object");
2932
+ const target = input.target;
2933
+ const setKeys = TARGET_KEYS.filter((key) => target[key] !== void 0);
2934
+ const extraKeys = Object.keys(target).filter((key) => !TARGET_KEYS.includes(key));
2935
+ if (setKeys.length !== 1 || extraKeys.length > 0) throw new Error("defineSchedule \"target\" needs exactly one of flowId, agentId, flowName, agentName");
2936
+ const trigger = input.trigger;
2937
+ if (!isPlainObject$1(trigger) || trigger.type !== "recurring" && trigger.type !== "one_time") throw new Error("defineSchedule requires \"trigger.type\" to be \"recurring\" or \"one_time\"");
2938
+ if (trigger.type === "recurring" && (!trigger.cron || !trigger.timezone)) throw new Error("defineSchedule: recurring triggers require \"cron\" and \"timezone\"");
2939
+ if (trigger.type === "one_time" && !trigger.runAt) throw new Error("defineSchedule: one_time triggers require \"runAt\"");
2940
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_SCHEDULE_KEYS.has(key));
2941
+ if (unknownKeys.length > 0) throw new Error(`defineSchedule: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are ${[...DEFINE_SCHEDULE_KEYS].join(", ")}.`);
2942
+ return { ...input };
2943
+ }
2944
+ /** A 409 from the ensure protocol (external_modification or remote_changed). */
2945
+ var ScheduleEnsureConflictError = class extends Error {
2946
+ constructor(body) {
2947
+ super(body.error ?? `Schedule ensure conflict: ${body.code}`);
2948
+ this.name = "ScheduleEnsureConflictError";
2949
+ this.code = body.code;
2950
+ this.lastModifiedSource = body.lastModifiedSource;
2951
+ this.modifiedAt = body.modifiedAt;
2952
+ this.currentHash = body.currentHash;
2953
+ }
2954
+ };
2955
+ /** Thrown by `ensure(def, { expectNoChanges: true })` when drift is detected. */
2956
+ var ScheduleDriftError = class extends Error {
2957
+ constructor(plan) {
2958
+ super(`Schedule "${plan.scheduleId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run schedules.pull(name, target) to absorb the remote edit into your repo, or re-run ensure to converge.`);
2959
+ this.name = "ScheduleDriftError";
2960
+ this.plan = plan;
2961
+ }
2962
+ };
2963
+ function errorStatusAndBody(err) {
2964
+ if (!(err instanceof Error)) return {
2965
+ status: null,
2966
+ body: null
2967
+ };
2968
+ const typed = err;
2969
+ if (typeof typed.statusCode === "number") return {
2970
+ status: typed.statusCode,
2971
+ body: typed.data
2972
+ };
2973
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
2974
+ if (!match) return {
2975
+ status: null,
2976
+ body: null
2977
+ };
2978
+ try {
2979
+ return {
2980
+ status: Number(match[1]),
2981
+ body: JSON.parse(match[2])
2982
+ };
2983
+ } catch {
2984
+ return {
2985
+ status: Number(match[1]),
2986
+ body: null
2987
+ };
2988
+ }
2989
+ }
2990
+ function toConflictError$7(err) {
2991
+ const { status, body } = errorStatusAndBody(err);
2992
+ if (status !== 409 || !isPlainObject$1(body)) return null;
2993
+ if (body.code !== "external_modification" && body.code !== "remote_changed") return null;
2994
+ return new ScheduleEnsureConflictError(body);
2995
+ }
2996
+ const serverHashMemo$8 = /* @__PURE__ */ new WeakMap();
2997
+ function memoFor$8(client) {
2998
+ let memo = serverHashMemo$8.get(client);
2999
+ if (!memo) {
3000
+ memo = /* @__PURE__ */ new Map();
3001
+ serverHashMemo$8.set(client, memo);
3002
+ }
3003
+ return memo;
3004
+ }
3005
+ async function request$6(client, body) {
3006
+ try {
3007
+ return await client.post("/schedules/ensure", body);
3008
+ } catch (err) {
3009
+ const conflict = toConflictError$7(err);
3010
+ if (conflict) throw conflict;
3011
+ throw err;
3012
+ }
3013
+ }
3014
+ /**
3015
+ * Idempotently converge a standalone schedule. Hash-first: probes with a
3016
+ * content hash and ships the full definition only on a miss. Never deletes.
3017
+ */
3018
+ async function ensureSchedule(client, definition, options = {}) {
3019
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
3020
+ const identity = {
3021
+ name: definition.name,
3022
+ target: definition.target
3023
+ };
3024
+ const passthrough = {
3025
+ ...onConflict ? { onConflict } : {},
3026
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
3027
+ };
3028
+ if (dryRun || expectNoChanges) {
3029
+ const plan = await request$6(client, {
3030
+ ...identity,
3031
+ definition,
3032
+ dryRun: true,
3033
+ ...passthrough
3034
+ });
3035
+ if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
3036
+ if (expectNoChanges && plan.changes !== "none") throw new ScheduleDriftError(plan);
3037
+ return plan;
3038
+ }
3039
+ const memo = memoFor$8(client);
3040
+ const localHash = await computeScheduleDefinitionHash(definition);
3041
+ const memoKey = `${JSON.stringify(identity)} ${localHash}`;
3042
+ if (!expectedRemoteHash) {
3043
+ const probe = await request$6(client, {
3044
+ ...identity,
3045
+ contentHash: memo.get(memoKey) ?? localHash,
3046
+ ...passthrough
3047
+ });
3048
+ if (probe.result !== "definitionRequired") {
3049
+ if (probe.result !== "plan") memo.set(memoKey, probe.contentHash);
3050
+ return probe;
3051
+ }
3052
+ }
3053
+ const converged = await request$6(client, {
3054
+ ...identity,
3055
+ definition,
3056
+ ...passthrough
3057
+ });
3058
+ if (converged.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-definition request");
3059
+ if (converged.result !== "plan") memo.set(memoKey, converged.contentHash);
3060
+ return converged;
3061
+ }
3062
+ /** Pull the canonical definition + provenance for a schedule by name and target. */
3063
+ async function pullSchedule(client, name, target) {
3064
+ return client.get("/schedules/pull", {
3065
+ name,
3066
+ ...target
3067
+ });
3068
+ }
3069
+ //#endregion
2669
3070
  //#region src/offload-markers.ts
2670
3071
  const LEDGER_ARTIFACT_LINE_PREFIX = "Ledger artifact: ";
2671
3072
  const SPILL_MARKER_PATTERN = /\[Output saved to (art_[A-Za-z0-9_-]+) \(([\d,]+) chars?\); read_offloaded_output to view\]/;
@@ -4639,6 +5040,28 @@ var CollectionsEndpoint = class {
4639
5040
  async typegen() {
4640
5041
  return this.client.get("/collections/types.d.ts");
4641
5042
  }
5043
+ /**
5044
+ * Idempotently converge a repo-declared collection (display fields, schema,
5045
+ * validationMode) by slug. Hash-first; `dryRun` / `expectNoChanges` plan
5046
+ * without writing. Never deletes a collection or touches records.
5047
+ *
5048
+ * @example
5049
+ * ```typescript
5050
+ * await client.collections.ensure({
5051
+ * slug: 'customers',
5052
+ * displayName: 'Customers',
5053
+ * schema: { type: 'object', properties: { status: { type: 'string' } } },
5054
+ * validationMode: 'warn',
5055
+ * })
5056
+ * ```
5057
+ */
5058
+ async ensure(definition, options) {
5059
+ return ensureCollection(this.client, definition, options);
5060
+ }
5061
+ /** Pull the canonical definition and provenance for a collection — the absorb-drift direction. */
5062
+ async pull(slug) {
5063
+ return pullCollection(this.client, slug);
5064
+ }
4642
5065
  };
4643
5066
  /**
4644
5067
  * Agent API key REQUESTS — a device-authorization-style grant.
@@ -8171,6 +8594,21 @@ var SchedulesEndpoint = class {
8171
8594
  return this.client.post("/schedules", data);
8172
8595
  }
8173
8596
  /**
8597
+ * Idempotently converge a `defineSchedule` definition on a standalone flow or
8598
+ * agent. Identity is name + target; the steady state is one hash probe. Never
8599
+ * deletes, and an omitted `enabled` leaves a paused schedule paused.
8600
+ */
8601
+ async ensure(definition, options = {}) {
8602
+ return ensureSchedule(this.client, definition, options);
8603
+ }
8604
+ /**
8605
+ * Pull the canonical definition + provenance for a schedule by name and
8606
+ * target — the absorb-drift direction of `ensure`.
8607
+ */
8608
+ async pull(name, target) {
8609
+ return pullSchedule(this.client, name, target);
8610
+ }
8611
+ /**
8174
8612
  * Update a schedule (partial update supported).
8175
8613
  */
8176
8614
  async update(id, data) {
@@ -8559,6 +8997,17 @@ var ToolApprovalGrantsEndpoint = class {
8559
8997
  };
8560
8998
  //#endregion
8561
8999
  //#region src/detached-buffered-follow.ts
9000
+ /** The release fields a response body carries, dropping any it does not name. */
9001
+ function releaseFieldsOf(data) {
9002
+ if (typeof data !== "object" || data === null) return {};
9003
+ const record = data;
9004
+ const resolution = record.agentTargetResolution;
9005
+ return {
9006
+ ...typeof record.agentVersionId === "string" ? { agentVersionId: record.agentVersionId } : {},
9007
+ ...typeof record.alias === "string" ? { alias: record.alias } : {},
9008
+ ...resolution === "alias" || resolution === "version" || resolution === "legacy-live-row" ? { agentTargetResolution: resolution } : {}
9009
+ };
9010
+ }
8562
9011
  function isDetachedToolOutputAck(data) {
8563
9012
  if (typeof data !== "object" || data === null) return false;
8564
9013
  const record = data;
@@ -8586,9 +9035,12 @@ function isLocalToolAwait(event) {
8586
9035
  * buffered continuation envelope a non-streaming local-tool loop expects.
8587
9036
  *
8588
9037
  * `after` is the last `seq` the loop had already consumed, so the replay
8589
- * never re-delivers the await the outputs just answered.
9038
+ * never re-delivers the await the outputs just answered. Version fields from
9039
+ * earlier responses (`carried`) and the ack land on the envelope, since the
9040
+ * events stream never repeats them.
8590
9041
  */
8591
- async function followDetachedResume(client, executionId, after) {
9042
+ async function followDetachedResume(client, ack, after, carried = {}) {
9043
+ const { executionId } = ack;
8592
9044
  const open = (cursor, signal) => client.requestStream(buildExecutionEventsPath(executionId, cursor ? { after: cursor } : {}), {
8593
9045
  method: "GET",
8594
9046
  ...signal ? { signal } : {}
@@ -8646,65 +9098,12 @@ async function followDetachedResume(client, executionId, after) {
8646
9098
  executionId,
8647
9099
  events,
8648
9100
  status,
8649
- ...error ? { error } : {}
9101
+ ...error ? { error } : {},
9102
+ ...carried,
9103
+ ...releaseFieldsOf(ack)
8650
9104
  };
8651
9105
  }
8652
9106
  //#endregion
8653
- //#region src/content-hash.ts
8654
- /**
8655
- * Canonical content-hash primitives shared by the `*.ensure()` config-as-code
8656
- * surfaces (products, tools, skills, surfaces, products-ensure-fpo). Each of
8657
- * those modules computes a SHA-256 content hash for its hash-first `/ensure`
8658
- * wire protocol (see docs/adr/0003-agent-config-as-code-ensure.md) — the hash
8659
- * output for any given definition is a WIRE CONTRACT with the server, so this
8660
- * module must stay dependency-free (the SDK is published to npm and must not
8661
- * import `@runtypelabs/shared`, a private package) and must not change its
8662
- * normalization behavior without a matching server-side change.
8663
- *
8664
- * `isPlainObject` and `normalizeValue` here are mirrored from
8665
- * `packages/shared/src/utils/*-content-hash.ts`. `normalizeValue` sorts object
8666
- * keys and drops `null`/`undefined` entries recursively — this is the
8667
- * canonical normalization used by products, tools, skills, surfaces, and the
8668
- * whole-FPO hash.
8669
- *
8670
- * NOT every `*-ensure.ts` module uses this normalization: `flows-ensure.ts`
8671
- * (`normalizeConfigForHash`/`normalizeStepForHash`) and `evals-ensure.ts`
8672
- * (`normalizeForHash`) keep `null` values (they drop only `undefined`) and,
8673
- * in the flow case, use a shallower array-recursion rule. Those are real
8674
- * semantic divergences from this canonical normalizer, not incidental
8675
- * duplication — changing either to reuse `normalizeValue` would alter the
8676
- * content hash for definitions containing explicit `null`s and break the
8677
- * wire contract with already-converged flows/evals. They stay local by
8678
- * design; see the divergence comment at each site.
8679
- */
8680
- function isPlainObject$1(value) {
8681
- return value !== null && typeof value === "object" && !Array.isArray(value);
8682
- }
8683
- /**
8684
- * Recursively normalizes a value for canonical hashing: object keys are
8685
- * sorted, and `null`/`undefined` entries are dropped. Arrays preserve order
8686
- * and have each item normalized in turn.
8687
- */
8688
- function normalizeValue$1(value) {
8689
- if (Array.isArray(value)) return value.map((item) => normalizeValue$1(item));
8690
- if (isPlainObject$1(value)) {
8691
- const normalized = {};
8692
- for (const key of Object.keys(value).sort()) {
8693
- const entry = value[key];
8694
- if (entry === void 0 || entry === null) continue;
8695
- normalized[key] = normalizeValue$1(entry);
8696
- }
8697
- return normalized;
8698
- }
8699
- return value;
8700
- }
8701
- /** SHA-256 (hex) over an already-serialized (typically `JSON.stringify`'d) string. */
8702
- async function sha256Hex(serialized) {
8703
- const encoded = new TextEncoder().encode(serialized);
8704
- const hashBuffer = await crypto.subtle.digest("SHA-256", encoded);
8705
- return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
8706
- }
8707
- //#endregion
8708
9107
  //#region src/evals-ensure.ts
8709
9108
  const CHECK_GRADER_KINDS = /* @__PURE__ */ new Set([
8710
9109
  "contains",
@@ -9098,6 +9497,25 @@ async function computeEvalContentHash(definition) {
9098
9497
  };
9099
9498
  return sha256Hex(JSON.stringify(canonical));
9100
9499
  }
9500
+ /** A 409 from the eval ensure protocol (external_modification or remote_changed). */
9501
+ var EvalEnsureConflictError = class extends Error {
9502
+ constructor(body) {
9503
+ super(body.error ?? `Eval ensure conflict: ${body.code}`);
9504
+ this.name = "EvalEnsureConflictError";
9505
+ this.code = body.code;
9506
+ this.lastModifiedSource = body.lastModifiedSource;
9507
+ this.modifiedAt = body.modifiedAt;
9508
+ this.currentHash = body.currentHash;
9509
+ }
9510
+ };
9511
+ /** Thrown by `ensure(def, { expectNoChanges: true })` when drift is detected. */
9512
+ var EvalDriftError = class extends Error {
9513
+ constructor(plan) {
9514
+ super(`Eval suite "${plan.suiteId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.evals.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`);
9515
+ this.name = "EvalDriftError";
9516
+ this.plan = plan;
9517
+ }
9518
+ };
9101
9519
  /**
9102
9520
  * Per-client memo of `{ name + localHash → server contentHash }` so a hot
9103
9521
  * process pays the full-definition round trip once per boot (steady state
@@ -9105,44 +9523,75 @@ async function computeEvalContentHash(definition) {
9105
9523
  * hash, so a definition that changes within the process can never false-match a
9106
9524
  * stale server hash. Mirrors the flow ensure memo.
9107
9525
  */
9108
- const serverHashMemo$6 = /* @__PURE__ */ new WeakMap();
9109
- function memoFor$6(client) {
9110
- let memo = serverHashMemo$6.get(client);
9526
+ const serverHashMemo$7 = /* @__PURE__ */ new WeakMap();
9527
+ function memoFor$7(client) {
9528
+ let memo = serverHashMemo$7.get(client);
9111
9529
  if (!memo) {
9112
9530
  memo = /* @__PURE__ */ new Map();
9113
- serverHashMemo$6.set(client, memo);
9531
+ serverHashMemo$7.set(client, memo);
9114
9532
  }
9115
9533
  return memo;
9116
9534
  }
9117
- /**
9118
- * Idempotently converge an eval suite definition onto the platform. Hash-first:
9119
- * probes with a content hash, and only ships the full definition when the
9120
- * server reports a miss (`definitionRequired`). Upserts the suite + replaces
9121
- * its cases; never executes the eval (use the `runtype eval` CLI / `/eval`
9122
- * submit surface to run it).
9123
- *
9124
- * `virtual: true` definitions are ephemeral and have nothing durable to
9125
- * converge — ensure rejects them. Run a virtual eval directly instead.
9126
- */
9127
- async function ensureEval(client, definition) {
9535
+ function toEvalConflictError(err) {
9536
+ if (!(err instanceof Error)) return null;
9537
+ const match = err.message.match(/^API request failed: 409 .*? - ([\s\S]*)$/);
9538
+ if (!match) return null;
9539
+ let body;
9540
+ try {
9541
+ body = JSON.parse(match[1]);
9542
+ } catch {
9543
+ return null;
9544
+ }
9545
+ if (!isPlainObject$1(body)) return null;
9546
+ if (body.code !== "external_modification" && body.code !== "remote_changed") return null;
9547
+ return new EvalEnsureConflictError(body);
9548
+ }
9549
+ async function postEvalEnsure(client, body) {
9550
+ try {
9551
+ return await client.post("/eval/ensure", body);
9552
+ } catch (err) {
9553
+ throw toEvalConflictError(err) ?? err;
9554
+ }
9555
+ }
9556
+ async function ensureEval(client, definition, options = {}) {
9128
9557
  if (definition.virtual) throw new Error("Cannot ensure a virtual eval: virtual evals are ephemeral (nothing is persisted to converge). Remove `virtual: true` to converge a durable suite, or run it directly.");
9129
- const memo = memoFor$6(client);
9558
+ const { dryRun, onConflict, expectedRemoteHash, expectNoChanges } = options;
9559
+ const passthrough = {
9560
+ ...onConflict ? { onConflict } : {},
9561
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
9562
+ };
9563
+ if (dryRun || expectNoChanges) {
9564
+ const plan = await postEvalEnsure(client, {
9565
+ name: definition.name,
9566
+ definition,
9567
+ dryRun: true,
9568
+ ...passthrough
9569
+ });
9570
+ if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
9571
+ if (expectNoChanges && plan.changes !== "none") throw new EvalDriftError(plan);
9572
+ return plan;
9573
+ }
9574
+ const memo = memoFor$7(client);
9130
9575
  const localHash = await computeEvalContentHash(definition);
9131
9576
  const memoKey = `${definition.name} ${localHash}`;
9132
9577
  const contentHash = memo.get(memoKey) ?? localHash;
9133
- const probe = await client.post("/eval/ensure", {
9578
+ const probe = await postEvalEnsure(client, {
9134
9579
  name: definition.name,
9135
- contentHash
9580
+ contentHash,
9581
+ ...passthrough
9136
9582
  });
9583
+ if (probe.result === "plan") throw new Error("Server returned a plan for a non-dryRun request");
9137
9584
  if (probe.result !== "definitionRequired") {
9138
9585
  memo.set(memoKey, probe.contentHash);
9139
9586
  return probe;
9140
9587
  }
9141
- const converged = await client.post("/eval/ensure", {
9588
+ const converged = await postEvalEnsure(client, {
9142
9589
  name: definition.name,
9143
- definition
9590
+ definition,
9591
+ ...passthrough
9144
9592
  });
9145
9593
  if (converged.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-definition request");
9594
+ if (converged.result === "plan") throw new Error("Server returned a plan for a non-dryRun request");
9146
9595
  memo.set(memoKey, converged.contentHash);
9147
9596
  return converged;
9148
9597
  }
@@ -9197,9 +9646,11 @@ function normalizeStepForHash(step) {
9197
9646
  }
9198
9647
  /** SHA-256 (hex) over the canonical normalized step list (steps only). */
9199
9648
  async function computeFlowContentHash(steps) {
9200
- const normalized = [...steps].sort((a, b) => {
9201
- return (isPlainObject$1(a) && typeof a.order === "number" ? a.order : 0) - (isPlainObject$1(b) && typeof b.order === "number" ? b.order : 0);
9202
- }).map(normalizeStepForHash);
9649
+ const normalized = steps.map((step) => isPlainObject$1(step) ? step : {}).map((step, index) => ({
9650
+ ...step,
9651
+ name: typeof step.name === "string" ? step.name : `Step ${index + 1}`,
9652
+ order: typeof step.order === "number" ? step.order : index + 1
9653
+ })).sort((a, b) => a.order - b.order).map(normalizeStepForHash);
9203
9654
  return sha256Hex(JSON.stringify(normalized));
9204
9655
  }
9205
9656
  const DEFINE_FLOW_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
@@ -9218,8 +9669,8 @@ const DEFINE_FLOW_STEP_KEYS = /* @__PURE__ */ new Set([
9218
9669
  /**
9219
9670
  * Collect account-scoped `tool_…` references from a step config (inlined copy
9220
9671
  * of `collectNonPortableAgentToolRefs` in @runtypelabs/shared, applied to the
9221
- * step's `config.tools` block and recursively to conditional branches). The
9222
- * v1 ensure surface accepts portable tool references only.
9672
+ * step's `config.tools` block, `toolId` and `agentId`). The v1 ensure surface
9673
+ * accepts portable tool references only.
9223
9674
  */
9224
9675
  function collectStepNonPortableToolRefs(config, path) {
9225
9676
  const found = [];
@@ -9253,12 +9704,73 @@ function collectStepNonPortableToolRefs(config, path) {
9253
9704
  }
9254
9705
  if (isAccountScoped(config.toolId)) found.push(`${path}.toolId`);
9255
9706
  if (isRawId(config.agentId, "agent_")) found.push(`${path}.agentId`);
9256
- for (const branch of ["trueSteps", "falseSteps"]) {
9257
- const nested = config[branch];
9258
- if (!Array.isArray(nested)) continue;
9259
- nested.forEach((nestedStep, i) => {
9260
- if (isPlainObject$1(nestedStep) && isPlainObject$1(nestedStep.config)) found.push(...collectStepNonPortableToolRefs(nestedStep.config, `${path}.${branch}[${i}].config`));
9707
+ return found;
9708
+ }
9709
+ const CONDITIONAL_BRANCH_STEP_KEYS = [
9710
+ "trueSteps",
9711
+ "falseSteps",
9712
+ "true_steps",
9713
+ "false_steps",
9714
+ "otherwiseSteps"
9715
+ ];
9716
+ /** Guards a hostile payload whose nesting is otherwise unbounded. */
9717
+ const MAX_REF_SITE_WALK_DEPTH = 100;
9718
+ /**
9719
+ * Collect account-scoped references at every flow step ref site (inlined copy of
9720
+ * `collectNonPortableFlowStepRefs` in @runtypelabs/shared): loop bodies, conditional
9721
+ * branches, inline flow-as-tool payloads and `errorHandling.fallbacks`.
9722
+ */
9723
+ function collectFlowStepsNonPortableRefs(steps, basePath, depth = 0) {
9724
+ if (!Array.isArray(steps) || depth > MAX_REF_SITE_WALK_DEPTH) return [];
9725
+ const found = [];
9726
+ steps.forEach((step, index) => {
9727
+ if (!isPlainObject$1(step) || !isPlainObject$1(step.config)) return;
9728
+ found.push(...collectStepConfigSiteRefs(step.type, step.config, `${basePath}[${index}].config`, depth));
9729
+ });
9730
+ return found;
9731
+ }
9732
+ function collectStepConfigSiteRefs(type, config, path, depth) {
9733
+ const found = collectStepNonPortableToolRefs(config, path);
9734
+ found.push(...collectToolBagFlowRefs(config.tools, `${path}.tools`, depth + 1));
9735
+ const errorHandling = config.errorHandling;
9736
+ if (isPlainObject$1(errorHandling) && Array.isArray(errorHandling.fallbacks)) errorHandling.fallbacks.forEach((fallback, i) => {
9737
+ if (!isPlainObject$1(fallback)) return;
9738
+ const fallbackPath = `${path}.errorHandling.fallbacks[${i}]`;
9739
+ if (fallback.type === "flow") {
9740
+ if (typeof fallback.flowId === "string" && fallback.flowId.startsWith("flow_")) found.push(`${fallbackPath}.flowId`);
9741
+ } else if (fallback.type === "step" && isPlainObject$1(fallback.stepConfig)) {
9742
+ found.push(...collectStepNonPortableToolRefs(fallback.stepConfig, `${fallbackPath}.stepConfig`));
9743
+ found.push(...collectToolBagFlowRefs(fallback.stepConfig.tools, `${fallbackPath}.stepConfig.tools`, depth + 1));
9744
+ }
9745
+ });
9746
+ if (type === "loop") found.push(...collectFlowStepsNonPortableRefs(config.steps, `${path}.steps`, depth + 1));
9747
+ else if (type === "conditional") {
9748
+ if (Array.isArray(config.branches)) config.branches.forEach((branch, i) => {
9749
+ if (!isPlainObject$1(branch)) return;
9750
+ found.push(...collectFlowStepsNonPortableRefs(branch.steps, `${path}.branches[${i}].steps`, depth + 1));
9261
9751
  });
9752
+ for (const key of CONDITIONAL_BRANCH_STEP_KEYS) found.push(...collectFlowStepsNonPortableRefs(config[key], `${path}.${key}`, depth + 1));
9753
+ }
9754
+ return found;
9755
+ }
9756
+ /** Descend the inline flow-as-tool and skill payloads a step's tool bag can carry. */
9757
+ function collectToolBagFlowRefs(tools, path, depth) {
9758
+ if (!isPlainObject$1(tools) || depth > MAX_REF_SITE_WALK_DEPTH) return [];
9759
+ const found = [];
9760
+ const walkBag = (bag, bagPath, bagDepth) => {
9761
+ if (bagDepth > MAX_REF_SITE_WALK_DEPTH) return;
9762
+ bag.forEach((tool, i) => {
9763
+ if (!isPlainObject$1(tool) || !isPlainObject$1(tool.config)) return;
9764
+ const toolPath = `${bagPath}[${i}].config`;
9765
+ const skill = tool.config.skill;
9766
+ if (isPlainObject$1(skill) && Array.isArray(skill.resolvedTools)) walkBag(skill.resolvedTools, `${toolPath}.skill.resolvedTools`, bagDepth + 1);
9767
+ const flow = tool.config.flow;
9768
+ if (isPlainObject$1(flow) && Array.isArray(flow.steps)) found.push(...collectFlowStepsNonPortableRefs(flow.steps, `${toolPath}.flow.steps`, bagDepth + 1));
9769
+ });
9770
+ };
9771
+ for (const key of ["runtimeTools", "inlineTools"]) {
9772
+ const bag = tools[key];
9773
+ if (Array.isArray(bag)) walkBag(bag, `${path}.${key}`, depth);
9262
9774
  }
9263
9775
  return found;
9264
9776
  }
@@ -9299,7 +9811,7 @@ function defineFlow(input) {
9299
9811
  if (unknownStepKeys.length > 0) throw new Error(`defineFlow: steps[${index}] has unknown field(s): ${unknownStepKeys.join(", ")}. Allowed step fields are type, name, order, enabled, when, config. (Step ids are server artifacts and not part of a portable definition.)`);
9300
9812
  const config = isPlainObject$1(step.config) ? step.config : void 0;
9301
9813
  if (config) {
9302
- const nonPortable = collectStepNonPortableToolRefs(config, `steps[${index}].config`);
9814
+ const nonPortable = collectStepConfigSiteRefs(step.type, config, `steps[${index}].config`, 0);
9303
9815
  if (nonPortable.length > 0) throw new Error(`defineFlow: account-scoped reference(s) at ${nonPortable.join(", ")}. Definitions must be environment-portable — tool_…/agent_…/flow_… IDs belong to one account/environment. Use builtin:/platform:/mcp: references, or reference a saved resource by name — tool:<name>, agent:<name>, or flow:<name> instead.`);
9304
9816
  }
9305
9817
  return {
@@ -9353,7 +9865,7 @@ var FlowEnsureConflictError = class extends Error {
9353
9865
  /** Thrown by `ensure(def, { expectNoChanges: true })` when drift is detected. */
9354
9866
  var FlowDriftError = class extends Error {
9355
9867
  constructor(plan) {
9356
- super(`Flow "${plan.flowId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.flows.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`);
9868
+ super(`Flow "${plan.flowId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${[...plan.changedKeys, ...plan.publishPending ? ["publish (pending)"] : []].join(", ") || "n/a"}). Run client.flows.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`);
9357
9869
  this.name = "FlowDriftError";
9358
9870
  this.plan = plan;
9359
9871
  }
@@ -9362,7 +9874,7 @@ var FlowDriftError = class extends Error {
9362
9874
  * Best-effort extraction of `{ status, body }` from the generic request
9363
9875
  * error thrown by RuntypeClient ("API request failed: 409 Conflict - {…}").
9364
9876
  */
9365
- function parseRequestError$5(err) {
9877
+ function parseRequestError$6(err) {
9366
9878
  if (!(err instanceof Error)) return {
9367
9879
  status: null,
9368
9880
  body: null
@@ -9384,8 +9896,8 @@ function parseRequestError$5(err) {
9384
9896
  };
9385
9897
  }
9386
9898
  }
9387
- function toConflictError$5(err) {
9388
- const { status, body } = parseRequestError$5(err);
9899
+ function toConflictError$6(err) {
9900
+ const { status, body } = parseRequestError$6(err);
9389
9901
  if (status !== 409 || !isPlainObject$1(body)) return null;
9390
9902
  const code = body.code;
9391
9903
  if (code !== "external_modification" && code !== "remote_changed") return null;
@@ -9401,23 +9913,23 @@ function toConflictError$5(err) {
9401
9913
  * so a stale server hash from the previous definition version can never be
9402
9914
  * probed (which would false-match and silently skip the update).
9403
9915
  */
9404
- const serverHashMemo$5 = /* @__PURE__ */ new WeakMap();
9405
- function memoFor$5(client) {
9406
- let memo = serverHashMemo$5.get(client);
9916
+ const serverHashMemo$6 = /* @__PURE__ */ new WeakMap();
9917
+ function memoFor$6(client) {
9918
+ let memo = serverHashMemo$6.get(client);
9407
9919
  if (!memo) {
9408
9920
  memo = /* @__PURE__ */ new Map();
9409
- serverHashMemo$5.set(client, memo);
9921
+ serverHashMemo$6.set(client, memo);
9410
9922
  }
9411
9923
  return memo;
9412
9924
  }
9413
9925
  function memoize$4(memo, memoKey, result) {
9414
9926
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
9415
9927
  }
9416
- async function request$4(client, body) {
9928
+ async function request$5(client, body) {
9417
9929
  try {
9418
9930
  return await client.post("/flows/ensure", body);
9419
9931
  } catch (err) {
9420
- const conflict = toConflictError$5(err);
9932
+ const conflict = toConflictError$6(err);
9421
9933
  if (conflict) throw conflict;
9422
9934
  throw err;
9423
9935
  }
@@ -9442,37 +9954,37 @@ async function ensureFlow(client, definition, options = {}) {
9442
9954
  steps: definition.steps
9443
9955
  };
9444
9956
  if (dryRun || expectNoChanges) {
9445
- const plan = await request$4(client, {
9957
+ const plan = await request$5(client, {
9446
9958
  name: definition.name,
9447
9959
  definition: wireDefinition,
9448
9960
  dryRun: true,
9449
9961
  ...passthrough
9450
9962
  });
9451
9963
  if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
9452
- if (expectNoChanges && plan.changes !== "none") throw new FlowDriftError(plan);
9964
+ if (expectNoChanges && (plan.changes !== "none" || release === "publish" && plan.publishPending === true)) throw new FlowDriftError(plan);
9453
9965
  return plan;
9454
9966
  }
9455
- const memo = memoFor$5(client);
9967
+ const memo = memoFor$6(client);
9456
9968
  const localHash = await computeFlowContentHash(definition.steps);
9457
9969
  const memoKey = `${definition.name} ${localHash}`;
9458
9970
  const contentHash = memo.get(memoKey) ?? localHash;
9459
- const probe = await request$4(client, {
9971
+ const probe = await request$5(client, {
9460
9972
  name: definition.name,
9461
9973
  contentHash,
9462
9974
  ...passthrough
9463
9975
  });
9464
9976
  if (probe.result !== "definitionRequired") {
9465
9977
  memoize$4(memo, memoKey, probe);
9466
- return convergeInlineEvals(client, definition, probe);
9978
+ return convergeInlineEvals(client, definition, probe, onConflict);
9467
9979
  }
9468
- const converged = await request$4(client, {
9980
+ const converged = await request$5(client, {
9469
9981
  name: definition.name,
9470
9982
  definition: wireDefinition,
9471
9983
  ...passthrough
9472
9984
  });
9473
9985
  if (converged.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-definition request");
9474
9986
  memoize$4(memo, memoKey, converged);
9475
- return convergeInlineEvals(client, definition, converged);
9987
+ return convergeInlineEvals(client, definition, converged, onConflict);
9476
9988
  }
9477
9989
  /**
9478
9990
  * After the flow itself converges on the real (non-plan) path, converge each
@@ -9485,10 +9997,10 @@ async function ensureFlow(client, definition, options = {}) {
9485
9997
  * unchanged (no empty `evals` array). The server's `/flows/ensure` surface is
9486
9998
  * never touched by eval semantics — they ride the eval endpoints only.
9487
9999
  */
9488
- async function convergeInlineEvals(client, definition, result) {
10000
+ async function convergeInlineEvals(client, definition, result, onConflict) {
9489
10001
  if (result.result === "plan" || !definition.evals?.length) return result;
9490
10002
  const evals = [];
9491
- for (const evalDef of definition.evals) evals.push(await ensureEval(client, evalDef));
10003
+ for (const evalDef of definition.evals) evals.push(await ensureEval(client, evalDef, onConflict ? { onConflict } : {}));
9492
10004
  return {
9493
10005
  ...result,
9494
10006
  evals
@@ -10219,9 +10731,11 @@ var RuntypeFlowBuilder = class {
10219
10731
  }
10220
10732
  };
10221
10733
  let currentResponse;
10734
+ let carriedRelease = {};
10222
10735
  if (isStreaming) currentResponse = await client.dispatch(config);
10223
10736
  else {
10224
10737
  const data = await client.dispatchJson(config);
10738
+ carriedRelease = releaseFieldsOf(data);
10225
10739
  currentResponse = new Response(JSON.stringify(data), { headers: { "content-type": "application/json" } });
10226
10740
  }
10227
10741
  while (true) {
@@ -10256,7 +10770,11 @@ var RuntypeFlowBuilder = class {
10256
10770
  }).catch(() => null));
10257
10771
  else {
10258
10772
  const data = await client.post("/dispatch/resume", resumeData);
10259
- const envelope = isDetachedToolOutputAck(data) ? await followDetachedResume(client, data.executionId, pausedResult.resumeCursor) : data;
10773
+ carriedRelease = {
10774
+ ...carriedRelease,
10775
+ ...releaseFieldsOf(data)
10776
+ };
10777
+ const envelope = isDetachedToolOutputAck(data) ? await followDetachedResume(client, data, pausedResult.resumeCursor, carriedRelease) : data;
10260
10778
  currentResponse = new Response(JSON.stringify(envelope), { headers: { "content-type": "application/json" } });
10261
10779
  }
10262
10780
  } catch (error) {
@@ -10718,24 +11236,8 @@ var EvalsNamespace = class {
10718
11236
  run(config) {
10719
11237
  return new EvalRunner(this.getClient, config);
10720
11238
  }
10721
- /**
10722
- * Idempotently converge a `defineEval` suite definition onto the platform —
10723
- * the deploy-time, non-executing converge for code-colocated evals. Hash-first:
10724
- * the steady state is one tiny probe request. Upserts the eval suite + replaces
10725
- * its cases; never runs the eval.
10726
- *
10727
- * @example
10728
- * ```typescript
10729
- * const suite = defineEval({
10730
- * target: { flow: 'support-triage' },
10731
- * graders: [noError()],
10732
- * cases: [{ name: 'billing', input: { variables: { message: 'I was double charged' } }, expect: [contains('finance')] }],
10733
- * })
10734
- * const result = await Runtype.evals.ensure(suite)
10735
- * ```
10736
- */
10737
- async ensure(definition) {
10738
- return ensureEval(this.getClient(), definition);
11239
+ async ensure(definition, options = {}) {
11240
+ return ensureEval(this.getClient(), definition, options);
10739
11241
  }
10740
11242
  /**
10741
11243
  * Pull the canonical definition + provenance for an eval suite by name — the
@@ -11002,7 +11504,7 @@ var SkillDriftError = class extends Error {
11002
11504
  * Best-effort extraction of `{ status, body }` from the generic request error
11003
11505
  * thrown by RuntypeClient ("API request failed: 409 Conflict - {…}").
11004
11506
  */
11005
- function parseRequestError$4(err) {
11507
+ function parseRequestError$5(err) {
11006
11508
  if (!(err instanceof Error)) return {
11007
11509
  status: null,
11008
11510
  body: null
@@ -11024,8 +11526,8 @@ function parseRequestError$4(err) {
11024
11526
  };
11025
11527
  }
11026
11528
  }
11027
- function toConflictError$4(err) {
11028
- const { status, body } = parseRequestError$4(err);
11529
+ function toConflictError$5(err) {
11530
+ const { status, body } = parseRequestError$5(err);
11029
11531
  if (status !== 409 || !isPlainObject$1(body)) return null;
11030
11532
  const code = body.code;
11031
11533
  if (code !== "external_modification" && code !== "remote_changed") return null;
@@ -11037,23 +11539,23 @@ function toConflictError$4(err) {
11037
11539
  * within the process lifetime invalidates a stale server hash (which would
11038
11540
  * false-match and silently skip the update). Same scheme as flows/tools.ensure.
11039
11541
  */
11040
- const serverHashMemo$4 = /* @__PURE__ */ new WeakMap();
11041
- function memoFor$4(client) {
11042
- let memo = serverHashMemo$4.get(client);
11542
+ const serverHashMemo$5 = /* @__PURE__ */ new WeakMap();
11543
+ function memoFor$5(client) {
11544
+ let memo = serverHashMemo$5.get(client);
11043
11545
  if (!memo) {
11044
11546
  memo = /* @__PURE__ */ new Map();
11045
- serverHashMemo$4.set(client, memo);
11547
+ serverHashMemo$5.set(client, memo);
11046
11548
  }
11047
11549
  return memo;
11048
11550
  }
11049
11551
  function memoize$3(memo, memoKey, result) {
11050
11552
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
11051
11553
  }
11052
- async function request$3(client, body) {
11554
+ async function request$4(client, body) {
11053
11555
  try {
11054
11556
  return await client.post("/skills/ensure", body);
11055
11557
  } catch (err) {
11056
- const conflict = toConflictError$4(err);
11558
+ const conflict = toConflictError$5(err);
11057
11559
  if (conflict) throw conflict;
11058
11560
  throw err;
11059
11561
  }
@@ -11065,14 +11567,15 @@ async function request$3(client, body) {
11065
11567
  * on every change; never deletes.
11066
11568
  */
11067
11569
  async function ensureSkill(client, definition, options = {}) {
11068
- const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
11570
+ const { dryRun, onConflict, release, expectedRemoteHash, version, expectNoChanges } = options;
11069
11571
  const passthrough = {
11070
11572
  ...onConflict ? { onConflict } : {},
11071
11573
  ...release ? { release } : {},
11072
- ...expectedRemoteHash ? { expectedRemoteHash } : {}
11574
+ ...expectedRemoteHash ? { expectedRemoteHash } : {},
11575
+ ...version ? { version } : {}
11073
11576
  };
11074
11577
  if (dryRun || expectNoChanges) {
11075
- const plan = await request$3(client, {
11578
+ const plan = await request$4(client, {
11076
11579
  name: definition.name,
11077
11580
  definition: manifestToWire(definition),
11078
11581
  dryRun: true,
@@ -11082,11 +11585,11 @@ async function ensureSkill(client, definition, options = {}) {
11082
11585
  if (expectNoChanges && plan.changes !== "none") throw new SkillDriftError(plan);
11083
11586
  return plan;
11084
11587
  }
11085
- const memo = memoFor$4(client);
11588
+ const memo = memoFor$5(client);
11086
11589
  const localHash = await computeSkillContentHash(definition);
11087
11590
  const memoKey = `${definition.name} ${localHash}`;
11088
11591
  const contentHash = memo.get(memoKey) ?? localHash;
11089
- const probe = await request$3(client, {
11592
+ const probe = await request$4(client, {
11090
11593
  name: definition.name,
11091
11594
  contentHash,
11092
11595
  ...passthrough
@@ -11095,7 +11598,7 @@ async function ensureSkill(client, definition, options = {}) {
11095
11598
  memoize$3(memo, memoKey, probe);
11096
11599
  return probe;
11097
11600
  }
11098
- const converged = await request$3(client, {
11601
+ const converged = await request$4(client, {
11099
11602
  name: definition.name,
11100
11603
  definition: manifestToWire(definition),
11101
11604
  ...passthrough
@@ -11432,6 +11935,8 @@ const DEFINE_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
11432
11935
  "icon",
11433
11936
  ...AGENT_CONFIG_KEYS
11434
11937
  ]);
11938
+ /** A minted tool typeid; `alwaysLoaded` also holds tool names, which may begin with `tool_`. */
11939
+ const TOOL_TYPEID_PATTERN = /^tool_[0-7][0-9a-hjkmnp-tv-z]{25}$/;
11435
11940
  /**
11436
11941
  * Collect account-scoped `tool_…` references (inlined copy of
11437
11942
  * `collectNonPortableAgentToolRefs` in @runtypelabs/shared). The v1 ensure
@@ -11458,6 +11963,9 @@ function collectNonPortableToolRefs(config) {
11458
11963
  if (isPlainObject(tools.approval)) scanArray(tools.approval.require, "tools.approval.require");
11459
11964
  if (isPlainObject(tools.subagentConfig)) scanArray(tools.subagentConfig.toolPool, "tools.subagentConfig.toolPool");
11460
11965
  if (isPlainObject(tools.codeModeConfig)) scanArray(tools.codeModeConfig.toolPool, "tools.codeModeConfig.toolPool");
11966
+ if (isPlainObject(tools.toolSearch) && Array.isArray(tools.toolSearch.alwaysLoaded)) tools.toolSearch.alwaysLoaded.forEach((ref, i) => {
11967
+ if (typeof ref === "string" && TOOL_TYPEID_PATTERN.test(ref)) found.push(`tools.toolSearch.alwaysLoaded[${i}]`);
11968
+ });
11461
11969
  if (Array.isArray(tools.runtimeTools)) tools.runtimeTools.forEach((runtimeTool, i) => {
11462
11970
  if (!isPlainObject(runtimeTool) || !isPlainObject(runtimeTool.config)) return;
11463
11971
  const base = `tools.runtimeTools[${i}].config`;
@@ -11506,6 +12014,18 @@ function defineAgent(input) {
11506
12014
  config
11507
12015
  };
11508
12016
  }
12017
+ function planHasSkillBindingChanges(plan) {
12018
+ return (plan.skillBindings ?? []).some((binding) => binding.action !== "unchanged");
12019
+ }
12020
+ /** Whether a dry run that asked for a deploy would move the alias or change its binding names. */
12021
+ function planHasDeploymentChanges(plan) {
12022
+ return plan.deployment?.changed === true || plan.deployment?.bindingsChanged === true;
12023
+ }
12024
+ function deploymentChangeLabels(plan) {
12025
+ const deployment = plan.deployment;
12026
+ if (!deployment) return [];
12027
+ return [...deployment.changed ? [`deploy:${deployment.alias} (pointer)`] : [], ...deployment.bindingsChanged ? [`deploy:${deployment.alias} (bindings)`] : []];
12028
+ }
11509
12029
  /** A 409 from the ensure protocol (external_modification or remote_changed). */
11510
12030
  var AgentEnsureConflictError = class extends Error {
11511
12031
  constructor(body) {
@@ -11520,7 +12040,12 @@ var AgentEnsureConflictError = class extends Error {
11520
12040
  /** Thrown by `ensure(def, { expectNoChanges: true })` when drift is detected. */
11521
12041
  var AgentDriftError = class extends Error {
11522
12042
  constructor(plan) {
11523
- super(`Agent "${plan.agentId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.agents.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`);
12043
+ const skillChanges = (plan.skillBindings ?? []).filter((binding) => binding.action !== "unchanged").map((binding) => `skill:${binding.name} (${binding.action})`);
12044
+ super(`Agent "${plan.agentId ?? "definition"}" drifted: plan is '${plan.changes}' (changed: ${[
12045
+ ...plan.changedKeys,
12046
+ ...skillChanges,
12047
+ ...deploymentChangeLabels(plan)
12048
+ ].join(", ") || "n/a"}). Run client.agents.pull(name) to absorb the remote edit into your repo, or re-run ensure to converge.`);
11524
12049
  this.name = "AgentDriftError";
11525
12050
  this.plan = plan;
11526
12051
  }
@@ -11529,7 +12054,7 @@ var AgentDriftError = class extends Error {
11529
12054
  * Best-effort extraction of `{ status, body }` from the generic request
11530
12055
  * error thrown by RuntypeClient ("API request failed: 409 Conflict - {…}").
11531
12056
  */
11532
- function parseRequestError$3(err) {
12057
+ function parseRequestError$4(err) {
11533
12058
  if (!(err instanceof Error)) return {
11534
12059
  status: null,
11535
12060
  body: null
@@ -11551,8 +12076,8 @@ function parseRequestError$3(err) {
11551
12076
  };
11552
12077
  }
11553
12078
  }
11554
- function toConflictError$3(err) {
11555
- const { status, body } = parseRequestError$3(err);
12079
+ function toConflictError$4(err) {
12080
+ const { status, body } = parseRequestError$4(err);
11556
12081
  if (status !== 409 || !isPlainObject(body)) return null;
11557
12082
  const code = body.code;
11558
12083
  if (code !== "external_modification" && code !== "remote_changed") return null;
@@ -11568,12 +12093,12 @@ function toConflictError$3(err) {
11568
12093
  * so a stale server hash from the previous definition version can never be
11569
12094
  * probed (which would false-match and silently skip the update).
11570
12095
  */
11571
- const serverHashMemo$3 = /* @__PURE__ */ new WeakMap();
11572
- function memoFor$3(client) {
11573
- let memo = serverHashMemo$3.get(client);
12096
+ const serverHashMemo$4 = /* @__PURE__ */ new WeakMap();
12097
+ function memoFor$4(client) {
12098
+ let memo = serverHashMemo$4.get(client);
11574
12099
  if (!memo) {
11575
12100
  memo = /* @__PURE__ */ new Map();
11576
- serverHashMemo$3.set(client, memo);
12101
+ serverHashMemo$4.set(client, memo);
11577
12102
  }
11578
12103
  return memo;
11579
12104
  }
@@ -11609,14 +12134,16 @@ var AgentsNamespace = class {
11609
12134
  */
11610
12135
  async ensure(definition, options = {}) {
11611
12136
  const client = this.getClient();
11612
- const { dryRun, onConflict, release, deploy, expectedRemoteHash, version, expectNoChanges } = options;
12137
+ const { dryRun, onConflict, release, deploy, expectedRemoteHash, version, expectNoChanges, skills, capabilities } = options;
11613
12138
  if (release !== void 0 && deploy !== void 0) throw new Error(ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE);
11614
12139
  const passthrough = {
11615
12140
  ...onConflict ? { onConflict } : {},
11616
12141
  ...release ? { release } : {},
11617
12142
  ...deploy ? { deploy } : {},
11618
12143
  ...expectedRemoteHash ? { expectedRemoteHash } : {},
11619
- ...version ? { version } : {}
12144
+ ...version ? { version } : {},
12145
+ ...skills ? { skills } : {},
12146
+ ...capabilities ? { capabilities } : {}
11620
12147
  };
11621
12148
  if (dryRun || expectNoChanges) {
11622
12149
  const plan = await this.request(client, {
@@ -11626,15 +12153,16 @@ var AgentsNamespace = class {
11626
12153
  ...passthrough
11627
12154
  });
11628
12155
  if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
11629
- if (expectNoChanges && plan.changes !== "none") throw new AgentDriftError(plan);
12156
+ const deployRequested = release === "publish" || deploy !== void 0;
12157
+ if (expectNoChanges && (plan.changes !== "none" || planHasSkillBindingChanges(plan) || deployRequested && planHasDeploymentChanges(plan))) throw new AgentDriftError(plan);
11630
12158
  return plan;
11631
12159
  }
11632
- const memo = memoFor$3(client);
12160
+ const memo = memoFor$4(client);
11633
12161
  const localHash = await computeAgentContentHash({
11634
12162
  ...definition,
11635
12163
  config: definition.config
11636
12164
  });
11637
- const memoKey = `${definition.name}\u0000${localHash}`;
12165
+ const memoKey = `${definition.name}\u0000${localHash}${capabilities ? `\u0000${JSON.stringify(capabilities)}` : ""}`;
11638
12166
  const contentHash = memo.get(memoKey) ?? localHash;
11639
12167
  const probe = await this.request(client, {
11640
12168
  name: definition.name,
@@ -11668,7 +12196,7 @@ var AgentsNamespace = class {
11668
12196
  try {
11669
12197
  return await client.post("/agents/ensure", body);
11670
12198
  } catch (err) {
11671
- const conflict = toConflictError$3(err);
12199
+ const conflict = toConflictError$4(err);
11672
12200
  if (conflict) throw conflict;
11673
12201
  throw err;
11674
12202
  }
@@ -11797,7 +12325,7 @@ var ToolDriftError = class extends Error {
11797
12325
  * Best-effort extraction of `{ status, body }` from the generic request
11798
12326
  * error thrown by RuntypeClient ("API request failed: 409 Conflict - {…}").
11799
12327
  */
11800
- function parseRequestError$2(err) {
12328
+ function parseRequestError$3(err) {
11801
12329
  if (!(err instanceof Error)) return {
11802
12330
  status: null,
11803
12331
  body: null
@@ -11819,8 +12347,8 @@ function parseRequestError$2(err) {
11819
12347
  };
11820
12348
  }
11821
12349
  }
11822
- function toConflictError$2(err) {
11823
- const { status, body } = parseRequestError$2(err);
12350
+ function toConflictError$3(err) {
12351
+ const { status, body } = parseRequestError$3(err);
11824
12352
  if (status !== 409 || !isPlainObject$1(body)) return null;
11825
12353
  const code = body.code;
11826
12354
  if (code !== "external_modification" && code !== "remote_changed") return null;
@@ -11832,23 +12360,23 @@ function toConflictError$2(err) {
11832
12360
  * within the process lifetime invalidates a stale server hash (which would
11833
12361
  * false-match and silently skip the update). Same scheme as flows.ensure.
11834
12362
  */
11835
- const serverHashMemo$2 = /* @__PURE__ */ new WeakMap();
11836
- function memoFor$2(client) {
11837
- let memo = serverHashMemo$2.get(client);
12363
+ const serverHashMemo$3 = /* @__PURE__ */ new WeakMap();
12364
+ function memoFor$3(client) {
12365
+ let memo = serverHashMemo$3.get(client);
11838
12366
  if (!memo) {
11839
12367
  memo = /* @__PURE__ */ new Map();
11840
- serverHashMemo$2.set(client, memo);
12368
+ serverHashMemo$3.set(client, memo);
11841
12369
  }
11842
12370
  return memo;
11843
12371
  }
11844
12372
  function memoize$2(memo, memoKey, result) {
11845
12373
  if (result.result !== "plan") memo.set(memoKey, result.contentHash);
11846
12374
  }
11847
- async function request$2(client, body) {
12375
+ async function request$3(client, body) {
11848
12376
  try {
11849
12377
  return await client.post("/tools/ensure", body);
11850
12378
  } catch (err) {
11851
- const conflict = toConflictError$2(err);
12379
+ const conflict = toConflictError$3(err);
11852
12380
  if (conflict) throw conflict;
11853
12381
  throw err;
11854
12382
  }
@@ -11865,7 +12393,7 @@ async function ensureTool(client, definition, options = {}) {
11865
12393
  ...expectedRemoteHash ? { expectedRemoteHash } : {}
11866
12394
  };
11867
12395
  if (dryRun || expectNoChanges) {
11868
- const plan = await request$2(client, {
12396
+ const plan = await request$3(client, {
11869
12397
  name: definition.name,
11870
12398
  definition,
11871
12399
  dryRun: true,
@@ -11875,11 +12403,11 @@ async function ensureTool(client, definition, options = {}) {
11875
12403
  if (expectNoChanges && plan.changes !== "none") throw new ToolDriftError(plan);
11876
12404
  return plan;
11877
12405
  }
11878
- const memo = memoFor$2(client);
12406
+ const memo = memoFor$3(client);
11879
12407
  const localHash = await computeToolContentHash(definition);
11880
12408
  const memoKey = `${definition.name} ${localHash}`;
11881
12409
  const contentHash = memo.get(memoKey) ?? localHash;
11882
- const probe = await request$2(client, {
12410
+ const probe = await request$3(client, {
11883
12411
  name: definition.name,
11884
12412
  contentHash,
11885
12413
  ...passthrough
@@ -11888,7 +12416,7 @@ async function ensureTool(client, definition, options = {}) {
11888
12416
  memoize$2(memo, memoKey, probe);
11889
12417
  return probe;
11890
12418
  }
11891
- const converged = await request$2(client, {
12419
+ const converged = await request$3(client, {
11892
12420
  name: definition.name,
11893
12421
  definition,
11894
12422
  ...passthrough
@@ -11944,10 +12472,197 @@ var ToolsNamespace = class {
11944
12472
  }
11945
12473
  };
11946
12474
  //#endregion
12475
+ //#region src/question-sets-ensure.ts
12476
+ /** Canonical form of a question set definition, without its slug. */
12477
+ function normalizeQuestionSetDefinition(definition) {
12478
+ return normalizeValue$1({
12479
+ name: definition.name,
12480
+ description: definition.description,
12481
+ questions: isPlainObject$1(definition.questions) ? definition.questions : {},
12482
+ inputMapping: definition.inputMapping,
12483
+ model: definition.model
12484
+ });
12485
+ }
12486
+ /** SHA-256 (hex) over the canonical question set definition. */
12487
+ async function computeQuestionSetContentHash(definition) {
12488
+ return sha256Hex(JSON.stringify(normalizeQuestionSetDefinition(definition)));
12489
+ }
12490
+ const SLUG_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/;
12491
+ const DEFINE_QUESTION_SET_KEYS = /* @__PURE__ */ new Set([
12492
+ "slug",
12493
+ "name",
12494
+ "description",
12495
+ "questions",
12496
+ "inputMapping",
12497
+ "model"
12498
+ ]);
12499
+ /**
12500
+ * Pure local constructor for a question set definition. Checks the structure
12501
+ * and rejects unknown fields; the server validates the questions at ensure time.
12502
+ *
12503
+ * @example
12504
+ * ```typescript
12505
+ * const triage = defineQuestionSet({
12506
+ * slug: 'ticket-triage',
12507
+ * name: 'Ticket triage',
12508
+ * questions: { urgent: { type: 'boolean', instructions: 'Is the ticket urgent?' } },
12509
+ * })
12510
+ * ```
12511
+ */
12512
+ function defineQuestionSet(input) {
12513
+ if (!input || typeof input !== "object") throw new Error("defineQuestionSet requires a definition object");
12514
+ if (typeof input.slug !== "string" || !SLUG_PATTERN.test(input.slug)) throw new Error("defineQuestionSet requires a \"slug\" of lowercase letters, digits, \"-\" and \"_\", up to 64 characters");
12515
+ if (typeof input.name !== "string" || input.name.length === 0) throw new Error("defineQuestionSet requires a non-empty string \"name\"");
12516
+ if (!isPlainObject$1(input.questions) || Object.keys(input.questions).length === 0) throw new Error("defineQuestionSet requires a non-empty \"questions\" object");
12517
+ const unknownKeys = Object.keys(input).filter((key) => !DEFINE_QUESTION_SET_KEYS.has(key));
12518
+ if (unknownKeys.length > 0) throw new Error(`defineQuestionSet: unknown field(s): ${unknownKeys.join(", ")}. Allowed fields are slug, name, description, questions, inputMapping and model.`);
12519
+ return { ...input };
12520
+ }
12521
+ /** A 409 from the ensure protocol (external_modification or remote_changed). */
12522
+ var QuestionSetEnsureConflictError = class extends Error {
12523
+ constructor(body) {
12524
+ super(body.error ?? `Question set ensure conflict: ${body.code}`);
12525
+ this.name = "QuestionSetEnsureConflictError";
12526
+ this.code = body.code;
12527
+ this.lastModifiedSource = body.lastModifiedSource;
12528
+ this.modifiedAt = body.modifiedAt;
12529
+ this.currentHash = body.currentHash;
12530
+ }
12531
+ };
12532
+ /** Thrown by `ensure(def, { expectNoChanges: true })` when the remote differs. */
12533
+ var QuestionSetDriftError = class extends Error {
12534
+ constructor(slug, plan) {
12535
+ super(`Question set "${slug}" drifted: plan is '${plan.changes}' (changed: ${plan.changedKeys.join(", ") || "n/a"}). Run client.questionSets.pull(slug) to absorb the remote edit, or re-run ensure to converge.`);
12536
+ this.name = "QuestionSetDriftError";
12537
+ this.plan = plan;
12538
+ }
12539
+ };
12540
+ function toConflictError$2(err) {
12541
+ if (!(err instanceof Error)) return null;
12542
+ const match = err.message.match(/^API request failed: (409) .*? - ([\s\S]*)$/);
12543
+ if (!match) return null;
12544
+ let body;
12545
+ try {
12546
+ body = JSON.parse(match[2]);
12547
+ } catch {
12548
+ return null;
12549
+ }
12550
+ if (!isPlainObject$1(body)) return null;
12551
+ if (body.code !== "external_modification" && body.code !== "remote_changed") return null;
12552
+ return new QuestionSetEnsureConflictError(body);
12553
+ }
12554
+ const serverHashMemo$2 = /* @__PURE__ */ new WeakMap();
12555
+ function memoFor$2(client) {
12556
+ let memo = serverHashMemo$2.get(client);
12557
+ if (!memo) {
12558
+ memo = /* @__PURE__ */ new Map();
12559
+ serverHashMemo$2.set(client, memo);
12560
+ }
12561
+ return memo;
12562
+ }
12563
+ async function request$2(client, body) {
12564
+ try {
12565
+ return await client.post("/question-sets/ensure", body);
12566
+ } catch (err) {
12567
+ throw toConflictError$2(err) ?? err;
12568
+ }
12569
+ }
12570
+ function toWireDefinition(definition) {
12571
+ const { slug: _slug, ...rest } = definition;
12572
+ return rest;
12573
+ }
12574
+ /**
12575
+ * Idempotently converge a question set onto the platform. Probes with a
12576
+ * content hash first and sends the full definition only on a miss. Appends a
12577
+ * version when the questions, input mapping or model change; never deletes.
12578
+ */
12579
+ async function ensureQuestionSet(client, definition, options = {}) {
12580
+ const { dryRun, onConflict, release, expectedRemoteHash, expectNoChanges } = options;
12581
+ const passthrough = {
12582
+ ...onConflict ? { onConflict } : {},
12583
+ ...release ? { release } : {},
12584
+ ...expectedRemoteHash ? { expectedRemoteHash } : {}
12585
+ };
12586
+ if (dryRun || expectNoChanges) {
12587
+ const plan = await request$2(client, {
12588
+ slug: definition.slug,
12589
+ definition: toWireDefinition(definition),
12590
+ dryRun: true,
12591
+ ...passthrough
12592
+ });
12593
+ if (plan.result !== "plan") throw new Error(`Expected a plan result from dryRun, got '${plan.result}'`);
12594
+ if (expectNoChanges && plan.changes !== "none") throw new QuestionSetDriftError(definition.slug, plan);
12595
+ return plan;
12596
+ }
12597
+ const memo = memoFor$2(client);
12598
+ const localHash = await computeQuestionSetContentHash(definition);
12599
+ const memoKey = `${definition.slug} ${localHash}`;
12600
+ const probe = await request$2(client, {
12601
+ slug: definition.slug,
12602
+ contentHash: memo.get(memoKey) ?? localHash,
12603
+ ...passthrough
12604
+ });
12605
+ if (probe.result !== "definitionRequired") {
12606
+ if (probe.result !== "plan") memo.set(memoKey, probe.contentHash);
12607
+ return probe;
12608
+ }
12609
+ const converged = await request$2(client, {
12610
+ slug: definition.slug,
12611
+ definition: toWireDefinition(definition),
12612
+ ...passthrough
12613
+ });
12614
+ if (converged.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-definition request");
12615
+ if (converged.result !== "plan") memo.set(memoKey, converged.contentHash);
12616
+ return converged;
12617
+ }
12618
+ /** Pull the canonical definition and provenance for a question set by slug. */
12619
+ async function pullQuestionSet(client, slug) {
12620
+ return client.get("/question-sets/pull", { slug });
12621
+ }
12622
+ //#endregion
12623
+ //#region src/question-sets-namespace.ts
12624
+ /** Question set config-as-code: `ensure` and `pull`. */
12625
+ var QuestionSetsNamespace = class {
12626
+ constructor(getClient) {
12627
+ this.getClient = getClient;
12628
+ }
12629
+ /**
12630
+ * Converge a `defineQuestionSet` definition. The steady state is one hash
12631
+ * probe. Pass `release: 'publish'` so flows and agents can resolve the
12632
+ * latest version.
12633
+ *
12634
+ * @example
12635
+ * ```typescript
12636
+ * await Runtype.questionSets.ensure(triage, { release: 'publish' })
12637
+ * await Runtype.questionSets.ensure(triage, { expectNoChanges: true })
12638
+ * ```
12639
+ */
12640
+ async ensure(definition, options = {}) {
12641
+ return ensureQuestionSet(this.getClient(), definition, options);
12642
+ }
12643
+ /** Pull the live definition and provenance for a slug, to absorb a dashboard edit. */
12644
+ async pull(slug) {
12645
+ return pullQuestionSet(this.getClient(), slug);
12646
+ }
12647
+ };
12648
+ //#endregion
11947
12649
  //#region src/products-ensure.ts
12650
+ /**
12651
+ * `spec` with its stack as a save stores it: without the server-set fields (`detected`, `declared`,
12652
+ * `hiddenAt`), which their own write paths set, and with `hidden` deduplicated.
12653
+ */
12654
+ function asSavedStack(spec) {
12655
+ if (!isPlainObject$1(spec.stack)) return spec;
12656
+ const { detected: _detected, declared: _declared, hiddenAt: _hiddenAt, ...stack } = spec.stack;
12657
+ if (Array.isArray(stack.hidden)) stack.hidden = [...new Set(stack.hidden)];
12658
+ return {
12659
+ ...spec,
12660
+ stack
12661
+ };
12662
+ }
11948
12663
  /** Canonical normalized form of a product definition (name + canvas excluded). */
11949
12664
  function normalizeProductDefinition(definition) {
11950
- const spec = isPlainObject$1(definition.spec) ? normalizeValue$1(definition.spec) : {};
12665
+ const spec = isPlainObject$1(definition.spec) ? normalizeValue$1(asSavedStack(definition.spec)) : {};
11951
12666
  return {
11952
12667
  ...definition.description ? { description: definition.description } : {},
11953
12668
  ...definition.icon ? { icon: definition.icon } : {},
@@ -12017,7 +12732,7 @@ var ProductDriftError = class extends Error {
12017
12732
  * Best-effort extraction of `{ status, body }` from the generic request
12018
12733
  * error thrown by RuntypeClient ("API request failed: 409 Conflict - {…}").
12019
12734
  */
12020
- function parseRequestError$1(err) {
12735
+ function parseRequestError$2(err) {
12021
12736
  if (!(err instanceof Error)) return {
12022
12737
  status: null,
12023
12738
  body: null
@@ -12040,7 +12755,7 @@ function parseRequestError$1(err) {
12040
12755
  }
12041
12756
  }
12042
12757
  function toConflictError$1(err) {
12043
- const { status, body } = parseRequestError$1(err);
12758
+ const { status, body } = parseRequestError$2(err);
12044
12759
  if (status !== 409 || !isPlainObject$1(body)) return null;
12045
12760
  const code = body.code;
12046
12761
  if (code !== "external_modification" && code !== "remote_changed") return null;
@@ -12162,20 +12877,85 @@ function defineFpo(fpo) {
12162
12877
  if (!product || typeof product.name !== "string" || product.name.length === 0) throw new Error("defineFpo requires a non-empty \"product.name\" (the converge identity)");
12163
12878
  return fpo;
12164
12879
  }
12880
+ const FPO_DRIFT_RESULTS = /* @__PURE__ */ new Set([
12881
+ "created",
12882
+ "updated",
12883
+ "pruned",
12884
+ "failed"
12885
+ ]);
12886
+ /** Thrown by `ensureFpo(fpo, { expectNoChanges: true })` when the plan is not clean. */
12887
+ var FpoDriftError = class extends Error {
12888
+ constructor(plan) {
12889
+ const drifted = plan.entities.filter((entity) => FPO_DRIFT_RESULTS.has(entity.result)).map((entity) => `${entity.kind}:${entity.name} (${entity.result})`);
12890
+ super(`Product "${plan.productId ?? "definition"}" drifted (${drifted.join(", ")}). Run client.products.pullFpo(name) to absorb the remote edit into your repo, or re-run ensureFpo to converge.`);
12891
+ this.name = "FpoDriftError";
12892
+ this.plan = plan;
12893
+ }
12894
+ };
12895
+ function fpoPlanHasDrift(plan) {
12896
+ return plan.entities.some((entity) => FPO_DRIFT_RESULTS.has(entity.result));
12897
+ }
12898
+ function parseRequestError$1(err) {
12899
+ if (!(err instanceof Error)) return {
12900
+ status: null,
12901
+ body: null
12902
+ };
12903
+ const match = err.message.match(/^API request failed: (\d{3}) .*? - ([\s\S]*)$/);
12904
+ if (!match) return {
12905
+ status: null,
12906
+ body: null
12907
+ };
12908
+ try {
12909
+ return {
12910
+ status: Number(match[1]),
12911
+ body: JSON.parse(match[2])
12912
+ };
12913
+ } catch {
12914
+ return {
12915
+ status: Number(match[1]),
12916
+ body: null
12917
+ };
12918
+ }
12919
+ }
12920
+ async function requestFpoEnsure(client, body) {
12921
+ try {
12922
+ return await client.post("/products/ensure-fpo", body);
12923
+ } catch (err) {
12924
+ const { status, body: errorBody } = parseRequestError$1(err);
12925
+ if (status === 409 && isPlainObject$1(errorBody) && (errorBody.code === "external_modification" || errorBody.code === "remote_changed")) throw new ProductEnsureConflictError(errorBody);
12926
+ throw err;
12927
+ }
12928
+ }
12165
12929
  /**
12166
- * Converge an entire FPO onto the platform. Ships the full FPO (no hash-only
12167
- * probe in this release); the server fans out to the per-entity ensure services
12168
- * and returns the whole-FPO hash + per-entity report. Non-atomic: inspect
12169
- * `result.hasFailures` / `entities[].result` and re-run to self-heal.
12930
+ * Converge an entire FPO onto the platform. Ships the full FPO unless a
12931
+ * drafts-only `hashProbe` finds the live graph already matches; the server fans out to the
12932
+ * per-entity ensure services and returns the whole-FPO hash + per-entity report.
12933
+ * Non-atomic: inspect `result.hasFailures` / `entities[].result` and re-run to self-heal.
12170
12934
  */
12171
12935
  async function ensureFpo(client, fpo, options = {}) {
12172
- const { dryRun, onConflict, prune } = options;
12173
- return client.post("/products/ensure-fpo", {
12936
+ const { dryRun, onConflict, prune, expectNoChanges, expectedRemoteHash, hashProbe, release } = options;
12937
+ const plan = dryRun || expectNoChanges;
12938
+ const guard = expectedRemoteHash ? { expectedRemoteHash } : {};
12939
+ if (hashProbe && release === "none" && !plan && !prune) {
12940
+ const probe = await requestFpoEnsure(client, {
12941
+ name: (isPlainObject$1(fpo.product) ? fpo.product : {}).name,
12942
+ contentHash: await computeFpoContentHash(fpo),
12943
+ ...guard,
12944
+ release
12945
+ });
12946
+ if (probe.result !== "definitionRequired") return probe;
12947
+ }
12948
+ const result = await requestFpoEnsure(client, {
12174
12949
  fpo,
12175
- ...dryRun ? { dryRun: true } : {},
12950
+ ...plan ? { dryRun: true } : {},
12176
12951
  ...onConflict ? { onConflict } : {},
12177
- ...prune ? { prune: true } : {}
12952
+ ...prune ? { prune: true } : {},
12953
+ ...guard,
12954
+ ...release ? { release } : {}
12178
12955
  });
12956
+ if (result.result === "definitionRequired") throw new Error("Server reported definitionRequired for a full-FPO request");
12957
+ if (expectNoChanges && fpoPlanHasDrift(result)) throw new FpoDriftError(result);
12958
+ return result;
12179
12959
  }
12180
12960
  /**
12181
12961
  * Reconstruct a self-contained FPO from the live product graph (resolved by
@@ -12350,8 +13130,8 @@ function defineSurface(input) {
12350
13130
  if (typeof input.name !== "string" || input.name.length === 0) throw new Error("defineSurface requires a non-empty string \"name\"");
12351
13131
  if (typeof input.type !== "string" || !SURFACE_DEFINITION_TYPES.has(input.type)) throw new Error(`defineSurface requires "type" to be one of: ${[...SURFACE_DEFINITION_TYPES].join(", ")}`);
12352
13132
  if (input.behavior !== void 0 && !isPlainObject$1(input.behavior)) throw new Error("defineSurface \"behavior\" must be an object when provided");
12353
- if (input.inbound !== void 0 && !isPlainObject$1(input.inbound)) throw new Error("defineSurface \"inbound\" must be an object when provided");
12354
- if (input.outbound !== void 0 && !isPlainObject$1(input.outbound)) throw new Error("defineSurface \"outbound\" must be an object when provided");
13133
+ if (input.inbound !== void 0 && input.inbound !== null && !isPlainObject$1(input.inbound)) throw new Error("defineSurface \"inbound\" must be an object or null when provided");
13134
+ if (input.outbound !== void 0 && input.outbound !== null && !isPlainObject$1(input.outbound)) throw new Error("defineSurface \"outbound\" must be an object or null when provided");
12355
13135
  if (input.status !== void 0 && ![
12356
13136
  "draft",
12357
13137
  "active",
@@ -12473,14 +13253,16 @@ async function ensureSurface(client, productId, definition, options = {}) {
12473
13253
  const localHash = await computeSurfaceContentHash(definition);
12474
13254
  const memoKey = `${productId} ${definition.name} ${localHash}`;
12475
13255
  const contentHash = memo.get(memoKey) ?? localHash;
12476
- const probe = await request(client, productId, {
12477
- name: definition.name,
12478
- contentHash,
12479
- ...passthrough
12480
- });
12481
- if (probe.result !== "definitionRequired") {
12482
- memoize(memo, memoKey, probe);
12483
- return probe;
13256
+ if (!(definition.inbound !== void 0 || definition.outbound !== void 0)) {
13257
+ const probe = await request(client, productId, {
13258
+ name: definition.name,
13259
+ contentHash,
13260
+ ...passthrough
13261
+ });
13262
+ if (probe.result !== "definitionRequired") {
13263
+ memoize(memo, memoKey, probe);
13264
+ return probe;
13265
+ }
12484
13266
  }
12485
13267
  const converged = await request(client, productId, {
12486
13268
  name: definition.name,
@@ -12493,8 +13275,8 @@ async function ensureSurface(client, productId, definition, options = {}) {
12493
13275
  }
12494
13276
  /**
12495
13277
  * Pull the canonical definition + provenance for a surface by name within a
12496
- * product — the absorb-drift direction. The contentHash reflects the live
12497
- * surface state.
13278
+ * product — the absorb-drift direction. Configured credentials come back as
13279
+ * `'<redacted>'`, which `ensure` accepts to keep the stored value.
12498
13280
  */
12499
13281
  async function pullSurface(client, productId, name) {
12500
13282
  return client.get(`/products/${encodeURIComponent(productId)}/surfaces/pull`, { name });
@@ -13209,6 +13991,25 @@ var Runtype = class {
13209
13991
  return new ToolsNamespace(() => this.getClient());
13210
13992
  }
13211
13993
  /**
13994
+ * Question sets namespace - Question set config-as-code (define / ensure / pull)
13995
+ *
13996
+ * @example
13997
+ * ```typescript
13998
+ * import { defineQuestionSet, Runtype } from '@runtypelabs/sdk'
13999
+ *
14000
+ * const triage = defineQuestionSet({
14001
+ * slug: 'ticket-triage',
14002
+ * name: 'Ticket triage',
14003
+ * questions: { urgent: { type: 'boolean', instructions: 'Is the ticket urgent?' } },
14004
+ * })
14005
+ *
14006
+ * await Runtype.questionSets.ensure(triage, { release: 'publish' })
14007
+ * ```
14008
+ */
14009
+ static get questionSets() {
14010
+ return new QuestionSetsNamespace(() => this.getClient());
14011
+ }
14012
+ /**
13212
14013
  * Products namespace - Product config-as-code (define / ensure / pull)
13213
14014
  *
13214
14015
  * Converges the top-level product record (description, icon, spec). Nested
@@ -13497,7 +14298,7 @@ function transformQueryParams(params) {
13497
14298
  }
13498
14299
  //#endregion
13499
14300
  //#region src/version.ts
13500
- const SDK_VERSION = "10.8.0";
14301
+ const SDK_VERSION = "10.9.0";
13501
14302
  /**
13502
14303
  * Canonical client identity the API uses for attribution
13503
14304
  * (`detectActorSource` in apps/api/src/lib/audit-log.ts keys `'sdk'` off the
@@ -13708,9 +14509,11 @@ var RuntypeClient = class {
13708
14509
  }
13709
14510
  };
13710
14511
  let currentResponse;
14512
+ let carriedRelease = {};
13711
14513
  if (isStreaming) currentResponse = await this.dispatch.executeStream(modifiedRequest);
13712
14514
  else {
13713
14515
  const data = await this.dispatch.execute(modifiedRequest);
14516
+ carriedRelease = releaseFieldsOf(data);
13714
14517
  currentResponse = new Response(JSON.stringify(data), { headers: { "content-type": "application/json" } });
13715
14518
  }
13716
14519
  while (true) {
@@ -13744,7 +14547,11 @@ var RuntypeClient = class {
13744
14547
  if (isStreaming) currentResponse = await this.dispatch.resume(resumeData);
13745
14548
  else {
13746
14549
  const data = await this.dispatch.resume(resumeData);
13747
- const envelope = isDetachedToolOutputAck(data) ? await followDetachedResume(this, data.executionId, result.resumeCursor) : data;
14550
+ carriedRelease = {
14551
+ ...carriedRelease,
14552
+ ...releaseFieldsOf(data)
14553
+ };
14554
+ const envelope = isDetachedToolOutputAck(data) ? await followDetachedResume(this, data, result.resumeCursor, carriedRelease) : data;
13748
14555
  currentResponse = new Response(JSON.stringify(envelope), { headers: { "content-type": "application/json" } });
13749
14556
  }
13750
14557
  } catch (error) {
@@ -15680,5 +16487,5 @@ const STEP_TYPE_TO_METHOD = {
15680
16487
  "memory-summary": "memorySummary"
15681
16488
  };
15682
16489
  //#endregion
15683
- export { AgentAliasDependencyError, AgentAliasNotFoundError, AgentAliasPreviewLimitError, AgentAliasRevisionMismatchError, AgentAliasRevisionRequiredError, AgentAliasesNamespace, AgentDeploymentsNamespace, AgentDriftError, AgentEnsureConflictError, AgentPromotionError, AgentVersionsEndpoint, AgentsEndpoint, AgentsNamespace, AnalyticsEndpoint, ApiKeyRequestsEndpoint, ApiKeysEndpoint, BatchBuilder, BatchesNamespace, BillingEndpoint, ChatEndpoint, ClientBatchBuilder, ClientEvalBuilder, ClientFlowBuilder, ClientTokenDriftError, ClientTokenEnsureError, ClientTokensEndpoint, ClientTokensNamespace, CollectionsEndpoint, ContextTemplatesEndpoint, ConversationsEndpoint, DEFAULT_MAX_DETACHED_RECONNECTS, DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS, DEFAULT_STALL_STOP_AFTER, DispatchEndpoint, ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE, EvalBuilder, EvalEndpoint, EvalRunner, EvalSuitesNamespace, EvalsNamespace, ExecutionsEndpoint, ExecutionsNamespace, FlowBuilder, FlowDriftError, FlowEnsureConflictError, FlowResult, FlowStepsEndpoint, FlowVersionsEndpoint, FlowsEndpoint, FlowsNamespace, IntegrationsEndpoint, LEDGER_ARTIFACT_LINE_PREFIX, LIVE_AGENT_ALIAS, LogsEndpoint, ModelConfigsEndpoint, ProductDriftError, ProductEnsureConflictError, ProductsNamespace, PromptsEndpoint, PromptsNamespace, ProviderKeysEndpoint, RUNTYPE_CLIENT_KIND, RecordsEndpoint, RunsNamespace, Runtype, RuntypeApiError, RuntypeClient, RuntypeFlowBuilder, SDK_USER_AGENT, SDK_VERSION, STEP_FIELD_REGISTRY, STEP_TYPE_TO_METHOD, SchedulesEndpoint, SecretsEndpoint, SkillDriftError, SkillEnsureConflictError, SkillProposalsNamespace, SkillsNamespace, SurfaceDriftError, SurfaceEnsureConflictError, SurfacesEndpoint, SurfacesNamespace, ToolApprovalGrantsEndpoint, ToolDriftError, ToolEnsureConflictError, ToolsEndpoint, ToolsNamespace, TypedRecordsScope, UNIFIED_EVENTS_QUERY, UsersEndpoint, activateAgentPromotion, agentAliasErrorCode, applyGeneratedRuntimeToolProposalToDispatchRequest, attachRuntimeToolsToDispatchRequest, buildAgentAdmissionHeaders, buildEmptySessionNudge, buildExecutionEventsPath, buildGeneratedRuntimeToolGateOutput, buildLedgerOffloadReference, buildObservationMaskMarker, buildPolicyGuidance, buildSendViewOffloadMarker, calledTool, combineAbortSignals, compileWorkflowConfig, completed, computeAgentContentHash, computeEvalContentHash, computeFlowContentHash, computeFpoContentHash, computeProductContentHash, computeSkillContentHash, computeSurfaceContentHash, computeToolContentHash, contains, cost, createAgentEventTranslator, createClient, createExternalTool, createFlowEventTranslator, defaultWorkflow, defaultWorkflowConfig, defineAgent, defineEval, defineFlow, defineFpo, definePlaybook, defineProduct, defineSkill, defineSurface, defineTool, deployWorkflow, ensureDefaultWorkflowHooks, ensureEval, ensureFpo, evaluateGeneratedRuntimeToolProposal, extractDeclaredToolResultChars, gameWorkflow, getDefaultPlanPath, getLikelySupportingCandidatePaths, interpolateWorkflowTemplate, isBuiltinToolExecuteResponse, isCatalogClientToolRef, isDiscoveryToolName, isMarathonArtifactPath, isPreservationSensitiveTask, isUnifiedEventType, isWorkflowHookRef, jsonField, judge, judges, latency, length, listWorkflowHooks, matchesExpected, maxToolCalls, noError, normalizeAgentDefinition, normalizeCandidatePath, normalizeFpoDefinition, normalizeProductDefinition, normalizeSkillDefinition, normalizeSurfaceDefinition, normalizeToolDefinition, notCalledTool, notContains, parseFinalBuffer, parseLedgerArtifactRelativePath, parseOffloadedOutputId, parseSSEChunk, prepareAgentPromotion, processStream, promoteAgent, promotionIdempotencyKey, pullEval, pullFpo, ranStep, regex, registerWorkflowHook, resolveStallStopAfter, resolveWorkflowHook, runEvalSuite, sanitizeTaskSlug, shouldInjectEmptySessionNudge, shouldRequestModelEscalation, stepOrder, streamEvents, toolOrder, unregisterWorkflowHook, usedNoTools, validJson, validateAgentPromotion, withDetachedReconnect, withUnifiedEvents };
16490
+ export { AgentAliasDependencyError, AgentAliasNotFoundError, AgentAliasPreviewLimitError, AgentAliasRevisionMismatchError, AgentAliasRevisionRequiredError, AgentAliasesNamespace, AgentDeploymentsNamespace, AgentDriftError, AgentEnsureConflictError, AgentPromotionError, AgentVersionsEndpoint, AgentsEndpoint, AgentsNamespace, AnalyticsEndpoint, ApiKeyRequestsEndpoint, ApiKeysEndpoint, BatchBuilder, BatchesNamespace, BillingEndpoint, ChatEndpoint, ClientBatchBuilder, ClientEvalBuilder, ClientFlowBuilder, ClientTokenDriftError, ClientTokenEnsureError, ClientTokensEndpoint, ClientTokensNamespace, CollectionDriftError, CollectionEnsureConflictError, CollectionsEndpoint, ContextTemplatesEndpoint, ConversationsEndpoint, DEFAULT_MAX_DETACHED_RECONNECTS, DEFAULT_RECOVERY_AFTER_EMPTY_SESSIONS, DEFAULT_STALL_STOP_AFTER, DispatchEndpoint, ENSURE_RELEASE_DEPLOY_CONFLICT_MESSAGE, EvalBuilder, EvalDriftError, EvalEndpoint, EvalEnsureConflictError, EvalRunner, EvalSuitesNamespace, EvalsNamespace, ExecutionsEndpoint, ExecutionsNamespace, FlowBuilder, FlowDriftError, FlowEnsureConflictError, FlowResult, FlowStepsEndpoint, FlowVersionsEndpoint, FlowsEndpoint, FlowsNamespace, FpoDriftError, IntegrationsEndpoint, LEDGER_ARTIFACT_LINE_PREFIX, LIVE_AGENT_ALIAS, LogsEndpoint, ModelConfigsEndpoint, ProductDriftError, ProductEnsureConflictError, ProductsNamespace, PromptsEndpoint, PromptsNamespace, ProviderKeysEndpoint, QuestionSetDriftError, QuestionSetEnsureConflictError, QuestionSetsNamespace, RUNTYPE_CLIENT_KIND, RecordsEndpoint, RunsNamespace, Runtype, RuntypeApiError, RuntypeClient, RuntypeFlowBuilder, SDK_USER_AGENT, SDK_VERSION, STEP_FIELD_REGISTRY, STEP_TYPE_TO_METHOD, ScheduleDriftError, ScheduleEnsureConflictError, SchedulesEndpoint, SecretsEndpoint, SkillDriftError, SkillEnsureConflictError, SkillProposalsNamespace, SkillsNamespace, SurfaceDriftError, SurfaceEnsureConflictError, SurfacesEndpoint, SurfacesNamespace, ToolApprovalGrantsEndpoint, ToolDriftError, ToolEnsureConflictError, ToolsEndpoint, ToolsNamespace, TypedRecordsScope, UNIFIED_EVENTS_QUERY, UsersEndpoint, activateAgentPromotion, agentAliasErrorCode, applyGeneratedRuntimeToolProposalToDispatchRequest, attachRuntimeToolsToDispatchRequest, buildAgentAdmissionHeaders, buildEmptySessionNudge, buildExecutionEventsPath, buildGeneratedRuntimeToolGateOutput, buildLedgerOffloadReference, buildObservationMaskMarker, buildPolicyGuidance, buildSendViewOffloadMarker, calledTool, combineAbortSignals, compileWorkflowConfig, completed, computeAgentContentHash, computeEvalContentHash, computeFlowContentHash, computeFpoContentHash, computeProductContentHash, computeQuestionSetContentHash, computeRecordCollectionContentHash, computeScheduleContentHash, computeScheduleDefinitionHash, computeSkillContentHash, computeSurfaceContentHash, computeToolContentHash, contains, cost, createAgentEventTranslator, createClient, createExternalTool, createFlowEventTranslator, defaultWorkflow, defaultWorkflowConfig, defineAgent, defineEval, defineFlow, defineFpo, definePlaybook, defineProduct, defineQuestionSet, defineSchedule, defineSkill, defineSurface, defineTool, deployWorkflow, ensureDefaultWorkflowHooks, ensureEval, ensureFpo, ensureSchedule, evaluateGeneratedRuntimeToolProposal, extractDeclaredToolResultChars, fpoPlanHasDrift, gameWorkflow, getDefaultPlanPath, getLikelySupportingCandidatePaths, interpolateWorkflowTemplate, isBuiltinToolExecuteResponse, isCatalogClientToolRef, isDiscoveryToolName, isMarathonArtifactPath, isPreservationSensitiveTask, isUnifiedEventType, isWorkflowHookRef, jsonField, judge, judges, latency, length, listWorkflowHooks, matchesExpected, maxToolCalls, noError, normalizeAgentDefinition, normalizeCandidatePath, normalizeFpoDefinition, normalizeProductDefinition, normalizeQuestionSetDefinition, normalizeRecordCollectionDefinition, normalizeScheduleContent, normalizeSkillDefinition, normalizeSurfaceDefinition, normalizeToolDefinition, notCalledTool, notContains, parseFinalBuffer, parseLedgerArtifactRelativePath, parseOffloadedOutputId, parseSSEChunk, prepareAgentPromotion, processStream, promoteAgent, promotionIdempotencyKey, pullEval, pullFpo, pullSchedule, ranStep, regex, registerWorkflowHook, resolveStallStopAfter, resolveWorkflowHook, runEvalSuite, sanitizeTaskSlug, shouldInjectEmptySessionNudge, shouldRequestModelEscalation, stepOrder, streamEvents, toolOrder, unregisterWorkflowHook, usedNoTools, validJson, validateAgentPromotion, withDetachedReconnect, withUnifiedEvents };
15684
16491