@stackbone/cli 0.9.3 → 0.9.4

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/main.js CHANGED
@@ -7,7 +7,7 @@ import "semver/functions/compare.js";
7
7
  import "semver/functions/prerelease.js";
8
8
  import "semver/functions/valid.js";
9
9
  import { access, appendFile, chmod, constants, copyFile, cp, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
10
- import { homedir, tmpdir } from "node:os";
10
+ import { homedir, release, tmpdir } from "node:os";
11
11
  import path, { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
12
12
  import semver from "semver";
13
13
  import pino from "pino";
@@ -132,7 +132,7 @@ var init_organizations = __esmMin((() => {
132
132
  name: z.string().trim().min(1).max(120).optional(),
133
133
  tier: z.literal("free").default("free")
134
134
  });
135
- })), modelProviderSlotSchema, MODEL_PROVIDER_SLOTS, SLOT_NAMES, modelProviderSourceSchema, isModelProviderValuePresent, firstPresentName, resolveModelProviderSlots, overriddenExportedSlots, MODEL_PROVIDER_OVERRIDE_SECRET, storedOverridesSchema, encodeModelProviderOverrides, decodeModelProviderOverrides, writeModelProviderSlotsToEnv, modelProviderKindSchema, modelProviderGetResponseSchema, modelProviderPutRequestSchema, modelProviderAgentsOutcomeSchema, modelProviderPutResponseSchema, modelProviderFirstRunResponseSchema, modelProviderKeyCheckSchema, modelProviderTestResponseSchema, modelProviderModelTypeSchema, modelProviderModelSchema, canServeText, modelProviderModelsRequestSchema, modelProviderModelsResponseSchema, MODEL_PROVIDER_DEFAULT_ID, MODEL_PROVIDER_ID_PATTERN, modelProviderIdSchema, pickedModelProviderIdSchema, pinModelId, unpinModelId, modelProviderListItemSchema, modelProviderListResponseSchema, providerFields, modelProviderCreateRequestSchema, modelProviderUpdateRequestSchema;
135
+ })), modelProviderSlotSchema, MODEL_PROVIDER_SLOTS, SLOT_NAMES, modelProviderSourceSchema, isModelProviderValuePresent, firstPresentName, resolveModelProviderSlots, overriddenExportedSlots, MODEL_PROVIDER_OVERRIDE_SECRET, storedOverridesSchema, encodeModelProviderOverrides, decodeModelProviderOverrides, writeModelProviderSlotsToEnv, modelProviderKindSchema, modelProviderGetResponseSchema, modelProviderPutRequestSchema, modelProviderAgentsOutcomeSchema, modelProviderPutResponseSchema, modelProviderFirstRunResponseSchema, modelProviderKeyCheckSchema, modelProviderTestResponseSchema, modelProviderModelTypeSchema, modelProviderModelSchema, canServeText, modelProviderModelsRequestSchema, modelProviderModelsResponseSchema, MODEL_PROVIDER_DEFAULT_ID, LIVEKIT_MODEL_PROVIDER_ID, MODEL_PROVIDER_ID_PATTERN, modelProviderIdSchema, pickedModelProviderIdSchema, pinModelId, unpinModelId, modelProviderListItemSchema, modelProviderListResponseSchema, providerFields, modelProviderCreateRequestSchema, modelProviderUpdateRequestSchema;
136
136
  var init_studio_model_provider = __esmMin((() => {
137
137
  modelProviderSlotSchema = z.enum(["baseUrl", "apiKey"]);
138
138
  MODEL_PROVIDER_SLOTS = {
@@ -226,7 +226,8 @@ var init_studio_model_provider = __esmMin((() => {
226
226
  "openrouter",
227
227
  "ollama",
228
228
  "lmstudio",
229
- "generic"
229
+ "generic",
230
+ "livekit"
230
231
  ]);
231
232
  modelProviderGetResponseSchema = z.object({
232
233
  configured: z.boolean(),
@@ -301,6 +302,7 @@ var init_studio_model_provider = __esmMin((() => {
301
302
  warning: z.string().optional()
302
303
  });
303
304
  MODEL_PROVIDER_DEFAULT_ID = "default";
305
+ LIVEKIT_MODEL_PROVIDER_ID = "livekit";
304
306
  MODEL_PROVIDER_ID_PATTERN = /^[a-z][a-z0-9-]{0,30}[a-z0-9]$/;
305
307
  modelProviderIdSchema = z.string().regex(MODEL_PROVIDER_ID_PATTERN, { message: "Lowercase letters, digits and dashes, 2 to 32, starting with a letter." }).refine((id) => id !== MODEL_PROVIDER_DEFAULT_ID, { message: `'${MODEL_PROVIDER_DEFAULT_ID}' is provider 1's id.` });
306
308
  pickedModelProviderIdSchema = z.string().regex(MODEL_PROVIDER_ID_PATTERN);
@@ -323,7 +325,11 @@ var init_studio_model_provider = __esmMin((() => {
323
325
  kind: modelProviderKindSchema,
324
326
  baseUrl: z.string(),
325
327
  apiKey: z.string().nullable(),
326
- source: z.enum(["env", "local"]),
328
+ source: z.enum([
329
+ "env",
330
+ "local",
331
+ "voice"
332
+ ]),
327
333
  removable: z.boolean(),
328
334
  responding: z.boolean().nullable(),
329
335
  error: z.string().optional()
@@ -997,9 +1003,9 @@ function requireCapability$1(capability, contract) {
997
1003
  error: null
998
1004
  };
999
1005
  }
1000
- var CAPABILITIES$1, MODULE_CAPABILITIES, contractBuildNameSchema, contractResponseSchema;
1006
+ var CAPABILITIES$2, MODULE_CAPABILITIES, contractBuildNameSchema, contractResponseSchema;
1001
1007
  var init_contract$1 = __esmMin((() => {
1002
- CAPABILITIES$1 = [
1008
+ CAPABILITIES$2 = [
1003
1009
  "database.postgres_direct",
1004
1010
  "database.drizzle",
1005
1011
  "rag.basic",
@@ -1017,7 +1023,7 @@ var init_contract$1 = __esmMin((() => {
1017
1023
  "insights.basic",
1018
1024
  "limits.basic"
1019
1025
  ];
1020
- new Set(CAPABILITIES$1);
1026
+ new Set(CAPABILITIES$2);
1021
1027
  MODULE_CAPABILITIES = {
1022
1028
  database: "database.postgres_direct",
1023
1029
  rag: "rag.basic",
@@ -3045,7 +3051,7 @@ var init_studio_internal_prompts = __esmMin((() => {
3045
3051
  ownerName: "guardrail-classifier",
3046
3052
  key: GUIDANCE_KEY,
3047
3053
  title: "Guardrail classifier",
3048
- description: "Frames the model a model-backed guardrail asks about a turn (prompt injection, moderation, off-limits subjects). The question, the answer format and the rule that the checked text is data are added after it.",
3054
+ description: "Frames the model a model-backed guardrail asks about a turn (prompt injection, moderation, off-limits subjects). The question, the answer format and the rule that the checked text is data are added after it. On an OpenRouter box the decision model (Jev) decides the incoming message and the reply first and does not read this text; it frames the checking model only, when Jev cannot answer. Put any nuance in the rule's own text.",
3049
3055
  model: {
3050
3056
  target: "setting",
3051
3057
  settingKey: "guardrailModel"
@@ -5693,11 +5699,14 @@ var init_studio_triggers = __esmMin((() => {
5693
5699
  /** Where the sample event came from (STA3-45 D5): the named link's own first
5694
5700
  * event (`link`), the provider's newest event fetched for a link that had none
5695
5701
  * (`fetched`), or the trigger's declared sample (`declared`) — which is also the
5696
- * answer when that fetch found nothing or failed. */
5702
+ * answer when that fetch found nothing or failed. `none` is a webhook trigger
5703
+ * with no event of its own yet (STA3-121): nothing was evaluated, `output` is
5704
+ * `null` and `issues` empty, so it is no verdict, not a passing one. */
5697
5705
  sampleSource: z.enum([
5698
5706
  "link",
5699
5707
  "fetched",
5700
- "declared"
5708
+ "declared",
5709
+ "none"
5701
5710
  ]),
5702
5711
  /** The event fetched from the provider, present only on a `fetched` answer: the
5703
5712
  * one evaluated over, even when the link already kept another in the meantime.
@@ -11650,7 +11659,7 @@ var init_tool_call_ids = __esmMin((() => {
11650
11659
  //#endregion
11651
11660
  //#region ../../libs/shared/model-provider-wire/src/lib/common/rules.ts
11652
11661
  var withNormalizedToolCalls, withNormalizedToolCallId, normalizeMessage, applyCommonRules;
11653
- var init_rules$4 = __esmMin((() => {
11662
+ var init_rules$5 = __esmMin((() => {
11654
11663
  init_json();
11655
11664
  init_reasoning();
11656
11665
  init_tool_call_ids();
@@ -11694,9 +11703,15 @@ var init_rules$4 = __esmMin((() => {
11694
11703
  };
11695
11704
  }));
11696
11705
  //#endregion
11706
+ //#region ../../libs/shared/model-provider-wire/src/lib/common/gateway.ts
11707
+ var keepRequest;
11708
+ var init_gateway = __esmMin((() => {
11709
+ keepRequest = (body) => body;
11710
+ }));
11711
+ //#endregion
11697
11712
  //#region ../../libs/shared/model-provider-wire/src/lib/common/catalogue.ts
11698
11713
  var UNKNOWN_CAPABILITIES, openAiRoot, nativeRoot, dataEntriesOf, responsesFor, listedEntries, fromCapabilityList;
11699
- var init_catalogue$5 = __esmMin((() => {
11714
+ var init_catalogue$6 = __esmMin((() => {
11700
11715
  init_json();
11701
11716
  UNKNOWN_CAPABILITIES = {
11702
11717
  tools: null,
@@ -11815,8 +11830,8 @@ var init_model_type = __esmMin((() => {
11815
11830
  //#endregion
11816
11831
  //#region ../../libs/shared/model-provider-wire/src/lib/generic/catalogue.ts
11817
11832
  var LITELLM_MODES, typeOf$2, genericCatalogue;
11818
- var init_catalogue$4 = __esmMin((() => {
11819
- init_catalogue$5();
11833
+ var init_catalogue$5 = __esmMin((() => {
11834
+ init_catalogue$6();
11820
11835
  init_json();
11821
11836
  init_model_type();
11822
11837
  LITELLM_MODES = new Map([
@@ -11851,25 +11866,97 @@ var init_catalogue$4 = __esmMin((() => {
11851
11866
  return types;
11852
11867
  },
11853
11868
  fallbackType: genericFallbackType,
11854
- refusesUnknownModelId: false
11869
+ refusesUnknownModelId: false,
11870
+ fixedModelIds: null
11855
11871
  };
11856
11872
  }));
11857
11873
  //#endregion
11858
11874
  //#region ../../libs/shared/model-provider-wire/src/lib/generic/rules.ts
11859
11875
  var genericRules;
11860
- var init_rules$3 = __esmMin((() => {
11861
- init_catalogue$4();
11876
+ var init_rules$4 = __esmMin((() => {
11877
+ init_gateway();
11878
+ init_catalogue$5();
11862
11879
  genericRules = {
11863
11880
  wantsReasoning: () => false,
11864
11881
  keepsProviderRouting: false,
11882
+ adaptRequest: keepRequest,
11865
11883
  catalogue: genericCatalogue
11866
11884
  };
11867
11885
  }));
11868
11886
  //#endregion
11887
+ //#region ../../libs/shared/model-provider-wire/src/lib/livekit/catalogue.ts
11888
+ var LIVEKIT_INFERENCE_MODEL_IDS, LISTED, CAPABILITIES$1, TYPES, livekitCatalogue;
11889
+ var init_catalogue$4 = __esmMin((() => {
11890
+ LIVEKIT_INFERENCE_MODEL_IDS = [
11891
+ "google/gemma-4-31b-it",
11892
+ "google/gemini-3.1-flash-lite",
11893
+ "google/gemini-3.5-flash",
11894
+ "google/gemini-3-flash",
11895
+ "google/gemini-3.1-pro",
11896
+ "google/gemini-2.5-flash-lite",
11897
+ "google/gemini-2.5-flash",
11898
+ "google/gemini-2.5-pro",
11899
+ "openai/gpt-5.5",
11900
+ "openai/gpt-5.4",
11901
+ "openai/gpt-5.4-mini",
11902
+ "openai/gpt-5.4-nano",
11903
+ "openai/gpt-5.2",
11904
+ "openai/gpt-5.1",
11905
+ "openai/gpt-5",
11906
+ "openai/gpt-5-mini",
11907
+ "openai/gpt-5-nano",
11908
+ "openai/gpt-4.1",
11909
+ "openai/gpt-4.1-mini",
11910
+ "openai/gpt-4.1-nano",
11911
+ "openai/gpt-4o",
11912
+ "openai/gpt-4o-mini",
11913
+ "openai/gpt-oss-120b",
11914
+ "moonshotai/kimi-k2.6",
11915
+ "deepseek-ai/deepseek-v3.2",
11916
+ "zai/glm-5.1",
11917
+ "xai/grok-4.3"
11918
+ ];
11919
+ LISTED = {
11920
+ tools: true,
11921
+ reasoning: null
11922
+ };
11923
+ CAPABILITIES$1 = new Map(LIVEKIT_INFERENCE_MODEL_IDS.map((id) => [id, LISTED]));
11924
+ TYPES = new Map(LIVEKIT_INFERENCE_MODEL_IDS.map((id) => [id, "chat"]));
11925
+ livekitCatalogue = {
11926
+ catalogueSources: () => [],
11927
+ capabilities: () => CAPABILITIES$1,
11928
+ types: () => TYPES,
11929
+ fallbackType: () => "chat",
11930
+ refusesUnknownModelId: false,
11931
+ fixedModelIds: LIVEKIT_INFERENCE_MODEL_IDS
11932
+ };
11933
+ }));
11934
+ //#endregion
11935
+ //#region ../../libs/shared/model-provider-wire/src/lib/livekit/rules.ts
11936
+ var COMPLETION_TOKENS_PREFIX, withCompletionTokens, livekitRules;
11937
+ var init_rules$3 = __esmMin((() => {
11938
+ init_catalogue$4();
11939
+ COMPLETION_TOKENS_PREFIX = "openai/gpt-5";
11940
+ withCompletionTokens = (body, modelId) => {
11941
+ if (!modelId?.startsWith(COMPLETION_TOKENS_PREFIX) || !("max_tokens" in body)) return body;
11942
+ const { max_tokens: maxTokens, ...rest } = body;
11943
+ return "max_completion_tokens" in rest ? rest : {
11944
+ ...rest,
11945
+ max_completion_tokens: maxTokens
11946
+ };
11947
+ };
11948
+ livekitRules = {
11949
+ wantsReasoning: () => false,
11950
+ keepsProviderRouting: false,
11951
+ adaptRequest: withCompletionTokens,
11952
+ catalogue: livekitCatalogue
11953
+ };
11954
+ }));
11955
+ //#endregion
11869
11956
  //#region ../../libs/shared/model-provider-wire/src/lib/lmstudio/catalogue.ts
11870
11957
  var capabilitiesOf$2, typeOf$1, describedModels, lmstudioCatalogue;
11871
11958
  var init_catalogue$3 = __esmMin((() => {
11872
- init_catalogue$5();
11959
+ init_catalogue$6();
11873
11960
  init_model_type$1();
11874
11961
  init_json();
11875
11962
  capabilitiesOf$2 = (entry) => {
@@ -11913,17 +12000,20 @@ var init_catalogue$3 = __esmMin((() => {
11913
12000
  return types;
11914
12001
  },
11915
12002
  fallbackType: deriveModelType,
11916
- refusesUnknownModelId: true
12003
+ refusesUnknownModelId: true,
12004
+ fixedModelIds: null
11917
12005
  };
11918
12006
  }));
11919
12007
  //#endregion
11920
12008
  //#region ../../libs/shared/model-provider-wire/src/lib/lmstudio/rules.ts
11921
12009
  var lmstudioRules;
11922
12010
  var init_rules$2 = __esmMin((() => {
12011
+ init_gateway();
11923
12012
  init_catalogue$3();
11924
12013
  lmstudioRules = {
11925
12014
  wantsReasoning: () => false,
11926
12015
  keepsProviderRouting: false,
12016
+ adaptRequest: keepRequest,
11927
12017
  catalogue: lmstudioCatalogue
11928
12018
  };
11929
12019
  }));
@@ -11931,7 +12021,7 @@ var init_rules$2 = __esmMin((() => {
11931
12021
  //#region ../../libs/shared/model-provider-wire/src/lib/ollama/catalogue.ts
11932
12022
  var TOOLS_CAPABILITIES, REASONING_CAPABILITIES, CHAT_CAPABILITIES, EMBEDDING_CAPABILITIES, capabilitiesOf$1, typeOf, taggedModels, taggedId, describedCapabilities, ollamaCatalogue;
11933
12023
  var init_catalogue$2 = __esmMin((() => {
11934
- init_catalogue$5();
12024
+ init_catalogue$6();
11935
12025
  init_model_type$1();
11936
12026
  init_json();
11937
12027
  TOOLS_CAPABILITIES = ["tools"];
@@ -12004,17 +12094,20 @@ var init_catalogue$2 = __esmMin((() => {
12004
12094
  return types;
12005
12095
  },
12006
12096
  fallbackType: deriveModelType,
12007
- refusesUnknownModelId: false
12097
+ refusesUnknownModelId: false,
12098
+ fixedModelIds: null
12008
12099
  };
12009
12100
  }));
12010
12101
  //#endregion
12011
12102
  //#region ../../libs/shared/model-provider-wire/src/lib/ollama/rules.ts
12012
12103
  var ollamaRules;
12013
12104
  var init_rules$1 = __esmMin((() => {
12105
+ init_gateway();
12014
12106
  init_catalogue$2();
12015
12107
  ollamaRules = {
12016
12108
  wantsReasoning: () => false,
12017
12109
  keepsProviderRouting: false,
12110
+ adaptRequest: keepRequest,
12018
12111
  catalogue: ollamaCatalogue
12019
12112
  };
12020
12113
  }));
@@ -12022,7 +12115,7 @@ var init_rules$1 = __esmMin((() => {
12022
12115
  //#region ../../libs/shared/model-provider-wire/src/lib/openrouter/catalogue.ts
12023
12116
  var TOOLS_PARAMETERS, REASONING_PARAMETERS, capabilitiesOf, openrouterCatalogue;
12024
12117
  var init_catalogue$1 = __esmMin((() => {
12025
- init_catalogue$5();
12118
+ init_catalogue$6();
12026
12119
  init_model_type$1();
12027
12120
  TOOLS_PARAMETERS = ["tools"];
12028
12121
  REASONING_PARAMETERS = ["reasoning", "include_reasoning"];
@@ -12042,18 +12135,21 @@ var init_catalogue$1 = __esmMin((() => {
12042
12135
  },
12043
12136
  types: () => /* @__PURE__ */ new Map(),
12044
12137
  fallbackType: deriveModelType,
12045
- refusesUnknownModelId: false
12138
+ refusesUnknownModelId: false,
12139
+ fixedModelIds: null
12046
12140
  };
12047
12141
  }));
12048
12142
  //#endregion
12049
12143
  //#region ../../libs/shared/model-provider-wire/src/lib/openrouter/rules.ts
12050
12144
  var openrouterRules;
12051
12145
  var init_rules = __esmMin((() => {
12146
+ init_gateway();
12052
12147
  init_reasoning_request();
12053
12148
  init_catalogue$1();
12054
12149
  openrouterRules = {
12055
12150
  wantsReasoning: (modelId) => !replaysSignedReasoning(modelId),
12056
12151
  keepsProviderRouting: true,
12152
+ adaptRequest: keepRequest,
12057
12153
  catalogue: openrouterCatalogue
12058
12154
  };
12059
12155
  }));
@@ -12061,6 +12157,7 @@ var init_rules = __esmMin((() => {
12061
12157
  //#region ../../libs/shared/model-provider-wire/src/lib/gateway-rules.ts
12062
12158
  var BY_KIND, gatewayRulesFor;
12063
12159
  var init_gateway_rules = __esmMin((() => {
12160
+ init_rules$4();
12064
12161
  init_rules$3();
12065
12162
  init_rules$2();
12066
12163
  init_rules$1();
@@ -12069,7 +12166,8 @@ var init_gateway_rules = __esmMin((() => {
12069
12166
  openrouter: openrouterRules,
12070
12167
  ollama: ollamaRules,
12071
12168
  lmstudio: lmstudioRules,
12072
- generic: genericRules
12169
+ generic: genericRules,
12170
+ livekit: livekitRules
12073
12171
  };
12074
12172
  gatewayRulesFor = (kind) => BY_KIND[kind];
12075
12173
  }));
@@ -12080,20 +12178,27 @@ var init_normalize_request = __esmMin((() => {
12080
12178
  init_json();
12081
12179
  init_provider_routing();
12082
12180
  init_reasoning_request();
12083
- init_rules$4();
12181
+ init_rules$5();
12084
12182
  init_gateway_rules();
12085
12183
  normalizeRequest = (body, target) => {
12086
12184
  if (!isJsonRecord(body)) return body;
12087
12185
  const common = applyCommonRules(body);
12088
12186
  const rules = gatewayRulesFor(target.gatewayKind);
12089
- return withProviderRouting(withReasoningRequest(common, rules.wantsReasoning(target.modelId)), rules.keepsProviderRouting);
12187
+ const routed = withProviderRouting(withReasoningRequest(common, rules.wantsReasoning(target.modelId)), rules.keepsProviderRouting);
12188
+ return rules.adaptRequest(routed, target.modelId);
12090
12189
  };
12091
- })), OPENROUTER_HOST, gatewayKindForBaseUrl;
12190
+ })), OPENROUTER_HOST, LIVEKIT_INFERENCE_BASE_URL, LIVEKIT_INFERENCE_HOST, KIND_BY_HOST, gatewayKindForBaseUrl;
12092
12191
  var init_target = __esmMin((() => {
12093
12192
  OPENROUTER_HOST = "openrouter.ai";
12193
+ LIVEKIT_INFERENCE_BASE_URL = "https://agent-gateway.livekit.cloud/v1";
12194
+ LIVEKIT_INFERENCE_HOST = "agent-gateway.livekit.cloud";
12195
+ KIND_BY_HOST = {
12196
+ [OPENROUTER_HOST]: "openrouter",
12197
+ [LIVEKIT_INFERENCE_HOST]: "livekit"
12198
+ };
12094
12199
  gatewayKindForBaseUrl = (baseUrl) => {
12095
12200
  try {
12096
- return new URL(baseUrl).hostname.replace(/\.$/, "") === OPENROUTER_HOST ? "openrouter" : "generic";
12201
+ return KIND_BY_HOST[new URL(baseUrl).hostname.replace(/\.$/, "")] ?? "generic";
12097
12202
  } catch {
12098
12203
  return "generic";
12099
12204
  }
@@ -12183,9 +12288,9 @@ var init_catalogue_entry = __esmMin((() => {
12183
12288
  }));
12184
12289
  //#endregion
12185
12290
  //#region ../../libs/shared/model-provider-wire/src/lib/catalogue.ts
12186
- var modelsListSource, commonSources, catalogueSources, parseCatalogue, listedModelIds, checkModelId;
12291
+ var modelsListSource, commonSources, catalogueSources, parseCatalogue, fixedCatalogue, listedModelIds, checkModelId;
12187
12292
  var init_catalogue = __esmMin((() => {
12188
- init_catalogue$5();
12293
+ init_catalogue$6();
12189
12294
  init_catalogue_entry();
12190
12295
  init_gateway_rules();
12191
12296
  modelsListSource = (baseUrl) => ({
@@ -12226,6 +12331,14 @@ var init_catalogue = __esmMin((() => {
12226
12331
  }
12227
12332
  return models;
12228
12333
  };
12334
+ fixedCatalogue = (kind, baseUrl) => {
12335
+ const ids = gatewayRulesFor(kind).catalogue.fixedModelIds;
12336
+ if (ids === null) return null;
12337
+ return parseCatalogue(kind, [{
12338
+ source: modelsListSource(baseUrl),
12339
+ json: { data: ids.map((id) => ({ id })) }
12340
+ }]);
12341
+ };
12229
12342
  listedModelIds = (json) => dataEntriesOf(json).flatMap((entry) => {
12230
12343
  const id = entry["id"];
12231
12344
  return typeof id === "string" ? [id] : [];
@@ -12255,10 +12368,14 @@ var init_src$18 = __esmMin((() => {
12255
12368
  }));
12256
12369
  //#endregion
12257
12370
  //#region ../../libs/sdk/src/surfaces/external/ai/model-call-target.ts
12258
- var MODEL_PROXY_URL_ENV, MODEL_PROXY_TOKEN_ENV, present$2, resolveModelCallTarget, pinModelForCallTarget;
12371
+ var STACKBONE_DEFAULT_HEADERS, MODEL_PROXY_URL_ENV, MODEL_PROXY_TOKEN_ENV, present$2, resolveModelCallTarget, pinModelForCallTarget;
12259
12372
  var init_model_call_target = __esmMin((() => {
12260
12373
  init_src$26();
12261
12374
  init_src$18();
12375
+ STACKBONE_DEFAULT_HEADERS = {
12376
+ "HTTP-Referer": "https://stackbone.ai",
12377
+ "X-Title": "Stackbone Agent Platform"
12378
+ };
12262
12379
  MODEL_PROXY_URL_ENV = "STACKBONE_MODEL_PROXY_URL";
12263
12380
  MODEL_PROXY_TOKEN_ENV = "STACKBONE_MODEL_PROXY_TOKEN";
12264
12381
  present$2 = (value) => value !== void 0 && value !== "";
@@ -15821,7 +15938,7 @@ function parseRewriteReply(content) {
15821
15938
  let parsed;
15822
15939
  try {
15823
15940
  const raw = (content ?? "").trim();
15824
- parsed = JSON.parse(JSON_FENCE.exec(raw)?.[1] ?? raw);
15941
+ parsed = JSON.parse(JSON_FENCE$1.exec(raw)?.[1] ?? raw);
15825
15942
  } catch {
15826
15943
  return null;
15827
15944
  }
@@ -15852,12 +15969,12 @@ function interleave(lists, topK) {
15852
15969
  }
15853
15970
  return merged;
15854
15971
  }
15855
- var MAX_REWRITE_QUERIES, JSON_FENCE, REPLY_FORMAT, Rewriter;
15972
+ var MAX_REWRITE_QUERIES, JSON_FENCE$1, REPLY_FORMAT, Rewriter;
15856
15973
  var init_rewriter = __esmMin((() => {
15857
15974
  init_src$26();
15858
15975
  init_result$1();
15859
15976
  MAX_REWRITE_QUERIES = 3;
15860
- JSON_FENCE = /^```(?:json)?([\s\S]*)```$/i;
15977
+ JSON_FENCE$1 = /^```(?:json)?([\s\S]*)```$/i;
15861
15978
  REPLY_FORMAT = `Write at most ${MAX_REWRITE_QUERIES} queries. Reply with JSON only: {"queries": ["...", "..."]}.`;
15862
15979
  Rewriter = class {
15863
15980
  constructor(options) {
@@ -17045,7 +17162,7 @@ function mapApiError(raw, model) {
17045
17162
  cause: raw
17046
17163
  };
17047
17164
  }
17048
- var AiModule, ChatCompletionsNamespace, EmbeddingsNamespace, ImagesNamespace, ModelsNamespace, RerankNamespace, rerankResponseSchema, STACKBONE_DEFAULT_HEADERS, OPENROUTER_KEY_MISSING_MESSAGE, DATA_URL_RE;
17165
+ var AiModule, ChatCompletionsNamespace, EmbeddingsNamespace, ImagesNamespace, ModelsNamespace, RerankNamespace, rerankResponseSchema, OPENROUTER_KEY_MISSING_MESSAGE, DATA_URL_RE;
17049
17166
  var init_ai = __esmMin((() => {
17050
17167
  init_module_gate();
17051
17168
  init_result$1();
@@ -17286,10 +17403,6 @@ var init_ai = __esmMin((() => {
17286
17403
  index: z.number().int().nonnegative(),
17287
17404
  relevance_score: z.number()
17288
17405
  })) });
17289
- STACKBONE_DEFAULT_HEADERS = {
17290
- "HTTP-Referer": "https://stackbone.ai",
17291
- "X-Title": "Stackbone Agent Platform"
17292
- };
17293
17406
  OPENROUTER_KEY_MISSING_MESSAGE = "No model provider is configured — set `MODEL_PROVIDER_API_KEY` in the container, pass `modelProviderKey` to `createClient({...})`, or configure it from the Studio \"Model provider\" screen.";
17294
17407
  DATA_URL_RE = /^data:(image\/[a-zA-Z0-9.+-]+);base64,(.*)$/;
17295
17408
  }));
@@ -18457,6 +18570,15 @@ function resolveBrowserbaseKeepAlive() {
18457
18570
  return !(raw === "0" || raw === "false");
18458
18571
  }
18459
18572
  /**
18573
+ * On WSL, `chrome-launcher` rewrites `--user-data-dir` to a Windows UNC path even for the
18574
+ * Linux Chromium we pin; that Chromium cannot use it and never opens its debugging port
18575
+ * (STA3-109). A Linux `--user-data-dir` in `args` lands after it, and Chromium keeps the
18576
+ * last occurrence. Setting `userDataDir` instead does not help: it gets rewritten too.
18577
+ */
18578
+ function isWsl() {
18579
+ return release().toLowerCase().includes("microsoft");
18580
+ }
18581
+ /**
18460
18582
  * Resolve the Chrome/Chromium executable Stagehand's `chrome-launcher` should launch for a
18461
18583
  * LOCAL browser. Precedence: an explicit `STACKBONE_BROWSER_EXECUTABLE_PATH`, else the
18462
18584
  * workspace's Playwright Chromium (`playwright`'s `chromium.executablePath()`). Returns
@@ -18719,28 +18841,44 @@ function createStagehandBrowserFactory(options) {
18719
18841
  } : void 0;
18720
18842
  const initialState = options?.loadInitialStorageState ? await options.loadInitialStorageState().catch(() => void 0) : void 0;
18721
18843
  const hasInitialState = initialState !== void 0 && isNonEmptyStorageState(initialState);
18844
+ const { Stagehand, CustomOpenAIClient } = await importStagehand();
18845
+ if (typeof CustomOpenAIClient !== "function") throw new Error(`browserTools: the installed @browserbasehq/stagehand does not export CustomOpenAIClient, which the browser tools need to reach the model provider. Install ${STAGEHAND_INSTALL_SPEC}.`);
18722
18846
  const localBrowserLaunchOptions = {};
18847
+ let wslProfileDir;
18723
18848
  if (!browserbase) {
18724
18849
  if (cdpUrl) localBrowserLaunchOptions["cdpUrl"] = cdpUrl;
18725
- if (headed) localBrowserLaunchOptions["headless"] = false;
18850
+ if (!cdpUrl) localBrowserLaunchOptions["headless"] = !headed;
18726
18851
  if (hasInitialState) localBrowserLaunchOptions["storageState"] = initialState;
18727
18852
  if (!cdpUrl) {
18728
18853
  const executablePath = await resolveChromeExecutablePath();
18729
18854
  if (executablePath) localBrowserLaunchOptions["executablePath"] = executablePath;
18730
- localBrowserLaunchOptions["args"] = ["--no-sandbox", "--disable-dev-shm-usage"];
18855
+ const args = ["--no-sandbox", "--disable-dev-shm-usage"];
18856
+ if (isWsl()) {
18857
+ wslProfileDir = await mkdtemp(join(tmpdir(), "stackbone-chromium-"));
18858
+ args.push(`--user-data-dir=${wslProfileDir}`);
18859
+ }
18860
+ localBrowserLaunchOptions["args"] = args;
18731
18861
  }
18732
18862
  }
18733
- const model = {
18734
- modelName: options?.modelName ?? DEFAULT_BROWSER_MODEL,
18735
- apiKey,
18736
- baseURL
18737
- };
18738
- const { Stagehand } = await importStagehand();
18863
+ const modelName = options?.modelName ?? DEFAULT_BROWSER_MODEL;
18864
+ const llmClient = new CustomOpenAIClient({
18865
+ modelName,
18866
+ client: callWithBrowserModel(new OpenAI({
18867
+ apiKey,
18868
+ baseURL,
18869
+ defaultHeaders: STACKBONE_DEFAULT_HEADERS
18870
+ }))
18871
+ });
18872
+ const removeWslProfileDir = () => wslProfileDir ? rm(wslProfileDir, {
18873
+ recursive: true,
18874
+ force: true
18875
+ }) : Promise.resolve();
18739
18876
  const stagehand = new Stagehand(browserbase ? {
18740
18877
  env: "BROWSERBASE",
18741
18878
  apiKey: browserbase.apiKey,
18742
18879
  projectId: browserbase.projectId,
18743
- model,
18880
+ model: modelName,
18881
+ llmClient,
18744
18882
  browserbaseSessionCreateParams: {
18745
18883
  projectId: browserbase.projectId,
18746
18884
  proxies: true,
@@ -18750,16 +18888,28 @@ function createStagehandBrowserFactory(options) {
18750
18888
  }
18751
18889
  } : {
18752
18890
  env: "LOCAL",
18753
- model,
18891
+ model: modelName,
18892
+ llmClient,
18754
18893
  ...Object.keys(localBrowserLaunchOptions).length > 0 ? { localBrowserLaunchOptions } : {}
18755
18894
  });
18756
- await stagehand.init();
18895
+ try {
18896
+ await stagehand.init();
18897
+ } catch (err) {
18898
+ await removeWslProfileDir();
18899
+ throw err;
18900
+ }
18757
18901
  const pageHandle = stagehand.context.activePage() ?? await stagehand.context.newPage();
18758
18902
  if (hasInitialState) await injectStorageStateViaCDP(pageHandle, initialState);
18759
18903
  const liveViewUrl = browserbase ? await resolveBrowserbaseLiveViewUrl(stagehand, browserbase, resolveLiveViewUrl) : void 0;
18760
18904
  return {
18761
18905
  page: adaptStagehandPage(stagehand, pageHandle),
18762
- close: () => stagehand.close(),
18906
+ close: async () => {
18907
+ try {
18908
+ await stagehand.close();
18909
+ } finally {
18910
+ await removeWslProfileDir();
18911
+ }
18912
+ },
18763
18913
  isLive: async () => {
18764
18914
  try {
18765
18915
  return await probeStagehandLive(stagehand.context.activePage() ?? pageHandle);
@@ -18772,6 +18922,43 @@ function createStagehandBrowserFactory(options) {
18772
18922
  } };
18773
18923
  }
18774
18924
  /**
18925
+ * Make `client`'s chat calls carry the model of the browser tool call in flight
18926
+ * ({@link withBrowserModel}), keeping the request's own model outside one, and hand back
18927
+ * a `json_object` reply in a Markdown code fence as the bare JSON ({@link unfenceJsonReplies}).
18928
+ * Patched on the `openai` client because `CustomOpenAIClient` writes its launch
18929
+ * `modelName` into every request body and runs a bare `JSON.parse` on the reply.
18930
+ */
18931
+ function callWithBrowserModel(client) {
18932
+ const completions = client.chat.completions;
18933
+ const create = completions.create.bind(completions);
18934
+ completions.create = (async (body, options) => {
18935
+ const completion = await create({
18936
+ ...body,
18937
+ model: browserModel.getStore() ?? body.model
18938
+ }, options);
18939
+ return body.response_format?.type === "json_object" ? unfenceJsonReplies(completion) : completion;
18940
+ });
18941
+ return client;
18942
+ }
18943
+ /**
18944
+ * Replace each choice's content that is one Markdown code fence with the JSON inside
18945
+ * it, in place. OpenRouter does not apply `json_object` to Anthropic models, so
18946
+ * `anthropic/claude-haiku-4.5` fences every JSON reply, which Stagehand's `JSON.parse`
18947
+ * rejects (STA3-120 D6). Anything else (a stream, a tool call, unfenced text) passes
18948
+ * through untouched.
18949
+ */
18950
+ function unfenceJsonReplies(completion) {
18951
+ const choices = completion?.choices;
18952
+ if (!Array.isArray(choices)) return completion;
18953
+ for (const choice of choices) {
18954
+ const message = choice?.message;
18955
+ if (typeof message?.content !== "string") continue;
18956
+ const json = JSON_FENCE.exec(message.content.trim())?.[1];
18957
+ if (json !== void 0) message.content = json.trim();
18958
+ }
18959
+ return completion;
18960
+ }
18961
+ /**
18775
18962
  * Resolve the Browserbase live-view URL for an initialized Stagehand instance
18776
18963
  * (STA-140): read the session id it exposes, then hand it to `resolve` with the
18777
18964
  * account creds. Returns `undefined` when the instance carries no session id — never
@@ -18790,10 +18977,10 @@ async function resolveBrowserbaseLiveViewUrl(stagehand, credentials, resolve) {
18790
18977
  * the page, but the AI operations moved OFF the page onto the instance and now take
18791
18978
  * POSITIONAL args (`act(instruction, opts)`, `extract(instruction, schema, opts)`,
18792
18979
  * `observe(instruction, opts)`). The tools still speak the v2 options-object shape
18793
- * (`act({ action, modelName })`, `extract({ instruction, schema, modelName })`), so
18794
- * this adapter translates — keeping every tool body and its tests untouched across
18795
- * the v2→v3 change. The creator's per-call `modelName` (and any other option) is
18796
- * forwarded verbatim as the trailing options object.
18980
+ * (`act({ action })`, `extract({ instruction, schema })`), so this adapter translates —
18981
+ * keeping every tool body and its tests untouched across the v2→v3 change. Any other
18982
+ * option is forwarded verbatim as the trailing options object. The model is not one of
18983
+ * them: it rides the call's async context ({@link withBrowserModel}).
18797
18984
  */
18798
18985
  function adaptStagehandPage(stagehand, initialPage) {
18799
18986
  const livePage = () => stagehand.context.activePage() ?? initialPage;
@@ -18841,7 +19028,7 @@ function adaptStagehandPage(stagehand, initialPage) {
18841
19028
  }
18842
19029
  };
18843
19030
  }
18844
- var MAX_DOWNLOAD_BYTES, DOWNLOAD_STALL_MS, delay, DEFAULT_BROWSER_MODEL, BROWSERBASE_SESSION_TIMEOUT_SECONDS, BROWSERBASE_KEEP_ALIVE_ENV, HEADED_ENV, EXECUTABLE_PATH_ENV, PROBE_RETRY_DELAY_MS, BROWSERBASE_API_BASE$1, defaultResolveLiveViewUrl, defaultImportStagehand, readBrowserProviderSecrets;
19031
+ var MAX_DOWNLOAD_BYTES, DOWNLOAD_STALL_MS, browserModel, delay, DEFAULT_BROWSER_MODEL, BROWSERBASE_SESSION_TIMEOUT_SECONDS, BROWSERBASE_KEEP_ALIVE_ENV, HEADED_ENV, EXECUTABLE_PATH_ENV, PROBE_RETRY_DELAY_MS, BROWSERBASE_API_BASE$1, defaultResolveLiveViewUrl, defaultImportStagehand, readBrowserProviderSecrets, JSON_FENCE, STAGEHAND_PEER, STAGEHAND_INSTALL_SPEC;
18845
19032
  var init_browser_tools = __esmMin((() => {
18846
19033
  init_ambient_client();
18847
19034
  init_browser_profile_state();
@@ -18915,6 +19102,7 @@ var init_browser_tools = __esmMin((() => {
18915
19102
  } },
18916
19103
  additionalProperties: false
18917
19104
  });
19105
+ browserModel = globalThis[Symbol.for("stackbone.browserTool.modelStore")] ??= new AsyncLocalStorage();
18918
19106
  delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
18919
19107
  DEFAULT_BROWSER_MODEL = "anthropic/claude-haiku-4.5";
18920
19108
  BROWSERBASE_SESSION_TIMEOUT_SECONDS = 3600;
@@ -18938,6 +19126,12 @@ var init_browser_tools = __esmMin((() => {
18938
19126
  "@browserbasehq/stagehand"
18939
19127
  );
18940
19128
  readBrowserProviderSecrets = (names) => stackbone.secrets.getMany(names);
19129
+ JSON_FENCE = /^```(?:json)?[^\S\n]*\n([\s\S]*)```$/i;
19130
+ STAGEHAND_PEER = {
19131
+ pkg: "@browserbasehq/stagehand",
19132
+ version: "3.6.0"
19133
+ };
19134
+ STAGEHAND_INSTALL_SPEC = `${STAGEHAND_PEER.pkg}@${STAGEHAND_PEER.version}`;
18941
19135
  }));
18942
19136
  //#endregion
18943
19137
  //#region ../../libs/sdk/src/runtime/console-capture.ts
@@ -22541,6 +22735,7 @@ var init_store_pool = __esmMin((() => {
22541
22735
  }));
22542
22736
  //#endregion
22543
22737
  //#region ../../libs/runtime/workspace-runtime/src/lib/util/provider-from-model-id.ts
22738
+ init_src$26();
22544
22739
  var GATEWAY_PREFIX = "openrouter";
22545
22740
  /**
22546
22741
  * The provider half of a `vendor/model` id, or null when the id does not name one.
@@ -22551,9 +22746,10 @@ var GATEWAY_PREFIX = "openrouter";
22551
22746
  */
22552
22747
  var providerFromModelId = (modelId) => {
22553
22748
  if (!modelId) return null;
22554
- const slash = modelId.indexOf("/");
22749
+ const bare = unpinModelId(modelId)?.modelId ?? modelId;
22750
+ const slash = bare.indexOf("/");
22555
22751
  if (slash <= 0) return null;
22556
- const prefix = modelId.slice(0, slash);
22752
+ const prefix = bare.slice(0, slash);
22557
22753
  return prefix === GATEWAY_PREFIX ? null : prefix;
22558
22754
  };
22559
22755
  //#endregion
@@ -22994,7 +23190,7 @@ var init_role = __esmMin((() => {
22994
23190
  var STACKBONE_CLI_BUILD_VERSION, EMULATOR_CAPABILITY_ROUTES, EMULATOR_CAPABILITIES, buildContractResponse;
22995
23191
  var init_contract = __esmMin((() => {
22996
23192
  init_src$26();
22997
- STACKBONE_CLI_BUILD_VERSION = "0.9.3";
23193
+ STACKBONE_CLI_BUILD_VERSION = "0.9.4";
22998
23194
  EMULATOR_CAPABILITY_ROUTES = {
22999
23195
  "database.postgres_direct": "/api/db",
23000
23196
  "rag.basic": "/api/rag",
@@ -28309,7 +28505,7 @@ var init_error_envelopes = __esmMin((() => {
28309
28505
  function componentRef(name) {
28310
28506
  return { $ref: `#/components/schemas/${name}` };
28311
28507
  }
28312
- var foldOpenApiDefinitions, named, errorEnvelopeSchema, CATALOG_EXPOSURE_EXTENSION, excludeFromCatalog, isCatalogExclusion, confirmBeforeCalling, INVALID_ANNOTATION, readCatalogAnnotation, jsonBody, describedJsonBody, readOptionalJsonBody, describedJsonBodyOf, renderedSchema, referencedJsonBody, referencedJsonResponse, readOptionalJsonBodyAs, multipartBody, uploadedFile, jsonResponse$1, htmlResponse, errorResponse, forbiddenResponse, TARGET_NOUNS, invalidPayloadHook, repeatedQueryKeys;
28508
+ var foldOpenApiDefinitions, named, errorEnvelopeSchema, CATALOG_EXPOSURE_EXTENSION, excludeFromCatalog, isCatalogExclusion, confirmBeforeCalling, INVALID_ANNOTATION, readCatalogAnnotation, jsonBody, describedJsonBody, readOptionalJsonBody, describedJsonBodyOf, renderedSchema, referencedJsonBody, referencedJsonResponse, readOptionalJsonBodyAs, multipartBody, uploadedFile, jsonResponse$2, htmlResponse, errorResponse, forbiddenResponse, TARGET_NOUNS, invalidPayloadHook, repeatedQueryKeys;
28313
28509
  var init_route_helpers = __esmMin((() => {
28314
28510
  init_src$25();
28315
28511
  init_error_boundary();
@@ -28421,7 +28617,7 @@ var init_route_helpers = __esmMin((() => {
28421
28617
  format: "binary",
28422
28618
  description
28423
28619
  });
28424
- jsonResponse$1 = (schema, description) => ({
28620
+ jsonResponse$2 = (schema, description) => ({
28425
28621
  description,
28426
28622
  content: { "application/json": { schema } }
28427
28623
  });
@@ -28429,7 +28625,7 @@ var init_route_helpers = __esmMin((() => {
28429
28625
  description,
28430
28626
  content: { "text/html": { schema: z.string() } }
28431
28627
  });
28432
- errorResponse = (description) => jsonResponse$1(errorEnvelopeSchema, description);
28628
+ errorResponse = (description) => jsonResponse$2(errorEnvelopeSchema, description);
28433
28629
  forbiddenResponse = (...capabilities) => errorResponse(`The caller's role ${capabilities.length === 1 ? `does not grant \`${capabilities[0]}\`` : `grants none of ${capabilities.map((capability) => `\`${capability}\``).join(", ")}`}. Only reachable on the browser-direct datapath — a proxy-signed call was authorized before it was signed.`);
28434
28630
  TARGET_NOUNS = {
28435
28631
  json: "body",
@@ -28490,27 +28686,27 @@ var init_vendor_surfaces = __esmMin((() => {
28490
28686
  method: "get",
28491
28687
  path: "/models",
28492
28688
  description: "The workspace's deep agents as an OpenAI model list — one entry per registered agent, which is what a client's model dropdown reads. Chat never routes through here.",
28493
- responses: { 200: jsonResponse$1(vendorPayload("An OpenAI `list` of `model` objects, one per registered deep agent."), "OpenAI's model list, with each agent's name as the model id.") }
28689
+ responses: { 200: jsonResponse$2(vendorPayload("An OpenAI `list` of `model` objects, one per registered deep agent."), "OpenAI's model list, with each agent's name as the model id.") }
28494
28690
  }),
28495
28691
  vendorRoute("openai", {
28496
28692
  method: "post",
28497
28693
  path: "/chat/completions",
28498
28694
  description: "Run one turn on the deep agent named by `model`. Stateless by default — send the whole `messages` array, as you would to OpenAI — unless the call carries `x-stackbone-session` (durable thread) or `x-stackbone-conversation` (grouping only). x-stackbone-identities is a JSON string map of at most 32 literal fields with 1–100 character names and 1–500 character opaque values. Use ASCII Unicode escapes in HTTP headers. The user scalar supplies only customerId; contradictory copies return 400.",
28499
28695
  request: { body: jsonBody(vendorPayload("An OpenAI Chat Completions request, with `model` naming the deep agent."), "OpenAI's Chat Completions request body.") },
28500
- responses: { 200: jsonResponse$1(vendorPayload("OpenAI's `chat.completion` object."), streamedElsewhere("One `chat.completion`. A request that sets `\"stream\": true` is answered with `text/event-stream` instead: OpenAI's `chat.completion.chunk` frames, terminated by `data: [DONE]`.")) }
28696
+ responses: { 200: jsonResponse$2(vendorPayload("OpenAI's `chat.completion` object."), streamedElsewhere("One `chat.completion`. A request that sets `\"stream\": true` is answered with `text/event-stream` instead: OpenAI's `chat.completion.chunk` frames, terminated by `data: [DONE]`.")) }
28501
28697
  }),
28502
28698
  vendorRoute("anthropic", {
28503
28699
  method: "get",
28504
28700
  path: "/models",
28505
28701
  description: "The same catalog as the OpenAI surface's, in Anthropic's shape. Chat never routes through here.",
28506
- responses: { 200: jsonResponse$1(vendorPayload("An Anthropic `data` list of model objects, one per registered deep agent."), "Anthropic's model list, with each agent's name as the model id.") }
28702
+ responses: { 200: jsonResponse$2(vendorPayload("An Anthropic `data` list of model objects, one per registered deep agent."), "Anthropic's model list, with each agent's name as the model id.") }
28507
28703
  }),
28508
28704
  vendorRoute("anthropic", {
28509
28705
  method: "post",
28510
28706
  path: "/messages",
28511
28707
  description: "Run one turn on the deep agent named by `model`. Same session and x-stackbone-identities JSON headers as the OpenAI surface, same statelessness by default. metadata.user_id supplies only customerId; contradictory copies return 400.",
28512
28708
  request: { body: jsonBody(vendorPayload("An Anthropic Messages request, with `model` naming the deep agent."), "Anthropic's Messages request body.") },
28513
- responses: { 200: jsonResponse$1(vendorPayload("Anthropic's `message` object."), streamedElsewhere("One `message`. A request that sets `\"stream\": true` is answered with `text/event-stream` instead: the `message_start` / `content_block_*` / `message_delta` / `message_stop` sequence.")) }
28709
+ responses: { 200: jsonResponse$2(vendorPayload("Anthropic's `message` object."), streamedElsewhere("One `message`. A request that sets `\"stream\": true` is answered with `text/event-stream` instead: the `message_start` / `content_block_*` / `message_delta` / `message_stop` sequence.")) }
28514
28710
  }),
28515
28711
  vendorRoute("agui", {
28516
28712
  method: "post",
@@ -29539,8 +29735,8 @@ var init_box_auth_gate = __esmMin((() => {
29539
29735
  summary: "Prove a candidate deployment secret",
29540
29736
  description: "Signed with the operator's candidate secret and answered against the box's own. Public by necessity: proving the secret is what happens BEFORE a valid control-plane signature can exist.",
29541
29737
  responses: {
29542
- 200: jsonResponse$1(proxyChallengeResponseSchema, "The secret matched."),
29543
- 401: jsonResponse$1(proxyChallengeResponseSchema, "The secret did not match.")
29738
+ 200: jsonResponse$2(proxyChallengeResponseSchema, "The secret matched."),
29739
+ 401: jsonResponse$2(proxyChallengeResponseSchema, "The secret did not match.")
29544
29740
  }
29545
29741
  }), async (c) => {
29546
29742
  const url = new URL(c.req.url);
@@ -29570,11 +29766,11 @@ var init_box_auth_gate = __esmMin((() => {
29570
29766
  description: "Signed with the same secret the challenge above proved. First registration binds the installation and organization; later calls must name the same pair. The MCP resource can change for that pair. The deployment row is written only after this call succeeds.",
29571
29767
  request: { body: CONTROL_PLANE_IDENTITY_BODY },
29572
29768
  responses: {
29573
- 200: jsonResponse$1(z.object({ ok: z.literal(true) }), "The box recorded the identity."),
29574
- 400: jsonResponse$1(controlPlaneIdentityRefusalSchema, "The body did not name both ids as non-empty strings."),
29575
- 401: jsonResponse$1(controlPlaneIdentityRefusalSchema, "The signature did not verify against the box's own secret."),
29576
- 409: jsonResponse$1(controlPlaneIdentityRefusalSchema, "The box is already registered to a different organization or installation."),
29577
- 500: jsonResponse$1(controlPlaneIdentityRefusalSchema, "The box could not record the identity.")
29769
+ 200: jsonResponse$2(z.object({ ok: z.literal(true) }), "The box recorded the identity."),
29770
+ 400: jsonResponse$2(controlPlaneIdentityRefusalSchema, "The body did not name both ids as non-empty strings."),
29771
+ 401: jsonResponse$2(controlPlaneIdentityRefusalSchema, "The signature did not verify against the box's own secret."),
29772
+ 409: jsonResponse$2(controlPlaneIdentityRefusalSchema, "The box is already registered to a different organization or installation."),
29773
+ 500: jsonResponse$2(controlPlaneIdentityRefusalSchema, "The box could not record the identity.")
29578
29774
  }
29579
29775
  }), async (c) => {
29580
29776
  const url = new URL(c.req.url);
@@ -44594,7 +44790,7 @@ var identityPrincipal = (identity) => ({
44594
44790
  */
44595
44791
  var BOX_SYSTEM_TOKEN = `sbsys_${randomBytes(32).toString("base64url")}`;
44596
44792
  /** How an in-process self-call presents it. */
44597
- var boxSystemAuthorization = () => `Bearer ${BOX_SYSTEM_TOKEN}`, signLiveKitToken;
44793
+ var boxSystemAuthorization = () => `Bearer ${BOX_SYSTEM_TOKEN}`, signLiveKitToken, signLiveKitInferenceToken;
44598
44794
  var init_livekit_token = __esmMin((() => {
44599
44795
  init_src$26();
44600
44796
  signLiveKitToken = async (input) => {
@@ -44616,6 +44812,13 @@ var init_livekit_token = __esmMin((() => {
44616
44812
  typ: "JWT"
44617
44813
  }).setIssuer(input.apiKey).setSubject(input.identity).setNotBefore(now - 60).setExpirationTime(now + 300).sign(new TextEncoder().encode(input.apiSecret));
44618
44814
  };
44815
+ signLiveKitInferenceToken = async (input) => {
44816
+ const now = input.nowSeconds ?? Math.floor(Date.now() / 1e3);
44817
+ return new SignJWT({ inference: { perform: true } }).setProtectedHeader({
44818
+ alg: "HS256",
44819
+ typ: "JWT"
44820
+ }).setIssuer(input.apiKey).setSubject("stackbone-box").setNotBefore(now - 60).setExpirationTime(now + 600).sign(new TextEncoder().encode(input.apiSecret));
44821
+ };
44619
44822
  }));
44620
44823
  //#endregion
44621
44824
  //#region ../../libs/runtime/workspace-runtime/src/lib/voice/call-token.ts
@@ -47802,7 +48005,7 @@ var SimulatedConnectorRegistry = class {
47802
48005
  };
47803
48006
  //#endregion
47804
48007
  //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/provider-catalogues.ts
47805
- var PROBE_TIMEOUT_MS$1, probeSource, readCatalogue, MAX_CONCURRENT_PROBES, fetchCatalogueSources, describePending, MAX_REASON_LENGTH, shortReason, createProviderCatalogues, orderedUnion, providerNames, notRespondingMessage, duplicatedModelIds, listServedModels, createServedModelsSnapshot;
48008
+ var PROBE_TIMEOUT_MS$1, probeSource, readCatalogue, MAX_CONCURRENT_PROBES, fetchCatalogueSources, describePending, MAX_REASON_LENGTH, shortReason, createProviderCatalogues, orderedUnion, providerNames, notRespondingMessage, duplicatedModelIds, claimsPlainModelIds, listServedModels, createServedModelsSnapshot;
47806
48009
  var init_provider_catalogues = __esmMin((() => {
47807
48010
  init_src$6();
47808
48011
  init_src$18();
@@ -47837,6 +48040,11 @@ var init_provider_catalogues = __esmMin((() => {
47837
48040
  }
47838
48041
  };
47839
48042
  readCatalogue = async ({ kind, baseUrl, apiKey }, fetchImpl) => {
48043
+ const fixed = fixedCatalogue(kind, baseUrl);
48044
+ if (fixed !== null) return {
48045
+ ok: true,
48046
+ models: [...fixed]
48047
+ };
47840
48048
  const first = await fetchCatalogueSources(catalogueSources(kind, baseUrl), apiKey, fetchImpl);
47841
48049
  const refused = first.find((response) => response.source.required && response.error !== void 0);
47842
48050
  if (refused) return {
@@ -47942,6 +48150,7 @@ var init_provider_catalogues = __esmMin((() => {
47942
48150
  providerIds
47943
48151
  }));
47944
48152
  };
48153
+ claimsPlainModelIds = (provider) => gatewayRulesFor(provider.kind).catalogue.fixedModelIds === null;
47945
48154
  listServedModels = async (sources) => {
47946
48155
  let providers;
47947
48156
  try {
@@ -48121,8 +48330,57 @@ var init_answered_chat = __esmMin((() => {
48121
48330
  };
48122
48331
  }));
48123
48332
  //#endregion
48333
+ //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/client/livekit-call.ts
48334
+ var LIVEKIT_RETRY_DELAYS_MS, MAX_RETRY_AFTER_MS, RETRIED_STATUSES, REGION_RESTRICTED_CODE, jsonResponse$1, liveKitCredentialsMissing, waitFor, sleep$2, explained, sendLiveKitCall;
48335
+ var init_livekit_call = __esmMin((() => {
48336
+ init_src$9();
48337
+ LIVEKIT_RETRY_DELAYS_MS = [250, 750];
48338
+ MAX_RETRY_AFTER_MS = 2e3;
48339
+ RETRIED_STATUSES = new Set([429, 503]);
48340
+ REGION_RESTRICTED_CODE = "inference_region_restricted";
48341
+ jsonResponse$1 = (status, body) => new Response(JSON.stringify(body), {
48342
+ status,
48343
+ headers: { "content-type": "application/json" }
48344
+ });
48345
+ liveKitCredentialsMissing = () => jsonResponse$1(503, openAiErrorBody("This model provider is LiveKit Inference, and the box has no LiveKit credentials to sign the call with. Save the LiveKit URL, API key and secret under Voice in Studio.", "api_error", "livekit_credentials_missing"));
48346
+ waitFor = (response, fallbackMs) => {
48347
+ const header = response.headers.get("retry-after");
48348
+ const seconds = header === null ? NaN : Number(header);
48349
+ if (!Number.isFinite(seconds) || seconds < 0) return fallbackMs;
48350
+ return Math.min(seconds * 1e3, MAX_RETRY_AFTER_MS);
48351
+ };
48352
+ sleep$2 = (ms, signal) => new Promise((resolve, reject) => {
48353
+ if (signal?.aborted) return reject(signal.reason);
48354
+ const timer = setTimeout(resolve, ms);
48355
+ signal?.addEventListener("abort", () => {
48356
+ clearTimeout(timer);
48357
+ reject(signal.reason);
48358
+ }, { once: true });
48359
+ });
48360
+ explained = async (response, modelId) => {
48361
+ if (response.status !== 403) return response;
48362
+ const text = await response.text();
48363
+ if (!text.includes(REGION_RESTRICTED_CODE)) return new Response(text, {
48364
+ status: response.status,
48365
+ headers: response.headers
48366
+ });
48367
+ return jsonResponse$1(403, openAiErrorBody(`LiveKit Inference refused the model \`${modelId ?? "unknown"}\`: it has no endpoint in this LiveKit project's data region. Pick a model with an endpoint there, or turn off the inference region restriction in the LiveKit project settings.`, "invalid_request_error", REGION_RESTRICTED_CODE));
48368
+ };
48369
+ sendLiveKitCall = async (input) => {
48370
+ const delays = input.delaysMs ?? LIVEKIT_RETRY_DELAYS_MS;
48371
+ const signal = input.init.signal ?? void 0;
48372
+ for (let attempt = 0;; attempt++) {
48373
+ const response = await input.fetchImpl(input.url, input.init);
48374
+ const delay = delays[attempt];
48375
+ if (!RETRIED_STATUSES.has(response.status) || delay === void 0) return explained(response, input.modelId);
48376
+ await response.body?.cancel();
48377
+ await sleep$2(waitFor(response, delay), signal);
48378
+ }
48379
+ };
48380
+ }));
48381
+ //#endregion
48124
48382
  //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/client/model-provider-client.ts
48125
- var ModelProviderUnreachableError, liveEnvironmentCredential, upstreamUrl, operationOf, isChatCompletion, ROUTED_OPERATIONS, PINNED_ONLY_OPERATIONS, takesPin, MODELS_PATH, modelIdOf, modelIdInBody, unpinRequest, readChatRequest, jsonResponse, notRespondingRefusal, routingRefusal, unreadablePinRefusal, createModelProviderClient;
48383
+ var ModelProviderUnreachableError, isLiveKitEndpoint, liveEnvironmentCredential, upstreamUrl, operationOf, isChatCompletion, ROUTED_OPERATIONS, PINNED_ONLY_OPERATIONS, takesPin, MODELS_PATH, modelIdOf, modelIdInBody, unpinRequest, readChatRequest, jsonResponse, notRespondingRefusal, routingRefusal, unreadablePinRefusal, createModelProviderClient;
48126
48384
  var init_model_provider_client = __esmMin((() => {
48127
48385
  init_src$9();
48128
48386
  init_src$18();
@@ -48133,6 +48391,7 @@ var init_model_provider_client = __esmMin((() => {
48133
48391
  init_proxy_paths();
48134
48392
  init_error_message();
48135
48393
  init_answered_chat();
48394
+ init_livekit_call();
48136
48395
  ModelProviderUnreachableError = class extends Error {
48137
48396
  constructor(provider, cause) {
48138
48397
  super(messageOf$6(cause), { cause });
@@ -48140,6 +48399,7 @@ var init_model_provider_client = __esmMin((() => {
48140
48399
  this.name = "ModelProviderUnreachableError";
48141
48400
  }
48142
48401
  };
48402
+ isLiveKitEndpoint = (endpoint, chatKind) => (chatKind ?? endpoint.kind ?? gatewayKindForBaseUrl(endpoint.baseUrl ?? "https://openrouter.ai/api/v1")) === "livekit";
48143
48403
  liveEnvironmentCredential = () => resolveModelProviderCredential(void 0, process.env);
48144
48404
  upstreamUrl = (baseUrl, path) => `${(baseUrl ?? "https://openrouter.ai/api/v1").replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
48145
48405
  operationOf = (path) => path.replace(/^\/+/, "");
@@ -48279,14 +48539,29 @@ var init_model_provider_client = __esmMin((() => {
48279
48539
  }
48280
48540
  const body = chat === void 0 ? request.body : chat.body;
48281
48541
  const headers = { ...request.headers };
48282
- const { apiKey } = endpoint;
48283
- if (apiKey && apiKey.trim() !== "") headers["authorization"] = `Bearer ${apiKey}`;
48284
- const upstream = await (options.fetchImpl ?? fetch)(upstreamUrl(endpoint.baseUrl, path), {
48542
+ const liveKit = isLiveKitEndpoint(endpoint, chatKind);
48543
+ if (liveKit) {
48544
+ const token = await (options.liveKitToken?.() ?? Promise.resolve(null));
48545
+ if (token === null) return liveKitCredentialsMissing();
48546
+ headers["authorization"] = `Bearer ${token}`;
48547
+ } else {
48548
+ const { apiKey } = endpoint;
48549
+ if (apiKey && apiKey.trim() !== "") headers["authorization"] = `Bearer ${apiKey}`;
48550
+ }
48551
+ const url = upstreamUrl(endpoint.baseUrl, path);
48552
+ const init = {
48285
48553
  method: request.method ?? "GET",
48286
48554
  headers,
48287
48555
  ...body !== void 0 ? { body } : {},
48288
48556
  ...request.signal ? { signal: request.signal } : {}
48289
- });
48557
+ };
48558
+ const fetchImpl = options.fetchImpl ?? fetch;
48559
+ const upstream = liveKit ? await sendLiveKitCall({
48560
+ url,
48561
+ init,
48562
+ modelId: chat?.modelId,
48563
+ fetchImpl
48564
+ }) : await fetchImpl(url, init);
48290
48565
  return chat === void 0 ? upstream : answeredChat(upstream);
48291
48566
  };
48292
48567
  /** Provider 1, the credential in effect: every call's path before STA3-17. */
@@ -48365,10 +48640,14 @@ var init_model_provider_client = __esmMin((() => {
48365
48640
  }
48366
48641
  if (providers.length < 2 || PINNED_ONLY_OPERATIONS.has(operation)) return fallback;
48367
48642
  if (listsModels) return listedUnion(providers);
48643
+ const claiming = providers.filter(claimsPlainModelIds);
48644
+ const [sole, ...others] = claiming;
48645
+ if (sole !== void 0 && others.length === 0) return sole.id === "default" ? void 0 : sole;
48646
+ const pool = claiming.length === 0 ? providers : claiming;
48368
48647
  const modelId = modelIdInBody(request.body);
48369
48648
  if (modelId === void 0) return fallback;
48370
- const resolution = await resolveRoute(providers, modelId);
48371
- return resolution.kind === "routed" ? resolution.provider : routingRefusal(resolution, modelId, providers);
48649
+ const resolution = await resolveRoute(pool, modelId);
48650
+ return resolution.kind === "routed" ? resolution.provider : routingRefusal(resolution, modelId, pool);
48372
48651
  };
48373
48652
  /** Send a call where {@link route} put it: provider 1, a provider, or the box's refusal. */
48374
48653
  const dispatch = async (path, request, routed) => {
@@ -49212,6 +49491,153 @@ var listModelProviderModels = async (opts, body, env = {}) => {
49212
49491
  });
49213
49492
  };
49214
49493
  //#endregion
49494
+ //#region ../../libs/runtime/workspace-runtime/src/lib/voice/livekit-credentials.ts
49495
+ init_src$26();
49496
+ var [URL_NAME, API_KEY_NAME, API_SECRET_NAME] = RESERVED_LIVEKIT_SECRET_NAMES;
49497
+ /**
49498
+ * Names a row this box's key cannot open (never its value), so the log says
49499
+ * which one to save again. True when `err` is that failure.
49500
+ */
49501
+ var warnUndecryptable = (scope, name) => console.warn(`[${scope}] ${name} cannot be opened with this box's key; save it again`);
49502
+ var warnIfUndecryptable = (scope, name, err) => {
49503
+ if (!(err instanceof SecretUndecryptableError)) return false;
49504
+ warnUndecryptable(scope, name);
49505
+ return true;
49506
+ };
49507
+ /**
49508
+ * The stored LiveKit credentials, read on EVERY call — no cache — or `null` when
49509
+ * any of the three is missing or empty. Half a credential set cannot open a
49510
+ * room, so it answers exactly what nothing stored answers.
49511
+ *
49512
+ * A row this box's key cannot open throws {@link SecretUndecryptableError},
49513
+ * after a warning that names the row (never its value): saving the credentials
49514
+ * again is the fix, and the caller tells the operator so. Any other failure
49515
+ * throws as it came.
49516
+ */
49517
+ var readLiveKitCredentials = async (opts) => {
49518
+ const values = await readSystemSecrets(opts, RESERVED_LIVEKIT_SECRET_NAMES, (name) => warnUndecryptable("voice", name));
49519
+ const url = values.get(URL_NAME);
49520
+ const apiKey = values.get(API_KEY_NAME);
49521
+ const apiSecret = values.get(API_SECRET_NAME);
49522
+ if (!url || !apiKey || !apiSecret) return null;
49523
+ return {
49524
+ url,
49525
+ apiKey,
49526
+ apiSecret
49527
+ };
49528
+ };
49529
+ /** The secret naming the LiveKit outbound SIP trunk `placeCall` dials through (STA3-56). */
49530
+ var SIP_OUTBOUND_TRUNK_SECRET = "LIVEKIT_SIP_OUTBOUND_TRUNK_ID";
49531
+ /** The optional secret naming the number the callee sees; the trunk's own when absent. */
49532
+ var SIP_CALLER_ID_SECRET = "LIVEKIT_SIP_CALLER_ID";
49533
+ /**
49534
+ * The outbound trunk, read on every call, or `null` when no trunk id is saved.
49535
+ * Not reserved names, unlike the three credentials: a trunk id opens nothing on
49536
+ * its own and has no privileged screen yet, so it is read from an `is_system`
49537
+ * row or the operator's own secret of that name, whichever exists.
49538
+ */
49539
+ var readSipOutboundConfig = async (opts) => {
49540
+ const values = await readSecretsAnyKind(opts, [SIP_OUTBOUND_TRUNK_SECRET, SIP_CALLER_ID_SECRET], (name) => warnUndecryptable("voice", name));
49541
+ const trunkId = values.get(SIP_OUTBOUND_TRUNK_SECRET)?.trim();
49542
+ if (!trunkId) return null;
49543
+ const callerId = values.get(SIP_CALLER_ID_SECRET)?.trim();
49544
+ return {
49545
+ trunkId,
49546
+ ...callerId ? { callerId } : {}
49547
+ };
49548
+ };
49549
+ var voiceNotConfigured = (message) => ({ refusal: {
49550
+ status: 503,
49551
+ body: {
49552
+ code: "voice_not_configured",
49553
+ message
49554
+ }
49555
+ } });
49556
+ /**
49557
+ * {@link readLiveKitCredentials} for a voice route: the credentials, or the 503 to
49558
+ * answer without them. A row sealed by another box key is fixed the way a missing one
49559
+ * is, by saving the credentials again, so it answers the same 503; any other read
49560
+ * failure is the box's own and throws, to the generic 500.
49561
+ */
49562
+ var lookUpLiveKitCredentials = async (opts) => {
49563
+ let credentials;
49564
+ try {
49565
+ credentials = await readLiveKitCredentials(opts);
49566
+ } catch (err) {
49567
+ if (!(err instanceof SecretUndecryptableError)) throw err;
49568
+ return voiceNotConfigured("This box cannot read the saved LiveKit credentials: they were saved with another key. Open the Voice provider screen and save them again.");
49569
+ }
49570
+ if (!credentials) return voiceNotConfigured("This box has no LiveKit credentials. Save the URL, API key and API secret on the Voice provider screen.");
49571
+ return { credentials };
49572
+ };
49573
+ /**
49574
+ * The screen's view: whether all three are saved, and the URL in clear.
49575
+ *
49576
+ * The key and the secret are asked after as EXISTENCE only, never decrypted:
49577
+ * this view never shows them, so opening them would be work done to throw away.
49578
+ * The URL is decrypted to be shown, and a URL row this box's key cannot open
49579
+ * (sealed by another box) reads as not configured instead of failing the screen
49580
+ * the operator needs to replace it from. Only that: a database or key failure
49581
+ * throws, because answering "not configured" would hide a configured box.
49582
+ */
49583
+ var getVoiceProvider = async (opts) => {
49584
+ const [url, apiKeySet, apiSecretSet] = await Promise.all([
49585
+ readSystemSecret(opts, URL_NAME).catch((err) => {
49586
+ if (!warnIfUndecryptable("voice-provider", URL_NAME, err)) throw err;
49587
+ return null;
49588
+ }),
49589
+ systemSecretExists(opts, API_KEY_NAME),
49590
+ systemSecretExists(opts, API_SECRET_NAME)
49591
+ ]);
49592
+ return {
49593
+ configured: url !== null && apiKeySet && apiSecretSet,
49594
+ url
49595
+ };
49596
+ };
49597
+ /**
49598
+ * Save all three as `is_system` rows, with the activity-log entry, in ONE
49599
+ * transaction: the box never holds a new URL beside an old secret, and never a
49600
+ * save the trail does not show. The entry names the URL alone.
49601
+ */
49602
+ var setVoiceProvider = async (opts, body, actor = { userId: null }) => {
49603
+ await opts.db.transaction(async (tx) => {
49604
+ await seedSystemSecrets(opts, {
49605
+ [URL_NAME]: body.url,
49606
+ [API_KEY_NAME]: body.apiKey,
49607
+ [API_SECRET_NAME]: body.apiSecret
49608
+ }, tx);
49609
+ await opts.audit.record({
49610
+ action: "voice_provider.update",
49611
+ actorUserId: actor.userId,
49612
+ subjectType: "voice_provider",
49613
+ payload: { url: body.url }
49614
+ }, tx);
49615
+ });
49616
+ await notifySecretsChanged();
49617
+ };
49618
+ /**
49619
+ * Remove all three in ONE transaction, and answer the names actually removed.
49620
+ *
49621
+ * `cleared` comes off each DELETE's own answer, so a partial store records only
49622
+ * what it held, and a removal that found nothing records nothing — the rule
49623
+ * `removeModelProvider` keeps.
49624
+ */
49625
+ var clearVoiceProvider = async (opts, actor = { userId: null }) => {
49626
+ const cleared = await opts.db.transaction(async (tx) => {
49627
+ const removed = [];
49628
+ for (const name of RESERVED_LIVEKIT_SECRET_NAMES) if (await deleteSystemSecret(opts, name, tx)) removed.push(name);
49629
+ if (removed.length > 0) await opts.audit.record({
49630
+ action: "voice_provider.delete",
49631
+ actorUserId: actor.userId,
49632
+ subjectType: "voice_provider",
49633
+ payload: { cleared: removed }
49634
+ }, tx);
49635
+ return removed;
49636
+ });
49637
+ if (cleared.length > 0) await notifySecretsChanged();
49638
+ return cleared;
49639
+ };
49640
+ //#endregion
49215
49641
  //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/provider-registry-store.ts
49216
49642
  init_src$26();
49217
49643
  init_src$18();
@@ -49234,6 +49660,14 @@ var assertExtraProviderId = (id) => {
49234
49660
  if (id === "default") throw new ProviderStoreError("default_provider", id);
49235
49661
  if (!MODEL_PROVIDER_ID_PATTERN.test(id)) throw new ProviderStoreError("invalid_id", id);
49236
49662
  };
49663
+ /**
49664
+ * Refuses a hand-made LiveKit provider: it is derived from the Voice credentials
49665
+ * (PLA-2416), and a second one would carry no credential of its own. Removal
49666
+ * stays open, so a row saved before the derivation can still be cleared.
49667
+ */
49668
+ var assertNotVoiceProvider = (id, kind) => {
49669
+ if (id === "livekit" || kind === "livekit") throw new ProviderStoreError("voice_provider", id);
49670
+ };
49237
49671
  var PROVIDER_SUFFIX = "__PROVIDER";
49238
49672
  /**
49239
49673
  * The two rows of an extra provider. The key is a row of its own holding the
@@ -49313,14 +49747,41 @@ var listExtraProviders = async (opts) => {
49313
49747
  return (await Promise.all(rowNames.filter((name) => name.endsWith(PROVIDER_SUFFIX)).map((name) => readExtraProvider(opts, name)))).filter((provider) => provider !== null);
49314
49748
  };
49315
49749
  /**
49750
+ * LiveKit Inference, while the box holds its Voice credentials (PLA-2416): the
49751
+ * endpoint is fixed and the call is signed with those credentials, so there is
49752
+ * nothing to store and nothing an operator could get wrong. Credentials the box
49753
+ * cannot read leave it out rather than failing the list every model call reads.
49754
+ */
49755
+ var readVoiceProvider = async (opts) => {
49756
+ try {
49757
+ if (await readLiveKitCredentials(opts) === null) return null;
49758
+ } catch (err) {
49759
+ console.warn(`[model-provider] LiveKit Inference left out, Voice credentials unreadable: ${String(err)}`);
49760
+ return null;
49761
+ }
49762
+ return {
49763
+ id: LIVEKIT_MODEL_PROVIDER_ID,
49764
+ name: "LiveKit Inference",
49765
+ kind: "livekit",
49766
+ baseUrl: LIVEKIT_INFERENCE_BASE_URL,
49767
+ apiKey: void 0,
49768
+ source: "voice"
49769
+ };
49770
+ };
49771
+ /**
49316
49772
  * Every configured provider, provider 1 first, then the extras in the order
49317
- * they were added.
49773
+ * they were added, then LiveKit Inference when Voice is configured.
49318
49774
  *
49319
49775
  * `env` is the boot snapshot, taken before boot seeded the stored credential
49320
49776
  * onto `process.env` (see `resolveModelProviderSource`).
49321
49777
  */
49322
49778
  var listProviders = async (opts, env = {}) => {
49323
- const [first, extras] = await Promise.all([readDefaultProvider(opts, env), listExtraProviders(opts)]);
49779
+ const [first, stored, voice] = await Promise.all([
49780
+ readDefaultProvider(opts, env),
49781
+ listExtraProviders(opts),
49782
+ readVoiceProvider(opts)
49783
+ ]);
49784
+ const extras = voice === null ? stored : [...stored.filter((p) => p.id !== voice.id), voice];
49324
49785
  return first === null ? extras : [first, ...extras];
49325
49786
  };
49326
49787
  /**
@@ -49362,11 +49823,13 @@ var writeProvider = async (opts, id, input, action, actor) => {
49362
49823
  var providerExists = (opts, id) => systemSecretExists(opts, rowNamesOf(id).provider);
49363
49824
  var addProvider = async (opts, provider, actor = { userId: null }) => {
49364
49825
  assertExtraProviderId(provider.id);
49826
+ assertNotVoiceProvider(provider.id, provider.kind);
49365
49827
  if (await providerExists(opts, provider.id)) throw new ProviderStoreError("duplicate", provider.id);
49366
49828
  await writeProvider(opts, provider.id, provider, "model_provider.create", actor);
49367
49829
  };
49368
49830
  var updateProvider = async (opts, id, input, actor = { userId: null }) => {
49369
49831
  assertExtraProviderId(id);
49832
+ assertNotVoiceProvider(id, input.kind);
49370
49833
  if (!await providerExists(opts, id)) throw new ProviderStoreError("not_found", id);
49371
49834
  await writeProvider(opts, id, input, "model_provider.update", actor);
49372
49835
  };
@@ -49398,6 +49861,40 @@ var removeProvider = async (opts, id, actor = { userId: null }) => {
49398
49861
  await notifySecretsChanged();
49399
49862
  };
49400
49863
  //#endregion
49864
+ //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/client/livekit-inference-auth.ts
49865
+ init_livekit_token();
49866
+ /**
49867
+ * Signs a new token this long before the cached one expires, so a call that
49868
+ * starts on a token never reaches LiveKit with it already expired.
49869
+ */
49870
+ var REFRESH_MARGIN_SECONDS = 60;
49871
+ /**
49872
+ * The bearer for LiveKit Inference (PLA-2416), or `null` when the box has no
49873
+ * LiveKit credentials. One token is reused until it nears expiry, and a new one
49874
+ * is signed when the key or the secret changes.
49875
+ */
49876
+ var createLiveKitInferenceAuth = (options) => {
49877
+ const now = options.nowSeconds ?? (() => Math.floor(Date.now() / 1e3));
49878
+ let cached = null;
49879
+ return async () => {
49880
+ const credentials = await options.credentials();
49881
+ if (credentials === null) return null;
49882
+ const signedWith = `${credentials.apiKey}\u0000${credentials.apiSecret}`;
49883
+ const at = now();
49884
+ if (cached?.signedWith === signedWith && cached.expiresAt - REFRESH_MARGIN_SECONDS > at) return cached.token;
49885
+ const token = await signLiveKitInferenceToken({
49886
+ ...credentials,
49887
+ nowSeconds: at
49888
+ });
49889
+ cached = {
49890
+ signedWith,
49891
+ token,
49892
+ expiresAt: at + 600
49893
+ };
49894
+ return token;
49895
+ };
49896
+ };
49897
+ //#endregion
49401
49898
  //#region ../../libs/runtime/workspace-runtime/src/lib/otlp/otlp-config.ts
49402
49899
  var CAPTURE_MODES = [
49403
49900
  "NO_CONTENT",
@@ -49585,7 +50082,8 @@ var createMountContext = (opts, hooks, app) => {
49585
50082
  }),
49586
50083
  storedKind: () => readStoredModelProviderKind(secrets),
49587
50084
  providers: () => listProviders(secrets, bootEnv),
49588
- catalogues: opts.providerCatalogues
50085
+ catalogues: opts.providerCatalogues,
50086
+ liveKitToken: createLiveKitInferenceAuth({ credentials: () => readLiveKitCredentials(secrets) })
49589
50087
  }),
49590
50088
  servedModels: opts.servedModels ?? (() => null),
49591
50089
  simulatedConnectors: new SimulatedConnectorRegistry({ runs: opts.invocations }),
@@ -56154,7 +56652,7 @@ function untilAborted(work, signal) {
56154
56652
  * download behind it; `return()` alone waits for a chunk a stalled stream never
56155
56653
  * sends.
56156
56654
  */
56157
- function release(data, iterator) {
56655
+ function release$1(data, iterator) {
56158
56656
  data.destroy?.();
56159
56657
  Promise.resolve(iterator.return?.()).catch(() => void 0);
56160
56658
  }
@@ -56181,7 +56679,7 @@ async function bytesOf({ fileName, data }, maxBytes, signal) {
56181
56679
  chunks.push(next.value);
56182
56680
  }
56183
56681
  } catch (err) {
56184
- release(data, iterator);
56682
+ release$1(data, iterator);
56185
56683
  throw err;
56186
56684
  }
56187
56685
  return Buffer.concat(chunks, length);
@@ -72207,7 +72705,7 @@ var createApprovalsRouter = (deps) => {
72207
72705
  description: "What the SDK calls to pause on a human. The box records the row, publishes `approval.created` on its event bus so an open inbox repaints, and answers with the callback URL the decision will be POSTed back to. Repeat calls carrying the same `idempotencyKey` return the SAME approval rather than raising a second one.",
72208
72706
  request: { body: jsonBody(named("CreateApprovalRequest", createApprovalRequestSchema), "The topic, the callback path the decision is delivered to, and the payload a reviewer reads.") },
72209
72707
  responses: {
72210
- 201: jsonResponse$1(named("ApprovalCreatedResponse", approvalCreatedResponseSchema), "The approval id, its callback URL and when it expires."),
72708
+ 201: jsonResponse$2(named("ApprovalCreatedResponse", approvalCreatedResponseSchema), "The approval id, its callback URL and when it expires."),
72211
72709
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
72212
72710
  500: errorResponse("The store refused the row (`internal_error`), message verbatim.")
72213
72711
  }
@@ -72248,7 +72746,7 @@ var createApprovalsRouter = (deps) => {
72248
72746
  description: "Cursor-paginated, newest first unless `order=asc`, filterable by status, topic and the run that raised them. The cursor is opaque and rewinds to before/after the last id at that timestamp.",
72249
72747
  request: { query: listApprovalsQuery },
72250
72748
  responses: {
72251
- 200: jsonResponse$1(named("ApprovalListResponse", approvalListResponseSchema), "One page of approvals, and the cursors either side of it."),
72749
+ 200: jsonResponse$2(named("ApprovalListResponse", approvalListResponseSchema), "One page of approvals, and the cursors either side of it."),
72252
72750
  400: errorResponse("A filter this inbox cannot honour — an unknown `status`, a `runId` that is not a uuid, a `limit` outside [1, 100] (`invalid_payload`) — or a cursor this box did not issue (`invalid_cursor`).")
72253
72751
  }
72254
72752
  }), async (c) => {
@@ -72306,7 +72804,7 @@ var createApprovalsRouter = (deps) => {
72306
72804
  description: "The row plus its audit trail: every decision recorded against it, including the one a timeout sweep wrote. A pending approval carries an empty `decisions` array, never a missing one.",
72307
72805
  request: { params: approvalIdPath },
72308
72806
  responses: {
72309
- 200: jsonResponse$1(named("ApprovalDetailResponse", approvalDetailResponseSchema), "The approval and its decisions, oldest first."),
72807
+ 200: jsonResponse$2(named("ApprovalDetailResponse", approvalDetailResponseSchema), "The approval and its decisions, oldest first."),
72310
72808
  404: errorResponse("No such approval (`approval_not_found`) — or an id that is not an id at all.")
72311
72809
  }
72312
72810
  }), async (c) => {
@@ -72327,7 +72825,7 @@ var createApprovalsRouter = (deps) => {
72327
72825
  body: jsonBody(named("DecideApprovalRequest", decideApprovalRequestSchema), "The decision, plus the optional edited payload and reviewer comment that ride with it.")
72328
72826
  },
72329
72827
  responses: {
72330
- 200: jsonResponse$1(named("DecideApprovalResponse", decideApprovalResponseSchema), "The approval as it now stands, and the decision recorded against it."),
72828
+ 200: jsonResponse$2(named("DecideApprovalResponse", decideApprovalResponseSchema), "The approval as it now stands, and the decision recorded against it."),
72331
72829
  400: errorResponse("The body failed its schema, or an `editedPayload` was sent for a pause holding more than one tool call (`invalid_payload`)."),
72332
72830
  403: forbiddenResponse("hitl:decide"),
72333
72831
  404: errorResponse("No such approval (`approval_not_found`)."),
@@ -72596,7 +73094,7 @@ var createActivityRouter = (deps) => {
72596
73094
  description: "The append-only trail of what humans did on THIS box — an approval decided, the creator's config saved or rolled back, a secret rotated. One SOURCE of the organization's activity log: the control plane serves its own rows separately and the Activity screen merges the two, so every page names the source it came from and how completely that source answered. `action` and `resourceType` are matched as OPEN strings, never against this build's catalog — a box on a newer image records actions an older bundle has never heard of, and dropping those rows would be the silent omission the log exists to prevent. Pages are walked with `nextCursor`. There is no write verb: the table refuses UPDATE and DELETE below every application.",
72597
73095
  request: { query: auditQuerySchema },
72598
73096
  responses: {
72599
- 200: jsonResponse$1(named("AuditPage", auditPageSchema), "One page of this source's rows, the cursor the next page is asked with (`null` on the last), and how completely this source answered."),
73097
+ 200: jsonResponse$2(named("AuditPage", auditPageSchema), "One page of this source's rows, the cursor the next page is asked with (`null` on the last), and how completely this source answered."),
72600
73098
  400: errorResponse("The query string failed its schema (`invalid_payload` — an unparseable bound, or a `limit` past the contract ceiling), or the cursor is not one this box minted (`invalid_cursor`). An unreadable cursor is REFUSED rather than answered with the first page: a caller handed page one for a cursor it cannot advance pages forever."),
72601
73099
  403: forbiddenResponse("activity:read")
72602
73100
  }
@@ -72746,7 +73244,7 @@ var createBrowserProfilesRouter = (deps) => {
72746
73244
  summary: "List the stored browser sessions",
72747
73245
  description: "METADATA ONLY, newest refresh first. The stored session state is a serialized Playwright `storageState` — credential material — and no route on this box answers it, so a row here carries the origin, the identity, whether a re-login is pending and when it was last captured, and nothing else.",
72748
73246
  responses: {
72749
- 200: jsonResponse$1(named("StudioBrowserProfileListResponse", studioBrowserProfileListResponseSchema), "Every stored profile, as metadata."),
73247
+ 200: jsonResponse$2(named("StudioBrowserProfileListResponse", studioBrowserProfileListResponseSchema), "Every stored profile, as metadata."),
72750
73248
  403: forbiddenResponse("secrets:rotate")
72751
73249
  }
72752
73250
  }), async (c) => c.json(await listBrowserProfiles(browserProfilesOptions), 200));
@@ -72760,7 +73258,7 @@ var createBrowserProfilesRouter = (deps) => {
72760
73258
  description: "Deletes the encrypted session for one `origin` + `identity`, so the agent has to log in again. The profile is named in the QUERY STRING, not in a body.",
72761
73259
  request: { query: revokeQuery },
72762
73260
  responses: {
72763
- 200: jsonResponse$1(named("StudioBrowserProfileDeleteResponse", studioBrowserProfileDeleteResponseSchema), "The profile is gone."),
73261
+ 200: jsonResponse$2(named("StudioBrowserProfileDeleteResponse", studioBrowserProfileDeleteResponseSchema), "The profile is gone."),
72764
73262
  403: forbiddenResponse("secrets:rotate"),
72765
73263
  404: errorResponse("No stored session for that origin — or no origin was named at all.")
72766
73264
  }
@@ -73146,7 +73644,7 @@ var createBrowserProviderRouter = (secretsOptions) => {
73146
73644
  summary: "Read the browser provider configuration",
73147
73645
  description: "The stored mode and its identifiers in clear, the API key masked, plus the mode a launch would actually open right now and any `BROWSER_*` variable in the box's environment that outranks the stored value. `effective_mode` is null when the configuration is unsatisfiable, and `effective_error` names the missing field.",
73148
73646
  responses: {
73149
- 200: jsonResponse$1(named("BrowserProviderGetResponse", browserProviderGetResponseSchema), "The stored configuration and the mode it resolves to."),
73647
+ 200: jsonResponse$2(named("BrowserProviderGetResponse", browserProviderGetResponseSchema), "The stored configuration and the mode it resolves to."),
73150
73648
  403: forbiddenResponse("secrets:rotate")
73151
73649
  }
73152
73650
  }), async (c) => c.json(await getBrowserProvider(secretsOptions), 200));
@@ -73159,7 +73657,7 @@ var createBrowserProviderRouter = (secretsOptions) => {
73159
73657
  description: "REPLACES the whole configuration — a field omitted or empty is deleted from the store, which is how the API key is cleared. Cross-field requirements (\"browserbase needs a project id\") are deliberately not refused here: an unsatisfiable mode surfaces on the read as `effective_error`, so a half-finished save is still savable.",
73160
73658
  request: { body: jsonBody(browserProviderRequestBody, "The mode and the fields that mode needs.") },
73161
73659
  responses: {
73162
- 200: jsonResponse$1(named("BrowserProviderGetResponse", browserProviderGetResponseSchema), "The fresh masked view, including the recomputed effective mode."),
73660
+ 200: jsonResponse$2(named("BrowserProviderGetResponse", browserProviderGetResponseSchema), "The fresh masked view, including the recomputed effective mode."),
73163
73661
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
73164
73662
  403: forbiddenResponse("secrets:rotate")
73165
73663
  }
@@ -73176,7 +73674,7 @@ var createBrowserProviderRouter = (secretsOptions) => {
73176
73674
  description: "Opens (or asks after) the browser the body describes, WITHOUT saving anything. A configuration that cannot be reached rides back as `ok: false` DATA with a reason the operator can act on — never as an error status.",
73177
73675
  request: { body: jsonBody(browserProviderRequestBody, "The candidate configuration to probe. Same shape as the PUT body.") },
73178
73676
  responses: {
73179
- 200: jsonResponse$1(named("BrowserProviderTestResponse", browserProviderTestResponseSchema), "Whether the candidate configuration answered, and why not when it did not."),
73677
+ 200: jsonResponse$2(named("BrowserProviderTestResponse", browserProviderTestResponseSchema), "Whether the candidate configuration answered, and why not when it did not."),
73180
73678
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
73181
73679
  403: forbiddenResponse("secrets:rotate")
73182
73680
  }
@@ -73611,7 +74109,7 @@ var createCatalogRouter = (deps) => {
73611
74109
  tags: [CATALOG_TAG],
73612
74110
  summary: "List everything this workspace serves",
73613
74111
  description: "Every deep agent and every workflow the box hosts, of both kinds, in one normalized shape and one total order (name, then kind). Platform entries are RETURNED and flagged `system: true`, never filtered out. Both halves are resolved on the read, so an agent hot-added by `stackbone dev` and a workflow set swapped by a reload both appear with no restart — and a row degraded only because nobody has published the prompt it declares goes healthy on the next read.",
73614
- responses: { 200: jsonResponse$1(named("WorkspaceCatalogListResponse", workspaceCatalogListResponseSchema), "Every entry this workspace serves. A row is legitimately PARTIAL: `metadata`, `reason`, `prompts`, `models` and `outputs` are each absent or null on an entry the build recorded nothing about, which is a different answer from an empty one.") }
74112
+ responses: { 200: jsonResponse$2(named("WorkspaceCatalogListResponse", workspaceCatalogListResponseSchema), "Every entry this workspace serves. A row is legitimately PARTIAL: `metadata`, `reason`, `prompts`, `models` and `outputs` are each absent or null on an entry the build recorded nothing about, which is a different answer from an empty one.") }
73615
74113
  }), async (c) => c.json(workspaceCatalogListResponseSchema.parse({ items: await currentEntries() }), 200));
73616
74114
  app.openapi(createRoute({
73617
74115
  method: "get",
@@ -73619,7 +74117,7 @@ var createCatalogRouter = (deps) => {
73619
74117
  tags: [CATALOG_TAG],
73620
74118
  summary: "Summarise the last week of runs, per entry",
73621
74119
  description: `For every entry the list above returns: when it last ran, and how its runs over the last 7 days came out. The window is FIXED, not a query param — it is echoed on the response so a screen labels what it shows, and a caller-chosen one would let two surfaces publish different "recent activity" for the same entry. Entry-driven by construction: an entry that has never run comes back with zeros rather than vanishing.`,
73622
- responses: { 200: jsonResponse$1(named("CatalogRunAggregatesResponse", catalogRunAggregatesResponseSchema), "One row per catalog entry, plus the window every row was counted over.") }
74120
+ responses: { 200: jsonResponse$2(named("CatalogRunAggregatesResponse", catalogRunAggregatesResponseSchema), "One row per catalog entry, plus the window every row was counted over.") }
73623
74121
  }), async (c) => {
73624
74122
  const since = (/* @__PURE__ */ new Date(Date.now() - 7 * DAY_MS$1)).toISOString();
73625
74123
  const items = await deps.invocations.aggregateRunsByEntity({
@@ -73645,8 +74143,8 @@ var createCatalogRouter = (deps) => {
73645
74143
  description: "The static diagram of the workflow's control flow, as the build drew it from the source. A known workflow with no diagram to show answers `{ diagram: null }` — the bundle predates the scan, or carries a diagram this box cannot read. Not part of `GET /api/catalog`, which every screen reads: a diagram weighs tens of kilobytes.",
73646
74144
  request: { params: workflowNamePath },
73647
74145
  responses: {
73648
- 200: jsonResponse$1(named("WorkflowDiagramResponse", workflowDiagramResponseSchema), "The diagram, or `null` when there is none to show."),
73649
- 404: jsonResponse$1(workflowNotFoundBodySchema, "No workflow by that name.")
74146
+ 200: jsonResponse$2(named("WorkflowDiagramResponse", workflowDiagramResponseSchema), "The diagram, or `null` when there is none to show."),
74147
+ 404: jsonResponse$2(workflowNotFoundBodySchema, "No workflow by that name.")
73650
74148
  }
73651
74149
  }), (c) => {
73652
74150
  const { name } = c.req.valid("param");
@@ -74077,7 +74575,7 @@ var createPromptsRouter = (opts) => {
74077
74575
  summary: "List the workspace prompts",
74078
74576
  description: "Every prompt this box holds, of every owner, with the PUBLISHED text resolved into `content` — so a list read shows what each owner actually runs, not the newest draft somebody saved. `content` is `null` on a prompt with nothing published, which is a different answer from the prompt not existing. Capped at 200 rows, ordered by owner then key.\n\nGated by `config:write` even though it is a read: a prompt body is authored content, and the seats that may not edit one were never shown it either.",
74079
74577
  responses: {
74080
- 200: jsonResponse$1(named("StudioPromptListResponse", studioPromptListResponseSchema), "Every prompt on this box, published text resolved."),
74578
+ 200: jsonResponse$2(named("StudioPromptListResponse", studioPromptListResponseSchema), "Every prompt on this box, published text resolved."),
74081
74579
  403: forbiddenResponse("config:write")
74082
74580
  }
74083
74581
  }), async (c) => {
@@ -74093,7 +74591,7 @@ var createPromptsRouter = (opts) => {
74093
74591
  description: "Writes a new prompt and PUBLISHES its version 1 — version 1 is the only version there is, so leaving it unpublished would mean every new prompt starts as text nothing runs and no read returns. The write/publish split earns its keep from the second version onwards. An `agent` owner is rebuilt after the write, because a deep agent bakes its instruction into its graph and would otherwise keep answering with nothing.\n\nThis is the ONE route that carries the owner in the BODY; every other route carries it in the path. Neither half has a default: a create that guessed an owner would put the prompt somewhere no read will look.",
74094
74592
  request: { body: jsonBody(named("CreateStudioPromptRequest", createStudioPromptRequestSchema), "The owner, the key, the display name and the template text.") },
74095
74593
  responses: {
74096
- 200: jsonResponse$1(named("StudioPrompt", studioPromptSchema), "The prompt as it now stands, publishing version 1."),
74594
+ 200: jsonResponse$2(named("StudioPrompt", studioPromptSchema), "The prompt as it now stands, publishing version 1."),
74097
74595
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
74098
74596
  403: forbiddenResponse("config:write"),
74099
74597
  404: errorResponse("The row was written and then could not be read back (`studio_prompt_not_found`) — a concurrent delete, not a bad request."),
@@ -74170,7 +74668,7 @@ var createPromptsRouter = (opts) => {
74170
74668
  query: promptVersionQuery
74171
74669
  },
74172
74670
  responses: {
74173
- 200: jsonResponse$1(named("StudioPrompt", studioPromptSchema), "The prompt, with the resolved text."),
74671
+ 200: jsonResponse$2(named("StudioPrompt", studioPromptSchema), "The prompt, with the resolved text."),
74174
74672
  400: errorResponse(PROMPT_OWNER_OR_VERSION_REFUSAL),
74175
74673
  403: forbiddenResponse("config:write"),
74176
74674
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL)
@@ -74208,7 +74706,7 @@ var createPromptsRouter = (opts) => {
74208
74706
  body: jsonBody(named("UpdateStudioPromptRequest", updateStudioPromptRequestSchema), "The fields to change. At least one of `content`, `name`, `description` or `metadata`.")
74209
74707
  },
74210
74708
  responses: {
74211
- 200: jsonResponse$1(named("StudioPrompt", studioPromptSchema), "The prompt as it now stands — still publishing whatever it published before."),
74709
+ 200: jsonResponse$2(named("StudioPrompt", studioPromptSchema), "The prompt as it now stands — still publishing whatever it published before."),
74212
74710
  400: errorResponse(`The body failed its schema (\`invalid_payload\`), or ${PROMPT_OWNER_REFUSAL}`),
74213
74711
  403: forbiddenResponse("config:write"),
74214
74712
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL)
@@ -74259,7 +74757,7 @@ var createPromptsRouter = (opts) => {
74259
74757
  description: "A SOFT delete — the rows are kept for the audit window and every read filters them out. Refused while the prompt still publishes something: deleting text an owner is reading takes it away mid-flight, and the operator who typed the delete is rarely the one who finds out. Unpublish first.",
74260
74758
  request: { params: loosePromptPath },
74261
74759
  responses: {
74262
- 200: jsonResponse$1(named("StudioPromptDeleteResponse", studioPromptDeleteResponseSchema), "The prompt that was removed."),
74760
+ 200: jsonResponse$2(named("StudioPromptDeleteResponse", studioPromptDeleteResponseSchema), "The prompt that was removed."),
74263
74761
  400: errorResponse(PROMPT_OWNER_REFUSAL),
74264
74762
  403: forbiddenResponse("config:write"),
74265
74763
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL),
@@ -74301,7 +74799,7 @@ var createPromptsRouter = (opts) => {
74301
74799
  description: "Every version written under this key, newest first, each flagged with whether it is the one that RUNS. The history is the only place an operator can see which of several drafts is live. Capped at 200.",
74302
74800
  request: { params: loosePromptPath },
74303
74801
  responses: {
74304
- 200: jsonResponse$1(named("StudioPromptVersionListResponse", studioPromptVersionListResponseSchema), "The versions written under this key, newest first."),
74802
+ 200: jsonResponse$2(named("StudioPromptVersionListResponse", studioPromptVersionListResponseSchema), "The versions written under this key, newest first."),
74305
74803
  400: errorResponse(PROMPT_OWNER_REFUSAL),
74306
74804
  403: forbiddenResponse("config:write"),
74307
74805
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL)
@@ -74326,7 +74824,7 @@ var createPromptsRouter = (opts) => {
74326
74824
  description: "A HARD delete, unlike the prompt's own: a draft nobody published is an attempt, not a record. The PUBLISHED version is refused outright — it is what the owner runs. `latestVersion` is recomputed afterwards, so the head never points at a number the history no longer holds.",
74327
74825
  request: { params: loosePromptVersionPath },
74328
74826
  responses: {
74329
- 200: jsonResponse$1(named("StudioPromptVersionDeleteResponse", studioPromptVersionDeleteResponseSchema), "The version that was removed."),
74827
+ 200: jsonResponse$2(named("StudioPromptVersionDeleteResponse", studioPromptVersionDeleteResponseSchema), "The version that was removed."),
74330
74828
  400: errorResponse(PROMPT_OWNER_OR_VERSION_REFUSAL),
74331
74829
  403: forbiddenResponse("config:write"),
74332
74830
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL),
@@ -74388,7 +74886,7 @@ var createPromptsRouter = (opts) => {
74388
74886
  body: jsonBody(named("StudioPromptPublishRequest", studioPromptPublishRequestSchema), "The version to make live.")
74389
74887
  },
74390
74888
  responses: {
74391
- 200: jsonResponse$1(named("StudioPrompt", studioPromptSchema), "The prompt, now publishing the version that was asked for."),
74889
+ 200: jsonResponse$2(named("StudioPrompt", studioPromptSchema), "The prompt, now publishing the version that was asked for."),
74392
74890
  400: errorResponse(`The body failed its schema (\`invalid_payload\`), or ${PROMPT_OWNER_REFUSAL}`),
74393
74891
  403: forbiddenResponse("config:write"),
74394
74892
  404: errorResponse(`${PROMPT_NOT_FOUND_REFUSAL} A version that was never written answers \`studio_prompt_version_not_found\`.`)
@@ -74435,7 +74933,7 @@ var createPromptsRouter = (opts) => {
74435
74933
  description: "Clears the published pointer. The versions stay — what goes is what RUNS, which is exactly what the two delete refusals on this surface are waiting on. An `agent` owner is rebuilt afterwards: taking text out of service changes what it answers just as much as putting text in.",
74436
74934
  request: { params: loosePromptPath },
74437
74935
  responses: {
74438
- 200: jsonResponse$1(named("StudioPrompt", studioPromptSchema), "The prompt, now publishing nothing."),
74936
+ 200: jsonResponse$2(named("StudioPrompt", studioPromptSchema), "The prompt, now publishing nothing."),
74439
74937
  400: errorResponse(PROMPT_OWNER_REFUSAL),
74440
74938
  403: forbiddenResponse("config:write"),
74441
74939
  404: errorResponse(PROMPT_NOT_FOUND_REFUSAL)
@@ -74480,7 +74978,7 @@ var createPromptsRouter = (opts) => {
74480
74978
  body: jsonBody(named("StudioPromptCompileRequest", studioPromptCompileRequestSchema), "The variables to render with, and optionally the version — or the unsaved draft — to render.")
74481
74979
  },
74482
74980
  responses: {
74483
- 200: jsonResponse$1(named("StudioPromptCompileResponse", studioPromptCompileResponseSchema), "The rendered text, or the diagnostic saying why there is none."),
74981
+ 200: jsonResponse$2(named("StudioPromptCompileResponse", studioPromptCompileResponseSchema), "The rendered text, or the diagnostic saying why there is none."),
74484
74982
  400: errorResponse(`The body failed its schema (\`invalid_payload\`), or ${PROMPT_OWNER_REFUSAL}`),
74485
74983
  403: forbiddenResponse("config:write"),
74486
74984
  404: errorResponse(`${PROMPT_NOT_FOUND_REFUSAL} A pinned version that was never written answers \`studio_prompt_version_not_found\`.`)
@@ -74861,7 +75359,7 @@ var createConfigRouter = (agentConfigOptions) => {
74861
75359
  summary: "Read the active agent config",
74862
75360
  description: "The active `AGENT_CONFIG` value, the JSON Schema the creator declared for it, and which version is live. `value` is null until the agent is configured for the first time; `schema` is null when the workspace declares none.",
74863
75361
  responses: {
74864
- 200: jsonResponse$1(named("StudioAgentConfig", studioAgentConfigGetResponseSchema), "The active config, its schema and its version."),
75362
+ 200: jsonResponse$2(named("StudioAgentConfig", studioAgentConfigGetResponseSchema), "The active config, its schema and its version."),
74865
75363
  403: forbiddenResponse("config:write")
74866
75364
  }
74867
75365
  }), async (c) => c.json(await getAgentConfig(agentConfigOptions), 200));
@@ -74874,7 +75372,7 @@ var createConfigRouter = (agentConfigOptions) => {
74874
75372
  description: "Appends a version and makes it active. A value structurally identical to the active one is a no-op: the reply comes back `unchanged: true` and no version is written.",
74875
75373
  request: { body: jsonBody(named("StudioAgentConfigPutRequest", studioAgentConfigPutRequestSchema), "The new `AGENT_CONFIG` value, as a JSON object.") },
74876
75374
  responses: {
74877
- 200: jsonResponse$1(named("StudioAgentConfigPutResponse", studioAgentConfigPutResponseSchema), "The new active config, the value it replaced, and whether anything changed."),
75375
+ 200: jsonResponse$2(named("StudioAgentConfigPutResponse", studioAgentConfigPutResponseSchema), "The new active config, the value it replaced, and whether anything changed."),
74878
75376
  400: errorResponse("The body is not a `{ value: object }` (`invalid_payload`), or the value fails the JSON Schema the creator declared (`agent_config_invalid`)."),
74879
75377
  403: forbiddenResponse("config:write")
74880
75378
  }
@@ -74895,7 +75393,7 @@ var createConfigRouter = (agentConfigOptions) => {
74895
75393
  summary: "List the config versions",
74896
75394
  description: "The 100 most recent versions, newest first, each with its value — so the editor can diff or preview one without a second round-trip.",
74897
75395
  responses: {
74898
- 200: jsonResponse$1(named("StudioAgentConfigVersionListResponse", studioAgentConfigVersionListResponseSchema), "The version history, newest first."),
75396
+ 200: jsonResponse$2(named("StudioAgentConfigVersionListResponse", studioAgentConfigVersionListResponseSchema), "The version history, newest first."),
74899
75397
  403: forbiddenResponse("config:write")
74900
75398
  }
74901
75399
  }), async (c) => c.json(await listAgentConfigVersions(agentConfigOptions), 200));
@@ -74908,7 +75406,7 @@ var createConfigRouter = (agentConfigOptions) => {
74908
75406
  description: "Re-applies an earlier version's value as a NEW version — the history is append-only, so a rollback is never a deletion.",
74909
75407
  request: { params: looseVersionPath },
74910
75408
  responses: {
74911
- 200: jsonResponse$1(named("StudioAgentConfigPutResponse", studioAgentConfigPutResponseSchema), "The rolled-back value, now active as the newest version."),
75409
+ 200: jsonResponse$2(named("StudioAgentConfigPutResponse", studioAgentConfigPutResponseSchema), "The rolled-back value, now active as the newest version."),
74912
75410
  403: forbiddenResponse("config:write"),
74913
75411
  404: errorResponse("No such version — or a version that is not a number at all.")
74914
75412
  }
@@ -76111,7 +76609,7 @@ var registerAuthorizeRoutes = (app, { getContext, registry }) => {
76111
76609
  description: "Mints a token against the provider with the stored `client_credentials` and reports what happened. An app-only connector has no interactive \"connect account\" step, so this is the only way an operator finds out the credentials and tenant actually work.\n\nA provider REJECTION is a 200 with `ok: false` and the provider’s own sentence, not an error status: the fix is in the provider’s console, and a 4xx here would read as the box refusing the call. Only a connector that cannot be tested this way at all — any `authMode` other than `client_credentials` — is a 400.",
76112
76610
  request: { params: connectorPath },
76113
76611
  responses: {
76114
- 200: jsonResponse$1(named("ConnectConnectorTestResponse", connectConnectorTestResponseSchema), "What the provider said. `ok: false` is a completed test with a negative answer."),
76612
+ 200: jsonResponse$2(named("ConnectConnectorTestResponse", connectConnectorTestResponseSchema), "What the provider said. `ok: false` is a completed test with a negative answer."),
76115
76613
  400: errorResponse("This connector uses `oauth` or `api_key` auth, which has no credentials to test this way (`connect_test_not_supported`); or the broker refused the arguments (`invalid_args`, `invalid_grant`)."),
76116
76614
  403: forbiddenResponse(CONNECT_CAPABILITY),
76117
76615
  ...brokerRefusalResponses("The test")
@@ -76164,7 +76662,7 @@ var registerAuthorizeRoutes = (app, { getContext, registry }) => {
76164
76662
  body: describedJsonBodyOf(app, "ConnectAuthorizeRequest", authorizeConnectorRequestSchema, "Who the authorization is on behalf of. Omit it entirely for the plain service-account flow.")
76165
76663
  },
76166
76664
  responses: {
76167
- 200: jsonResponse$1(named("ConnectAuthorizeResponse", connectAuthorizeResponseSchema), "The consent challenge. Note its `expiresAt` is an ISO 8601 STRING, unlike every other `expiresAt` on this surface."),
76665
+ 200: jsonResponse$2(named("ConnectAuthorizeResponse", connectAuthorizeResponseSchema), "The consent challenge. Note its `expiresAt` is an ISO 8601 STRING, unlike every other `expiresAt` on this surface."),
76168
76666
  400: errorResponse("The body failed its schema (`invalid_payload`), or the broker refused the request — an `app` principal cannot drive an interactive flow (`invalid_args`, `invalid_grant`)."),
76169
76667
  403: forbiddenResponse(CONNECT_CAPABILITY),
76170
76668
  ...brokerRefusalResponses("Starting the authorization")
@@ -76405,7 +76903,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76405
76903
  description: "Stores the operator’s credentials for one connector, keyed on `connectorId` — posting the same id again UPSERTS the registration in place rather than failing. `clientSecret` is write-only: it is encrypted at rest by the box cipher and the readback carries only `hasClientSecret`.\n\nWhich fields are required depends on `authMode`, and the rule is enforced but not expressible in this schema: `api_key` needs only `clientSecret` (the key itself); `client_credentials` needs `clientId`, `clientSecret` and `tokenUrl`; `oauth` needs those three plus `authorizeUrl`. A body that misses one is refused with `invalid_payload` and the issue pinned to that field.\n\nA CUSTOM connector — one the workspace defines rather than picking from the catalog — additionally sends `baseUrl` and `specSource`; `protocol` is persisted on the row only for those.\n\nAn imported PIECE (`protocol: piece`) registers with `authMode: custom`, no `placement` and no `clientSecret`: it sends `credentialValues`, one value per `credentialFields` entry of its catalog definition. They are stored encrypted as the connection credential and never read back; a field that is not `secret` (a select with `options`, or a `type: number` field) is also kept as `extraFields` for the edit form. A field with `options` takes one of them, and a `number` field a number, sent as its text. A missing required field, a value off those, or an unknown key is refused on that field, naming it and never the value.\n\nA piece whose catalog entry lists more than one of `authMethods` (a GitHub App or a personal access token) also sends `authMethod`, the chosen `id`, and its values are checked against the fields of that method alone. The row keeps the method; re-registering under another one replaces it and the stored values together.\n\nA piece method of type `OAUTH2` registers with `authMode: oauth`, its `authMethod` and an OAuth app’s `clientId` and `clientSecret`: the row takes the method’s `authorizeUrl`, `tokenUrl` and `scopes` from the catalog definition, and a body that sends different ones is refused on that field. The grant comes from `POST /connectors/{connectorId}/authorize`. A method whose catalog entry lists `client_credentials` among its `grants` registers under `authMode: client_credentials` instead: no authorize step, the token is minted from the app itself.\n\nA method with `fields` (props such as a cloud, a tenant or a mailbox) also sends them as `credentialValues`; a blank one takes the field’s `defaultValue`, and a field with `options` takes one of them. Its URLs and scopes may name those fields as `{name}`: the row stores them filled in (URL-encoded in a URL, a scope value split on whitespace), and a name left without a value is refused on `credentialValues.<name>` with nothing written. The values are not secret; they are stored as `extraFields` and handed to the piece.\n\n`allowedOperations` has three states: absent keeps the stored fence (a key rotation posts the blank form), `null` removes it, an array sets it — and an array is checked against the armed triggers exactly as `PUT /connectors/{connectorId}/allowed-operations` checks it.",
76406
76904
  request: { body: jsonBody(named("RegisterConnectorRequest", registerConnectorRequestSchema), "The registration. Mirrors the cloud studio-module DTO field for field.") },
76407
76905
  responses: {
76408
- 200: jsonResponse$1(CONNECT_CONNECTOR, "The registration as it now stands, read back through the masking projection."),
76906
+ 200: jsonResponse$2(CONNECT_CONNECTOR, "The registration as it now stands, read back through the masking projection."),
76409
76907
  400: errorResponse("The body failed its schema, including the per-`authMode` required-field rules (`invalid_payload`).\n\nAlso `invalid_payload` when a `specSource` is sent for a connector this box already defines: a stored spec supersedes the catalog entry ENTIRELY, taking the delete guard, the provider error classes and the filter operators with it. Register a custom connector under an id of your own.\n\n`invalid_credential` when a piece’s own check refused the credential; `message` is the piece’s sentence. Nothing was saved."),
76410
76908
  403: forbiddenResponse(CONNECT_CAPABILITY),
76411
76909
  502: errorResponse("A piece could not be asked whether the credential works (`piece_runtime`): its bundle did not load, or its check did not answer in time. Nothing was saved; this is not a verdict on the credential."),
@@ -76510,7 +77008,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76510
77008
  summary: "List the connector gallery",
76511
77009
  description: "Every brand this box knows how to connect: the built-in catalog, the Activepieces pieces the box lists, plus whatever the workspace declares under `<projectRoot>/connect/*.json` (a workspace document supersedes a built-in by reusing its id). Presentation and PRE-FILL only — `authorizeUrl`, `tokenUrl`, `scopes` and `placement` are what the register form fills in for the operator, and `requiredFields` is what they must still supply.\n\n`unavailable` lists the Activepieces pieces that never build, never load or carry a malformed manifest entry, each with its code, reason and logo. None of them is among `items`, and registering one is refused under any `authMode` (`invalid_payload`).\n\nThis is the catalog, not the registrations: `GET /api/connect/connectors` is what this box has actually registered.",
76512
77010
  responses: {
76513
- 200: jsonResponse$1(named("ConnectCatalogResponse", connectCatalogResponseSchema), "The gallery entries, in registry order."),
77011
+ 200: jsonResponse$2(named("ConnectCatalogResponse", connectCatalogResponseSchema), "The gallery entries, in registry order."),
76514
77012
  403: forbiddenResponse(CONNECT_CAPABILITY)
76515
77013
  }
76516
77014
  }), (c) => c.json({
@@ -76526,7 +77024,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76526
77024
  summary: "Retry loading the default connector catalog",
76527
77025
  description: "Tries the default Activepieces catalog this box missed at boot right away, instead of waiting for the next background attempt. A call while an attempt is already running waits for that one rather than starting a second. When the attempt fails, the background retries start over from their first wait (30 s).\n\nAnswers once the attempt is over, with the same `catalogLoad` that `GET /api/connect/catalog` carries: `failed: false` means the catalog loaded and the gallery now lists its pieces.",
76528
77026
  responses: {
76529
- 200: jsonResponse$1(named("ConnectCatalogRetryResponse", connectCatalogRetryResponseSchema), "Where the box stands on the default catalog after the attempt."),
77027
+ 200: jsonResponse$2(named("ConnectCatalogRetryResponse", connectCatalogRetryResponseSchema), "Where the box stands on the default catalog after the attempt."),
76530
77028
  403: forbiddenResponse(CONNECT_CAPABILITY),
76531
77029
  404: errorResponse("This box is not retrying a default catalog (`catalog_retry_not_found`): it loaded one at boot, names its own, or has none configured.")
76532
77030
  }
@@ -76543,7 +77041,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76543
77041
  summary: "List the registered connectors",
76544
77042
  description: "What this box has credentials for, ordered by provider. `hasClientSecret` is the entire readback of the stored secret — the ciphertext never leaves the box, and neither does any token.",
76545
77043
  responses: {
76546
- 200: jsonResponse$1(named("ConnectConnectorListResponse", connectConnectorListResponseSchema), "The registrations, each through the masking projection."),
77044
+ 200: jsonResponse$2(named("ConnectConnectorListResponse", connectConnectorListResponseSchema), "The registrations, each through the masking projection."),
76547
77045
  403: forbiddenResponse(CONNECT_CAPABILITY)
76548
77046
  }
76549
77047
  }), async (c) => {
@@ -76572,7 +77070,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76572
77070
  description: "Drops the stored credentials AND disarms the inbound triggers that polled through this connection, answering how many went off with it (PLA-1095). Those were two acts that looked like one: a link left armed on a deleted connection failed every tick forever while the Triggers screen still read it as healthy. Disarming stops the polling and nothing else — the poll cursor stays put, so re-registering the connection and re-arming the link resumes from where it left off rather than from now.\n\nThe GRANTS made against this connector are NOT dropped with it — the grant row carries no foreign key to the registration, deliberately, so a re-registration under the same id keeps working. Revoke a grant separately with `DELETE /api/connect/grants/{grantId}`.",
76573
77071
  request: { params: connectorPath },
76574
77072
  responses: {
76575
- 200: jsonResponse$1(named("ConnectConnectorDeleteResponse", connectConnectorDeleteResponseSchema), "The registration is gone."),
77073
+ 200: jsonResponse$2(named("ConnectConnectorDeleteResponse", connectConnectorDeleteResponseSchema), "The registration is gone."),
76576
77074
  403: forbiddenResponse(CONNECT_CAPABILITY),
76577
77075
  404: errorResponse("No registration under that id (`connector_installation_required`).")
76578
77076
  }
@@ -76620,7 +77118,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76620
77118
  body: jsonBody(named("ConnectConnectorFenceRequest", connectConnectorFenceRequestSchema), "The fence, or `null` to remove it.")
76621
77119
  },
76622
77120
  responses: {
76623
- 200: jsonResponse$1(CONNECT_CONNECTOR, "The registration as it now stands."),
77121
+ 200: jsonResponse$2(CONNECT_CONNECTOR, "The registration as it now stands."),
76624
77122
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
76625
77123
  403: forbiddenResponse(CONNECT_CAPABILITY),
76626
77124
  404: errorResponse("No registration under that id (`connector_installation_required`)."),
@@ -76658,7 +77156,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76658
77156
  description: "Drops the cached spec document for this registration. The next action-picker open (and the next action run) downloads the provider's current spec instead of the one this box already holds — which is how a table added to a Supabase project since the last read shows up.\n\nNothing is downloaded here: the refresh evicts, the next read fetches. A connector whose spec is EMBEDDED has nothing cached and answers `not_cached`.",
76659
77157
  request: { params: connectorPath },
76660
77158
  responses: {
76661
- 200: jsonResponse$1(named("ConnectConnectorSpecRefresh", connectConnectorSpecRefreshResponseSchema), "The cached spec is gone, or there was none to drop."),
77159
+ 200: jsonResponse$2(named("ConnectConnectorSpecRefresh", connectConnectorSpecRefreshResponseSchema), "The cached spec is gone, or there was none to drop."),
76662
77160
  403: forbiddenResponse(CONNECT_CAPABILITY),
76663
77161
  404: errorResponse("No registration under that id (`connector_installation_required`).")
76664
77162
  }
@@ -76699,7 +77197,7 @@ var registerConnectorRoutes = (app, { opts, getContext, registry, unavailable, c
76699
77197
  description: "What the connector can be asked to do, for the action picker: `operations` for an OpenAPI/Swagger connector, `tools` for an MCP one. A CUSTOM connector is introspected from the spec on its own row; a built-in from the registry definition its registration references.\n\nA probe that FAILS is still a 200: the reason lands in `warnings` with an empty result, because \"this connector advertises nothing right now\" is a state the picker renders rather than an error it has to recover from. The same is true when the workspace has no definition to probe at all.",
76700
77198
  request: { params: connectorPath },
76701
77199
  responses: {
76702
- 200: jsonResponse$1(named("ConnectConnectorOperations", introspectionResultSchema), "The operations or tools discovered, plus any non-fatal notes from the probe."),
77200
+ 200: jsonResponse$2(named("ConnectConnectorOperations", introspectionResultSchema), "The operations or tools discovered, plus any non-fatal notes from the probe."),
76703
77201
  403: forbiddenResponse(CONNECT_CAPABILITY),
76704
77202
  404: errorResponse("No registration under that id (`connector_installation_required`). A registration with nothing to introspect is a 200 with warnings, not this.")
76705
77203
  }
@@ -76798,7 +77296,7 @@ var registerGrantRoutes = (app, { getContext }) => {
76798
77296
  summary: "List the stored authorizations",
76799
77297
  description: "Every grant this box holds, ordered by connector then principal. METADATA ONLY: the row behind each entry holds an encrypted access token and, for an interactive grant, an encrypted refresh token, and neither is reachable from any management route. A credential leaves this box only as a short-lived mint on `POST /api/connect/token`.",
76800
77298
  responses: {
76801
- 200: jsonResponse$1(named("ConnectGrantListResponse", connectGrantListResponseSchema), "The grants, with health and expiry but no token material."),
77299
+ 200: jsonResponse$2(named("ConnectGrantListResponse", connectGrantListResponseSchema), "The grants, with health and expiry but no token material."),
76802
77300
  403: forbiddenResponse(CONNECT_CAPABILITY)
76803
77301
  }
76804
77302
  }), async (c) => {
@@ -76819,7 +77317,7 @@ var registerGrantRoutes = (app, { getContext }) => {
76819
77317
  description: "Drops the durable authorization AND the token cached from it, so nothing minted before this call survives. The principal has to authorize from scratch next time — which is what separates this from `POST /api/connect/evict`, the agent wire’s targeted cache drop that leaves the grant standing.",
76820
77318
  request: { params: grantPath },
76821
77319
  responses: {
76822
- 200: jsonResponse$1(named("ConnectGrantRevokeResponse", connectGrantRevokeResponseSchema), "The grant and its cached token are gone."),
77320
+ 200: jsonResponse$2(named("ConnectGrantRevokeResponse", connectGrantRevokeResponseSchema), "The grant and its cached token are gone."),
76823
77321
  403: forbiddenResponse(CONNECT_CAPABILITY),
76824
77322
  404: errorResponse("No grant with that row id on this box (`not_found`).")
76825
77323
  }
@@ -76920,7 +77418,7 @@ var init_support = __esmMin((() => {
76920
77418
  AGENT_WIRE_TAG = AGENT_CONNECT_TAG;
76921
77419
  AGENT_WIRE_401 = errorResponse("The scheme-A signature is missing, malformed, computed with the wrong secret or outside the 5-minute drift window (`access_denied`) — or the signature was fine and the BROKER has no authorization to work with (`no_token`, `user_authorization_required`, `principal_required`).");
76922
77420
  CONNECT_EXECUTE_FAILURE = named("ConnectExecuteFailure", connectExecuteFailureSchema);
76923
- executeRefusal = (description) => jsonResponse$1(z.union([CONNECT_EXECUTE_FAILURE, errorEnvelopeSchema]), description);
77421
+ executeRefusal = (description) => jsonResponse$2(z.union([CONNECT_EXECUTE_FAILURE, errorEnvelopeSchema]), description);
76924
77422
  answerExecuteFailure = (c, error) => {
76925
77423
  const body = {
76926
77424
  ok: false,
@@ -77039,7 +77537,7 @@ var createConnectTokenRouter = (opts, sharedContext) => {
77039
77537
  description: "The runtime hot path: a `@stackbone/sdk/connect` adapter calls this before a tool call. The response carries `placement` — every slot the credential occupies — so the caller applies it where the OPERATOR configured it rather than guessing. It is the same set `POST /execute` applies inside the box, which is what keeps the two paths from disagreeing about one connector.\n\nThe two consumers read it differently, and only one of them can. `connectHeaders()` builds the request headers itself, so it honours the set: a `header` placement writes the raw token to that header, a `bearer` one writes `Authorization: Bearer`. `connect()` / `withConnect()` cannot — a connection runtime carries only a bearer through `auth.getToken`, and `TokenResult` has no slot for a placement — so that path stays BEARER-ONLY by construction. A non-bearer connector wired through it fails at the provider, not here: a key the provider reads from a header of its own, such as the `apikey` header the built-in `supabase` places beside its bearer, never reaches that header. Use `connectHeaders()` or `POST /execute` for those.\n\nThe signature proves POSSESSION of the workspace secret, not WHICH install is calling, so the body asserts its own scoping claims: `installationId` and `agentName` are read as claims, `connector` and `principal` pick the grant. A connector with no grant answers `no_token` on 401 and the adapter turns that into an authorization prompt.",
77040
77538
  request: { body: jsonBody(named("ConnectTokenRequest", tokenRequestSchema), "Which connector, for which principal, on behalf of which install.") },
77041
77539
  responses: {
77042
- 200: jsonResponse$1(named("ConnectTokenResponse", connectTokenResponseSchema), "The minted credential. `expiresAt` is absent when the provider declared no expiry."),
77540
+ 200: jsonResponse$2(named("ConnectTokenResponse", connectTokenResponseSchema), "The minted credential. `expiresAt` is absent when the provider declared no expiry."),
77043
77541
  400: errorResponse("The body failed its schema (`invalid_payload`), or the broker refused the request (`invalid_args`, `invalid_grant`)."),
77044
77542
  401: AGENT_WIRE_401,
77045
77543
  404: errorResponse("The connector is not registered on this box (`connector_installation_required`, `not_found`)."),
@@ -77079,7 +77577,7 @@ var createConnectTokenRouter = (opts, sharedContext) => {
77079
77577
  description: "Mints the credential, applies it in the connector’s declared placement, calls the operation and hands back ONLY its output. Nothing about the credential — not the token, not its placement — crosses this boundary, which is the whole reason a workflow step calls the box instead of the provider.\n\n`operation` names an operation id (OpenAPI) or a tool (MCP) as `GET /api/connect/connectors/{connectorId}/operations` reports it. `runId` is the one field that changes WHICH connector answers: a run wired to a stand-in reaches the stand-in and never leaves the box, which is how an eval exercises a send without sending anything.\n\nA refusal comes back in TWO shapes on the same statuses: `{ ok: false, error }` when the executor completed and the provider or the broker said no, including a broker error thrown before execution completes. Signature and payload validation failures use the box’s `{ code, message }` envelope.",
77080
77578
  request: { body: jsonBody(named("ConnectExecuteRequest", executeRequestSchema), "The connector, the operation, and the args to run it with.") },
77081
77579
  responses: {
77082
- 200: jsonResponse$1(named("ConnectExecuteSuccess", connectExecuteSuccessSchema), "The operation’s own output, passed through untouched."),
77580
+ 200: jsonResponse$2(named("ConnectExecuteSuccess", connectExecuteSuccessSchema), "The operation’s own output, passed through untouched."),
77083
77581
  400: executeRefusal("The body failed its schema (`invalid_payload`), or the broker refused the arguments (`invalid_args`, `invalid_grant`)."),
77084
77582
  401: executeRefusal("The signature is missing or wrong (`access_denied`), or the connector has no usable authorization (`no_token`, `user_authorization_required`, `principal_required`)."),
77085
77583
  404: executeRefusal("No such connector on this box, or no such operation on it (`not_found`, `connector_installation_required`)."),
@@ -77122,7 +77620,7 @@ var createConnectTokenRouter = (opts, sharedContext) => {
77122
77620
  description: "The adapter’s 401-recovery hook: a provider rejected a token that has not expired yet, so the cached copy is dropped and the NEXT mint fetches a fresh one. The durable GRANT survives — that is what separates this from `DELETE /api/connect/grants/{grantId}`, which drops the authorization and makes the principal start over.\n\nEvicting a connector with nothing cached is not an error; it answers the same `evicted` either way.",
77123
77621
  request: { body: jsonBody(named("ConnectEvictRequest", evictRequestSchema), "Which connector, for which principal.") },
77124
77622
  responses: {
77125
- 200: jsonResponse$1(named("ConnectEvictResponse", connectEvictResponseSchema), "The cached credential for that pair is gone. The grant is untouched."),
77623
+ 200: jsonResponse$2(named("ConnectEvictResponse", connectEvictResponseSchema), "The cached credential for that pair is gone. The grant is untouched."),
77126
77624
  400: errorResponse("The body failed its schema (`invalid_payload`), or the broker refused the arguments (`invalid_args`, `invalid_grant`)."),
77127
77625
  401: AGENT_WIRE_401,
77128
77626
  404: errorResponse("The connector is not registered on this box (`connector_installation_required`, `not_found`)."),
@@ -77343,7 +77841,7 @@ var createDbExplorerRouter = (deps) => {
77343
77841
  summary: "List the schemas and their tables",
77344
77842
  description: "The explorer sidebar: every schema `stackbone_viewer` can see, each with its tables and an ESTIMATED row count off `pg_class.reltuples` (never a `COUNT(*)`, which would scan the table to paint a sidebar). `stackbone_platform` sorts first.",
77345
77843
  responses: {
77346
- 200: jsonResponse$1(named("DbSchemasResponse", dbSchemasResponseSchema), "Every readable schema and its tables."),
77844
+ 200: jsonResponse$2(named("DbSchemasResponse", dbSchemasResponseSchema), "Every readable schema and its tables."),
77347
77845
  403: forbiddenResponse("db:query")
77348
77846
  }
77349
77847
  }), async (c) => c.json(await listSchemas(dbExplorerOptions), 200));
@@ -77359,7 +77857,7 @@ var createDbExplorerRouter = (deps) => {
77359
77857
  query: dbBrowseQuerySchema
77360
77858
  },
77361
77859
  responses: {
77362
- 200: jsonResponse$1(named("DbBrowseResponse", dbBrowseResponseSchema), "One page of rows, and the cursor for the next."),
77860
+ 200: jsonResponse$2(named("DbBrowseResponse", dbBrowseResponseSchema), "One page of rows, and the cursor for the next."),
77363
77861
  400: errorResponse("A segment is not a Postgres identifier, or the page controls failed their schema (`invalid_payload`) — or the cursor is not one this box issued (`invalid_cursor`)."),
77364
77862
  403: forbiddenResponse("db:query"),
77365
77863
  404: errorResponse("No such table in that schema (`table_not_found`).")
@@ -77392,7 +77890,7 @@ var createDbExplorerRouter = (deps) => {
77392
77890
  description: `The ad-hoc editor. SELECT-only, one statement, run under \`stackbone_viewer\` with \`statement_timeout\` at ${VIEWER_STATEMENT_TIMEOUT_S}s; a query with no LIMIT (or a bigger one) is capped at ${STUDIO_DB_QUERY_ROW_LIMIT} rows and comes back \`truncated: true\`.`,
77393
77891
  request: { body: jsonBody(named("DbQueryRequest", dbQueryRequestSchema), "The SQL to run.") },
77394
77892
  responses: {
77395
- 200: jsonResponse$1(named("DbQueryResponse", dbQueryResponseSchema), "The rows, their columns, whether the cap was applied, and how long it took."),
77893
+ 200: jsonResponse$2(named("DbQueryResponse", dbQueryResponseSchema), "The rows, their columns, whether the cap was applied, and how long it took."),
77396
77894
  400: errorResponse("The body failed its schema (`invalid_payload`), or the SQL is one this surface refuses to run: `non_select`, `multi_statement`, `select_for_update`, `cte_write`, `invalid_sql`."),
77397
77895
  403: forbiddenResponse("db:query")
77398
77896
  }
@@ -77547,7 +78045,7 @@ var createAgentsRouter = (deps) => {
77547
78045
  tags: [AGENTS_TAG],
77548
78046
  summary: "List the in-process deep agents",
77549
78047
  description: "Read straight off the registry, never probed over HTTP — a deep agent runs in this process, so `healthy` is its readiness and not a reachability check. A DEGRADED agent is listed, honestly: it is registered and named, its graph never built, and `reason` says whether that is a missing model provider or a bug in the creator's code. A workspace with no deep agents answers an empty list.",
77550
- responses: { 200: jsonResponse$1(named("CatalogAgentListResponse", catalogAgentListResponseSchema), "Every deep agent this workspace registers.") }
78048
+ responses: { 200: jsonResponse$2(named("CatalogAgentListResponse", catalogAgentListResponseSchema), "Every deep agent this workspace registers.") }
77551
78049
  }), (c) => {
77552
78050
  const items = (deps.getDeepAgentRegistry()?.list() ?? []).map(projectDeepAgentSummary);
77553
78051
  return c.json({ items }, 200);
@@ -77560,7 +78058,7 @@ var createAgentsRouter = (deps) => {
77560
78058
  description: "The full inspection view: the model, the system prompt, the tool names and whether the agent can hold durable sessions. Tools are NAME-ONLY — a deep agent exposes no per-tool JSON Schema over the catalog — and there are no channels, skills, schedules or hooks.",
77561
78059
  request: { params: agentNamePath },
77562
78060
  responses: {
77563
- 200: jsonResponse$1(named("CatalogAgentDetail", catalogAgentDetailSchema), "The agent, as the inspection screen reads it."),
78061
+ 200: jsonResponse$2(named("CatalogAgentDetail", catalogAgentDetailSchema), "The agent, as the inspection screen reads it."),
77564
78062
  404: errorResponse("No agent by that name (`agent_not_found`). The names this workspace DOES register ride back under `details.known`, so a typo is one round-trip to fix.")
77565
78063
  }
77566
78064
  }), async (c) => {
@@ -81034,7 +81532,7 @@ var createEvalsRouter = (options = {}) => {
81034
81532
  tags: [EVALS_TAG],
81035
81533
  summary: "List the criteria this build can judge with",
81036
81534
  description: "Every criterion a suite may be written against, each carrying what it can judge (`targets`), a plain-language description and the JSON Schema of its own settings — a screen renders the criterion form straight from this. Two of those settings are painted with facts only this box holds and are therefore asked PER REQUEST: the rubric criterion's judging model (the chat models the configured provider lists, plus the one this workspace judges with) and the customer criterion's judging workflow (the workflows this box could start and await). Nothing is ever withheld: a box that can reach neither serves the same entries with EMPTY pickers. `simulatorWorkflow` rides beside the entries because which workflow plays the user is a SUITE setting, not a criterion one.",
81037
- responses: { 200: jsonResponse$1(named("EvalScorerCatalogResponse", evalScorerCatalogResponseSchema), "One entry per criterion, plus the suite-level simulator picker.") }
81535
+ responses: { 200: jsonResponse$2(named("EvalScorerCatalogResponse", evalScorerCatalogResponseSchema), "One entry per criterion, plus the suite-level simulator picker.") }
81038
81536
  }), async (c) => c.json(evalScorerCatalogResponseSchema.parse({
81039
81537
  entries: await servedCatalog(options),
81040
81538
  simulatorWorkflow: await servedSimulatorWorkflow(options)
@@ -81155,7 +81653,7 @@ var createEvalDatasetsRouter = (opts) => {
81155
81653
  tags: [EVALS_TAG],
81156
81654
  summary: "List the case lists",
81157
81655
  description: "Every active list, WITHOUT its cases — the index screen paints a row from `caseCount` and `currentVersion`, and a thousand-case list per row would make the read useless. Read one list to get its cases.",
81158
- responses: { 200: jsonResponse$1(named("EvalDatasetListResponse", evalDatasetListResponseSchema), "Every active list, cases omitted.") }
81656
+ responses: { 200: jsonResponse$2(named("EvalDatasetListResponse", evalDatasetListResponseSchema), "Every active list, cases omitted.") }
81159
81657
  }), async (c) => c.json({ items: await opts.store.list() }, 200));
81160
81658
  app.openapi(createRoute({
81161
81659
  method: "post",
@@ -81166,7 +81664,7 @@ var createEvalDatasetsRouter = (opts) => {
81166
81664
  description: "Writes a list at version 1. The cases ride in the body as ordinary JSON rows — there is no file upload anywhere on this surface — and an EMPTY `cases` is a real request: a list an operator will capture into from run details later.",
81167
81665
  request: { body: jsonBody(named("CreateEvalDatasetRequest", createEvalDatasetRequestSchema), "The key, the name, and the case rows version 1 holds. Each case carries the `input` the target is given and, optionally, the `expected` answer, a `persona` to answer it back and the tags a suite filters on.") },
81168
81666
  responses: {
81169
- 201: jsonResponse$1(named("EvalDataset", evalDatasetSchema), "The stored list at version 1, cases included."),
81667
+ 201: jsonResponse$2(named("EvalDataset", evalDatasetSchema), "The stored list at version 1, cases included."),
81170
81668
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
81171
81669
  403: forbiddenResponse("config:write"),
81172
81670
  409: errorResponse("Another list already holds that key (`eval_dataset_key_taken`). Deleting the old one frees the name.")
@@ -81206,7 +81704,7 @@ var createEvalDatasetsRouter = (opts) => {
81206
81704
  body: jsonBody(named("CaptureEvalCaseRequest", captureEvalCaseRequestSchema), "The case to append, plus the free-text `note` kept on the version this mints.")
81207
81705
  },
81208
81706
  responses: {
81209
- 201: jsonResponse$1(named("EvalDataset", evalDatasetSchema), "The list at its NEW version, with the captured case on it."),
81707
+ 201: jsonResponse$2(named("EvalDataset", evalDatasetSchema), "The list at its NEW version, with the captured case on it."),
81210
81708
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
81211
81709
  403: forbiddenResponse("config:write"),
81212
81710
  404: errorResponse(LIST_NOT_FOUND_DESCRIPTION),
@@ -81248,7 +81746,7 @@ var createEvalDatasetsRouter = (opts) => {
81248
81746
  query: datasetReadQuery
81249
81747
  },
81250
81748
  responses: {
81251
- 200: jsonResponse$1(named("EvalDataset", evalDatasetSchema), "The list, with the cases of the version read."),
81749
+ 200: jsonResponse$2(named("EvalDataset", evalDatasetSchema), "The list, with the cases of the version read."),
81252
81750
  400: errorResponse("The query string failed its schema (`invalid_payload`) — a `version` that is not a positive integer, or a `tags` filter that names no tag."),
81253
81751
  404: errorResponse(LIST_NOT_FOUND_DESCRIPTION)
81254
81752
  }
@@ -81276,7 +81774,7 @@ var createEvalDatasetsRouter = (opts) => {
81276
81774
  body: jsonBody(named("UpdateEvalDatasetRequest", updateEvalDatasetRequestSchema), "The fields to change. Everything is optional, but at least one of `cases`, `name` or `description` is required.")
81277
81775
  },
81278
81776
  responses: {
81279
- 200: jsonResponse$1(named("EvalDataset", evalDatasetSchema), "The list as it now stands, at the version this edit left it on."),
81777
+ 200: jsonResponse$2(named("EvalDataset", evalDatasetSchema), "The list as it now stands, at the version this edit left it on."),
81280
81778
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
81281
81779
  403: forbiddenResponse("config:write"),
81282
81780
  404: errorResponse(LIST_NOT_FOUND_DESCRIPTION),
@@ -81319,7 +81817,7 @@ var createEvalDatasetsRouter = (opts) => {
81319
81817
  description: "A SOFT delete, so the runs that measured against this list keep their subject. The key is freed for reuse. No body — a delete addressed by its path takes nothing.",
81320
81818
  request: { params: looseKeyPath },
81321
81819
  responses: {
81322
- 200: jsonResponse$1(named("EvalDatasetDeleteResponse", evalDatasetDeleteResponseSchema), "The key that was deleted, so a screen can drop the row it was showing without remembering which delete just landed."),
81820
+ 200: jsonResponse$2(named("EvalDatasetDeleteResponse", evalDatasetDeleteResponseSchema), "The key that was deleted, so a screen can drop the row it was showing without remembering which delete just landed."),
81323
81821
  403: forbiddenResponse("config:write"),
81324
81822
  404: errorResponse(LIST_NOT_FOUND_DESCRIPTION)
81325
81823
  }
@@ -81394,7 +81892,7 @@ var createEvalRunsRouter = (opts) => {
81394
81892
  body: describedJsonBodyOf(app, "LaunchEvalRunRequest", launchEvalRunRequestSchema, "What this launch seals about the thing it is measuring — the commit and the agent build nobody else can know — and, optionally, the variant columns to measure side by side. Every field is optional; `{}` is a complete launch.")
81395
81893
  },
81396
81894
  responses: {
81397
- 202: jsonResponse$1(named("EvalRun", evalRunSchema), "The run row, durable and queued. NOTHING has been measured yet."),
81895
+ 202: jsonResponse$2(named("EvalRun", evalRunSchema), "The run row, durable and queued. NOTHING has been measured yet."),
81398
81896
  400: errorResponse("The body failed its schema (`invalid_payload`), or the suite cannot be measured here — `eval_dataset_empty`, `eval_dataset_version_missing`, `eval_rubric_missing`, `eval_rubric_version_missing`, `eval_rubric_ambiguous`, `eval_target_unsupported` or `eval_variant_unsupported`. Each is refused before a case is measured."),
81399
81897
  403: forbiddenResponse("evals:run"),
81400
81898
  404: errorResponse("No suite with that id on this box (`eval_suite_not_found`).")
@@ -81457,7 +81955,7 @@ var createEvalRunsRouter = (opts) => {
81457
81955
  summary: "Read a suite's run history",
81458
81956
  description: "Every run of that suite, newest first, WITHOUT the per-case verdicts — the history screen paints a row from the totals, and a run with a thousand case results per row would make the read useless. Read one run to get its verdicts.",
81459
81957
  request: { params: looseSuiteIdPath$1 },
81460
- responses: { 200: jsonResponse$1(named("EvalRunListResponse", evalRunListResponseSchema), "That suite's runs, newest first. A suite this box does not hold has no runs, which is an empty list rather than a 404.") }
81958
+ responses: { 200: jsonResponse$2(named("EvalRunListResponse", evalRunListResponseSchema), "That suite's runs, newest first. A suite this box does not hold has no runs, which is an empty list rather than a 404.") }
81461
81959
  }), async (c) => c.json({ items: await opts.store.listForSuite(c.req.param("suiteId")) }, 200));
81462
81960
  app.openapi(createRoute({
81463
81961
  method: "get",
@@ -81467,7 +81965,7 @@ var createEvalRunsRouter = (opts) => {
81467
81965
  description: "The most detailed answer on this surface: the run row, everything it SEALED about what it measured (the commit, the agent build, the model that answered and the model that graded), and one result per case with the score each criterion gave it.",
81468
81966
  request: { params: looseRunIdPath },
81469
81967
  responses: {
81470
- 200: jsonResponse$1(named("EvalRunDetail", evalRunDetailSchema), "The run with its per-case verdicts."),
81968
+ 200: jsonResponse$2(named("EvalRunDetail", evalRunDetailSchema), "The run with its per-case verdicts."),
81471
81969
  404: errorResponse(RUN_NOT_FOUND_DESCRIPTION)
81472
81970
  }
81473
81971
  }), async (c) => {
@@ -81483,7 +81981,7 @@ var createEvalRunsRouter = (opts) => {
81483
81981
  description: "A suite's FIRST run does not get a row of zeros: `comparison` is `null` and `reason` says why in words, because \"we have never measured this before\" and \"nothing changed\" are opposite pieces of news. When there is a comparison it names the run it was measured against, so the deltas have a subject.",
81484
81982
  request: { params: looseRunIdPath },
81485
81983
  responses: {
81486
- 200: jsonResponse$1(named("EvalRunComparisonResponse", evalRunComparisonResponseSchema), "The deltas and the run they are against — or `comparison: null` with the reason."),
81984
+ 200: jsonResponse$2(named("EvalRunComparisonResponse", evalRunComparisonResponseSchema), "The deltas and the run they are against — or `comparison: null` with the reason."),
81487
81985
  404: errorResponse(RUN_NOT_FOUND_DESCRIPTION)
81488
81986
  }
81489
81987
  }), async (c) => {
@@ -81518,7 +82016,7 @@ var createEvalRunsRouter = (opts) => {
81518
82016
  description: "The reporting read: one row per agent or workflow that has ever been measured here, without opening a suite. `system=exclude` leaves out the targets the platform installed for itself, as the suites list does. A literal segment beside `/runs/{runId}`, and registered before it, so it can never be read as a run id.",
81519
82017
  request: { query: evalSystemEntriesQuerySchema },
81520
82018
  responses: {
81521
- 200: jsonResponse$1(named("EvalTargetSummaryResponse", evalTargetSummaryResponseSchema), "One row per measured target."),
82019
+ 200: jsonResponse$2(named("EvalTargetSummaryResponse", evalTargetSummaryResponseSchema), "One row per measured target."),
81522
82020
  400: errorResponse("The query failed its schema (`invalid_payload`).")
81523
82021
  }
81524
82022
  }), async (c) => c.json({ items: await opts.store.summaryByTarget(c.req.valid("query")) }, 200));
@@ -81532,7 +82030,7 @@ var createEvalRunsRouter = (opts) => {
81532
82030
  description: "The engine reads this row between cases and stops there, SEALING what it already measured. Nothing here interrupts a case in flight — the platform has no live run cancellation, and pretending otherwise would leave a half-measured case looking like a real verdict. No body: the run is named by its path.",
81533
82031
  request: { params: looseRunIdPath },
81534
82032
  responses: {
81535
- 200: jsonResponse$1(named("EvalRun", evalRunSchema), "The run row, now cancelled."),
82033
+ 200: jsonResponse$2(named("EvalRun", evalRunSchema), "The run row, now cancelled."),
81536
82034
  403: forbiddenResponse("config:write"),
81537
82035
  404: errorResponse(RUN_NOT_FOUND_DESCRIPTION)
81538
82036
  }
@@ -81733,7 +82231,7 @@ var createEvalSuitesRouter = (opts) => {
81733
82231
  description: "Every active suite, with the criteria and the settings each one measures under. `system=exclude` leaves out the suites that measure an entry the platform installed for itself.",
81734
82232
  request: { query: evalSystemEntriesQuerySchema },
81735
82233
  responses: {
81736
- 200: jsonResponse$1(named("EvalSuiteListResponse", evalSuiteListResponseSchema), "Every active suite."),
82234
+ 200: jsonResponse$2(named("EvalSuiteListResponse", evalSuiteListResponseSchema), "Every active suite."),
81737
82235
  400: errorResponse("The query failed its schema (`invalid_payload`).")
81738
82236
  }
81739
82237
  }), async (c) => c.json({ items: await opts.store.list(c.req.valid("query")) }, 200));
@@ -81746,7 +82244,7 @@ var createEvalSuitesRouter = (opts) => {
81746
82244
  description: "Writes a suite: WHAT is measured (`targetKind` + `targetName`), over WHICH case list (`datasetId`, optionally pinned to a `datasetPin` version), with WHICH criteria, how many `repeats` and against what pass mark (`threshold`). Three validations run in this order and stop at the first — the wire schema, then the CATALOG (does this criterion exist, can it judge this target, does it understand these settings), then the target itself (is it served here). The catalog half is the very check `POST /api/evals/scorers/validate` runs, so a criterion the screen validated cannot be refused at save time.",
81747
82245
  request: { body: jsonBody(named("CreateEvalSuiteRequest", createEvalSuiteRequestSchema), "The target, the case list, the criteria and the run settings.") },
81748
82246
  responses: {
81749
- 201: jsonResponse$1(named("EvalSuite", evalSuiteSchema), "The stored suite."),
82247
+ 201: jsonResponse$2(named("EvalSuite", evalSuiteSchema), "The stored suite."),
81750
82248
  400: errorResponse(SUITE_WRITE_REFUSAL),
81751
82249
  403: forbiddenResponse("config:write"),
81752
82250
  409: errorResponse("Another suite already holds that name (`eval_suite_name_taken`). Deleting the old one frees it.")
@@ -81799,7 +82297,7 @@ var createEvalSuitesRouter = (opts) => {
81799
82297
  description: "The whole suite, criteria included.",
81800
82298
  request: { params: looseSuiteIdPath },
81801
82299
  responses: {
81802
- 200: jsonResponse$1(named("EvalSuite", evalSuiteSchema), "The suite as it stands."),
82300
+ 200: jsonResponse$2(named("EvalSuite", evalSuiteSchema), "The suite as it stands."),
81803
82301
  404: errorResponse(SUITE_NOT_FOUND_DESCRIPTION)
81804
82302
  }
81805
82303
  }), async (c) => {
@@ -81819,7 +82317,7 @@ var createEvalSuitesRouter = (opts) => {
81819
82317
  body: jsonBody(named("UpdateEvalSuiteRequest", updateEvalSuiteRequestSchema), "The fields to change. Everything is optional.")
81820
82318
  },
81821
82319
  responses: {
81822
- 200: jsonResponse$1(named("EvalSuite", evalSuiteSchema), "The suite as it now stands."),
82320
+ 200: jsonResponse$2(named("EvalSuite", evalSuiteSchema), "The suite as it now stands."),
81823
82321
  400: errorResponse(SUITE_WRITE_REFUSAL),
81824
82322
  403: forbiddenResponse("config:write"),
81825
82323
  404: errorResponse(SUITE_NOT_FOUND_DESCRIPTION),
@@ -81873,7 +82371,7 @@ var createEvalSuitesRouter = (opts) => {
81873
82371
  description: "A SOFT delete, so the runs that measured under this suite keep their subject. The name is freed for reuse. No body — a delete addressed by its path takes nothing.",
81874
82372
  request: { params: looseSuiteIdPath },
81875
82373
  responses: {
81876
- 200: jsonResponse$1(named("EvalSuiteDeleteResponse", evalSuiteDeleteResponseSchema), "The id that was deleted, so a screen can drop the row it was showing without remembering which delete just landed."),
82374
+ 200: jsonResponse$2(named("EvalSuiteDeleteResponse", evalSuiteDeleteResponseSchema), "The id that was deleted, so a screen can drop the row it was showing without remembering which delete just landed."),
81877
82375
  403: forbiddenResponse("config:write"),
81878
82376
  404: errorResponse(SUITE_NOT_FOUND_DESCRIPTION)
81879
82377
  }
@@ -82444,7 +82942,7 @@ var createFixturesRouter = (deps) => {
82444
82942
  tags: [FIXTURES_TAG],
82445
82943
  summary: "List the saved Playground inputs",
82446
82944
  description: "Every fixture this agent holds. The cursors are always null — the store is a per-agent JSONL file read whole — and stay on the wire so the shape matches the cloud list a Studio screen may also be reading.",
82447
- responses: { 200: jsonResponse$1(named("FixtureListResponse", fixtureListResponseSchema), "Every saved fixture for this agent.") }
82945
+ responses: { 200: jsonResponse$2(named("FixtureListResponse", fixtureListResponseSchema), "Every saved fixture for this agent.") }
82448
82946
  }), async (c) => c.json({
82449
82947
  items: await fixturesStore.list(),
82450
82948
  nextCursor: null,
@@ -82459,7 +82957,7 @@ var createFixturesRouter = (deps) => {
82459
82957
  description: "Names an input so it can be replayed from the Playground. The name is unique per agent — saving over an existing one is refused rather than silently replacing the input somebody else pinned.",
82460
82958
  request: { body: jsonBody(named("CreateFixtureRequest", createFixtureRequestSchema), "The name, the input to save under it, and an optional description.") },
82461
82959
  responses: {
82462
- 201: jsonResponse$1(named("Fixture", fixtureSchema), "The fixture as it was stored."),
82960
+ 201: jsonResponse$2(named("Fixture", fixtureSchema), "The fixture as it was stored."),
82463
82961
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
82464
82962
  403: forbiddenResponse("runs:invoke"),
82465
82963
  409: errorResponse("This agent already holds a fixture by that name (`fixture_name_conflict`).")
@@ -83016,7 +83514,7 @@ var createGuardrailsRouter = (opts) => {
83016
83514
  tags: [GUARDRAILS_TAG],
83017
83515
  summary: "List the checks this build can run",
83018
83516
  description: "Every check a rule may be written against, each carrying the sides of a turn it can guard (`targets`) and the JSON Schema of its own settings. A screen renders the rule form straight from this — the same description the runtime enforces a write against, so a form and the box cannot disagree about what a check accepts. It is a CONSTANT of the build: no store is read, and the answer only changes when the box's image does.",
83019
- responses: { 200: jsonResponse$1(named("GuardrailCatalogResponse", guardrailCatalogResponseSchema), "One entry per check this build can run.") }
83517
+ responses: { 200: jsonResponse$2(named("GuardrailCatalogResponse", guardrailCatalogResponseSchema), "One entry per check this build can run.") }
83020
83518
  }), (c) => c.json(CATALOG_RESPONSE, 200));
83021
83519
  app.openapi(createRoute({
83022
83520
  method: "get",
@@ -83024,7 +83522,7 @@ var createGuardrailsRouter = (opts) => {
83024
83522
  tags: [GUARDRAILS_TAG],
83025
83523
  summary: "List the stored rules",
83026
83524
  description: "Every guardrail this box holds, each with its attachments inline — so a screen can show what an edit or a delete would touch without a second request per row.",
83027
- responses: { 200: jsonResponse$1(named("GuardrailsListResponse", guardrailsListResponseSchema), "Every stored rule, with its attachments.") }
83525
+ responses: { 200: jsonResponse$2(named("GuardrailsListResponse", guardrailsListResponseSchema), "Every stored rule, with its attachments.") }
83028
83526
  }), async (c) => c.json({ items: await opts.store.list() }, 200));
83029
83527
  app.openapi(createRoute({
83030
83528
  method: "post",
@@ -83035,7 +83533,7 @@ var createGuardrailsRouter = (opts) => {
83035
83533
  description: "Writes a new rule and, when the body carries an `attachment`, points it somewhere in the same breath — a rule and its blast radius are one fact, and splitting them would leave a window where a rule meant for one agent is live across the whole workspace. Absent means the workspace default set.",
83036
83534
  request: { body: jsonBody(named("CreateGuardrailRequest", createGuardrailRequestSchema), "The check, its settings, what it does when it fires, and optionally where it applies.") },
83037
83535
  responses: {
83038
- 201: jsonResponse$1(named("Guardrail", guardrailSchema), "The stored rule, with the attachments it now carries."),
83536
+ 201: jsonResponse$2(named("Guardrail", guardrailSchema), "The stored rule, with the attachments it now carries."),
83039
83537
  400: errorResponse("The body failed its schema (`invalid_payload`), or it read fine and the CHOSEN CHECK would not run under these settings — `details.errors` names which, one at a time."),
83040
83538
  403: forbiddenResponse("config:write"),
83041
83539
  409: errorResponse("Another rule already holds that name (`guardrail_name_taken`). Names are what a blocked user reads, so they are unique per box.")
@@ -83072,7 +83570,7 @@ var createGuardrailsRouter = (opts) => {
83072
83570
  description: "The whole definition, attachments included.",
83073
83571
  request: { params: looseGuardrailIdPath },
83074
83572
  responses: {
83075
- 200: jsonResponse$1(named("Guardrail", guardrailSchema), "The rule as it stands."),
83573
+ 200: jsonResponse$2(named("Guardrail", guardrailSchema), "The rule as it stands."),
83076
83574
  404: errorResponse(RULE_NOT_FOUND_DESCRIPTION)
83077
83575
  }
83078
83576
  }), async (c) => {
@@ -83092,7 +83590,7 @@ var createGuardrailsRouter = (opts) => {
83092
83590
  body: jsonBody(named("UpdateGuardrailRequest", updateGuardrailRequestSchema), "The fields to change. Everything is optional.")
83093
83591
  },
83094
83592
  responses: {
83095
- 200: jsonResponse$1(named("Guardrail", guardrailSchema), "The rule as it now stands."),
83593
+ 200: jsonResponse$2(named("Guardrail", guardrailSchema), "The rule as it now stands."),
83096
83594
  400: errorResponse("The body failed its schema (`invalid_payload`), or the MERGED row — this patch over the stored values — is a rule its check would not run."),
83097
83595
  403: forbiddenResponse("config:write"),
83098
83596
  404: errorResponse(RULE_NOT_FOUND_DESCRIPTION),
@@ -83177,7 +83675,7 @@ var createGuardrailsRouter = (opts) => {
83177
83675
  body: jsonBody(named("AttachGuardrailRequest", attachGuardrailRequestSchema), "The scope, plus the unit name when the scope is a single agent or workflow.")
83178
83676
  },
83179
83677
  responses: {
83180
- 201: jsonResponse$1(named("Guardrail", guardrailSchema), "The WHOLE rule, not just the new row: the screen repaints the blast radius from this answer instead of re-reading the list."),
83678
+ 201: jsonResponse$2(named("Guardrail", guardrailSchema), "The WHOLE rule, not just the new row: the screen repaints the blast radius from this answer instead of re-reading the list."),
83181
83679
  400: errorResponse("The body failed its schema (`invalid_payload`) — a per-unit scope with no `unitName`, or a scope this build does not know."),
83182
83680
  403: forbiddenResponse("config:write"),
83183
83681
  404: errorResponse(RULE_NOT_FOUND_DESCRIPTION)
@@ -85724,7 +86222,7 @@ var createInternalPromptsRouter = (opts) => {
85724
86222
  summary: "List the box's own prompts and models",
85725
86223
  description: "One row per model caller Stackbone itself runs on this box — the Studio copilot, the guardrail classifier, the eval judge, the persona simulator, Auto-map, memory extraction and the query rewrite. `prompt` says where its text is edited: a caller owned by an agent or a workflow names that catalog entry (its Prompts block edits it); a `system` caller carries the text compiled into this box as its default, the operator's PUBLISHED text when there is one (`null` means it runs on the default) and whether a prompt is stored at its address at all. `model` says where the model is picked and what it falls back to.\n\nRead-only. The text is written through `/api/prompts` (create or update, then publish; unpublish returns to the default), the copilot's and Auto-map's models through `/api/model-selections`, and the other settings-backed models through `/api/settings`.\n\nGated by `config:write` even though it is a read: it carries prompt bodies, and the seats that may not edit one were never shown it either.",
85726
86224
  responses: {
85727
- 200: jsonResponse$1(named("StudioInternalPromptListResponse", studioInternalPromptListResponseSchema), "Every internal caller, in the order the System screen lists them."),
86225
+ 200: jsonResponse$2(named("StudioInternalPromptListResponse", studioInternalPromptListResponseSchema), "Every internal caller, in the order the System screen lists them."),
85728
86226
  403: forbiddenResponse("config:write")
85729
86227
  }
85730
86228
  }), async (c) => {
@@ -85814,7 +86312,7 @@ var createLogsRouter = (deps) => {
85814
86312
  description: "The lines this box printed, read back out of its own database long after the ring buffer that streamed them has rolled over — this is what answers \"what did it print three weeks ago\", which the tail beside it cannot. Filters narrow the rows; a `q` text search additionally REQUIRES a `from`/`to` window, because searching log text is an unindexed scan. Pages are walked with `nextCursor`, and the rows carry the live tail's vocabulary (`msg`, `run_id`, `trace_id`) so one presentation layer renders both screens.",
85815
86313
  request: { query: studioLogHistoryQuerySchema },
85816
86314
  responses: {
85817
- 200: jsonResponse$1(named("StudioLogHistoryResponse", studioLogHistoryResponseSchema), "One page of stored lines, plus the cursor the next page is asked with (`null` on the last)."),
86315
+ 200: jsonResponse$2(named("StudioLogHistoryResponse", studioLogHistoryResponseSchema), "One page of stored lines, plus the cursor the next page is asked with (`null` on the last)."),
85818
86316
  400: errorResponse("The query string failed its schema (`invalid_payload`), or the WINDOW cannot be answered — `text_search_needs_range`, `time_range_too_wide`, `time_range_unreadable`, `time_range_inverted` — or the search itself came back refused: a cursor this box did not mint (`invalid_cursor`) or a scan that outran its deadline (`search_timed_out`). Never a 500: each is the reader's own search coming back with a sentence about how to narrow it."),
85819
86317
  403: forbiddenResponse("logs:read"),
85820
86318
  503: errorResponse("This build has no log database behind it (`logs_history_unavailable`). An empty page would read as \"this box has never printed anything\", which is a different and much worse diagnosis.")
@@ -86078,7 +86576,7 @@ var createMemoryRouter = (deps) => {
86078
86576
  body: jsonBody(named("SearchStudioMemoryPeopleRequest", searchStudioMemoryPeopleRequestSchema), "The search text, the cursor of the previous page and the page size.")
86079
86577
  },
86080
86578
  responses: {
86081
- 200: jsonResponse$1(named("StudioMemoryPersonListResponse", studioMemoryPersonListResponseSchema), "One page of people, their ids, fact counts and last call dates."),
86579
+ 200: jsonResponse$2(named("StudioMemoryPersonListResponse", studioMemoryPersonListResponseSchema), "One page of people, their ids, fact counts and last call dates."),
86082
86580
  400: errorResponse("The agent scope, the body or the cursor is invalid."),
86083
86581
  403: forbiddenResponse("db:query")
86084
86582
  }
@@ -86099,7 +86597,7 @@ var createMemoryRouter = (deps) => {
86099
86597
  description: "Facts in stored order with their creation and update call provenance, followed by the person’s calls newest first.",
86100
86598
  request: { params: studioMemoryPersonParamSchema },
86101
86599
  responses: {
86102
- 200: jsonResponse$1(named("StudioMemoryDetailResponse", studioMemoryDetailResponseSchema), "The facts and calls retained for this person."),
86600
+ 200: jsonResponse$2(named("StudioMemoryDetailResponse", studioMemoryDetailResponseSchema), "The facts and calls retained for this person."),
86103
86601
  400: errorResponse("The agent scope or person id is invalid."),
86104
86602
  404: errorResponse("No person has this id on this agent, or no facts or calls remain for them."),
86105
86603
  403: forbiddenResponse("db:query")
@@ -86120,7 +86618,7 @@ var createMemoryRouter = (deps) => {
86120
86618
  description: "The agent runs whose frozen identity is exactly this person, newest first. Another format of the same number, or a mention of it in a transcript, is another person.",
86121
86619
  request: { params: studioMemoryPersonParamSchema },
86122
86620
  responses: {
86123
- 200: jsonResponse$1(named("StudioMemoryPersonRunsResponse", studioMemoryPersonRunsResponseSchema), "The person’s runs on this agent, with their status and creation date."),
86621
+ 200: jsonResponse$2(named("StudioMemoryPersonRunsResponse", studioMemoryPersonRunsResponseSchema), "The person’s runs on this agent, with their status and creation date."),
86124
86622
  400: errorResponse("The agent scope or person id is invalid."),
86125
86623
  404: errorResponse("No person has this id on this agent."),
86126
86624
  403: forbiddenResponse("db:query")
@@ -86141,7 +86639,7 @@ var createMemoryRouter = (deps) => {
86141
86639
  description: "Deletes the person’s runs on this agent with their steps (the same runs `GET /agents/{agentId}/people/{personId}/runs` lists), the conversation checkpoints of those runs’ sessions, then every fact and call summary. The memory erase bumps the same revision tombstone as `stackbone.memory.deleteAll`, preventing an in-flight update from restoring erased data; `stackbone.memory.deleteAll` itself stays memory-only and leaves runs and checkpoints in place. The tombstone keeps only the opaque id: an erased id answers 404 here, on read and on its runs, and a person who comes back gets a new id.",
86142
86640
  request: { params: studioMemoryPersonParamSchema },
86143
86641
  responses: {
86144
- 200: jsonResponse$1(named("DeleteStudioMemoryResponse", deleteStudioMemoryResponseSchema), "The number of fact and call rows removed, and the number of runs removed."),
86642
+ 200: jsonResponse$2(named("DeleteStudioMemoryResponse", deleteStudioMemoryResponseSchema), "The number of fact and call rows removed, and the number of runs removed."),
86145
86643
  400: errorResponse("The agent scope or person id is invalid."),
86146
86644
  403: forbiddenResponse("rag:write"),
86147
86645
  404: errorResponse("No person has this id on this agent."),
@@ -86411,7 +86909,7 @@ var response = async (agent, overrides, db) => ({
86411
86909
  ...resolveEffectiveMemorySettings(agent.memory, overrides)
86412
86910
  });
86413
86911
  var responses = {
86414
- 200: jsonResponse$1(named("StudioMemorySettingsResponse", studioMemorySettingsResponseSchema), "The overrides and effective per-field values and origins."),
86912
+ 200: jsonResponse$2(named("StudioMemorySettingsResponse", studioMemorySettingsResponseSchema), "The overrides and effective per-field values and origins."),
86415
86913
  400: errorResponse("The agent or settings payload is invalid."),
86416
86914
  404: errorResponse("The agent is not registered on this box.")
86417
86915
  };
@@ -86564,7 +87062,7 @@ var createMeRouter = () => {
86564
87062
  summary: "Read what the calling credential may do",
86565
87063
  description: "The capability list of the credential this request arrived with — what Studio draws its owner/admin surfaces from — and its role, as a label the Studio copilot is told. A browser-direct caller gets the capabilities its identity token was signed with; a self-host operator and an ungated `stackbone dev` box get the full owner expansion, because on neither is there anything the box would refuse. Carries no capability gate of its own: it reveals nothing the caller does not already hold, and gating the answer on a capability would make the answer depend on the answer.",
86566
87064
  responses: {
86567
- 200: jsonResponse$1(named("CallerCapabilities", callerCapabilitiesSchema), "What this caller may do on this box."),
87065
+ 200: jsonResponse$2(named("CallerCapabilities", callerCapabilitiesSchema), "What this caller may do on this box."),
86568
87066
  403: errorResponse("The credential is not a SEAT: a proxy signature, a workflow callback or a workspace API key. Each of those names a machine rather than a person, so there are no capabilities to report — an API key carries its own permission list, written on the key and checked by the invocation surface it was minted for.")
86569
87067
  }
86570
87068
  }), (c) => {
@@ -86689,6 +87187,68 @@ var resetFirstRun = async (opts) => {
86689
87187
  await deleteSystemSecret(opts, FIRST_RUN_COMPLETED_SECRET);
86690
87188
  };
86691
87189
  //#endregion
87190
+ //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/livekit-test.ts
87191
+ init_src$18();
87192
+ init_livekit_token();
87193
+ init_error_message();
87194
+ init_provider_catalogues();
87195
+ /** Served by LiveKit itself, so a refusal is about the credentials, not an upstream. */
87196
+ var PROBE_MODEL = "google/gemma-4-31b-it";
87197
+ var testLiveKitInference = async (opts, fetchImpl = fetch) => {
87198
+ let credentials;
87199
+ try {
87200
+ credentials = await readLiveKitCredentials(opts);
87201
+ } catch (err) {
87202
+ return {
87203
+ ok: false,
87204
+ error: `The Voice credentials cannot be read (${messageOf$6(err)}). Save them again under Voice.`
87205
+ };
87206
+ }
87207
+ if (credentials === null) return {
87208
+ ok: false,
87209
+ error: "No LiveKit credentials are saved under Voice."
87210
+ };
87211
+ let response;
87212
+ try {
87213
+ response = await fetchImpl(`${LIVEKIT_INFERENCE_BASE_URL}/chat/completions`, {
87214
+ method: "POST",
87215
+ headers: {
87216
+ authorization: `Bearer ${await signLiveKitInferenceToken(credentials)}`,
87217
+ "content-type": "application/json"
87218
+ },
87219
+ body: JSON.stringify({
87220
+ model: PROBE_MODEL,
87221
+ max_tokens: 1,
87222
+ messages: [{
87223
+ role: "user",
87224
+ content: "ping"
87225
+ }]
87226
+ }),
87227
+ redirect: "manual",
87228
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS$1)
87229
+ });
87230
+ } catch (err) {
87231
+ return {
87232
+ ok: false,
87233
+ error: `LiveKit Inference could not be reached (${messageOf$6(err)}).`
87234
+ };
87235
+ }
87236
+ await response.body?.cancel();
87237
+ if (response.status === 401 || response.status === 403) return {
87238
+ ok: false,
87239
+ error: `LiveKit rejected the Voice credentials (HTTP ${response.status}). Check the API key and secret saved under Voice.`
87240
+ };
87241
+ if (!response.ok) return {
87242
+ ok: false,
87243
+ error: `LiveKit Inference answered HTTP ${response.status}.`
87244
+ };
87245
+ return {
87246
+ ok: true,
87247
+ model_count: fixedCatalogue("livekit", "https://agent-gateway.livekit.cloud/v1")?.length ?? 0,
87248
+ key_checked: "verified"
87249
+ };
87250
+ };
87251
+ //#endregion
86692
87252
  //#region ../../libs/runtime/workspace-runtime/src/lib/model-provider/http/model-provider-handlers.ts
86693
87253
  init_src$26();
86694
87254
  init_provider_catalogues();
@@ -86735,13 +87295,15 @@ var STORE_REFUSAL_STATUS = {
86735
87295
  invalid_id: 400,
86736
87296
  not_found: 404,
86737
87297
  duplicate: 409,
86738
- default_provider: 409
87298
+ default_provider: 409,
87299
+ voice_provider: 409
86739
87300
  };
86740
87301
  var STORE_REFUSAL_MESSAGE = {
86741
87302
  invalid_id: (id) => `'${id}' is not a provider id: lowercase letters, digits and dashes, 2 to 32, starting with a letter.`,
86742
87303
  not_found: (id) => `No model provider '${id}' is configured on this box.`,
86743
87304
  duplicate: (id) => `A model provider '${id}' already exists.`,
86744
- default_provider: () => "Provider 1 is saved and removed by the model provider form itself, not by its id."
87305
+ default_provider: () => "Provider 1 is saved and removed by the model provider form itself, not by its id.",
87306
+ voice_provider: () => "LiveKit Inference is added by saving the LiveKit credentials under Voice, not here."
86745
87307
  };
86746
87308
  var storeRefusal = (err) => ({
86747
87309
  code: `model_provider_${err.code}`,
@@ -86820,12 +87382,12 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86820
87382
  baseUrl: provider.baseUrl,
86821
87383
  apiKey: maskedApiKey(provider.apiKey !== void 0),
86822
87384
  source: provider.source,
86823
- removable: !(provider.id === "default" && provider.source === "env"),
87385
+ removable: provider.source !== "voice" && !(provider.id === "default" && provider.source === "env"),
86824
87386
  responding: read?.responding ?? null,
86825
87387
  ...read?.error === void 0 ? {} : { error: read.error }
86826
87388
  };
86827
87389
  }),
86828
- duplicatedIds: duplicatedModelIds(orderedUnion(providers, catalogues))
87390
+ duplicatedIds: duplicatedModelIds(orderedUnion(providers.filter(claimsPlainModelIds), catalogues))
86829
87391
  };
86830
87392
  };
86831
87393
  const listModels = async (body) => {
@@ -86869,7 +87431,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86869
87431
  tags: [MODEL_PROVIDER_TAG],
86870
87432
  summary: "Read the model provider configuration",
86871
87433
  description: "The stored base URL and a MASKED key — the plaintext never rides back through this surface. `source` says which of the two the runtime is actually resolving: a `MODEL_PROVIDER_*` variable in the box's boot environment outranks anything this screen holds, and saying so is the difference between a screen that lies and one an operator can act on.",
86872
- responses: { 200: jsonResponse$1(modelProviderGetResponse, "The configuration, masked, and where the live value comes from.") }
87434
+ responses: { 200: jsonResponse$2(modelProviderGetResponse, "The configuration, masked, and where the live value comes from.") }
86873
87435
  }), async (c) => c.json(await getModelProvider(secretsOptions, bootEnv), 200));
86874
87436
  app.openapi(createRoute({
86875
87437
  method: "put",
@@ -86881,7 +87443,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86881
87443
  description: "Persists, then makes the credential LIVE on the running workspace, then re-registers whatever was degraded — in that order. Anything else and the operator is told the save worked while the workspace stays exactly as broken as it was, with a `stackbone dev` restart as the undocumented missing step. The reply carries the masked view and, when the runtime could act on it, how many agents came up.",
86882
87444
  request: { body: jsonBody(modelProviderRequestBody, "The base URL, and the key when the gateway needs one.") },
86883
87445
  responses: {
86884
- 200: jsonResponse$1(modelProviderPutResponse, "The fresh masked view, plus what came up when the runtime could report it."),
87446
+ 200: jsonResponse$2(modelProviderPutResponse, "The fresh masked view, plus what came up when the runtime could report it."),
86885
87447
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
86886
87448
  403: forbiddenResponse("secrets:rotate")
86887
87449
  }
@@ -86903,7 +87465,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86903
87465
  summary: "Remove the saved model provider",
86904
87466
  description: "Deletes the base URL, key and gateway kind the Model provider screen stored, with the override flags beside them, and takes them off the running workspace. Every value the box exports is back in force at once, with no restart; the reply is the view afterwards, so `source` and `env_override` say what is still resolving and `overridden` is empty. The first-run marker is untouched.",
86905
87467
  responses: {
86906
- 200: jsonResponse$1(modelProviderGetResponse, "The view after the removal, naming whatever the environment still supplies."),
87468
+ 200: jsonResponse$2(modelProviderGetResponse, "The view after the removal, naming whatever the environment still supplies."),
86907
87469
  403: forbiddenResponse("secrets:rotate")
86908
87470
  }
86909
87471
  }), async (c) => {
@@ -86926,7 +87488,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86926
87488
  description: "Drops the override flag on one slot (`baseUrl` or `apiKey`), so the variable the box exports for it is back in force on the running workspace. The value this screen saved is kept, and wins again only if the variable is unset. The reply is the view afterwards; a slot that was not overridden answers the view unchanged and records nothing.",
86927
87489
  request: { params: modelProviderOverrideSlotPath },
86928
87490
  responses: {
86929
- 200: jsonResponse$1(modelProviderGetResponse, "The view after the override was dropped, naming the variable back in force."),
87491
+ 200: jsonResponse$2(modelProviderGetResponse, "The view after the override was dropped, naming the variable back in force."),
86930
87492
  400: errorResponse("The slot is neither `baseUrl` nor `apiKey` (`invalid_payload`)."),
86931
87493
  403: forbiddenResponse("secrets:rotate")
86932
87494
  }
@@ -86948,7 +87510,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86948
87510
  summary: "Continue without a provider",
86949
87511
  description: "Writes NO credential. It records the decision and releases the boot gate, so the agents come up DEGRADED — registered, visible, with an actionable reason — rather than the process sitting there waiting for a browser. The provider view rides back unchanged (`configured: false`), so the screen can say plainly that nothing is configured and mean it.",
86950
87512
  responses: {
86951
- 200: jsonResponse$1(modelProviderPutResponse, "The unchanged view, plus what came up degraded."),
87513
+ 200: jsonResponse$2(modelProviderPutResponse, "The unchanged view, plus what came up degraded."),
86952
87514
  403: forbiddenResponse("secrets:rotate")
86953
87515
  }
86954
87516
  }), async (c) => {
@@ -86961,7 +87523,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86961
87523
  tags: [MODEL_PROVIDER_TAG],
86962
87524
  summary: "Is this still the deployment’s first run?",
86963
87525
  description: "A presence check on the marker, never a decrypted read. The guided screen asks before it starts walking somebody through a setup that is already done, and lands them on the ordinary configuration screen instead.",
86964
- responses: { 200: jsonResponse$1(modelProviderFirstRunResponse, "Whether somebody has already been through the guided first run.") }
87526
+ responses: { 200: jsonResponse$2(modelProviderFirstRunResponse, "Whether somebody has already been through the guided first run.") }
86965
87527
  }), async (c) => {
86966
87528
  const state = await readFirstRunState(secretsOptions);
86967
87529
  return c.json(state, 200);
@@ -86974,7 +87536,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86974
87536
  summary: "Make the guided first run eligible again",
86975
87537
  description: "Clears the MARKER only — the saved credential is untouched, because re-walking the setup must not log the operator out of the configuration they already have. Answers with the state it left behind so the screen can relaunch the flow without a second round-trip.",
86976
87538
  responses: {
86977
- 200: jsonResponse$1(modelProviderFirstRunResponse, "The marker, now cleared."),
87539
+ 200: jsonResponse$2(modelProviderFirstRunResponse, "The marker, now cleared."),
86978
87540
  403: forbiddenResponse("secrets:rotate")
86979
87541
  }
86980
87542
  }), async (c) => {
@@ -86991,7 +87553,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
86991
87553
  description: "Calls `GET {base_url}/models` with the body's credential WITHOUT saving anything. A gateway that refuses rides back as `ok: false` DATA carrying a reason the operator can act on, never as an error status.",
86992
87554
  request: { body: jsonBody(modelProviderRequestBody, "The candidate credential to probe.") },
86993
87555
  responses: {
86994
- 200: jsonResponse$1(modelProviderTestResponse, "Whether the gateway answered, and why not when it did not."),
87556
+ 200: jsonResponse$2(modelProviderTestResponse, "Whether the gateway answered, and why not when it did not."),
86995
87557
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
86996
87558
  403: forbiddenResponse("secrets:rotate")
86997
87559
  }
@@ -87004,7 +87566,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87004
87566
  description: "A POST because the body may carry a DRAFT credential the operator has not saved yet. With a `base_url` it probes that endpoint; with an empty body it resolves the SAVED provider server-side, which is how the stored key stays out of the browser. With an empty body on a box with providers beside provider 1, it lists every provider's models, each tagged with the `provider` that serves it, and fails only when no provider answers.",
87005
87567
  request: { body: jsonBody(named("ModelProviderModelsRequest", modelProviderModelsRequestSchema), "The draft endpoint to probe, or `{}` to use the saved providers.") },
87006
87568
  responses: {
87007
- 200: jsonResponse$1(named("ModelProviderModelsResponse", modelProviderModelsResponseSchema), "The models the providers advertise, normalized."),
87569
+ 200: jsonResponse$2(named("ModelProviderModelsResponse", modelProviderModelsResponseSchema), "The models the providers advertise, normalized."),
87008
87570
  400: errorResponse("The body failed its schema (`invalid_payload`).")
87009
87571
  }
87010
87572
  }), async (c) => c.json(await listModels(c.req.valid("json")), 200));
@@ -87014,7 +87576,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87014
87576
  tags: [MODEL_PROVIDER_TAG],
87015
87577
  summary: "List the model providers",
87016
87578
  description: "Provider 1 and every provider added beside it, provider 1 first, each catalogue read now. Keys are masked. `responding: false` is a provider whose read failed. `duplicatedIds` names every model id two providers serve: a call naming one is refused as ambiguous. Provider 1's own view stays `GET /api/model-provider`.",
87017
- responses: { 200: jsonResponse$1(modelProviderList, "Every provider, masked, and the ids two serve.") }
87579
+ responses: { 200: jsonResponse$2(modelProviderList, "Every provider, masked, and the ids two serve.") }
87018
87580
  }), async (c) => c.json(await providerList(), 200));
87019
87581
  app.openapi(createRoute({
87020
87582
  method: "post",
@@ -87026,10 +87588,10 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87026
87588
  description: "Saves a provider beside provider 1. From the next call on, a model only it lists is routed to it; a model another provider lists too is refused as ambiguous, which `duplicatedIds` in the reply names. The reply is the list.",
87027
87589
  request: { body: jsonBody(named("ModelProviderCreateRequest", modelProviderCreateRequestSchema), "The id, name, gateway kind and endpoint, and the key when the gateway needs one.") },
87028
87590
  responses: {
87029
- 201: jsonResponse$1(modelProviderList, "The list with the new provider."),
87591
+ 201: jsonResponse$2(modelProviderList, "The list with the new provider."),
87030
87592
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
87031
87593
  403: forbiddenResponse("secrets:rotate"),
87032
- 409: errorResponse("A provider with this id exists (`model_provider_duplicate`).")
87594
+ 409: errorResponse("A provider with this id exists (`model_provider_duplicate`), or it is LiveKit Inference, which Voice adds (`model_provider_voice_provider`).")
87033
87595
  }
87034
87596
  }), async (c) => {
87035
87597
  const body = c.req.valid("json");
@@ -87052,7 +87614,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87052
87614
  body: jsonBody(named("ModelProviderUpdateRequest", modelProviderUpdateRequestSchema), "The name, gateway kind and endpoint, and the key only to replace or clear it.")
87053
87615
  },
87054
87616
  responses: {
87055
- 200: jsonResponse$1(modelProviderList, "The list with the provider as saved."),
87617
+ 200: jsonResponse$2(modelProviderList, "The list with the provider as saved."),
87056
87618
  400: errorResponse("The body failed its schema (`invalid_payload`), or the id is not a slug (`model_provider_invalid_id`)."),
87057
87619
  403: forbiddenResponse("secrets:rotate"),
87058
87620
  404: errorResponse("No provider has this id (`model_provider_not_found`)."),
@@ -87082,7 +87644,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87082
87644
  description: "Deletes an extra provider and its key. Its models leave the list and stop routing at once. Provider 1 is removed by `DELETE /api/model-provider`, and one the box exports cannot be removed here (D6), so `default` is refused. The reply is the list.",
87083
87645
  request: { params: modelProviderIdPath },
87084
87646
  responses: {
87085
- 200: jsonResponse$1(modelProviderList, "The list without the provider."),
87647
+ 200: jsonResponse$2(modelProviderList, "The list without the provider."),
87086
87648
  400: errorResponse("The id is not a slug (`model_provider_invalid_id`)."),
87087
87649
  403: forbiddenResponse("secrets:rotate"),
87088
87650
  404: errorResponse("No provider has this id (`model_provider_not_found`)."),
@@ -87102,10 +87664,10 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87102
87664
  tags: [MODEL_PROVIDER_TAG],
87103
87665
  middleware: [requireCapability("secrets:rotate")],
87104
87666
  summary: "Probe one saved model provider",
87105
- description: "Calls `GET {baseUrl}/models` on that provider alone, with its stored key, and nothing else. `default` probes provider 1 with the credential in effect. A gateway that refuses rides back as `ok: false` DATA, never as an error status.",
87667
+ description: "Calls `GET {baseUrl}/models` on that provider alone, with its stored key, and nothing else. `default` probes provider 1 with the credential in effect. LiveKit Inference, which has no key and lists nothing, is probed with a one-token chat signed with the Voice credentials. A gateway that refuses rides back as `ok: false` DATA, never as an error status.",
87106
87668
  request: { params: modelProviderIdPath },
87107
87669
  responses: {
87108
- 200: jsonResponse$1(modelProviderTestResponse, "Whether the gateway answered, and why not when it did not."),
87670
+ 200: jsonResponse$2(modelProviderTestResponse, "Whether the gateway answered, and why not when it did not."),
87109
87671
  403: forbiddenResponse("secrets:rotate"),
87110
87672
  404: errorResponse("No provider has this id (`model_provider_not_found`).")
87111
87673
  }
@@ -87113,6 +87675,7 @@ var createModelProviderRouter = (secretsOptions, bootEnv = {}, live = {}) => {
87113
87675
  const { id } = c.req.valid("param");
87114
87676
  const provider = (await listProviders(secretsOptions, bootEnv)).find((p) => p.id === id);
87115
87677
  if (!provider) return c.json(storeRefusal(new ProviderStoreError("not_found", id)), 404);
87678
+ if (provider.kind === "livekit") return c.json(await testLiveKitInference(secretsOptions), 200);
87116
87679
  return c.json(await testModelProviderConnection({
87117
87680
  base_url: provider.baseUrl,
87118
87681
  api_key: provider.apiKey ?? null
@@ -87348,7 +87911,7 @@ var createModelSelectionsRouter = (opts) => {
87348
87911
  tags: [MODEL_SELECTIONS_TAG],
87349
87912
  summary: "List the model selections",
87350
87913
  description: "Every model an operator picked on this box, of every owner, ordered by owner then key. A key with no row runs on its code default. Capped at 500 rows.",
87351
- responses: { 200: jsonResponse$1(named("StudioModelSelectionListResponse", studioModelSelectionListResponseSchema), "Every pick on this box.") }
87914
+ responses: { 200: jsonResponse$2(named("StudioModelSelectionListResponse", studioModelSelectionListResponseSchema), "Every pick on this box.") }
87352
87915
  }), async (c) => {
87353
87916
  const rows = await opts.db.select().from(modelSelections).orderBy(modelSelections.ownerKind, modelSelections.ownerName, modelSelections.key).limit(LIST_PAGE_SIZE);
87354
87917
  return c.json({ items: rows.map(rowToSelection) }, 200);
@@ -87365,7 +87928,7 @@ var createModelSelectionsRouter = (opts) => {
87365
87928
  body: jsonBody(named("PutStudioModelSelectionRequest", putStudioModelSelectionRequestSchema), "The model id, as the model provider names it, and optionally the provider it goes to.")
87366
87929
  },
87367
87930
  responses: {
87368
- 200: jsonResponse$1(named("StudioModelSelection", studioModelSelectionSchema), "The pick as it now stands."),
87931
+ 200: jsonResponse$2(named("StudioModelSelection", studioModelSelectionSchema), "The pick as it now stands."),
87369
87932
  400: errorResponse(`The body failed its schema, or ${PATH_REFUSAL}`),
87370
87933
  403: forbiddenResponse("config:write")
87371
87934
  }
@@ -87418,7 +87981,7 @@ var createModelSelectionsRouter = (opts) => {
87418
87981
  description: "Deletes the pick, so the key runs on the code's default model again — or on the box's `defaultModel` when the code declares none.",
87419
87982
  request: { params: studioModelSelectionPathSchema },
87420
87983
  responses: {
87421
- 200: jsonResponse$1(named("StudioModelSelectionDeleteResponse", studioModelSelectionDeleteResponseSchema), "The pick that was removed."),
87984
+ 200: jsonResponse$2(named("StudioModelSelectionDeleteResponse", studioModelSelectionDeleteResponseSchema), "The pick that was removed."),
87422
87985
  400: errorResponse(PATH_REFUSAL),
87423
87986
  403: forbiddenResponse("config:write"),
87424
87987
  404: errorResponse("No model is picked for that owner and key (`studio_model_selection_not_found`).")
@@ -87453,7 +88016,7 @@ var createModelSelectionsRouter = (opts) => {
87453
88016
  body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider routing, or a plain sort.")
87454
88017
  },
87455
88018
  responses: {
87456
- 200: jsonResponse$1(named("StudioModelRouting", studioModelRoutingSchema), "The routing as it now stands."),
88019
+ 200: jsonResponse$2(named("StudioModelRouting", studioModelRoutingSchema), "The routing as it now stands."),
87457
88020
  400: errorResponse(`The body failed its schema, or ${ROUTING_PATH_REFUSAL}`),
87458
88021
  403: forbiddenResponse("config:write")
87459
88022
  }
@@ -87507,7 +88070,7 @@ var createModelSelectionsRouter = (opts) => {
87507
88070
  description: "Deletes the routing, so the key runs on the code's `modelRouting` again: for a subagent with none, the agent's routing; else OpenRouter's default routing.",
87508
88071
  request: { params: studioModelRoutingPathSchema },
87509
88072
  responses: {
87510
- 200: jsonResponse$1(named("StudioModelRoutingDeleteResponse", studioModelRoutingDeleteResponseSchema), "The routing that was removed."),
88073
+ 200: jsonResponse$2(named("StudioModelRoutingDeleteResponse", studioModelRoutingDeleteResponseSchema), "The routing that was removed."),
87511
88074
  400: errorResponse(ROUTING_PATH_REFUSAL),
87512
88075
  403: forbiddenResponse("config:write"),
87513
88076
  404: errorResponse("No provider routing is picked for that agent and key (`studio_model_routing_not_found`).")
@@ -87567,7 +88130,7 @@ var createTelemetryExportRouter = (deps) => {
87567
88130
  tags: [TELEMETRY_EXPORT_TAG],
87568
88131
  summary: "Read the OTLP export posture",
87569
88132
  description: "Whether an export endpoint is configured, whether the box is actually exporting, the one line the boot logged about it, and how many records never reached the collector. The endpoint itself is never in the answer — a collector URL routinely carries a token, and this read is open to every seat that can open settings.",
87570
- responses: { 200: jsonResponse$1(named("TelemetryExportPosture", telemetryExportPostureSchema), "The export posture, and what the export has lost.") }
88133
+ responses: { 200: jsonResponse$2(named("TelemetryExportPosture", telemetryExportPostureSchema), "The export posture, and what the export has lost.") }
87571
88134
  }), async (c) => {
87572
88135
  const state = await deps.readState();
87573
88136
  return c.json({
@@ -87696,7 +88259,7 @@ var createQueuesHandlers = () => {
87696
88259
  tags: [AGENT_QUEUES_TAG],
87697
88260
  summary: "List the agent-registered cron schedules (retired)",
87698
88261
  description: "RETIRED, and deliberately empty FOREVER — not \"empty because nothing is scheduled\". This box's real recurring schedules are read at `GET /api/recurring-jobs`, which reports every declared cadence (workflow crons, one polling repeatable per intake link, the connection-refresh sweep) with its next fire time. Never wire a screen to this one.",
87699
- responses: { 200: jsonResponse$1(named("ListSchedulesResponse", listSchedulesResponseSchema), "Always `{ \"items\": [] }`.") }
88262
+ responses: { 200: jsonResponse$2(named("ListSchedulesResponse", listSchedulesResponseSchema), "Always `{ \"items\": [] }`.") }
87700
88263
  }), (c) => c.json({ items: [] }, 200));
87701
88264
  const queues = new OpenAPIHono({ defaultHook: invalidPayloadHook });
87702
88265
  queues.openapi(createRoute({
@@ -87705,7 +88268,7 @@ var createQueuesHandlers = () => {
87705
88268
  tags: [QUEUES_TAG],
87706
88269
  summary: "List the queues (retired)",
87707
88270
  description: `The Queues Inspector sidebar. ${RETIRED} There are no queues to report, so the list is empty.`,
87708
- responses: { 200: jsonResponse$1(named("QueueListResponse", queueListResponseSchema), "Always `{ \"items\": [] }`.") }
88271
+ responses: { 200: jsonResponse$2(named("QueueListResponse", queueListResponseSchema), "Always `{ \"items\": [] }`.") }
87709
88272
  }), (c) => c.json({ items: [] }, 200));
87710
88273
  queues.openapi(createRoute({
87711
88274
  method: "get",
@@ -87718,7 +88281,7 @@ var createQueuesHandlers = () => {
87718
88281
  query: peekQueueQuerySchema
87719
88282
  },
87720
88283
  responses: {
87721
- 200: jsonResponse$1(named("PeekQueueResponse", peekQueueResponseSchema), "Always `{ \"items\": [] }`."),
88284
+ 200: jsonResponse$2(named("PeekQueueResponse", peekQueueResponseSchema), "Always `{ \"items\": [] }`."),
87722
88285
  400: errorResponse("The queue name is not a BullMQ queue identifier, or the `limit` is outside [1, 50] (`invalid_payload`).")
87723
88286
  }
87724
88287
  }), (c) => c.json({ items: [] }, 200));
@@ -87732,7 +88295,7 @@ var createQueuesHandlers = () => {
87732
88295
  description: `Drops every removable job — an active one stays, a worker holds it. ${RETIRED} There is nothing to remove, so the count is zero.`,
87733
88296
  request: { params: queueNameParamsSchema },
87734
88297
  responses: {
87735
- 200: jsonResponse$1(named("PurgeQueueResponse", purgeQueueResponseSchema), "Always `{ \"purged\": 0 }`."),
88298
+ 200: jsonResponse$2(named("PurgeQueueResponse", purgeQueueResponseSchema), "Always `{ \"purged\": 0 }`."),
87736
88299
  400: errorResponse("The queue name is not a BullMQ queue identifier (`invalid_payload`)."),
87737
88300
  403: forbiddenResponse("runs:invoke")
87738
88301
  }
@@ -87763,7 +88326,7 @@ var createQueuesHandlers = () => {
87763
88326
  description: `The Jobs table. \`queue\` is REQUIRED — a BullMQ queue scopes its jobs, so there is no "all jobs" to list. ${RETIRED} There are no jobs to report, so the page is empty.`,
87764
88327
  request: { query: listJobsQuerySchema },
87765
88328
  responses: {
87766
- 200: jsonResponse$1(named("JobListResponse", jobListResponseSchema), "Always `{ \"items\": [], \"nextCursor\": null }`."),
88329
+ 200: jsonResponse$2(named("JobListResponse", jobListResponseSchema), "Always `{ \"items\": [], \"nextCursor\": null }`."),
87767
88330
  400: errorResponse("The query failed its schema — a missing or malformed `queue` included (`invalid_payload`).")
87768
88331
  }
87769
88332
  }), (c) => c.json({
@@ -89723,7 +90286,7 @@ function mountRagRebuildRoutes(app, resolve) {
89723
90286
  summary: "Read collection rebuild status",
89724
90287
  request: { params },
89725
90288
  responses: {
89726
- 200: jsonResponse$1(named("RagRebuildStatusResponse", ragRebuildStatusResponseSchema), "Current and target identity, World lifecycle and retained progress; null before the first rebuild."),
90289
+ 200: jsonResponse$2(named("RagRebuildStatusResponse", ragRebuildStatusResponseSchema), "Current and target identity, World lifecycle and retained progress; null before the first rebuild."),
89727
90290
  400: errors$1[400],
89728
90291
  404: errors$1[404],
89729
90292
  409: errors$1[409],
@@ -89756,7 +90319,7 @@ function mountRagRebuildRoutes(app, resolve) {
89756
90319
  body: jsonBody(named("ChangeRagEmbeddingRequest", changeRagEmbeddingRequestSchema), "Target provider and model.")
89757
90320
  },
89758
90321
  responses: {
89759
- 202: jsonResponse$1(named("RagRebuildStatus", ragRebuildStatusSchema), "Accepted logical rebuild and its current durable state."),
90322
+ 202: jsonResponse$2(named("RagRebuildStatus", ragRebuildStatusSchema), "Accepted logical rebuild and its current durable state."),
89760
90323
  ...errors$1
89761
90324
  }
89762
90325
  }), async (c) => {
@@ -89785,7 +90348,7 @@ function mountRagRebuildRoutes(app, resolve) {
89785
90348
  body: jsonBody(named("RetryRagRebuildRequest", retryRagRebuildRequestSchema), "The logical rebuild to resume.")
89786
90349
  },
89787
90350
  responses: {
89788
- 202: jsonResponse$1(named("RagRebuildStatus", ragRebuildStatusSchema), "Same logical rebuild, new fenced attempt."),
90351
+ 202: jsonResponse$2(named("RagRebuildStatus", ragRebuildStatusSchema), "Same logical rebuild, new fenced attempt."),
89789
90352
  ...errors$1
89790
90353
  }
89791
90354
  }), async (c) => {
@@ -89951,7 +90514,7 @@ var createRagRouter = (deps) => {
89951
90514
  summary: "List the collections in this agent",
89952
90515
  description: "Every row of `stackbone_rag_collections`, with the counts and the index metadata the Studio explorer renders. The counts come from the documents and chunks tables, so an empty collection created before its first ingest reads `0` with a null `last_ingested_at`.",
89953
90516
  responses: {
89954
- 200: jsonResponse$1(named("RagCollectionListResponse", ragCollectionListResponseSchema), "The collections, each with its dimensions, distance metric and counts."),
90517
+ 200: jsonResponse$2(named("RagCollectionListResponse", ragCollectionListResponseSchema), "The collections, each with its dimensions, distance metric and counts."),
89955
90518
  409: errorResponse("The canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`). Studio gates its write affordances off THIS read rather than on an empty list, so the refusal is structured instead of an empty page.")
89956
90519
  }
89957
90520
  }), async (c) => {
@@ -89974,7 +90537,7 @@ var createRagRouter = (deps) => {
89974
90537
  query: listRagDocsQuerySchema
89975
90538
  },
89976
90539
  responses: {
89977
- 200: jsonResponse$1(named("RagDocumentListResponse", ragDocumentListResponseSchema), "One page of documents, and the cursor for the next."),
90540
+ 200: jsonResponse$2(named("RagDocumentListResponse", ragDocumentListResponseSchema), "One page of documents, and the cursor for the next."),
89978
90541
  400: errorResponse("The query string failed its schema, or `filter` is not a JSON object this store can match (`invalid_payload`)."),
89979
90542
  404: errorResponse("The `{name}` segment is not a collection name this box can read (`rag_collection_not_found`).")
89980
90543
  }
@@ -90006,7 +90569,7 @@ var createRagRouter = (deps) => {
90006
90569
  body: jsonBody(named("RagQueryRequest", ragQueryRequestSchema), "The query: `text` to embed server-side, or an `embedding` to search with directly, optional metadata `filter` and typed `ranges`, `hybrid: false` to search by meaning only, and optionally a `preview` that searches as if that version of a document were published.")
90007
90570
  },
90008
90571
  responses: {
90009
- 200: jsonResponse$1(named("RagQueryResponse", ragQueryResponseSchema), "The hits, closest first (in fused order on a hybrid search), with the dimensions searched at and the model that embedded the query (null when the caller supplied the vector)."),
90572
+ 200: jsonResponse$2(named("RagQueryResponse", ragQueryResponseSchema), "The hits, closest first (in fused order on a hybrid search), with the dimensions searched at and the model that embedded the query (null when the caller supplied the vector)."),
90010
90573
  400: errorResponse("Invalid metadata `ranges` are `rag_invalid_request`. Other body schema failures, including neither `text` nor `embedding` or an invalid `preview`, remain `invalid_payload`. An `embedding` with a different width than the collection returns `rag_dim_mismatch`, with `expected` and `received` under `details`."),
90011
90574
  404: errorResponse("No such collection, or this agent has no RAG schema at all (`rag_collection_not_found`) — or `preview` names no document in it (`rag_document_not_found`) or a version that document never had (`rag_document_version_not_found`)."),
90012
90575
  409: errorResponse("The explicit model conflicts with the collection (`rag_embedding_conflict`), or its embeddings are rebuilding or failed (`rag_collection_unavailable`)."),
@@ -90066,7 +90629,7 @@ var createRagRouter = (deps) => {
90066
90629
  description: "The jobs an upload enqueues, newest first. Since feature 76 an ingest is a durable `rag-ingest` WORKFLOW RUN rather than a `rag_jobs` row, so this projects those runs onto the job shape the CLI and Studio have always read — same field names, same status vocabulary. `attempts` is therefore always 0: a retry starts a NEW run rather than incrementing a counter on this one.\n\n`collection` filters the page AFTER it is read (the runs query filters by entity, not by the workflow input), so a page can come back shorter than `limit` with a cursor still set.",
90067
90630
  request: { query: listRagJobsQuerySchema },
90068
90631
  responses: {
90069
- 200: jsonResponse$1(named("RagJobListResponse", ragJobListResponseSchema.extend({ items: z.array(RAG_JOB) })), "One page of ingest jobs, and the cursor for the next."),
90632
+ 200: jsonResponse$2(named("RagJobListResponse", ragJobListResponseSchema.extend({ items: z.array(RAG_JOB) })), "One page of ingest jobs, and the cursor for the next."),
90070
90633
  400: errorResponse("The query string failed its schema (`invalid_payload`).")
90071
90634
  }
90072
90635
  }), async (c) => {
@@ -90094,7 +90657,7 @@ var createRagRouter = (deps) => {
90094
90657
  description: "Flips the ingest RUN `running → cancelled`, closing its open steps. The status is final: nothing reopens the run. State-only, like the generic runs cancel: the World worker carries no live cancel signal yet, so an ingest that is ACTIVELY mid-flight keeps executing in the background, and whatever it writes afterwards does not change the job. A run that is not a `rag-ingest` workflow run answers 404 and keeps running.",
90095
90658
  request: { params: jobPath },
90096
90659
  responses: {
90097
- 200: jsonResponse$1(RAG_JOB_ACTION_RESPONSE, "The job and the status it now holds."),
90660
+ 200: jsonResponse$2(RAG_JOB_ACTION_RESPONSE, "The job and the status it now holds."),
90098
90661
  403: forbiddenResponse("rag:write", "rag:review"),
90099
90662
  404: errorResponse("No such ingest run, a run that is not one, or a `{jobId}` this box cannot read as an id (`rag_job_not_found`)."),
90100
90663
  409: errorResponse("The job already reached a terminal state (`rag_job_not_cancelable`), with the state it holds under `details.state`.")
@@ -90128,7 +90691,7 @@ var createRagRouter = (deps) => {
90128
90691
  description: "Creates an empty collection. With embedding_provider and embedding_model, probes that saved provider through the box, derives a valid storage dimension and initializes its search mode before saving. Provider and probe failures save nothing. Name-only requests keep the default provider, openai/text-embedding-3-small and 1536 dimensions without a network probe, unless the model provider is known not to serve that model: then the collection gets the first embedding model it serves, pinned to its provider, and that model is probed for its dimension. When the model provider lists neither that model nor any embedding model, a name-only request is refused and saves nothing. hybrid_enabled turns on hybrid search (meaning plus exact terms such as product codes) and number_format (eu or us) sets how it reads numbers and dates; omitted, hybrid search is off and the format is eu. extract_enabled defaults to false and extract_fields to an empty list; each field has a name, a number/date/text type and an optional removed flag.",
90129
90692
  request: { body: jsonBody(named("CreateRagCollectionRequest", createRagCollectionRequestSchema), "The collection name, any org-defined metadata to hang on it, its embedding model and its hybrid search settings.") },
90130
90693
  responses: {
90131
- 201: jsonResponse$1(named("CreateRagCollectionResponse", createRagCollectionResponseSchema), "The collection as the list endpoint would report it, with zero counts."),
90694
+ 201: jsonResponse$2(named("CreateRagCollectionResponse", createRagCollectionResponseSchema), "The collection as the list endpoint would report it, with zero counts."),
90132
90695
  400: errorResponse("The body failed its schema (`invalid_payload`), a collection of that name already exists (`rag_collection_already_exists`), or the request names no embedding model and the model provider lists none (`rag_embedding_model_required`: pass embedding_provider and embedding_model). The duplicate is a 400 and NOT a 409, unlike its already-exists siblings on this surface — the status has been 400 since the cloud surface this mirrors, and Studio keys on the code."),
90133
90696
  403: forbiddenResponse("rag:write"),
90134
90697
  502: errorResponse("The selected provider/model could not produce a valid embedding (`rag_embedding_probe_failed`), including missing/down providers, timeout or an invalid/storage-incompatible vector."),
@@ -90156,7 +90719,7 @@ var createRagRouter = (deps) => {
90156
90719
  body: jsonBody(named("UpdateRagCollectionRequest", updateRagCollectionRequestSchema), "The settings to change. At least one of them; an omitted one keeps its value.")
90157
90720
  },
90158
90721
  responses: {
90159
- 200: jsonResponse$1(named("UpdateRagCollectionResponse", updateRagCollectionResponseSchema), "The saved collection settings; extraction changes also report unconverted."),
90722
+ 200: jsonResponse$2(named("UpdateRagCollectionResponse", updateRagCollectionResponseSchema), "The saved collection settings; extraction changes also report unconverted."),
90160
90723
  400: errorResponse("The body is empty, names an unknown key, number_format or field type (`invalid_payload`)."),
90161
90724
  403: forbiddenResponse("rag:write"),
90162
90725
  404: errorResponse("No such collection, or a `{name}` this box cannot read as a collection name (`rag_collection_not_found`). A box older than this route answers 404 `not_found` instead."),
@@ -90184,7 +90747,7 @@ var createRagRouter = (deps) => {
90184
90747
  description: "Drops the collection with its documents, its chunks and the originals staged under the reserved `rag/` storage prefix. Not reversible, and not a soft delete.",
90185
90748
  request: { params: collectionPath },
90186
90749
  responses: {
90187
- 200: jsonResponse$1(named("DeleteRagCollectionResponse", deleteRagCollectionResponseSchema), "The name that was dropped."),
90750
+ 200: jsonResponse$2(named("DeleteRagCollectionResponse", deleteRagCollectionResponseSchema), "The name that was dropped."),
90188
90751
  403: forbiddenResponse("rag:write"),
90189
90752
  404: errorResponse("No such collection, or a `{name}` this box cannot read as a collection name (`rag_collection_not_found`)."),
90190
90753
  409: errorResponse("The canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`), or this collection is paused (`rag_collection_unavailable`).")
@@ -90212,7 +90775,7 @@ var createRagRouter = (deps) => {
90212
90775
  body: multipartBody(named("UploadRagDocumentForm", uploadFormSchema$1), "The source bytes, the name to file them under, and their content type.")
90213
90776
  },
90214
90777
  responses: {
90215
- 200: jsonResponse$1(named("UploadRagDocumentResponse", uploadRagDocumentResponseSchema), "The enqueued ingest job: the id to poll, the status it starts in, and the version the upload became."),
90778
+ 200: jsonResponse$2(named("UploadRagDocumentResponse", uploadRagDocumentResponseSchema), "The enqueued ingest job: the id to poll, the status it starts in, and the version the upload became."),
90216
90779
  400: errorResponse("The body is not `multipart/form-data`, or it is missing `file`, `filename` or `content_type` — or sent `file` as ordinary text, or a `content_type` outside the three supported source formats, or a `metadata` that parses to something other than a JSON object (`invalid_payload`)."),
90217
90780
  403: errorResponse("The caller's role grants neither `rag:write` nor `rag:review` (`forbidden`), or grants `rag:review` alone and the form has no `replaces`: only `rag:write` starts a new document. Only reachable on the browser-direct datapath — a proxy-signed call was authorized before it was signed."),
90218
90781
  404: errorResponse("No such collection, or a `{name}` this box cannot read as a collection name (`rag_collection_not_found`) — or `replaces` names no document in it (`rag_document_not_found`)."),
@@ -90261,7 +90824,7 @@ var createRagRouter = (deps) => {
90261
90824
  description: "Removes the document with every version of it, whichever version `{docId}` names: every chunk they produced and every original staged under the reserved `rag/` prefix. The collection itself stays.",
90262
90825
  request: { params: collectionDocPath },
90263
90826
  responses: {
90264
- 200: jsonResponse$1(named("DeleteRagDocumentResponse", deleteRagDocumentResponseSchema), "The document id that was removed, and the collection it left."),
90827
+ 200: jsonResponse$2(named("DeleteRagDocumentResponse", deleteRagDocumentResponseSchema), "The document id that was removed, and the collection it left."),
90265
90828
  403: forbiddenResponse("rag:write"),
90266
90829
  404: errorResponse("No such collection or a `{name}` this box cannot read (`rag_collection_not_found`), or no such document in it (`rag_document_not_found`)."),
90267
90830
  409: errorResponse("The canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`), or this collection is paused (`rag_collection_unavailable`).")
@@ -90287,7 +90850,7 @@ var createRagRouter = (deps) => {
90287
90850
  description: "Mints a short-lived signed URL for the original staged under the reserved `rag/` prefix, plus the filename and content type the browser needs to name the download. A POST rather than a GET because it MINTS a credential; it reads nothing back into the response body.",
90288
90851
  request: { params: collectionDocPath },
90289
90852
  responses: {
90290
- 200: jsonResponse$1(named("DownloadRagDocumentResponse", downloadRagDocumentResponseSchema), "The signed URL and the metadata to name the file with."),
90853
+ 200: jsonResponse$2(named("DownloadRagDocumentResponse", downloadRagDocumentResponseSchema), "The signed URL and the metadata to name the file with."),
90291
90854
  403: forbiddenResponse("rag:write", "rag:review"),
90292
90855
  404: errorResponse("No such collection or a `{name}` this box cannot read (`rag_collection_not_found`), or no such document in it (`rag_document_not_found`)."),
90293
90856
  409: errorResponse("The canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`).")
@@ -90316,7 +90879,7 @@ var createRagRouter = (deps) => {
90316
90879
  body: jsonBody(named("ExtractRagDocumentRequest", extractRagDocumentRequestSchema), "An optional document version; omitted selects the published version.")
90317
90880
  },
90318
90881
  responses: {
90319
- 200: jsonResponse$1(named("ExtractRagDocumentResponse", extractRagDocumentResponseSchema), "The proposal, whether extraction failed, and whether the middle was skipped."),
90882
+ 200: jsonResponse$2(named("ExtractRagDocumentResponse", extractRagDocumentResponseSchema), "The proposal, whether extraction failed, and whether the middle was skipped."),
90320
90883
  400: errorResponse("The request body failed schema validation (invalid_payload)."),
90321
90884
  403: forbiddenResponse("rag:write"),
90322
90885
  404: errorResponse("No such collection (rag_collection_not_found), document (rag_document_not_found), or version (rag_document_version_not_found)."),
@@ -90351,7 +90914,7 @@ var createRagRouter = (deps) => {
90351
90914
  body: jsonBody(named("UpdateRagDocumentValuesRequest", updateRagDocumentValuesRequestSchema), "The values to merge, an optional version, and optional field definitions to add.")
90352
90915
  },
90353
90916
  responses: {
90354
- 200: jsonResponse$1(named("UpdateRagDocumentValuesResponse", updateRagDocumentValuesResponseSchema), "The saved version and its merged metadata."),
90917
+ 200: jsonResponse$2(named("UpdateRagDocumentValuesResponse", updateRagDocumentValuesResponseSchema), "The saved version and its merged metadata."),
90355
90918
  400: errorResponse("A malformed body is invalid_payload; an unknown field or a value that does not match its field type is rag_invalid_request."),
90356
90919
  403: forbiddenResponse("rag:write"),
90357
90920
  404: errorResponse("No such collection (rag_collection_not_found), document (rag_document_not_found), or requested or published version (rag_document_version_not_found)."),
@@ -90377,7 +90940,7 @@ var createRagRouter = (deps) => {
90377
90940
  description: "Every version of the document `{docId}` names (the id of any one of its versions), newest first, in one page. `is_active` marks the version the agent reads. `ready` says whether publishing a version is accepted: it has chunks, and its newest ingest, if any, succeeded. `ingest_status` alone does not say that, because an ingest can succeed and write no chunk.",
90378
90941
  request: { params: collectionDocPath },
90379
90942
  responses: {
90380
- 200: jsonResponse$1(named("RagDocumentVersionListResponse", ragDocumentVersionListResponseSchema), "Every version of the document, newest first."),
90943
+ 200: jsonResponse$2(named("RagDocumentVersionListResponse", ragDocumentVersionListResponseSchema), "Every version of the document, newest first."),
90381
90944
  404: errorResponse("No such collection or a `{name}` this box cannot read (`rag_collection_not_found`), or no such document in it (`rag_document_not_found`)."),
90382
90945
  409: errorResponse("The canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`).")
90383
90946
  }
@@ -90403,7 +90966,7 @@ var createRagRouter = (deps) => {
90403
90966
  description: "Makes `{version}` the version of the document the agent reads, `{docId}` being the id of any of its versions. Publishing an earlier version is the rollback. Every version keeps its own chunks, so nothing is re-embedded: the published flag moves under a lock on the document, and the version it leaves stays in the history.\n\nPublishing the version already published changes nothing and answers `changed: false`. A version with no chunks, or whose newest ingest did not succeed, is refused: publishing it would take the document away from the agent.",
90404
90967
  request: { params: collectionDocVersionPath },
90405
90968
  responses: {
90406
- 200: jsonResponse$1(named("ActivateRagDocumentVersionResponse", activateRagDocumentVersionResponseSchema), "The version now published, the one published before, and whether anything changed."),
90969
+ 200: jsonResponse$2(named("ActivateRagDocumentVersionResponse", activateRagDocumentVersionResponseSchema), "The version now published, the one published before, and whether anything changed."),
90407
90970
  403: forbiddenResponse("rag:write"),
90408
90971
  404: errorResponse("No such collection or a `{name}` this box cannot read (`rag_collection_not_found`), no such document in it (`rag_document_not_found`), or no such version of it, a `{version}` that is not a positive integer included (`rag_document_version_not_found`)."),
90409
90972
  409: errorResponse("The version is not ready to publish: it has no chunks, or its newest ingest did not succeed (`rag_version_not_ready`, with that `version` under `details`). Or the canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`). A paused collection answers `rag_collection_unavailable`.")
@@ -90432,7 +90995,7 @@ var createRagRouter = (deps) => {
90432
90995
  description: "Removes `{version}` of the document `{docId}` names (the id of any of its versions): its row, every chunk it produced and its original staged under the reserved `rag/` prefix. The published version and every other version stay. The published version is refused: publish another one first, or delete the whole document.",
90433
90996
  request: { params: collectionDocVersionPath },
90434
90997
  responses: {
90435
- 200: jsonResponse$1(named("DeleteRagDocumentVersionResponse", deleteRagDocumentVersionResponseSchema), "The id of the version that was removed, its number, and the collection it left."),
90998
+ 200: jsonResponse$2(named("DeleteRagDocumentVersionResponse", deleteRagDocumentVersionResponseSchema), "The id of the version that was removed, its number, and the collection it left."),
90436
90999
  403: forbiddenResponse("rag:write", "rag:review"),
90437
91000
  404: errorResponse("No such collection or a `{name}` this box cannot read (`rag_collection_not_found`), no such document in it (`rag_document_not_found`), or no such version of it, a `{version}` that is not a positive integer included (`rag_document_version_not_found`)."),
90438
91001
  409: errorResponse("The version is the published one (`rag_version_active`), or its ingest is still queued or running (`rag_version_ingesting`), each with that `version` under `details`. Or the canonical `stackbone_rag_*` schema is not installed on this agent (`rag_schema_missing`). A paused collection answers `rag_collection_unavailable`.")
@@ -90460,7 +91023,7 @@ var createRagRouter = (deps) => {
90460
91023
  description: "RE-RUNS the `rag-ingest` workflow over the failed run's stored input — the original is still staged under the reserved `rag/` prefix — so this is an actual re-ingest and not a row flip. Idempotent: the workflow's persist replaces the document in place. The answer carries the NEW run's id; the failed one keeps its own history. A run that is not a `rag-ingest` workflow run answers 404 and starts nothing.",
90461
91024
  request: { params: jobPath },
90462
91025
  responses: {
90463
- 200: jsonResponse$1(RAG_JOB_ACTION_RESPONSE, "The NEW ingest run, queued."),
91026
+ 200: jsonResponse$2(RAG_JOB_ACTION_RESPONSE, "The NEW ingest run, queued."),
90464
91027
  403: forbiddenResponse("rag:write", "rag:review"),
90465
91028
  404: errorResponse("No such ingest run, a run that is not one, or a `{jobId}` this box cannot read as an id (`rag_job_not_found`)."),
90466
91029
  409: errorResponse("The run did not fail, so there is nothing to retry (`rag_job_not_retryable`, with the state it holds under `details.state`) — or it failed but its stored input is incomplete, or the `rag-ingest` workflow runtime is not armed on this box, or the collection is paused (`rag_collection_unavailable`).")
@@ -90811,7 +91374,7 @@ var createRunsRouter = (deps) => {
90811
91374
  description: "Cursor-paginated, newest first unless `order=asc`, filterable by status, trigger, entity name and kind, session, a `created_at` window and one `key:value` run tag (`tag`, an exact match on `metadata.tags`); `system=exclude` leaves out the runs of the entries the platform installed for itself. A pure READ: listing an open run never reconciles it — closing a finished workflow run is the background sweep's job, so this route never contends with the projector's writes.",
90812
91375
  request: { query: listRunsQuery },
90813
91376
  responses: {
90814
- 200: jsonResponse$1(named("RunListResponse", runListResponseSchema), "One page of runs, and the cursors either side of it."),
91377
+ 200: jsonResponse$2(named("RunListResponse", runListResponseSchema), "One page of runs, and the cursors either side of it."),
90815
91378
  400: errorResponse("The query failed its schema (`invalid_payload`), or the cursor is not one this box issued (`invalid_cursor`).")
90816
91379
  }
90817
91380
  }), async (c) => {
@@ -90832,7 +91395,7 @@ var createRunsRouter = (deps) => {
90832
91395
  description: "The distinct entities present in this box's runs, feeding the Runs screen's name dropdown. TWO lists off ONE read so they can never disagree: `entries` keys each entity by (name, kind) — two same-named entries of different kinds are two rows — and `names` keeps the flat, pre-`entity_kind` shape for a Studio bundle that predates the kind. An entity that has never run does not appear. `system=exclude` leaves out the entries the platform installed for itself, as the list read does.",
90833
91396
  request: { query: runEntityNamesQuerySchema },
90834
91397
  responses: {
90835
- 200: jsonResponse$1(named("RunEntityNamesResponse", runEntityNamesResponseSchema), "Every entity name present in the runs, and the (name, kind) pairs behind them."),
91398
+ 200: jsonResponse$2(named("RunEntityNamesResponse", runEntityNamesResponseSchema), "Every entity name present in the runs, and the (name, kind) pairs behind them."),
90836
91399
  400: errorResponse("The query failed its schema (`invalid_payload`).")
90837
91400
  }
90838
91401
  }), async (c) => {
@@ -90851,7 +91414,7 @@ var createRunsRouter = (deps) => {
90851
91414
  description: "The run row as it now stands: status, trigger, timings, the input it started from, the output or error it ended with, and the token counters a chat turn folded onto it.",
90852
91415
  request: { params: runIdPath },
90853
91416
  responses: {
90854
- 200: jsonResponse$1(named("Run", runSchema), "The run as it now stands."),
91417
+ 200: jsonResponse$2(named("Run", runSchema), "The run as it now stands."),
90855
91418
  404: errorResponse("No such run (`run_not_found`) — or an id that is not an id at all.")
90856
91419
  }
90857
91420
  }), async (c) => {
@@ -90870,7 +91433,7 @@ var createRunsRouter = (deps) => {
90870
91433
  query: runStepsQuerySchema
90871
91434
  },
90872
91435
  responses: {
90873
- 200: jsonResponse$1(named("RunStepsResponse", runStepsResponseSchema), "One page of the run's steps, and the cursors either side of it."),
91436
+ 200: jsonResponse$2(named("RunStepsResponse", runStepsResponseSchema), "One page of the run's steps, and the cursors either side of it."),
90874
91437
  400: errorResponse("The query failed its schema (`invalid_payload`), or the cursor is not one this box issued (`invalid_cursor`).")
90875
91438
  }
90876
91439
  }), async (c) => {
@@ -90896,7 +91459,7 @@ var createRunsRouter = (deps) => {
90896
91459
  description: "One step with its payloads. The steps list leaves a `model_call`'s `input` and `output` out, because each is the whole prompt and reply of that call, stored uncapped; this is where they are read.",
90897
91460
  request: { params: runStepPath },
90898
91461
  responses: {
90899
- 200: jsonResponse$1(named("RunStep", runStepSchema), "The step, payloads included."),
91462
+ 200: jsonResponse$2(named("RunStep", runStepSchema), "The step, payloads included."),
90900
91463
  404: errorResponse("No such step in this run (`run_step_not_found`) — or an id that is not an id at all.")
90901
91464
  }
90902
91465
  }), async (c) => {
@@ -90915,7 +91478,7 @@ var createRunsRouter = (deps) => {
90915
91478
  description: "Flips the row to `cancelled`, closes its open steps in the same transaction, and publishes `run.finished`. `cancelled` is final: a later close from the worker, a resume or the reconcile sweep does not change it. The SDK has no live cancel signal yet, so this closes the ROW — the worker behind it may keep running until it finishes on its own.",
90916
91479
  request: { params: runIdPath },
90917
91480
  responses: {
90918
- 200: jsonResponse$1(named("CancelRunResponse", cancelRunResponseSchema), "The run as it now stands, `cancelled`."),
91481
+ 200: jsonResponse$2(named("CancelRunResponse", cancelRunResponseSchema), "The run as it now stands, `cancelled`."),
90919
91482
  403: forbiddenResponse("runs:invoke"),
90920
91483
  404: errorResponse("No such run (`run_not_found`)."),
90921
91484
  409: errorResponse("The run is not running, so there is nothing to cancel (`run_not_running`).")
@@ -91088,7 +91651,7 @@ var createRecurringJobsRouter = (options) => {
91088
91651
  tags: [RECURRING_JOBS_TAG],
91089
91652
  summary: "List the box's recurring jobs",
91090
91653
  description: "One item per DECLARED cadence — a workflow cron, an intake link's polling repeatable, the connection-refresh sweep — never one per repeatable Redis happens to hold, so a job whose repeatable failed to arm is reported here as inactive. A box with nothing armed yet answers an empty list, not an error.",
91091
- responses: { 200: jsonResponse$1(named("RecurringJobsResponse", recurringJobsResponseSchema), "Every declared recurring job, plus the box's own clock.") }
91654
+ responses: { 200: jsonResponse$2(named("RecurringJobsResponse", recurringJobsResponseSchema), "Every declared recurring job, plus the box's own clock.") }
91092
91655
  }), async (c) => {
91093
91656
  const registry = options.resolveRecurringJobs();
91094
91657
  const items = registry ? (await registry.list()).map(toWire) : [];
@@ -91109,7 +91672,7 @@ var createRecurringJobsRouter = (options) => {
91109
91672
  description: paused ? "Stops every recurring job carrying `pause_key` — every cron of one workflow for a `pause_key`, one cron for a `cron_pause_key` — and stores the pause, so it holds across restarts and redeploys. A tick BullMQ already queued is skipped. The box's own sweeps carry no key and cannot be paused." : "Re-arms every recurring job carrying `pause_key` and drops the stored pause. A cron still paused under its other key stays off. BullMQ fires each at its next scheduled time, not immediately.",
91110
91673
  request: { body: jsonBody(named("RecurringJobPauseRequest", recurringJobPauseRequestSchema), "The pause key the list reported for the jobs to switch.") },
91111
91674
  responses: {
91112
- 200: jsonResponse$1(named("RecurringJobPauseResponse", recurringJobPauseResponseSchema), "The key, its new state, and how many jobs it covered."),
91675
+ 200: jsonResponse$2(named("RecurringJobPauseResponse", recurringJobPauseResponseSchema), "The key, its new state, and how many jobs it covered."),
91113
91676
  400: errorResponse("The body failed its schema."),
91114
91677
  403: forbiddenResponse("config:write"),
91115
91678
  404: errorResponse("No recurring job on this box carries that pause key.")
@@ -91763,7 +92326,7 @@ var createLimitsRouter = (options) => {
91763
92326
  tags: [LIMITS_TAG],
91764
92327
  summary: "List the workspace's concurrency limits",
91765
92328
  description: "One item per limit `stackbone.config.ts` declares: the declared capacity, the operator's override, the capacity the box enforces, and how many slots are busy and waiting now. A box with no limits answers an empty list.",
91766
- responses: { 200: jsonResponse$1(named("LimitersResponse", limitersResponseSchema), "Every declared limit, plus the box's own clock.") }
92329
+ responses: { 200: jsonResponse$2(named("LimitersResponse", limitersResponseSchema), "Every declared limit, plus the box's own clock.") }
91767
92330
  }), async (c) => {
91768
92331
  const limits = options.resolveLimits();
91769
92332
  const items = limits ? await limits.listLimiters() : [];
@@ -91780,7 +92343,7 @@ var createLimitsRouter = (options) => {
91780
92343
  description: "The slots of one limit: held first, then granted, then the queue in arrival order. A granted slot waits for its run to confirm it until `claimDeadline`; a busy slot goes back to the box at `expiresAt`.",
91781
92344
  request: { params: namePath },
91782
92345
  responses: {
91783
- 200: jsonResponse$1(named("SlotsResponse", slotsResponseSchema), "The limit and its slots."),
92346
+ 200: jsonResponse$2(named("SlotsResponse", slotsResponseSchema), "The limit and its slots."),
91784
92347
  400: errorResponse("The name is not a limit name (`invalid_payload`)."),
91785
92348
  404: errorResponse("The workspace declares no limit of that name.")
91786
92349
  }
@@ -91805,7 +92368,7 @@ var createLimitsRouter = (options) => {
91805
92368
  description: "Gives a granted or held slot back and grants the next run in the queue. For a slot whose release was lost: the work ended but nothing called `releaseSlot`. Idempotent: a slot already released, or still waiting, answers `released: false` and changes nothing.",
91806
92369
  request: { params: slotPath },
91807
92370
  responses: {
91808
- 200: jsonResponse$1(named("ReleaseSlotResponse", releaseSlotResponseSchema), "Whether this call released the slot."),
92371
+ 200: jsonResponse$2(named("ReleaseSlotResponse", releaseSlotResponseSchema), "Whether this call released the slot."),
91809
92372
  400: errorResponse("The id is not a uuid (`invalid_payload`)."),
91810
92373
  403: forbiddenResponse(RELEASE_CAPABILITY),
91811
92374
  503: errorResponse("This box runs no concurrency-limits service.")
@@ -91843,7 +92406,7 @@ var createLimitsRouter = (options) => {
91843
92406
  body: jsonBody(named("LimiterUpdateRequest", limiterUpdateRequestSchema), "The new capacity and/or max hold; `null` clears one.")
91844
92407
  },
91845
92408
  responses: {
91846
- 200: jsonResponse$1(LIMITER, "The limit with its new overrides."),
92409
+ 200: jsonResponse$2(LIMITER, "The limit with its new overrides."),
91847
92410
  400: errorResponse("The name or the body failed its schema (`invalid_payload`)."),
91848
92411
  403: forbiddenResponse(OVERRIDES_CAPABILITY),
91849
92412
  404: errorResponse("The workspace declares no limit of that name."),
@@ -91897,153 +92460,6 @@ var mountLimits = (app, ctx) => {
91897
92460
  }));
91898
92461
  };
91899
92462
  //#endregion
91900
- //#region ../../libs/runtime/workspace-runtime/src/lib/voice/livekit-credentials.ts
91901
- init_src$26();
91902
- var [URL_NAME, API_KEY_NAME, API_SECRET_NAME] = RESERVED_LIVEKIT_SECRET_NAMES;
91903
- /**
91904
- * Names a row this box's key cannot open (never its value), so the log says
91905
- * which one to save again. True when `err` is that failure.
91906
- */
91907
- var warnUndecryptable = (scope, name) => console.warn(`[${scope}] ${name} cannot be opened with this box's key; save it again`);
91908
- var warnIfUndecryptable = (scope, name, err) => {
91909
- if (!(err instanceof SecretUndecryptableError)) return false;
91910
- warnUndecryptable(scope, name);
91911
- return true;
91912
- };
91913
- /**
91914
- * The stored LiveKit credentials, read on EVERY call — no cache — or `null` when
91915
- * any of the three is missing or empty. Half a credential set cannot open a
91916
- * room, so it answers exactly what nothing stored answers.
91917
- *
91918
- * A row this box's key cannot open throws {@link SecretUndecryptableError},
91919
- * after a warning that names the row (never its value): saving the credentials
91920
- * again is the fix, and the caller tells the operator so. Any other failure
91921
- * throws as it came.
91922
- */
91923
- var readLiveKitCredentials = async (opts) => {
91924
- const values = await readSystemSecrets(opts, RESERVED_LIVEKIT_SECRET_NAMES, (name) => warnUndecryptable("voice", name));
91925
- const url = values.get(URL_NAME);
91926
- const apiKey = values.get(API_KEY_NAME);
91927
- const apiSecret = values.get(API_SECRET_NAME);
91928
- if (!url || !apiKey || !apiSecret) return null;
91929
- return {
91930
- url,
91931
- apiKey,
91932
- apiSecret
91933
- };
91934
- };
91935
- /** The secret naming the LiveKit outbound SIP trunk `placeCall` dials through (STA3-56). */
91936
- var SIP_OUTBOUND_TRUNK_SECRET = "LIVEKIT_SIP_OUTBOUND_TRUNK_ID";
91937
- /** The optional secret naming the number the callee sees; the trunk's own when absent. */
91938
- var SIP_CALLER_ID_SECRET = "LIVEKIT_SIP_CALLER_ID";
91939
- /**
91940
- * The outbound trunk, read on every call, or `null` when no trunk id is saved.
91941
- * Not reserved names, unlike the three credentials: a trunk id opens nothing on
91942
- * its own and has no privileged screen yet, so it is read from an `is_system`
91943
- * row or the operator's own secret of that name, whichever exists.
91944
- */
91945
- var readSipOutboundConfig = async (opts) => {
91946
- const values = await readSecretsAnyKind(opts, [SIP_OUTBOUND_TRUNK_SECRET, SIP_CALLER_ID_SECRET], (name) => warnUndecryptable("voice", name));
91947
- const trunkId = values.get(SIP_OUTBOUND_TRUNK_SECRET)?.trim();
91948
- if (!trunkId) return null;
91949
- const callerId = values.get(SIP_CALLER_ID_SECRET)?.trim();
91950
- return {
91951
- trunkId,
91952
- ...callerId ? { callerId } : {}
91953
- };
91954
- };
91955
- var voiceNotConfigured = (message) => ({ refusal: {
91956
- status: 503,
91957
- body: {
91958
- code: "voice_not_configured",
91959
- message
91960
- }
91961
- } });
91962
- /**
91963
- * {@link readLiveKitCredentials} for a voice route: the credentials, or the 503 to
91964
- * answer without them. A row sealed by another box key is fixed the way a missing one
91965
- * is, by saving the credentials again, so it answers the same 503; any other read
91966
- * failure is the box's own and throws, to the generic 500.
91967
- */
91968
- var lookUpLiveKitCredentials = async (opts) => {
91969
- let credentials;
91970
- try {
91971
- credentials = await readLiveKitCredentials(opts);
91972
- } catch (err) {
91973
- if (!(err instanceof SecretUndecryptableError)) throw err;
91974
- return voiceNotConfigured("This box cannot read the saved LiveKit credentials: they were saved with another key. Open the Voice provider screen and save them again.");
91975
- }
91976
- if (!credentials) return voiceNotConfigured("This box has no LiveKit credentials. Save the URL, API key and API secret on the Voice provider screen.");
91977
- return { credentials };
91978
- };
91979
- /**
91980
- * The screen's view: whether all three are saved, and the URL in clear.
91981
- *
91982
- * The key and the secret are asked after as EXISTENCE only, never decrypted:
91983
- * this view never shows them, so opening them would be work done to throw away.
91984
- * The URL is decrypted to be shown, and a URL row this box's key cannot open
91985
- * (sealed by another box) reads as not configured instead of failing the screen
91986
- * the operator needs to replace it from. Only that: a database or key failure
91987
- * throws, because answering "not configured" would hide a configured box.
91988
- */
91989
- var getVoiceProvider = async (opts) => {
91990
- const [url, apiKeySet, apiSecretSet] = await Promise.all([
91991
- readSystemSecret(opts, URL_NAME).catch((err) => {
91992
- if (!warnIfUndecryptable("voice-provider", URL_NAME, err)) throw err;
91993
- return null;
91994
- }),
91995
- systemSecretExists(opts, API_KEY_NAME),
91996
- systemSecretExists(opts, API_SECRET_NAME)
91997
- ]);
91998
- return {
91999
- configured: url !== null && apiKeySet && apiSecretSet,
92000
- url
92001
- };
92002
- };
92003
- /**
92004
- * Save all three as `is_system` rows, with the activity-log entry, in ONE
92005
- * transaction: the box never holds a new URL beside an old secret, and never a
92006
- * save the trail does not show. The entry names the URL alone.
92007
- */
92008
- var setVoiceProvider = async (opts, body, actor = { userId: null }) => {
92009
- await opts.db.transaction(async (tx) => {
92010
- await seedSystemSecrets(opts, {
92011
- [URL_NAME]: body.url,
92012
- [API_KEY_NAME]: body.apiKey,
92013
- [API_SECRET_NAME]: body.apiSecret
92014
- }, tx);
92015
- await opts.audit.record({
92016
- action: "voice_provider.update",
92017
- actorUserId: actor.userId,
92018
- subjectType: "voice_provider",
92019
- payload: { url: body.url }
92020
- }, tx);
92021
- });
92022
- await notifySecretsChanged();
92023
- };
92024
- /**
92025
- * Remove all three in ONE transaction, and answer the names actually removed.
92026
- *
92027
- * `cleared` comes off each DELETE's own answer, so a partial store records only
92028
- * what it held, and a removal that found nothing records nothing — the rule
92029
- * `removeModelProvider` keeps.
92030
- */
92031
- var clearVoiceProvider = async (opts, actor = { userId: null }) => {
92032
- const cleared = await opts.db.transaction(async (tx) => {
92033
- const removed = [];
92034
- for (const name of RESERVED_LIVEKIT_SECRET_NAMES) if (await deleteSystemSecret(opts, name, tx)) removed.push(name);
92035
- if (removed.length > 0) await opts.audit.record({
92036
- action: "voice_provider.delete",
92037
- actorUserId: actor.userId,
92038
- subjectType: "voice_provider",
92039
- payload: { cleared: removed }
92040
- }, tx);
92041
- return removed;
92042
- });
92043
- if (cleared.length > 0) await notifySecretsChanged();
92044
- return cleared;
92045
- };
92046
- //#endregion
92047
92463
  //#region ../../libs/runtime/workspace-runtime/src/lib/voice/agent-voice-settings.ts
92048
92464
  init_src$26();
92049
92465
  /**
@@ -92761,7 +93177,7 @@ var createApiKeysRouter = (options) => {
92761
93177
  summary: "List the workspace API keys",
92762
93178
  description: "Every key this workspace has ever minted, newest first — REVOKED ONES INCLUDED, because a key that vanished from the list cannot answer \"what was that, and who turned it off\". Each row carries the prefix and the selector with the secret masked; the secret itself is not stored and cannot be answered here.",
92763
93179
  responses: {
92764
- 200: jsonResponse$1(named("StudioApiKeyListResponse", studioApiKeyListResponseSchema), "Every key this workspace holds, masked."),
93180
+ 200: jsonResponse$2(named("StudioApiKeyListResponse", studioApiKeyListResponseSchema), "Every key this workspace holds, masked."),
92765
93181
  403: forbiddenResponse(API_KEYS_CAPABILITY)
92766
93182
  }
92767
93183
  }), async (c) => c.json(await listApiKeys(options), 200));
@@ -92775,7 +93191,7 @@ var createApiKeysRouter = (options) => {
92775
93191
  description: "Answers the full `sbk_live_…` key ONCE, in `key`. The box keeps the selector and a SHA-256 digest of the secret, so this response is the only time the plaintext exists anywhere — a caller that loses it revokes the key and mints another.\n\n`permissions` is fixed at mint: a key's reach cannot be edited afterwards, because widening a credential in place leaves nothing on the trail saying what it used to be allowed to do. `expires_at` may be omitted or null, which means \"valid until revoked\".",
92776
93192
  request: { body: jsonBody(named("CreateStudioApiKeyRequest", createStudioApiKeyRequestSchema), "The display name, what the key may call, and when it stops working.") },
92777
93193
  responses: {
92778
- 201: jsonResponse$1(named("CreateStudioApiKeyResponse", createStudioApiKeyResponseSchema), "The masked key, and the plaintext — answered here and nowhere else, ever."),
93194
+ 201: jsonResponse$2(named("CreateStudioApiKeyResponse", createStudioApiKeyResponseSchema), "The masked key, and the plaintext — answered here and nowhere else, ever."),
92779
93195
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
92780
93196
  403: forbiddenResponse(API_KEYS_CAPABILITY)
92781
93197
  }
@@ -92789,7 +93205,7 @@ var createApiKeysRouter = (options) => {
92789
93205
  description: "The masked row. There is no route that answers the secret a second time, and no row that could.",
92790
93206
  request: { params: looseIdPath },
92791
93207
  responses: {
92792
- 200: jsonResponse$1(named("StudioApiKeyResponse", studioApiKeyResponseSchema), "The key, masked."),
93208
+ 200: jsonResponse$2(named("StudioApiKeyResponse", studioApiKeyResponseSchema), "The key, masked."),
92793
93209
  403: forbiddenResponse(API_KEYS_CAPABILITY),
92794
93210
  404: errorResponse("No such key — or an id that is not an id at all.")
92795
93211
  }
@@ -92813,7 +93229,7 @@ var createApiKeysRouter = (options) => {
92813
93229
  description: "Stops the key working, immediately and for good. A SOFT transition: the row stays and is stamped with when it happened and who did it, so the list can still explain a key that used to work. There is no un-revoke — mint another.",
92814
93230
  request: { params: looseIdPath },
92815
93231
  responses: {
92816
- 200: jsonResponse$1(named("StudioApiKeyResponse", studioApiKeyResponseSchema), "The key as it now stands, revoked."),
93232
+ 200: jsonResponse$2(named("StudioApiKeyResponse", studioApiKeyResponseSchema), "The key as it now stands, revoked."),
92817
93233
  403: forbiddenResponse(API_KEYS_CAPABILITY),
92818
93234
  404: errorResponse("No such key — or an id that is not an id at all."),
92819
93235
  409: errorResponse("The key was already revoked (`api_key_already_revoked`). The first revocation stands: re-stamping it would move the moment the key died and rename whoever stopped it.")
@@ -92908,7 +93324,7 @@ var createSecretsRouter = (secretsOptions) => {
92908
93324
  summary: "List the workspace secrets",
92909
93325
  description: "The names, descriptions and rotation timestamps. Values never leave through this route — each row carries a masked placeholder, and the plaintext is only ever answered by the reveal below.",
92910
93326
  responses: {
92911
- 200: jsonResponse$1(named("StudioSecretListResponse", studioSecretListResponseSchema), "Every secret this workspace holds, masked."),
93327
+ 200: jsonResponse$2(named("StudioSecretListResponse", studioSecretListResponseSchema), "Every secret this workspace holds, masked."),
92912
93328
  403: forbiddenResponse("secrets:rotate")
92913
93329
  }
92914
93330
  }), async (c) => c.json(await listSecrets$1(secretsOptions), 200));
@@ -92922,7 +93338,7 @@ var createSecretsRouter = (secretsOptions) => {
92922
93338
  description: "Writes the value under `name`, replacing whatever was there. The reply carries the masked row, never the value that was just written.",
92923
93339
  request: { body: jsonBody(named("CreateStudioSecretRequest", createStudioSecretRequestSchema), "The name, the value and an optional description.") },
92924
93340
  responses: {
92925
- 200: jsonResponse$1(named("StudioSecretMutationResponse", studioSecretMutationResponseSchema), "The secret as it now stands, masked."),
93341
+ 200: jsonResponse$2(named("StudioSecretMutationResponse", studioSecretMutationResponseSchema), "The secret as it now stands, masked."),
92926
93342
  400: errorResponse("The body failed its schema (`invalid_payload`), or the name is one the install owns (`studio_secret_system_name`)."),
92927
93343
  403: forbiddenResponse("secrets:rotate")
92928
93344
  }
@@ -92947,7 +93363,7 @@ var createSecretsRouter = (secretsOptions) => {
92947
93363
  description: "Removes the row and reloads the log scrubber before answering, so the value stops being masked only once it is gone.",
92948
93364
  request: { params: looseSecretNamePath },
92949
93365
  responses: {
92950
- 200: jsonResponse$1(named("StudioSecretDeleteResponse", studioSecretDeleteResponseSchema), "The secret is gone."),
93366
+ 200: jsonResponse$2(named("StudioSecretDeleteResponse", studioSecretDeleteResponseSchema), "The secret is gone."),
92951
93367
  400: errorResponse("The name is one the install owns (`studio_secret_system_name`)."),
92952
93368
  403: forbiddenResponse("secrets:rotate"),
92953
93369
  404: errorResponse("No such secret — or a name that is not a name at all.")
@@ -92980,7 +93396,7 @@ var createSecretsRouter = (secretsOptions) => {
92980
93396
  description: "The only route that answers the plaintext, and the only one gated on `secrets:reveal` (owner/admin). The local box skips the re-auth ceremony the cloud path runs — a single-developer machine has nobody to challenge. Every answered reveal is recorded on the activity log, naming the secret and whoever asked.",
92981
93397
  request: { params: looseSecretNamePath },
92982
93398
  responses: {
92983
- 200: jsonResponse$1(named("StudioSecretRevealResponse", studioSecretRevealResponseSchema), "The plaintext, and when the reveal stops being valid."),
93399
+ 200: jsonResponse$2(named("StudioSecretRevealResponse", studioSecretRevealResponseSchema), "The plaintext, and when the reveal stops being valid."),
92984
93400
  403: forbiddenResponse("secrets:reveal"),
92985
93401
  404: errorResponse("No such secret — or a name that is not a name at all.")
92986
93402
  }
@@ -93128,7 +93544,7 @@ var createSessionsRouter = (deps) => {
93128
93544
  description: "Cursor-paginated, newest first, filterable by agent, status and one `key:value` run tag (`tag`: the sessions with at least one run carrying it); `system=exclude` leaves out the sessions of the agents the platform installed for itself. Each row carries the read-time aggregates (turn count, last activity, preview, token totals). A workspace with no sessions store wired — a classic project — reports an empty page rather than an error.",
93129
93545
  request: { query: listSessionsQuerySchema },
93130
93546
  responses: {
93131
- 200: jsonResponse$1(named("SessionListResponse", sessionListResponseSchema), "One page of sessions, and the cursors either side of it."),
93547
+ 200: jsonResponse$2(named("SessionListResponse", sessionListResponseSchema), "One page of sessions, and the cursors either side of it."),
93132
93548
  400: errorResponse("The query failed its schema (`invalid_payload`), or the cursor is not one this box issued (`invalid_cursor`).")
93133
93549
  }
93134
93550
  }), async (c) => {
@@ -93154,7 +93570,7 @@ var createSessionsRouter = (deps) => {
93154
93570
  description: "The lifecycle row plus the same read-time aggregates the list carries. Not the turns — those are runs, read through `/api/runs`.",
93155
93571
  request: { params: looseSessionIdPath },
93156
93572
  responses: {
93157
- 200: jsonResponse$1(named("Session", sessionSchema), "The session as it now stands."),
93573
+ 200: jsonResponse$2(named("Session", sessionSchema), "The session as it now stands."),
93158
93574
  404: errorResponse("No such session — or an id that is not an id at all.")
93159
93575
  }
93160
93576
  }), async (c) => {
@@ -93323,7 +93739,7 @@ var createSettingsRouter = (opts, hooks = {}) => {
93323
93739
  tags: [SETTINGS_TAG],
93324
93740
  summary: "Read the workspace settings",
93325
93741
  description: "Every knob, with the documented default filled in for anything nobody has written — a workspace that was never configured reads defaults, never an empty object. `updatedAt`/`updatedBy` are null until the first save, and `updatedByEmail` names only the self-host principal — every other author is an id for the caller to resolve against its member directory.",
93326
- responses: { 200: jsonResponse$1(named("WorkspaceSettingsResponse", workspaceSettingsResponseSchema), "The resolved settings, and who last changed them.") }
93742
+ responses: { 200: jsonResponse$2(named("WorkspaceSettingsResponse", workspaceSettingsResponseSchema), "The resolved settings, and who last changed them.") }
93327
93743
  }), async (c) => c.json(await getWorkspaceSettings(opts), 200));
93328
93744
  app.openapi(createRoute({
93329
93745
  method: "put",
@@ -93335,7 +93751,7 @@ var createSettingsRouter = (opts, hooks = {}) => {
93335
93751
  description: "A SHALLOW merge: an absent key leaves the stored value alone, an explicit `null` clears it back to unset. The reply echoes the fresh view so the screen reflects the merged result without a second round-trip. Some keys carry a SECOND, per-key gate on top of `config:write` — see the 403.",
93336
93752
  request: { body: jsonBody(named("WorkspaceSettingsPatch", workspaceSettingsPatchSchema), "The keys to change. Unknown keys are refused: an unrecognised key is almost always a typo, and accepting it would look like a save that changed nothing.") },
93337
93753
  responses: {
93338
- 200: jsonResponse$1(named("WorkspaceSettingsResponse", workspaceSettingsResponseSchema), "The settings as they now stand, with this call stamped as the author."),
93754
+ 200: jsonResponse$2(named("WorkspaceSettingsResponse", workspaceSettingsResponseSchema), "The settings as they now stand, with this call stamped as the author."),
93339
93755
  400: errorResponse("The patch failed its schema (`invalid_payload`)."),
93340
93756
  403: errorResponse("The caller does not hold `config:write` — or the patch names a key that needs a SECOND capability on top of it (`forbidden`): `logRetentionDays` / `logStorageBudgetGb` / `eventRetentionDays` need `retention:write`, `mcpEnabled` needs `mcp:manage`, `copilotEnabled` needs `copilot:manage`. Those govern how long the box keeps its own evidence, whether it answers MCP at all and whether Studio shows its copilot to the workspace, so the coarser capability is not enough. The WHOLE patch is refused, never half of it.")
93341
93757
  }
@@ -93434,7 +93850,7 @@ var createStorageRouter = (deps) => {
93434
93850
  tags: [STORAGE_TAG],
93435
93851
  summary: "List the buckets this box can browse",
93436
93852
  description: "Every bucket the box's S3 credential reaches — one under `stackbone dev` (the docker-compose MinIO), the install's own under a deployed image.",
93437
- responses: { 200: jsonResponse$1(named("StudioStorageBucketListResponse", studioStorageBucketListResponseSchema), "The buckets, each with the kind of store behind it.") }
93853
+ responses: { 200: jsonResponse$2(named("StudioStorageBucketListResponse", studioStorageBucketListResponseSchema), "The buckets, each with the kind of store behind it.") }
93438
93854
  }), async (c) => c.json(await listBuckets$1(deps.storage), 200));
93439
93855
  app.openapi(createRoute({
93440
93856
  method: "get",
@@ -93447,7 +93863,7 @@ var createStorageRouter = (deps) => {
93447
93863
  query: listStudioStorageObjectsQuerySchema
93448
93864
  },
93449
93865
  responses: {
93450
- 200: jsonResponse$1(named("StudioStorageObjectListResponse", studioStorageObjectListResponseSchema), "One page of objects, the folders below the prefix, and the cursor for the next page."),
93866
+ 200: jsonResponse$2(named("StudioStorageObjectListResponse", studioStorageObjectListResponseSchema), "One page of objects, the folders below the prefix, and the cursor for the next page."),
93451
93867
  400: errorResponse("The query failed its schema (`invalid_payload`)."),
93452
93868
  404: errorResponse("No such bucket (`bucket_not_found`).")
93453
93869
  }
@@ -93469,7 +93885,7 @@ var createStorageRouter = (deps) => {
93469
93885
  body: multipartBody(named("StudioStorageUploadForm", uploadFormSchema), "The object bytes and the key to write them under.")
93470
93886
  },
93471
93887
  responses: {
93472
- 200: jsonResponse$1(named("StudioStorageUploadResponse", studioStorageUploadResponseSchema), "The stored key, its size and the etag the store minted."),
93888
+ 200: jsonResponse$2(named("StudioStorageUploadResponse", studioStorageUploadResponseSchema), "The stored key, its size and the etag the store minted."),
93473
93889
  400: errorResponse("The body is not `multipart/form-data`, or it is missing `file` or `key` — or sent `file` as text rather than a part (`invalid_payload`)."),
93474
93890
  403: errorResponse("The caller's role does not grant `runs:invoke` — or the key is under `rag/`, which is reserved for managed RAG originals and cannot be written from the explorer (`rag_storage_prefix_reserved`)."),
93475
93891
  404: errorResponse("No such bucket (`bucket_not_found`), or a key this box cannot write (`object_not_found`).")
@@ -93512,7 +93928,7 @@ var createStorageRouter = (deps) => {
93512
93928
  body: jsonBody(named("CreateStudioStorageFolderRequest", createStudioStorageFolderRequestSchema), "The full folder path, composed from the breadcrumb and the name the user typed.")
93513
93929
  },
93514
93930
  responses: {
93515
- 200: jsonResponse$1(named("StudioStorageCreateFolderResponse", studioStorageCreateFolderResponseSchema), "The normalised prefix that was created, always ending in `/`."),
93931
+ 200: jsonResponse$2(named("StudioStorageCreateFolderResponse", studioStorageCreateFolderResponseSchema), "The normalised prefix that was created, always ending in `/`."),
93516
93932
  400: errorResponse("The body failed its schema, or the folder name is one the store will not take — `..` traversal and the like (`invalid_folder`)."),
93517
93933
  403: errorResponse("The caller's role does not grant `runs:invoke` — or the prefix is `rag/`, which is reserved for managed RAG originals and cannot be written from the explorer (`rag_storage_prefix_reserved`)."),
93518
93934
  404: errorResponse("No such bucket (`bucket_not_found`).")
@@ -93592,7 +94008,7 @@ var createStorageRouter = (deps) => {
93592
94008
  body: jsonBody(named("StudioStoragePresignRequest", studioStoragePresignRequestSchema), "The key to sign for, and how long the URL should live.")
93593
94009
  },
93594
94010
  responses: {
93595
- 200: jsonResponse$1(named("StudioStoragePresignResponse", studioStoragePresignResponseSchema), "The signed URL and the moment it stops working."),
94011
+ 200: jsonResponse$2(named("StudioStoragePresignResponse", studioStoragePresignResponseSchema), "The signed URL and the moment it stops working."),
93596
94012
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
93597
94013
  404: errorResponse("No such bucket (`bucket_not_found`).")
93598
94014
  }
@@ -93941,7 +94357,7 @@ var createVoiceProviderRouter = (secretsOptions, options = {}) => {
93941
94357
  summary: "Read the LiveKit credentials status",
93942
94358
  description: "`configured` is true only when the URL, the API key and the API secret are all saved. The URL rides back in clear; the key and the secret never do, not even masked. A partial store reads as not configured, with whatever URL it holds.",
93943
94359
  responses: {
93944
- 200: jsonResponse$1(voiceProviderGetResponse, "Whether voice is configured, and the URL."),
94360
+ 200: jsonResponse$2(voiceProviderGetResponse, "Whether voice is configured, and the URL."),
93945
94361
  403: forbiddenResponse("secrets:rotate")
93946
94362
  }
93947
94363
  }), async (c) => c.json(await getVoiceProvider(secretsOptions), 200));
@@ -93955,7 +94371,7 @@ var createVoiceProviderRouter = (secretsOptions, options = {}) => {
93955
94371
  description: "Writes the URL, the API key and the API secret together, in one transaction with the activity-log entry: all three land or none does. The URL must be `wss://` or `https://`, and the key and secret non-empty. The reply is the fresh view, which never carries the key or the secret.",
93956
94372
  request: { body: jsonBody(named("VoiceProviderPutRequest", voiceProviderPutRequestSchema), "The LiveKit URL, API key and API secret.") },
93957
94373
  responses: {
93958
- 200: jsonResponse$1(voiceProviderGetResponse, "The fresh view after the save."),
94374
+ 200: jsonResponse$2(voiceProviderGetResponse, "The fresh view after the save."),
93959
94375
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
93960
94376
  403: forbiddenResponse("secrets:rotate")
93961
94377
  }
@@ -93977,7 +94393,7 @@ var createVoiceProviderRouter = (secretsOptions, options = {}) => {
93977
94393
  summary: "Remove the LiveKit credentials",
93978
94394
  description: "Deletes the URL, the API key and the API secret in one transaction. Removing nothing is not an error: the reply is the view afterwards either way, and only a removal that deleted a row is recorded on the activity log.",
93979
94395
  responses: {
93980
- 200: jsonResponse$1(voiceProviderGetResponse, "The view after the removal."),
94396
+ 200: jsonResponse$2(voiceProviderGetResponse, "The view after the removal."),
93981
94397
  403: forbiddenResponse("secrets:rotate")
93982
94398
  }
93983
94399
  }), async (c) => {
@@ -94008,7 +94424,7 @@ var createVoiceRecordingsRouter = (options) => {
94008
94424
  description: "The latest call recording of a session, from the box bucket the room route's egress uploads to (an audio-only OGG under `voice/recordings/<session_id>/<room>.ogg`). `status` is `recording` until LiveKit reports the egress ended, then `ready` or `failed`; the read asks LiveKit, so a call that just ended reads `ready` on the next read. Only a `ready` recording carries `url`, a presigned read valid one hour and unaudited (D32), and `expires_at`.",
94009
94425
  request: { query: voiceRecordingQuerySchema },
94010
94426
  responses: {
94011
- 200: jsonResponse$1(named("VoiceRecordingResponse", voiceRecordingResponseSchema), "The recording, with a presigned read once it is ready."),
94427
+ 200: jsonResponse$2(named("VoiceRecordingResponse", voiceRecordingResponseSchema), "The recording, with a presigned read once it is ready."),
94012
94428
  400: errorResponse("`session_id` is not a UUID (`invalid_payload`)."),
94013
94429
  403: forbiddenResponse(VOICE_CAPABILITY$2),
94014
94430
  404: errorResponse("The session has no call recording (`recording_not_found`).")
@@ -94354,7 +94770,7 @@ var createVoiceRoomsRouter = (options) => {
94354
94770
  description: "Opens a fresh room `call-<agent>-<8 hex>` and answers the LiveKit URL, the browser's token to join it (it joins within five minutes; a call already running is not cut) and the settings the call runs with. The token carries the room's agent dispatch: joining the room sends the voice worker in with the agent's name, its effective voice settings, the session key `voice-<room>` every turn lands in, the tags each turn's run carries (`channel: voice`, `room`, and `origin`, which the caller states and which defaults to `studio`), the box's public URL the worker posts its turns to, and a call token: the worker's bearer for this call, valid two hours, until the box restarts, on the AG-UI chat of this agent and this session key on the trace relay `POST /voice/v1/traces`, and on this call's identity bootstrap, knowledge prefetch and heard completion at `/voice/v1/calls/{sessionKey}/bootstrap`, `/voice/v1/calls/{sessionKey}/knowledge` and `/voice/v1/calls/{sessionKey}/complete`, nowhere else. The box freezes memory policy, the agent's knowledge settings (`rag`), its refusal phrase (`refusalPhrase`, what the caller hears when a guardrail stops their sentence or the agent's reply) and the zone its turns are told today's date in (`timeZone`), none of them in the dispatch, at room acceptance, so a change saved in Studio reaches the next call; the worker supplies the original SIP participant's phoneNumber before output, or the browser call uses explicit identities supplied on this request. `variables` fill the `{{name}}` placeholders of the agent's greeting before it enters the dispatch (a missing one is left out) and reach every model call of the call as data. The bootstrap response contains no recalled facts or policy. On a box that exports traces (`OTEL_EXPORTER_OTLP_*`), the dispatch also asks the worker to send the call's spans to that relay with that token. The call is recorded to the box bucket as audio-only OGG only when the agent's `recording` voice setting is on (it is off by default) and the box bucket is not on a loopback or private host; a recording that fails never fails the call. The LiveKit API key and secret are never answered. The call token is answered only inside the room token's dispatch, which anyone holding the room token can read.",
94355
94771
  request: { body: jsonBody(named("VoiceRoomRequest", voiceRoomRequestSchema), "The agent to call.") },
94356
94772
  responses: {
94357
- 200: jsonResponse$1(named("VoiceRoomResponse", voiceRoomResponseSchema), "The room, its participant token, and what the dispatch carries."),
94773
+ 200: jsonResponse$2(named("VoiceRoomResponse", voiceRoomResponseSchema), "The room, its participant token, and what the dispatch carries."),
94358
94774
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
94359
94775
  403: forbiddenResponse(VOICE_CAPABILITY$1),
94360
94776
  ...VOICE_AGENT_REFUSALS,
@@ -94458,7 +94874,7 @@ var createVoiceSettingsRouter = (options) => {
94458
94874
  description: "The settings the agent's next call runs with: the fields saved in Studio over the agent's code over the box defaults, the layer each leaf came from (`studio`, `code` or `default`), and `issue`, why a call cannot carry the merged value (`path: message`), or `null`. A saved field can stop combining with the code after a later deploy; the read describes it rather than failing.",
94459
94875
  request: { params: agentPath },
94460
94876
  responses: {
94461
- 200: jsonResponse$1(SETTINGS_RESPONSE, "The effective settings and their origins."),
94877
+ 200: jsonResponse$2(SETTINGS_RESPONSE, "The effective settings and their origins."),
94462
94878
  400: errorResponse("The name is not a deep agent name (`invalid_payload`)."),
94463
94879
  ...VOICE_AGENT_REFUSALS
94464
94880
  }
@@ -94480,7 +94896,7 @@ var createVoiceSettingsRouter = (options) => {
94480
94896
  body: jsonBody(named("StudioVoiceSettingsPatch", studioVoiceSettingsPatchSchema), "The fields to save.")
94481
94897
  },
94482
94898
  responses: {
94483
- 200: jsonResponse$1(SETTINGS_RESPONSE, "The effective settings once saved."),
94899
+ 200: jsonResponse$2(SETTINGS_RESPONSE, "The effective settings once saved."),
94484
94900
  400: errorResponse("The body failed its schema (`invalid_payload`), or the saved fields merged over the agent's code and the box defaults break a cross-field rule (`voice_settings_invalid`, the message names the field)."),
94485
94901
  403: forbiddenResponse(WRITE_CAPABILITY),
94486
94902
  ...VOICE_AGENT_REFUSALS
@@ -94528,7 +94944,7 @@ var createVoiceSettingsRouter = (options) => {
94528
94944
  query: resetQuery
94529
94945
  },
94530
94946
  responses: {
94531
- 200: jsonResponse$1(SETTINGS_RESPONSE, "The effective settings once reset."),
94947
+ 200: jsonResponse$2(SETTINGS_RESPONSE, "The effective settings once reset."),
94532
94948
  400: errorResponse("The name is not a deep agent name, or `fields` names something other than a leaf path (`invalid_payload`), or the fields Studio keeps merged over the agent's code and the box defaults break a cross-field rule (`voice_settings_invalid`, the message names the field)."),
94533
94949
  403: forbiddenResponse(WRITE_CAPABILITY),
94534
94950
  ...VOICE_AGENT_REFUSALS
@@ -94641,7 +95057,7 @@ var createVoiceStatusRouter = (options) => {
94641
95057
  summary: "Check whether the box reaches LiveKit",
94642
95058
  description: "Lists the LiveKit project's rooms with the saved URL, API key and API secret, and answers whether that worked: `reachable: false` covers a refused key, a host that cannot be reached and a check that ran past five seconds alike, and LiveKit's own error is never answered. The answer is kept ten seconds for the same credentials, so a check repeated inside that window answers the first one's result and its `checkedAt`; saving other credentials checks again on the next read. It never says whether a voice worker is registered: no LiveKit API lists them.",
94643
95059
  responses: {
94644
- 200: jsonResponse$1(named("VoiceStatusResponse", voiceStatusResponseSchema), "Whether the box reached LiveKit with its saved credentials."),
95060
+ 200: jsonResponse$2(named("VoiceStatusResponse", voiceStatusResponseSchema), "Whether the box reached LiveKit with its saved credentials."),
94645
95061
  403: forbiddenResponse(VOICE_CAPABILITY),
94646
95062
  503: errorResponse("The LiveKit credentials are not saved on this box, or a saved one cannot be opened with this box's key and must be saved again (`voice_not_configured`).")
94647
95063
  }
@@ -94811,7 +95227,7 @@ var createVoiceCallsRouter = (options) => {
94811
95227
  return c.json(errorEnvelope("voice_lifecycle_failed", "The voice lifecycle operation failed."), 500);
94812
95228
  });
94813
95229
  const callResponses = {
94814
- 200: jsonResponse$1(named("VoiceCallLifecycleResponse", voiceCallLifecycleResponseSchema), "The call operation completed."),
95230
+ 200: jsonResponse$2(named("VoiceCallLifecycleResponse", voiceCallLifecycleResponseSchema), "The call operation completed."),
94815
95231
  400: errorResponse("Invalid bounded call payload."),
94816
95232
  401: errorResponse("Missing, invalid or expired call token."),
94817
95233
  403: errorResponse("Only the call token for this session is admitted."),
@@ -100288,7 +100704,7 @@ var init_trigger_capabilities_handlers = __esmMin((() => {
100288
100704
  tags: [TRIGGER_CAPABILITIES_TAG],
100289
100705
  summary: "List the triggers this box can link a workflow to",
100290
100706
  description: "Every RESOLVABLE trigger across the connector registry, flattened. A descriptor whose poll cannot be resolved — a `strategy` with no registered code — is omitted, so the picker never offers a broken link source. A POLLING or WEBHOOK trigger of a piece connector is listed off its document, its props as `scopeParams`; arming a link on one runs the piece's own `onEnable`, which is where a bundle that cannot load or a credential the provider refuses is reported. A WEBHOOK one is never polled: arming it registers the link's own `/api/hooks/{subscriptionId}/{token}` URL at the provider, so it arms only on a box that knows its public URL.\n\nPresent only on a box with an inbound-trigger intake armed. A box that runs deep agents alone does not serve this route and does not describe it.",
100291
- responses: { 200: jsonResponse$1(triggerCapabilitiesResponseSchema, "The link sources, one row per connector trigger.") }
100707
+ responses: { 200: jsonResponse$2(triggerCapabilitiesResponseSchema, "The link sources, one row per connector trigger.") }
100292
100708
  }), (c) => c.json({ items: listTriggerCapabilities(registry) }, 200));
100293
100709
  };
100294
100710
  }));
@@ -101199,7 +101615,7 @@ var init_intake_runtime = __esmMin((() => {
101199
101615
  }));
101200
101616
  //#endregion
101201
101617
  //#region ../../libs/runtime/workspace-runtime/src/lib/connect-inbound/subscriptions-router.ts
101202
- var TRIGGER_SUBSCRIPTIONS_TAG, looseSubscriptionIdPath, deleteTriggerSubscriptionQuery, mappingCompileError, KEY_REFUSALS, keyCompileRefusal, keyCompileError, keysCompileError, previewKey, editingWaitOrKey, patchKeysRefusal, linkSampleEvent, namedLink, namedLinkSampleEvent, previewSample, armingMappingRefusal, trimmedScope, VERIFY_TOKEN_PARAM, withGeneratedVerifyToken, scopeValuesRefusal, sameScope, unreachableConnectorRefusal, fencedOperationRefusal, armingCursorSeed, webhookArm, armedWebhookUrl, answerEnableRefusal, armingEnable, transientRefusal, ENABLE_RETRYABLE_RESPONSE, disarmingHook, linkNames, createTriggerSubscriptionsRouter;
101618
+ var TRIGGER_SUBSCRIPTIONS_TAG, looseSubscriptionIdPath, deleteTriggerSubscriptionQuery, mappingCompileError, KEY_REFUSALS, keyCompileRefusal, keyCompileError, keysCompileError, compileKey, targetSchemaRefusal, previewKey, editingWaitOrKey, patchKeysRefusal, linkSampleEvent, namedLink, namedLinkSampleEvent, previewSample, armingMappingRefusal, trimmedScope, VERIFY_TOKEN_PARAM, withGeneratedVerifyToken, scopeValuesRefusal, sameScope, unreachableConnectorRefusal, fencedOperationRefusal, armingCursorSeed, webhookArm, armedWebhookUrl, answerEnableRefusal, armingEnable, transientRefusal, ENABLE_RETRYABLE_RESPONSE, disarmingHook, linkNames, createTriggerSubscriptionsRouter;
101203
101619
  var init_subscriptions_router = __esmMin((() => {
101204
101620
  init_src$7();
101205
101621
  init_src$6();
@@ -101250,19 +101666,33 @@ var init_subscriptions_router = __esmMin((() => {
101250
101666
  return compiled.ok ? null : keyCompileRefusal(key, compiled.error);
101251
101667
  };
101252
101668
  keysCompileError = (keys) => keyCompileError(keys.correlationKey, "correlationKey") ?? keyCompileError(keys.conversationKey, "conversationKey");
101253
- previewKey = async (expression, key, sampleData) => {
101669
+ compileKey = (expression, key) => {
101254
101670
  if (!expression?.trim()) return {
101255
101671
  ok: true,
101256
- value: {}
101672
+ value: null
101257
101673
  };
101258
101674
  const compiled = compileMapping(expression);
101259
- if (!compiled.ok) return {
101675
+ if (compiled.ok) return compiled;
101676
+ return {
101260
101677
  ok: false,
101261
101678
  refusal: {
101262
101679
  ...keyCompileRefusal(key, compiled.error),
101263
101680
  error: compiled.error
101264
101681
  }
101265
101682
  };
101683
+ };
101684
+ targetSchemaRefusal = (validated) => !validated.ok && validated.error.kind !== "schema_validation_error" ? {
101685
+ code: validated.error.kind,
101686
+ message: validated.error.message,
101687
+ error: validated.error
101688
+ } : null;
101689
+ previewKey = async (expression, key, sampleData) => {
101690
+ const compiled = compileKey(expression, key);
101691
+ if (!compiled.ok) return compiled;
101692
+ if (compiled.value === null) return {
101693
+ ok: true,
101694
+ value: {}
101695
+ };
101266
101696
  const applied = await applyMapping(compiled.value, sampleData);
101267
101697
  if (!applied.ok) {
101268
101698
  const { label, failed } = KEY_REFUSALS[key];
@@ -101297,6 +101727,10 @@ var init_subscriptions_router = __esmMin((() => {
101297
101727
  event: own,
101298
101728
  source: "link"
101299
101729
  };
101730
+ if (isWebhookTrigger(deps.registry, request.connector, request.trigger)) return {
101731
+ event: void 0,
101732
+ source: "none"
101733
+ };
101300
101734
  if (deps.fetchSampleEvent && link !== null && link.connector === request.connector && link.trigger === request.trigger) {
101301
101735
  const warn = deps.logger?.warn ?? ((message) => console.warn(message));
101302
101736
  let fetched = null;
@@ -101501,7 +101935,7 @@ var init_subscriptions_router = __esmMin((() => {
101501
101935
  description: "Arms a new link and, when it is enabled, its polling repeatable. A link on a piece's webhook trigger arms no repeatable: its `onEnable` registers the link's own `/api/hooks/{subscriptionId}/{token}` URL at the provider, under a token minted for this arm and never returned. Every way a link can be born broken is refused here rather than at tick time: a trigger the registry does not resolve, a trigger missing one of the per-subscription values it declares (a mailbox and a Drive folder are both one of these) or carrying one that does not match the shape the param declares, and a `mapping`, `correlationKey` or `conversationKey` that does not compile as JSONata. A link carries the one key its kind reads: a workflow link resumes a run by `correlationKey`, an agent link continues a conversation by `conversationKey`, and the other kind's key is refused (`invalid_payload`) unless it is blank (null, empty or whitespace). A body that names no `targetKind` is a workflow link here too. An agent link with a `conversationKey` may also carry `debounceMs`, from 1 to 10000 ms: how long it waits after an event for more events of the same conversation, so a burst of chat messages gets one answer. Null or absent answers each event as it arrives. A non-null `debounceMs` is refused (`invalid_payload`) on a workflow link and on an agent link whose `conversationKey` is absent or blank. A link being ARMED is held to more: its `mapping` is run over the trigger's sample event and the result validated against the destination's input schema, so a mapping that could only ever be rejected never arms a poll that would consume events and start nothing. A blank or `null` `mapping` is judged on what the link delivers without one: a workflow link gets the event unchanged; an agent link gets it unchanged when it carries a non-empty text `message`, and otherwise as `{ message }` holding the event as text (a string as is, anything else as JSON), so an agent link with no mapping is not refused for an event whose `message` is missing or not text. Nothing is stored in its place: the stored `mapping` stays `null`. A trigger that declares no sample, or an empty one (`{}`), gives that check nothing to judge, and the link arms; once it has received an event, that event is the sample the check reads on every later patch. A link born with `enabled: false` skips that check — it is made on the patch that turns it on.",
101502
101936
  request: { body: jsonBody(named("RegisterTriggerSubscriptionRequest", registerTriggerSubscriptionRequestSchema), "The capability to poll, the workflow or agent to start, and the optional JSONata in between. `principalKey` defaults to `app` — the integration's own credential rather than a user grant.") },
101503
101937
  responses: {
101504
- 201: jsonResponse$1(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link as it was stored. Its repeatable is armed when `enabled`."),
101938
+ 201: jsonResponse$2(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link as it was stored. Its repeatable is armed when `enabled`."),
101505
101939
  403: forbiddenResponse("config:write"),
101506
101940
  400: errorResponse("The body failed its schema (`invalid_payload`, including a key the link's kind does not read and a `debounceMs` on a workflow link or on an agent link with no `conversationKey`), or the link would be born broken: `unknown_capability` (no such polling trigger on that integration, including when the integration itself is unknown), `scope_required` (a value the trigger declares in `scopeParams` is missing — the message names which, and a mailbox-scoped trigger reports its mailbox this way too), `invalid_scope` (a value that does not match the pattern its param declares), `invalid_mapping`, `invalid_correlation_key` or `invalid_conversation_key` (the JSONata does not compile). A link being ARMED is also refused when its mapping (or, with none, the default its kind delivers) compiles but cannot fill the destination: `mapping_evaluation_failed` (it throws on the trigger's sample event) or `mapping_cannot_produce_input` (the mapped sample violates the destination's input schema — `details.mappingIssues` names the offending paths), and a piece trigger whose own `onEnable` fails: `trigger_enable_failed`, with the piece's message, or the broker's own code when the link's credential could not be minted (`no_token`, `user_authorization_required`, `invalid_grant`, `credential_error`). A piece's webhook link with no event yet is not judged on its mapping: its trigger's declared sample is another account's example, so the check waits for the link's first event. A piece's webhook trigger is refused `webhook_url_unavailable` on a box that knows no public URL its provider could deliver to (`stackbone dev` with no tunnel). Nothing is stored and nothing is armed."),
101507
101941
  503: ENABLE_RETRYABLE_RESPONSE
@@ -101638,11 +102072,11 @@ var init_subscriptions_router = __esmMin((() => {
101638
102072
  path: "/preview",
101639
102073
  tags: [TRIGGER_SUBSCRIPTIONS_TAG],
101640
102074
  summary: "Dry-run a candidate mapping over the trigger sample event",
101641
- description: "Compiles the JSONata, evaluates it over the sample event, then validates the result against the destination input JSON Schema: the workflow's declared schema, or an agent's required `message` string with at least one character. Whitespace is valid. No mapping is written, no repeatable armed, no delivery journalled and no cursor moved, so it is the safe way to author a mapping before saving one.\n\nThe sample event is the trigger descriptor's own, unless `subscriptionId` names a link on the same trigger. A link that has received an event is evaluated over its first real event (`sampleSource: link`). A link with none yet is evaluated over the newest event its provider holds, read once without claiming or delivering it; that event is kept as the link's sample and echoed back as `sampleEvent` (`sampleSource: fetched`). When that read finds nothing or fails (an empty mailbox, an expired credential, a timeout), the answer is over the descriptor's sample (`sampleSource: declared`), never an error.\n\nA blank or `null` `mapping` maps the way the link delivers: on a workflow it is the identity mapping and the sample event comes back unchanged; on an agent the sample event comes back unchanged when it carries a non-empty text `message`, and otherwise as `{ message }` holding the event as text (a string as is, anything else as JSON). `output` is whatever the expression produced, which may be a scalar or an array as easily as an object.\n\nThe key the link's kind reads is evaluated over the same sample event: `correlationKey` on a workflow link (the run a real event would resume), `conversationKey` on an agent link (the conversation it would land in). As on register, a non-blank key of the other kind is refused (`invalid_payload`); a blank one (null, empty or whitespace) is no key.\n\nPresent only on a box with an inbound-trigger intake armed.",
102075
+ description: "Compiles the JSONata, evaluates it over the sample event, then validates the result against the destination input JSON Schema: the workflow's declared schema, or an agent's required `message` string with at least one character. Whitespace is valid. No mapping is written, no repeatable armed, no delivery journalled and no cursor moved, so it is the safe way to author a mapping before saving one.\n\nThe sample event is the trigger descriptor's own, unless `subscriptionId` names a link on the same trigger. A link that has received an event is evaluated over its first real event (`sampleSource: link`). A link with none yet is evaluated over the newest event its provider holds, read once without claiming or delivering it; that event is kept as the link's sample and echoed back as `sampleEvent` (`sampleSource: fetched`). When that read finds nothing or fails (an empty mailbox, an expired credential, a timeout), the answer is over the descriptor's sample (`sampleSource: declared`), never an error.\n\nA webhook trigger has no feed to read and its descriptor's sample comes from another account, so until the named link receives its first event nothing is evaluated: the answer is `{ output: null, issues: [], sampleSource: none }`, with no key and no verdict on the mapping. The same holds when no `subscriptionId` is given. A key or a destination input schema that does not compile is still refused with its 400, since it is wrong on any event.\n\nA blank or `null` `mapping` maps the way the link delivers: on a workflow it is the identity mapping and the sample event comes back unchanged; on an agent the sample event comes back unchanged when it carries a non-empty text `message`, and otherwise as `{ message }` holding the event as text (a string as is, anything else as JSON). `output` is whatever the expression produced, which may be a scalar or an array as easily as an object.\n\nThe key the link's kind reads is evaluated over the same sample event: `correlationKey` on a workflow link (the run a real event would resume), `conversationKey` on an agent link (the conversation it would land in). As on register, a non-blank key of the other kind is refused (`invalid_payload`); a blank one (null, empty or whitespace) is no key.\n\nPresent only on a box with an inbound-trigger intake armed.",
101642
102076
  request: { body: jsonBody(named("PreviewTriggerMappingRequest", previewTriggerMappingRequestSchema), "The capability to read the sample event from, the workflow or agent to validate against, and the candidate `mapping` (plus the optional `correlationKey` or `conversationKey`) to try.") },
101643
102077
  responses: {
101644
- 200: jsonResponse$1(named("TriggerMappingPreview", triggerMappingPreviewResponseSchema), "The mapped output, plus one issue per field the target schema rejected. An empty `issues` with a 200 IS the passing dry-run. `sampleSource` says which sample event was evaluated over, and `sampleEvent` is present only when it was `fetched`. `correlationKey` and `conversationKey` are each present only when the request carried a non-blank expression for it, and `null` when that expression matched nothing."),
101645
- 400: jsonResponse$1(named("TriggerMappingRefusal", triggerMappingRefusalSchema), "The body failed its schema (`invalid_payload`, including a key the link's kind does not read), the capability does not resolve (`unknown_capability`), or the operator's own expression is at fault: `invalid_mapping` / `invalid_correlation_key` / `invalid_conversation_key` (it does not parse) and `mapping_evaluation_failed` / `correlation_key_evaluation_failed` / `conversation_key_evaluation_failed` (it parsed and threw). A `invalid_target_schema` here is the WORKFLOW's declared input schema failing to compile — reported rather than answered as a passing dry-run with no issues."),
102078
+ 200: jsonResponse$2(named("TriggerMappingPreview", triggerMappingPreviewResponseSchema), "The mapped output, plus one issue per field the target schema rejected. An empty `issues` with a 200 IS the passing dry-run, except under `sampleSource: none`: a webhook trigger with no event yet, where nothing was evaluated and `output` is `null`. Otherwise `sampleSource` says which sample event was evaluated over, and `sampleEvent` is present only when it was `fetched`. `correlationKey` and `conversationKey` are each present only when the request carried a non-blank expression for it, and `null` when that expression matched nothing."),
102079
+ 400: jsonResponse$2(named("TriggerMappingRefusal", triggerMappingRefusalSchema), "The body failed its schema (`invalid_payload`, including a key the link's kind does not read), the capability does not resolve (`unknown_capability`), or the operator's own expression is at fault: `invalid_mapping` / `invalid_correlation_key` / `invalid_conversation_key` (it does not parse) and `mapping_evaluation_failed` / `correlation_key_evaluation_failed` / `conversation_key_evaluation_failed` (it parsed and threw). A `invalid_target_schema` here is the WORKFLOW's declared input schema failing to compile — reported rather than answered as a passing dry-run with no issues."),
101646
102080
  404: errorResponse("This box holds no destination of that kind and name (`workflow_not_found` or `agent_not_found`). Platform-installed agents are unavailable as destinations."),
101647
102081
  501: errorResponse("This runtime wired no destination-schema resolver, so there is nothing to validate against (`preview_unavailable`).")
101648
102082
  }
@@ -101672,6 +102106,22 @@ var init_subscriptions_router = __esmMin((() => {
101672
102106
  trigger,
101673
102107
  subscriptionId
101674
102108
  });
102109
+ if (sample.source === "none") {
102110
+ const correlation = compileKey(correlationKey, "correlationKey");
102111
+ if (!correlation.ok) return c.json(correlation.refusal, 400);
102112
+ const conversation = compileKey(conversationKey, "conversationKey");
102113
+ if (!conversation.ok) return c.json(conversation.refusal, 400);
102114
+ const declared = schemaPair.input;
102115
+ if (declared != null && typeof declared === "object") {
102116
+ const schemaRefusal = targetSchemaRefusal(validateAgainstSchema(null, declared));
102117
+ if (schemaRefusal) return c.json(schemaRefusal, 400);
102118
+ }
102119
+ return c.json({
102120
+ output: null,
102121
+ issues: [],
102122
+ sampleSource: sample.source
102123
+ }, 200);
102124
+ }
101675
102125
  const sampleData = sample.event;
101676
102126
  const source = {
101677
102127
  sampleSource: sample.source,
@@ -101703,11 +102153,8 @@ var init_subscriptions_router = __esmMin((() => {
101703
102153
  ...source
101704
102154
  }, 200);
101705
102155
  const validated = validateAgainstSchema(output, inputSchema);
101706
- if (!validated.ok && validated.error.kind !== "schema_validation_error") return c.json({
101707
- code: validated.error.kind,
101708
- message: validated.error.message,
101709
- error: validated.error
101710
- }, 400);
102156
+ const schemaRefusal = targetSchemaRefusal(validated);
102157
+ if (schemaRefusal) return c.json(schemaRefusal, 400);
101711
102158
  const issues = !validated.ok && validated.error.kind === "schema_validation_error" ? validated.error.issues : [];
101712
102159
  return c.json({
101713
102160
  output,
@@ -101725,7 +102172,7 @@ var init_subscriptions_router = __esmMin((() => {
101725
102172
  description: "Runs the reserved `mapping-suggest` workflow inline over the org's model, grounded on the sample event — the first real event of the link `subscriptionId` names when it has one, else the trigger's own — and on the trigger's declared event schema, when it has one, and aimed at the destination input JSON Schema: the workflow's declared schema, or an agent's required `message` string with at least one character. The answer is a pair-list, not a mapping — the editor materialises it, and the operator still has to save.\n\nThe conversation key is offered as a named output too, so a suggestion may target `@correlationKey` instead of a destination input field.\n\nPresent only on a box with an inbound-trigger intake armed.",
101726
102173
  request: { body: jsonBody(named("AutoMapTriggerMappingRequest", autoMapTriggerMappingRequestSchema), "The capability to map FROM and the workflow or agent to map TO.") },
101727
102174
  responses: {
101728
- 200: jsonResponse$1(named("TriggerAutoMapProposal", triggerAutoMapResponseSchema), "The model's proposed pairs, each a target path and the JSONata that fills it. An empty list is a legitimate answer."),
102175
+ 200: jsonResponse$2(named("TriggerAutoMapProposal", triggerAutoMapResponseSchema), "The model's proposed pairs, each a target path and the JSONata that fills it. An empty list is a legitimate answer."),
101729
102176
  403: forbiddenResponse("config:write"),
101730
102177
  400: errorResponse("The body failed its schema (`invalid_payload`), the capability does not resolve (`unknown_capability`), or the workflow declares no input schema, so there is nothing to map to (`no_target_schema`)."),
101731
102178
  404: errorResponse("This box holds no destination of that kind and name (`workflow_not_found` or `agent_not_found`). Platform-installed agents are unavailable as destinations."),
@@ -101787,7 +102234,7 @@ var init_subscriptions_router = __esmMin((() => {
101787
102234
  description: "Runs the options loader of a scope param the capability flags `dynamicOptions: true` (a piece dropdown, such as WhatsApp's business number) through the credential of `principalKey`, and answers the values it may take. Each `value` is the text a link stores under the param and is sent back as is on register: a string the piece answered stays that string, and anything else is its JSON. Writes nothing and arms nothing.\n\nPresent only on a box with an inbound-trigger intake armed.",
101788
102235
  request: { body: jsonBody(named("TriggerScopeOptionsRequest", triggerScopeOptionsRequestSchema), "The capability, the param to load, the principal whose credential loads it, and the scope drafted so far.") },
101789
102236
  responses: {
101790
- 200: jsonResponse$1(named("TriggerScopeOptions", triggerScopeOptionsResponseSchema), "The options, in the piece's order, with its `placeholder` and `disabled` when it gave them. An empty list is a legitimate answer: the account has none."),
102237
+ 200: jsonResponse$2(named("TriggerScopeOptions", triggerScopeOptionsResponseSchema), "The options, in the piece's order, with its `placeholder` and `disabled` when it gave them. An empty list is a legitimate answer: the account has none."),
101791
102238
  403: forbiddenResponse("config:write"),
101792
102239
  400: errorResponse("The body failed its schema (`invalid_payload`), the param is typed rather than filled by the provider (`not_dynamic`), or the options could not be loaded for a reason somebody has to fix first: the broker's own code when the principal's credential could not be issued (`no_token`, `user_authorization_required`, `invalid_grant`, `credential_error`), `scope_options_failed` otherwise, with the piece's message."),
101793
102240
  404: errorResponse("No such trigger on that integration (`unknown_capability`), or the trigger declares no scope param by that name (`unknown_scope_param`)."),
@@ -101839,7 +102286,7 @@ var init_subscriptions_router = __esmMin((() => {
101839
102286
  tags: [TRIGGER_SUBSCRIPTIONS_TAG],
101840
102287
  summary: "List every trigger → workflow link",
101841
102288
  description: "Each link with the rollup of what it has actually done — when it last polled, when it last fired, and how many events it started or turned away. Polling and firing are two separate facts: an inbox with no new mail is a healthy link that has never fired.\n\n`activity.echoLoop` is set when a run the link started wrote back through the link's own connector with an `echoPath`, inside the last hour, and never asked `isOwnEcho` about that connector: the workflow may start itself. One per link, naming the connector, the operation and the run of the newest such write; `null` for a link that is off.\n\nPresent only on a box with an inbound-trigger intake armed.",
101842
- responses: { 200: jsonResponse$1(named("TriggerSubscriptionListResponse", triggerSubscriptionListResponseSchema), "Every link, plus the cadence the scheduler polls them at.") }
102289
+ responses: { 200: jsonResponse$2(named("TriggerSubscriptionListResponse", triggerSubscriptionListResponseSchema), "Every link, plus the cadence the scheduler polls them at.") }
101843
102290
  }), async (c) => c.json({
101844
102291
  subscriptions: await deps.store.list(),
101845
102292
  supportsResumeFromNow: true,
@@ -101853,7 +102300,7 @@ var init_subscriptions_router = __esmMin((() => {
101853
102300
  description: "The operator-owned link WITHOUT the activity rollup the list carries — this read is also the intake tick's own, once per link per poll cadence, and joining the delivery journal on every tick would pay for it forever.\n\nAn armed link on a webhook trigger also carries `webhookUrl`, where the provider POSTs its events, for a piece that registers nothing itself and leaves the operator to paste it in.",
101854
102301
  request: { params: looseSubscriptionIdPath },
101855
102302
  responses: {
101856
- 200: jsonResponse$1(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link."),
102303
+ 200: jsonResponse$2(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link."),
101857
102304
  404: errorResponse("This box holds no link with that id (`not_found`).")
101858
102305
  }
101859
102306
  }), async (c) => {
@@ -101883,7 +102330,7 @@ var init_subscriptions_router = __esmMin((() => {
101883
102330
  body: jsonBody(named("PatchTriggerSubscriptionRequest", patchTriggerSubscriptionRequestSchema), "The knobs a link can change after registration. Every field is optional on its own, but the body may not be empty.")
101884
102331
  },
101885
102332
  responses: {
101886
- 200: jsonResponse$1(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link as it now stands."),
102333
+ 200: jsonResponse$2(named("TriggerSubscriptionResponse", triggerSubscriptionResponseSchema), "The link as it now stands."),
101887
102334
  403: forbiddenResponse("config:write"),
101888
102335
  400: errorResponse("The body failed its schema — including the \"provide at least one field\" rule — (`invalid_payload`), it gives the link the key of the other kind (`invalid_payload`), it would leave a `debounceMs` on a workflow link or on an agent link with no `conversationKey` (`invalid_payload`), or the new JSONata does not compile (`invalid_mapping`, `invalid_correlation_key`, `invalid_conversation_key`). A patch that TURNS THE LINK ON is refused on the mapping it would leave behind when that mapping cannot fill the destination, a blank or `null` one judged on the default its kind delivers, as on register (`mapping_evaluation_failed`, `mapping_cannot_produce_input` — `details.mappingIssues` names the offending paths), and when a piece trigger's own `onEnable` fails (`trigger_enable_failed`, with the piece's message, or the broker's own code when the link's credential could not be minted), or when a piece's webhook trigger has no public URL to register (`webhook_url_unavailable`). A piece's webhook link that has received no event yet is not judged on its mapping; from its first event on, every save that leaves it armed is judged on that event. A `scope` edit is refused on the same two checks the register path runs (`scope_required`, `invalid_scope`), and — when the link stays armed — on the connection fence the new scope resolves onto (`operation_not_permitted`) and on a connector no request could reach (`connector_installation_required`). `resumeFrom` on a body that does not turn the link on is `invalid_payload`, `resumeFrom: \"cursor\"` on a link that has never polled is `no_cursor_to_resume_from` (it has no bookmark to resume from), and `resumeFrom: \"now\"` when no cursor can be minted for \"now\" is `cursor_seed_failed` — clearing the stored one instead would replay the provider's whole history. Nothing is written, `enabled` is left untouched, the previous expression stands and the cursor is not moved."),
101889
102336
  404: errorResponse("This box holds no link with that id (`not_found`)."),
@@ -102039,7 +102486,7 @@ var init_subscriptions_router = __esmMin((() => {
102039
102486
  query: deleteTriggerSubscriptionQuery
102040
102487
  },
102041
102488
  responses: {
102042
- 200: jsonResponse$1(named("TriggerSubscriptionDeleteResponse", triggerSubscriptionDeleteResponseSchema), "The link is gone and its repeatable is disarmed."),
102489
+ 200: jsonResponse$2(named("TriggerSubscriptionDeleteResponse", triggerSubscriptionDeleteResponseSchema), "The link is gone and its repeatable is disarmed."),
102043
102490
  403: forbiddenResponse("config:write"),
102044
102491
  404: errorResponse("This box holds no link with that id (`not_found`)."),
102045
102492
  502: errorResponse("The piece trigger's own `onDisable` failed (`trigger_disable_failed`), so whatever it registered at the provider may still be there. Nothing was removed and the link is still armed; retry, or send `?force=true` to delete it anyway.")
@@ -102189,7 +102636,7 @@ var init_webhook_router = __esmMin((() => {
102189
102636
  body: describedJsonBody({}, "The provider's event, as it sends it. JSON is parsed, a form is read as its fields, and anything else reaches the trigger as text.")
102190
102637
  },
102191
102638
  responses: {
102192
- 200: jsonResponse$1(webhookReceiptSchema, "Every event in the delivery reached the journal."),
102639
+ 200: jsonResponse$2(webhookReceiptSchema, "Every event in the delivery reached the journal."),
102193
102640
  400: errorResponse("The body is declared JSON and does not parse (`invalid_payload`)."),
102194
102641
  404: errorResponse("No link is armed at this URL (`not_found`): the id names no link, the link is off or is not a webhook trigger, or the token is not its own. One answer for all of them."),
102195
102642
  413: errorResponse("The body is over the delivery cap (`payload_too_large`)."),
@@ -102833,7 +103280,7 @@ var init_destinations_router = __esmMin((() => {
102833
103280
  description: "The SDK's call, signed with the box HMAC like `POST /api/connect/execute`. The SDK has already staged the bytes in the box's object store; the body names the key, never the bytes. The box resolves the output's binding, runs the connector's file writer through the action executor — so the connection fence applies to every call the sequence makes — and answers a RECEIPT.\n\nA receipt, not an error, because by the time a run reaches its output the expensive work is done. `filed: true` carries the provider's file id. `filed: false` carries the parked row: `unbound` when nobody has bound this output yet, `write_failed` when the provider refused — the row keeps the staged key so the document is not lost, and the installation screen lists it.\n\nThe one error is a binding this box cannot act on (`destination_misconfigured`): a connector with no file writer, or a destination with no `id`. Retrying cannot fix that, so nothing is parked for it.",
102834
103281
  request: { body: jsonBody(named("OutputWriteRequest", outputWriteRequestSchema), "Which output of which workflow, the name and type the document gets, and the staged key.") },
102835
103282
  responses: {
102836
- 200: jsonResponse$1(named("OutputWriteReceipt", outputWriteReceiptSchema), "The document is at the provider (`filed`), or kept on this box (`parked`)."),
103283
+ 200: jsonResponse$2(named("OutputWriteReceipt", outputWriteReceiptSchema), "The document is at the provider (`filed`), or kept on this box (`parked`)."),
102837
103284
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
102838
103285
  401: AGENT_WIRE_401,
102839
103286
  409: errorResponse("The binding cannot be acted on (`destination_misconfigured`): its connector declares no file writer, names one this box has no code for, or stores a destination with no `id`. Fix the binding; nothing was parked.")
@@ -102861,7 +103308,7 @@ var init_destinations_router = __esmMin((() => {
102861
103308
  description: "The SDK's other call, signed with the box HMAC exactly as `POST /write` is. The box resolves the action's binding, builds the same executor `/api/connect/execute` builds, and runs ONE operation through it.\n\nWhat crosses is smaller than a write's body because the decisions split differently: the WORKFLOW names the operation and composes the arguments, and the operator picked the connection and nothing else. There is no destination, no staged bytes and no file name.\n\nAnswers a RESULT, not an error, for the reason the write answers a receipt: by the time a run reaches its action the expensive work is done. `called: true` carries the operation's own output. `called: false` carries a refusal — `unbound` when nobody has bound this action yet, and otherwise the BROKER's own code and message, forwarded untouched. PLA-1093's connection fence lands in that arm, naming the operation attempted and the ones the connection permits, and it refuses before any credential is minted.",
102862
103309
  request: { body: jsonBody(named("ActionCallRequest", actionCallRequestSchema), "Which action of which workflow, the operation to run, and its arguments.") },
102863
103310
  responses: {
102864
- 200: jsonResponse$1(named("ActionCallResult", actionCallResultSchema), "The operation ran (`called`), or nothing was called and the refusal says why."),
103311
+ 200: jsonResponse$2(named("ActionCallResult", actionCallResultSchema), "The operation ran (`called`), or nothing was called and the refusal says why."),
102865
103312
  400: errorResponse("The body failed its schema (`invalid_payload`)."),
102866
103313
  401: AGENT_WIRE_401
102867
103314
  }
@@ -102875,7 +103322,7 @@ var init_destinations_router = __esmMin((() => {
102875
103322
  tags: [DESTINATIONS_TAG],
102876
103323
  summary: "List every output binding",
102877
103324
  description: "Each declared output an operator has bound: which workflow, which output, which connector and grant, and the destination as the operator stored it.\n\nPresent only on a box with an inbound-trigger intake armed.",
102878
- responses: { 200: jsonResponse$1(named("DestinationBindingListResponse", destinationBindingListResponseSchema), "Every binding on this box.") }
103325
+ responses: { 200: jsonResponse$2(named("DestinationBindingListResponse", destinationBindingListResponseSchema), "Every binding on this box.") }
102879
103326
  }), async (c) => c.json({ bindings: await deps.store.listBindings() }, 200));
102880
103327
  app.openapi(createRoute({
102881
103328
  method: "post",
@@ -102885,7 +103332,7 @@ var init_destinations_router = __esmMin((() => {
102885
103332
  description: "Decides how one of a workflow's declarations reaches the outside. WHICH rules apply comes from what the workspace declared the name as, never from the body: a `file` needs a connector that can file and a destination it accepts; an `action` needs a connector this box knows and NO destination, because the operator picks the connection and nothing else. A name nothing declares takes the file rules.\n\nAn UPSERT on the (workflow, output) pair: binding an output that is already bound replaces the binding and keeps its id, because the later decision is the one that holds. `principalKey` defaults to `app`.\n\nThe connector must be one `GET /api/destinations/capabilities` lists — a name this box does not know, or one with no file writer, is refused here rather than days later on a run nobody is watching — and `principalKey` must be a grant the broker can resolve.\n\n`destination` is stored as the operator sent it; the one shape rule is an `id` string, which is what the connector's writer receives. Every other key is display metadata the box never reads.",
102886
103333
  request: { body: jsonBody(named("UpsertDestinationBindingRequest", upsertDestinationBindingRequestSchema), "The output to bind, the connector and grant to write through, and where.") },
102887
103334
  responses: {
102888
- 201: jsonResponse$1(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding as it was stored."),
103335
+ 201: jsonResponse$2(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding as it was stored."),
102889
103336
  400: errorResponse("The body failed its schema (`invalid_payload`), names a connector this box does not know or that cannot file a document (`unknown_connector`, `connector_cannot_file`), names a grant the broker cannot resolve (`invalid_principal_key`), or sends a destination the declaration does not accept — missing or malformed for a `file` (`destination_required`, `invalid_destination`), present at all for an `action` (`destination_not_accepted`).")
102890
103337
  }
102891
103338
  }), async (c) => {
@@ -102909,7 +103356,7 @@ var init_destinations_router = __esmMin((() => {
102909
103356
  summary: "Read one output binding",
102910
103357
  request: { params: looseBindingIdPath },
102911
103358
  responses: {
102912
- 200: jsonResponse$1(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding."),
103359
+ 200: jsonResponse$2(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding."),
102913
103360
  404: errorResponse("This box holds no binding with that id (`not_found`).")
102914
103361
  }
102915
103362
  }), async (c) => {
@@ -102927,7 +103374,7 @@ var init_destinations_router = __esmMin((() => {
102927
103374
  body: jsonBody(named("PatchDestinationBindingRequest", patchDestinationBindingRequestSchema), "The fields to change.")
102928
103375
  },
102929
103376
  responses: {
102930
- 200: jsonResponse$1(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding after the edit."),
103377
+ 200: jsonResponse$2(named("DestinationBindingResponse", destinationBindingResponseSchema), "The binding after the edit."),
102931
103378
  400: errorResponse("The body failed its schema (`invalid_payload`), carried none of the three editable fields, named a connector this box cannot file through (`unknown_connector`, `connector_cannot_file`), named a grant the broker cannot resolve (`invalid_principal_key`), or sent a destination the declaration does not accept (`destination_required`, `invalid_destination`, `destination_not_accepted`)."),
102932
103379
  404: errorResponse("This box holds no binding with that id (`not_found`).")
102933
103380
  }
@@ -102961,7 +103408,7 @@ var init_destinations_router = __esmMin((() => {
102961
103408
  description: "Removes the binding. The next write for that output parks as `unbound` instead of failing. A repeat delete answers 404, so a client can tell \"I removed it\" from \"it was already gone\".",
102962
103409
  request: { params: looseBindingIdPath },
102963
103410
  responses: {
102964
- 200: jsonResponse$1(named("DestinationBindingDeleteResponse", destinationBindingDeleteResponseSchema), "The binding is gone."),
103411
+ 200: jsonResponse$2(named("DestinationBindingDeleteResponse", destinationBindingDeleteResponseSchema), "The binding is gone."),
102965
103412
  404: errorResponse("This box holds no binding with that id (`not_found`).")
102966
103413
  }
102967
103414
  }), async (c) => {
@@ -102983,7 +103430,7 @@ var init_destinations_router = __esmMin((() => {
102983
103430
  tags: [DESTINATIONS_TAG],
102984
103431
  summary: "List the documents waiting to be filed",
102985
103432
  description: "Every output a run produced that has not reached its destination, newest first: `unbound` rows waiting for a binding, `write_failed` rows carrying what the provider said and how many attempts have been made. A row leaves this list once a later attempt files it — `POST {id}/drain` is that attempt.",
102986
- responses: { 200: jsonResponse$1(named("ParkedOutputListResponse", parkedOutputListResponseSchema), "What is still waiting.") }
103433
+ responses: { 200: jsonResponse$2(named("ParkedOutputListResponse", parkedOutputListResponseSchema), "What is still waiting.") }
102987
103434
  }), async (c) => c.json({ parked: (await deps.store.listParked()).map(parkedListRow) }, 200));
102988
103435
  app.openapi(createRoute({
102989
103436
  method: "get",
@@ -102993,7 +103440,7 @@ var init_destinations_router = __esmMin((() => {
102993
103440
  description: "The values behind a parked call, one row at a time (PLA-1273).\n\nIts own route because the arguments ARE the write: a workflow composes them freely, `stackbone.secrets` included, so handing them to an operator deciding whether to press Drain is a disclosure and not a lookup. The list route carries the argument NAMES and stops there — the operator sees the shape of the call unasked, and asks for what is inside it.\n\nEvery answered read leaves a `destination.reveal_args` row on the box's activity trail, naming the workflow, the output, the operation and the person. Never a key and never a value: a trail carrying the argument names of a call would put half the disclosure back into the record that exists to account for it.\n\n`{}` is a real answer, not an empty one. A document was never a call, and an action whose run composed nothing is still a row an operator can open — the kind of the row is not a reason to fail a read.",
102994
103441
  request: { params: looseBindingIdPath },
102995
103442
  responses: {
102996
- 200: jsonResponse$1(named("ParkedOutputArgsResponse", parkedOutputArgsResponseSchema), "The argument object the run composed, whole."),
103443
+ 200: jsonResponse$2(named("ParkedOutputArgsResponse", parkedOutputArgsResponseSchema), "The argument object the run composed, whole."),
102997
103444
  404: errorResponse("No parked row with that id on this box (`not_found`).")
102998
103445
  }
102999
103446
  }), async (c) => {
@@ -103022,7 +103469,7 @@ var init_destinations_router = __esmMin((() => {
103022
103469
  tags: [DESTINATIONS_TAG],
103023
103470
  summary: "List what an output can be bound through",
103024
103471
  description: "Every connector on this box that can file a document — one declaring a `fileWriter` — and the fields an operator must fill in to say where it lands.\n\nThese are the connector's OWN `destinationParams`, not a trigger's scope params: a binding is not a trigger, and a write-only provider has no trigger to borrow from.\n\n`pattern` is deliberately absent, as it is on the trigger capabilities surface. The regex is an escape boundary the box enforces on the way in; a client holding a copy is a client that can disagree with the box about what is valid.",
103025
- responses: { 200: jsonResponse$1(named("DestinationCapabilityListResponse", destinationCapabilityListResponseSchema), "What this box can file through, and what each needs.") }
103472
+ responses: { 200: jsonResponse$2(named("DestinationCapabilityListResponse", destinationCapabilityListResponseSchema), "What this box can file through, and what each needs.") }
103026
103473
  }), async (c) => c.json({ capabilities: destinationCapabilities(deps.definitions) }, 200));
103027
103474
  app.openapi(createRoute({
103028
103475
  method: "post",
@@ -103032,7 +103479,7 @@ var init_destinations_router = __esmMin((() => {
103032
103479
  description: "The operator's button, one row at a time. Deliberately NOT a sweep: this happens once per customer, at setup, with a human present — which is exactly when a failure is read and when nothing should be happening behind anyone's back.\n\nWHICH KIND the row is decides what runs: a document goes through the file writer, an action through the action caller (PLA-1253). The operator presses one button either way, because the question they are answering is one question.\n\nThe binding is re-read NOW, not as it was when the row parked, so an `unbound` row goes out as soon as its output has been bound.\n\nAnswers a RECEIPT, and the two shapes differ in one field. A document lands as `filed: true` with the provider's id; an action as `fired: true` with whatever the operation returned, which may be a list, a boolean, or nothing. The failing arm is identical: the PROVIDER'S OWN message and the row's attempt count — the operator pressing the button is the person who can act on \"this connection does not permit createIssue\", so it is not flattened into a status code.",
103033
103480
  request: { params: looseBindingIdPath },
103034
103481
  responses: {
103035
- 200: jsonResponse$1(named("ParkedDrainReceipt", z.union([outputDrainReceiptSchema, actionDrainReceiptSchema])), "It is at the provider, or it is still here and this is why."),
103482
+ 200: jsonResponse$2(named("ParkedDrainReceipt", z.union([outputDrainReceiptSchema, actionDrainReceiptSchema])), "It is at the provider, or it is still here and this is why."),
103036
103483
  404: errorResponse("No parked row with that id on this box (`not_found`)."),
103037
103484
  409: errorResponse("Nothing to send: the row already went out or another drain has it right now (`already_filed`, `already_fired`), nobody has bound it yet (`unbound`), or its binding cannot be acted on (`destination_misconfigured`). None is fixed by pressing again.")
103038
103485
  }
@@ -103237,7 +103684,7 @@ var init_discovery_handlers = __esmMin((() => {
103237
103684
  agents: [...manifest.agents, ...deepAgentManifestEntries(ctx.getDeepAgentRegistry?.())]
103238
103685
  };
103239
103686
  };
103240
- const discoveryResponses = { 200: jsonResponse$1(discoveryResponseSchema, "Everything this workspace serves: its in-process deep agents, and its workflows.") };
103687
+ const discoveryResponses = { 200: jsonResponse$2(discoveryResponseSchema, "Everything this workspace serves: its in-process deep agents, and its workflows.") };
103241
103688
  app.openapi(createRoute({
103242
103689
  method: "get",
103243
103690
  path: "/api/discovery",
@@ -103260,7 +103707,7 @@ var init_discovery_handlers = __esmMin((() => {
103260
103707
  tags: [WORKFLOWS_TAG],
103261
103708
  summary: "List the workflows this box serves",
103262
103709
  description: "The catalog rows a creator is offered by default. A workflow whose compile FAILED keeps its row, marked `degraded` with the compiler's own message, rather than vanishing from the list. The platform's own entries (`rag-ingest`, `mapping-suggest`) are hidden here — `GET /api/catalog` returns them flagged.",
103263
- responses: { 200: jsonResponse$1(workflowListResponseSchema, "The workflow rows.") }
103710
+ responses: { 200: jsonResponse$2(workflowListResponseSchema, "The workflow rows.") }
103264
103711
  }), (c) => c.json({ items: ctx.set.manifest().workflows }, 200));
103265
103712
  app.openapi(createRoute({
103266
103713
  method: "get",
@@ -103270,8 +103717,8 @@ var init_discovery_handlers = __esmMin((() => {
103270
103717
  description: "Resolved against the UNFILTERED catalog rows, so a platform-owned entry the catalog lists can be opened. `steps` is heuristic — the WDK manifest keys steps by source FILE, so a step imported from a shared module is missing.",
103271
103718
  request: { params: workflowNamePath },
103272
103719
  responses: {
103273
- 200: jsonResponse$1(workflowDetailSchema, "The workflow row."),
103274
- 404: jsonResponse$1(workflowNotFoundBodySchema, "This workspace declares no workflow by that name. The names it does declare are in `details.known`.")
103720
+ 200: jsonResponse$2(workflowDetailSchema, "The workflow row."),
103721
+ 404: jsonResponse$2(workflowNotFoundBodySchema, "This workspace declares no workflow by that name. The names it does declare are in `details.known`.")
103275
103722
  }
103276
103723
  }), (c) => {
103277
103724
  const { name } = c.req.valid("param");
@@ -103291,9 +103738,9 @@ var init_discovery_handlers = __esmMin((() => {
103291
103738
  description: "The input/output JSON Schema the build folded onto this workflow. A known workflow that declared none answers `{ input: null, output: null }` — the Playground falls back to a raw-JSON field.",
103292
103739
  request: { params: workflowNamePath },
103293
103740
  responses: {
103294
- 200: jsonResponse$1(workflowSchemaPairSchema, "The folded contract, either half nullable."),
103295
- 404: jsonResponse$1(workflowNotFoundBodySchema, "No workflow by that name."),
103296
- 503: jsonResponse$1(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed. Never a 404: the entry exists, so the reason is the compiler's own message rather than \"unknown workflow\".")
103741
+ 200: jsonResponse$2(workflowSchemaPairSchema, "The folded contract, either half nullable."),
103742
+ 404: jsonResponse$2(workflowNotFoundBodySchema, "No workflow by that name."),
103743
+ 503: jsonResponse$2(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed. Never a 404: the entry exists, so the reason is the compiler's own message rather than \"unknown workflow\".")
103297
103744
  }
103298
103745
  }), (c) => {
103299
103746
  const { name } = c.req.valid("param");
@@ -103342,12 +103789,12 @@ var init_start_handlers = __esmMin((() => {
103342
103789
  body: describedJsonBody(workflowInputBodySchema, "The workflow's own input. Validated against the contract the creator declared (`GET /api/workflows/{name}/schema`), not against a box schema. Omit it entirely for a workflow that takes none.")
103343
103790
  },
103344
103791
  responses: {
103345
- 200: jsonResponse$1(workflowStartReceiptSchema, "The run was enqueued — or, for a `serial` workflow whose lock is held, the trigger was queued behind the active run."),
103346
- 400: jsonResponse$1(workflowInputInvalidBodySchema, "The workflow's own declared validator rejected this input. NO run is started."),
103347
- 403: jsonResponse$1(z.union([invocationDeniedBodySchema, errorEnvelopeSchema]), "Refused before the handler ran, for one of two reasons. `invocation_not_permitted`: the caller is a workspace API key whose grants do not name this workflow under `workflow:start` (`http/invocation-guard.ts`). `forbidden`: the caller's verified identity does not hold `runs:invoke` — only reachable on the browser-direct datapath, since a proxy-signed call was authorized before it was signed. No run is started either way."),
103348
- 404: jsonResponse$1(workflowNotFoundBodySchema, "This workspace declares no workflow by that name. The names it does declare are in `details.known`."),
103349
- 422: jsonResponse$1(workflowGuardrailBlockedBodySchema, "The operator's own guardrail policy declined this payload. A clean 4xx, never a 500 — the request was understood, and the body names WHICH rule refused it."),
103350
- 503: jsonResponse$1(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed. Never the 404 a typo gets: the entry exists, so the body carries the compiler's own message.")
103792
+ 200: jsonResponse$2(workflowStartReceiptSchema, "The run was enqueued — or, for a `serial` workflow whose lock is held, the trigger was queued behind the active run."),
103793
+ 400: jsonResponse$2(workflowInputInvalidBodySchema, "The workflow's own declared validator rejected this input. NO run is started."),
103794
+ 403: jsonResponse$2(z.union([invocationDeniedBodySchema, errorEnvelopeSchema]), "Refused before the handler ran, for one of two reasons. `invocation_not_permitted`: the caller is a workspace API key whose grants do not name this workflow under `workflow:start` (`http/invocation-guard.ts`). `forbidden`: the caller's verified identity does not hold `runs:invoke` — only reachable on the browser-direct datapath, since a proxy-signed call was authorized before it was signed. No run is started either way."),
103795
+ 404: jsonResponse$2(workflowNotFoundBodySchema, "This workspace declares no workflow by that name. The names it does declare are in `details.known`."),
103796
+ 422: jsonResponse$2(workflowGuardrailBlockedBodySchema, "The operator's own guardrail policy declined this payload. A clean 4xx, never a 500 — the request was understood, and the body names WHICH rule refused it."),
103797
+ 503: jsonResponse$2(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed. Never the 404 a typo gets: the entry exists, so the body carries the compiler's own message.")
103351
103798
  }
103352
103799
  }), async (c) => {
103353
103800
  const { name } = c.req.valid("param");
@@ -103440,12 +103887,12 @@ var init_chat_handlers = __esmMin((() => {
103440
103887
  },
103441
103888
  responses: {
103442
103889
  200: sseResponse("The turn, frame by frame.", CHAT_FRAMES),
103443
- 400: jsonResponse$1(workflowInputInvalidBodySchema, "The workflow's own declared validator rejected this input. NO run is started, and no stream opens."),
103444
- 403: jsonResponse$1(invocationDeniedBodySchema, "The caller is a workspace API key whose grants do not name this workflow under `workflow:chat` — its own action, so a key that may `/start` this workflow is still refused here. Answered by `http/invocation-guard.ts` BEFORE this handler, and identical for a name that does not exist."),
103445
- 404: jsonResponse$1(workflowNotFoundBodySchema, "No workflow by that name."),
103446
- 409: jsonResponse$1(workflowSerialEntryBodySchema, "The workflow is `serial`. Fire it through `POST /api/workflows/{name}/start` instead: a serial run is queued behind the active one, which a held-open stream cannot wait for."),
103447
- 422: jsonResponse$1(workflowGuardrailBlockedBodySchema, "The operator's own guardrail policy declined this payload — the same border `/start` crosses, re-run here so the streaming door refuses what the durable one refuses."),
103448
- 503: jsonResponse$1(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed.")
103890
+ 400: jsonResponse$2(workflowInputInvalidBodySchema, "The workflow's own declared validator rejected this input. NO run is started, and no stream opens."),
103891
+ 403: jsonResponse$2(invocationDeniedBodySchema, "The caller is a workspace API key whose grants do not name this workflow under `workflow:chat` — its own action, so a key that may `/start` this workflow is still refused here. Answered by `http/invocation-guard.ts` BEFORE this handler, and identical for a name that does not exist."),
103892
+ 404: jsonResponse$2(workflowNotFoundBodySchema, "No workflow by that name."),
103893
+ 409: jsonResponse$2(workflowSerialEntryBodySchema, "The workflow is `serial`. Fire it through `POST /api/workflows/{name}/start` instead: a serial run is queued behind the active one, which a held-open stream cannot wait for."),
103894
+ 422: jsonResponse$2(workflowGuardrailBlockedBodySchema, "The operator's own guardrail policy declined this payload — the same border `/start` crosses, re-run here so the streaming door refuses what the durable one refuses."),
103895
+ 503: jsonResponse$2(workflowNotReadyBodySchema, "The workflow is REGISTERED but cannot answer — its compile failed, or no runtime is armed.")
103449
103896
  }
103450
103897
  }), async (c) => {
103451
103898
  const { name } = c.req.valid("param");
@@ -103570,7 +104017,7 @@ var init_hook_handlers = __esmMin((() => {
103570
104017
  body: describedJsonBody({ description: "Whatever the parked hook was written to receive." }, "What the parked hook receives. Omit it for a hook whose answer is simply \"continue\".")
103571
104018
  },
103572
104019
  responses: {
103573
- 200: jsonResponse$1(hookResumedResponseSchema, "The run was woken. The F5 sweep is nudged, so the projected run reopens `interrupted → running` without waiting for the next interval."),
104020
+ 200: jsonResponse$2(hookResumedResponseSchema, "The run was woken. The F5 sweep is nudged, so the projected run reopens `interrupted → running` without waiting for the next interval."),
103574
104021
  400: errorResponse("`invalid_token` — the token segment was empty or whitespace."),
103575
104022
  404: errorResponse("`hook_not_found` — no PARKED hook matches that token."),
103576
104023
  500: errorResponse("`resume_failed` — the World rejected the resume. `details.cause` carries its message.")
@@ -103601,7 +104048,7 @@ var init_hook_handlers = __esmMin((() => {
103601
104048
  description: "The ONLY World-reading route on the box, joined by `runs.trace_id` (= the World run id). Raw `defineHook` hooks are listed too, not just helper-created ones.\n\nRun STATE is deliberately not here — it comes from the projection at `GET /api/runs/{id}`. `resumeKind` is `null` for a hook parked on an EXTERNAL signal, and a run detail must not offer a manual approve for one; a resume-kind lookup that fails degrades every hook to `null` rather than guessing.",
103602
104049
  request: { params: z.object({ traceId: z.string().openapi({ description: "The run's World run id — `runs.trace_id`, not `runs.id`. Blank is refused with `invalid_trace_id`." }) }) },
103603
104050
  responses: {
103604
- 200: jsonResponse$1(pendingHooksResponseSchema, "The run's pending hooks. An empty list means nothing is parked — never that the run is unknown."),
104051
+ 200: jsonResponse$2(pendingHooksResponseSchema, "The run's pending hooks. An empty list means nothing is parked — never that the run is unknown."),
103605
104052
  400: errorResponse("`invalid_trace_id` — the segment was empty or whitespace."),
103606
104053
  500: errorResponse("`hooks_read_failed` — the World could not be read. `details.cause` carries its message.")
103607
104054
  }
@@ -104321,7 +104768,7 @@ var buildEmulatorApp = (opts, hooks = {}) => {
104321
104768
  security: PUBLIC_ROUTE_SECURITY,
104322
104769
  summary: "Stackbone Agent Protocol handshake",
104323
104770
  description: "The protocol version this box speaks, the oldest it still serves, the capabilities it advertises and the build that answers. The SDK reads it before calling any gated surface.",
104324
- responses: { 200: jsonResponse$1(named("ContractResponse", contractResponseSchema), "The handshake this box answers with.") }
104771
+ responses: { 200: jsonResponse$2(named("ContractResponse", contractResponseSchema), "The handshake this box answers with.") }
104325
104772
  }), (c) => c.json(buildContractResponse({ sdkVersion: opts.sdkVersion }), 200));
104326
104773
  app.get("/", (c) => c.html(renderStudioStub({
104327
104774
  agentSlug: opts.agent.slug,
@@ -104336,7 +104783,7 @@ var buildEmulatorApp = (opts, hooks = {}) => {
104336
104783
  security: PUBLIC_ROUTE_SECURITY,
104337
104784
  summary: "Which host this box is",
104338
104785
  description: "The cheap dev-facing health read. `host` is the tag the box was booted with; `emulator` is the legacy dev marker and is present on the dev host only.",
104339
- responses: { 200: jsonResponse$1(boxHealthSchema, "The host this box answers as.") }
104786
+ responses: { 200: jsonResponse$2(boxHealthSchema, "The host this box answers as.") }
104340
104787
  }), (c) => {
104341
104788
  const body = {
104342
104789
  status: "ok",
@@ -104354,7 +104801,7 @@ var buildEmulatorApp = (opts, hooks = {}) => {
104354
104801
  security: PUBLIC_ROUTE_SECURITY,
104355
104802
  summary: "Liveness",
104356
104803
  description: "Answers the moment the server binds — no probe, no await. Fly's machine liveness check reads it.",
104357
- responses: { 200: jsonResponse$1(z.object({ status: z.literal("ok") }), "The server is listening.") }
104804
+ responses: { 200: jsonResponse$2(z.object({ status: z.literal("ok") }), "The server is listening.") }
104358
104805
  }), (c) => c.json({ status: "ok" }, 200));
104359
104806
  app.openapi(createRoute({
104360
104807
  method: "get",
@@ -104364,8 +104811,8 @@ var buildEmulatorApp = (opts, hooks = {}) => {
104364
104811
  summary: "Subsystem health",
104365
104812
  description: "Runs every probe this host declared, in parallel, and reports each one. A host that declares no subsystems answers `checks:{}` — read that as \"nothing to report\", not as proof of health.",
104366
104813
  responses: {
104367
- 200: jsonResponse$1(SUBSYSTEM_HEALTH, "Every declared subsystem passed."),
104368
- 503: jsonResponse$1(SUBSYSTEM_HEALTH, "At least one subsystem is failing.")
104814
+ 200: jsonResponse$2(SUBSYSTEM_HEALTH, "Every declared subsystem passed."),
104815
+ 503: jsonResponse$2(SUBSYSTEM_HEALTH, "At least one subsystem is failing.")
104369
104816
  }
104370
104817
  }), async (c) => {
104371
104818
  const results = await Promise.all(probes.map(async ([name, probe]) => {
@@ -104400,7 +104847,7 @@ var buildEmulatorApp = (opts, hooks = {}) => {
104400
104847
  summary: "Injected service endpoints",
104401
104848
  description: "The Postgres, object-storage and agent-process endpoints this box injected into the agent, read back. A creator uses it to point `psql` or an S3 client at the same stack their agent is running against. No secret is in the answer: the Postgres password is removed from the DSN, and the storage secret key is left out (the access key is in).",
104402
104849
  responses: {
104403
- 200: jsonResponse$1(boxServicesSchema, "The endpoints this box handed the agent."),
104850
+ 200: jsonResponse$2(boxServicesSchema, "The endpoints this box handed the agent."),
104404
104851
  403: errorResponse("The caller's role does not grant `db:query`. Only a browser-direct identity token carries a role; a signed call and the self-host operator token are not checked against one.")
104405
104852
  }
104406
104853
  }), (c) => c.json({
@@ -105194,7 +105641,7 @@ var calls_workflow_ts_default = "import { z } from \"zod\";\nimport { streamDeep
105194
105641
  var plain_workflow_ts_default = "import { z } from \"zod\";\n\n/**\n * Workflow \"{{name}}\" — a durable pipeline.\n *\n * Each `\"use step\"` is a durable checkpoint: kill the runtime mid-run and it\n * resumes from the last completed step instead of replaying everything.\n */\n\n// THE contract — sibling exports next to the bare workflow function. The build\n// harvests these (by convention) into the manifest + a live validator. The\n// parameter type is DERIVED from `inputSchema` and the RETURN type from\n// `outputSchema` (both `z.infer`), so neither side can drift from the contract\n// the build harvests. Without the return annotation the schema is published to\n// Studio and the generated types and never compared to what the body returns\n// (PLA-1272).\nexport const inputSchema = z.object({\n message: z.string().describe(\"The input to process\"),\n});\n\nexport const outputSchema = z.object({\n message: z.string(),\n processed: z.boolean(),\n});\n\ntype {{Name}}Input = z.infer<typeof inputSchema>;\n\nexport async function {{export}}(input: {{Name}}Input): Promise<z.infer<typeof outputSchema>> {\n \"use workflow\";\n\n const result = await processMessage(input); // step 1 — durable checkpoint\n return result;\n}\n\nasync function processMessage(input: {{Name}}Input) {\n \"use step\";\n // Replace this with the deterministic work of your step (DB write, HTTP call,\n // an LLM call, …). Returning a value records it as the step's checkpoint.\n return { message: input.message, processed: true };\n}\n";
105195
105642
  //#endregion
105196
105643
  //#region src/services/scaffold/template-loader.ts
105197
- var SDK_TEMPLATE_VERSION = "0.10.3";
105644
+ var SDK_TEMPLATE_VERSION = "0.10.4";
105198
105645
  var PREFIX = "./templates/";
105199
105646
  var templates = new Map(Object.entries(/* @__PURE__ */ Object.assign({
105200
105647
  "./templates/deep-agents/index.ts.tmpl": index_ts_default,
@@ -114417,7 +114864,7 @@ var readLocalManifest = async (ctx) => {
114417
114864
  capabilities: [],
114418
114865
  deviations: ["agent.yaml `capabilities` is not a list — skipped capability validation."]
114419
114866
  };
114420
- const known = new Set(CAPABILITIES$1);
114867
+ const known = new Set(CAPABILITIES$2);
114421
114868
  const capabilities = [];
114422
114869
  const deviations = [];
114423
114870
  for (const entry of declared) if (typeof entry === "string" && known.has(entry)) capabilities.push(entry);