@stackbone/cli 0.9.1 → 0.9.2

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
@@ -3229,7 +3229,7 @@ var init_studio_internal_prompts = __esmMin((() => {
3229
3229
  }));
3230
3230
  //#endregion
3231
3231
  //#region ../../libs/shared/validators/src/lib/studio/studio-model-routing.ts
3232
- var MODEL_PROVIDER_SORTS, MODEL_PROVIDER_SORTS_TEXT, modelProviderSortSchema, isModelProviderSort, studioModelRoutingPathSchema, studioModelRoutingSchema, putStudioModelRoutingRequestSchema, studioModelRoutingDeleteResponseSchema;
3232
+ var MODEL_PROVIDER_SORTS, MODEL_PROVIDER_SORTS_TEXT, modelProviderSortSchema, MODEL_PROVIDER_QUANTIZATIONS, providerSlugSchema, providerSlugsSchema, percentileTargetSchema, dollarsSchema, modelProviderRoutingSchema, modelRoutingInputSchema, toModelProviderRouting, readModelProviderRouting, plainSortOf, studioModelRoutingPathSchema, studioModelRoutingSchema, putStudioModelRoutingRequestSchema, routingOfPutRequest, studioModelRoutingDeleteResponseSchema;
3233
3233
  var init_studio_model_routing = __esmMin((() => {
3234
3234
  init_studio_model_selections();
3235
3235
  MODEL_PROVIDER_SORTS = [
@@ -3239,14 +3239,69 @@ var init_studio_model_routing = __esmMin((() => {
3239
3239
  ];
3240
3240
  MODEL_PROVIDER_SORTS_TEXT = MODEL_PROVIDER_SORTS.map((sort) => `'${sort}'`).join(", ");
3241
3241
  modelProviderSortSchema = z.enum(MODEL_PROVIDER_SORTS);
3242
- isModelProviderSort = (value) => modelProviderSortSchema.safeParse(value).success;
3242
+ MODEL_PROVIDER_QUANTIZATIONS = [
3243
+ "int4",
3244
+ "int8",
3245
+ "fp4",
3246
+ "mxfp4",
3247
+ "nvfp4",
3248
+ "fp6",
3249
+ "fp8",
3250
+ "mxfp8",
3251
+ "fp16",
3252
+ "bf16",
3253
+ "fp32",
3254
+ "unknown"
3255
+ ];
3256
+ providerSlugSchema = z.string().regex(/^\S+$/, "a provider slug has no spaces").max(128);
3257
+ providerSlugsSchema = z.array(providerSlugSchema).min(1).max(32);
3258
+ percentileTargetSchema = z.union([z.number().positive(), z.strictObject({
3259
+ p50: z.number().positive(),
3260
+ p75: z.number().positive(),
3261
+ p90: z.number().positive(),
3262
+ p99: z.number().positive()
3263
+ }).partial().refine((value) => Object.keys(value).length > 0, "name at least one percentile")]);
3264
+ dollarsSchema = z.number().nonnegative();
3265
+ modelProviderRoutingSchema = z.strictObject({
3266
+ order: providerSlugsSchema,
3267
+ only: providerSlugsSchema,
3268
+ ignore: providerSlugsSchema,
3269
+ allow_fallbacks: z.boolean(),
3270
+ require_parameters: z.boolean(),
3271
+ data_collection: z.enum(["allow", "deny"]),
3272
+ zdr: z.boolean(),
3273
+ enforce_distillable_text: z.boolean(),
3274
+ quantizations: z.array(z.enum(MODEL_PROVIDER_QUANTIZATIONS)).min(1),
3275
+ sort: z.union([modelProviderSortSchema, z.strictObject({
3276
+ by: modelProviderSortSchema,
3277
+ partition: z.enum(["model", "none"]).optional()
3278
+ })]),
3279
+ preferred_min_throughput: percentileTargetSchema,
3280
+ preferred_max_latency: percentileTargetSchema,
3281
+ max_price: z.strictObject({
3282
+ prompt: dollarsSchema,
3283
+ completion: dollarsSchema,
3284
+ request: dollarsSchema,
3285
+ image: dollarsSchema
3286
+ }).partial()
3287
+ }).partial().refine((value) => Object.keys(value).length > 0, "name at least one routing field");
3288
+ modelRoutingInputSchema = z.union([modelProviderSortSchema, modelProviderRoutingSchema]);
3289
+ toModelProviderRouting = (input) => typeof input === "string" ? { sort: input } : input;
3290
+ readModelProviderRouting = (value) => {
3291
+ const parsed = modelRoutingInputSchema.safeParse(value);
3292
+ return parsed.success ? toModelProviderRouting(parsed.data) : null;
3293
+ };
3294
+ plainSortOf = (routing) => routing && Object.keys(routing).length === 1 && typeof routing.sort === "string" ? routing.sort : null;
3243
3295
  studioModelRoutingPathSchema = studioModelSelectionPathSchema.extend({ ownerKind: z.literal("agent") });
3244
3296
  studioModelRoutingSchema = studioModelRoutingPathSchema.extend({
3245
- sort: modelProviderSortSchema,
3297
+ routing: modelProviderRoutingSchema,
3298
+ /** `routing` as a plain sort, for a Studio older than the object; `null` otherwise. */
3299
+ sort: modelProviderSortSchema.nullable(),
3246
3300
  updatedBy: z.string().nullable(),
3247
3301
  updatedAt: z.iso.datetime()
3248
3302
  });
3249
- putStudioModelRoutingRequestSchema = z.strictObject({ sort: modelProviderSortSchema });
3303
+ putStudioModelRoutingRequestSchema = z.union([z.strictObject({ routing: modelProviderRoutingSchema }), z.strictObject({ sort: modelProviderSortSchema })]);
3304
+ routingOfPutRequest = (body) => "routing" in body ? body.routing : { sort: body.sort };
3250
3305
  studioModelRoutingDeleteResponseSchema = studioModelRoutingPathSchema.extend({ status: z.literal("deleted") });
3251
3306
  }));
3252
3307
  //#endregion
@@ -3613,7 +3668,7 @@ var init_studio_rag = __esmMin((() => {
3613
3668
  error: z.string().nullable()
3614
3669
  });
3615
3670
  ragRebuildStatusResponseSchema = z.object({ rebuild: ragRebuildStatusSchema.nullable() });
3616
- })), VOICE_STT_MODELS, VOICE_TTS_MODELS, isCartesiaTtsModel, VOICE_BACKGROUND_CLIPS, VOICE_AMBIENT_SOUNDS, VOICE_THINKING_SOUNDS, thresholdSchema, millisecondsSchema, keytermsSchema, vadSchema, turnSchema, interruptionsSchema, ragSchema, volumeSchema, backgroundAudioSchema, VOICE_BACKGROUND_AUDIO_DEFAULTS, effectiveVoiceSettingsObjectSchema, checkCrossFieldRules, effectiveVoiceSettingsSchema, callVoiceSettingsObjectSchema, callVoiceSettingsSchema, toCallVoiceSettings, VOICE_SETTINGS_LEAF_PATHS, VOICE_TTS_PAIR, voiceSettingOriginSchema, effectiveVoiceSettingsViewSchema, checkTtsPair, voiceSettingsSchema, checkStudioTtsPair, studioVoiceSettingsPatchSchema, voiceSettingsResponseSchema, describeVoiceSettingsIssue, deepAgentNameSchema, voiceCallIdSchema, voiceTagsSchema, VOICE_TTS_MARKUP_CONTEXT, VOICE_TTS_MARKUP_GUIDES, isVoiceBoxUrl, VOICE_BOX_URL_MAX, voiceBoxUrlSchema, VOICE_TRACES_PATH, VOICE_WORKER_AGENT_NAME_ENV, VOICE_WORKER_PORT_ENV, VOICE_CALL_TOKEN_MAX, voiceDispatchSchema, voiceRoomRequestSchema, voiceRoomResponseSchema, VOICE_RECORDING_STATUSES, voiceRecordingQuerySchema, VOICE_RECORDING_URL_TTL_SECONDS, voiceRecordingResponseSchema, voiceStatusResponseSchema, HEARD_TEXT_MAX, aguiHeardSchema, VOICE_CALLS_PATH, voiceCallBootstrapSchema, voiceCallTranscriptSchema, VOICE_CALL_TURNS_MAX, VOICE_TURN_LATENCY_MAX_MS, turnLatencyMs, voiceCallTurnMetricsSchema, voiceCallTurnsSchema, readVoiceCallTurns, voiceCallCompleteSchema, voiceCallLifecycleResponseSchema;
3671
+ })), VOICE_STT_MODELS, VOICE_TTS_MODELS, isCartesiaTtsModel, VOICE_BACKGROUND_CLIPS, VOICE_AMBIENT_SOUNDS, VOICE_THINKING_SOUNDS, thresholdSchema, millisecondsSchema, keytermsSchema, vadSchema, turnSchema, VOICE_EAGER_THRESHOLD_DEFAULT, interruptionsSchema, ragSchema, volumeSchema, backgroundAudioSchema, VOICE_BACKGROUND_AUDIO_DEFAULTS, effectiveVoiceSettingsObjectSchema, checkCrossFieldRules, effectiveVoiceSettingsSchema, callVoiceSettingsObjectSchema, callVoiceSettingsSchema, toCallVoiceSettings, VOICE_SETTINGS_LEAF_PATHS, VOICE_TTS_PAIR, voiceSettingOriginSchema, LEAVES_NEWER_THAN_SOME_BOXES, effectiveVoiceSettingsViewSchema, checkTtsPair, voiceSettingsSchema, checkStudioTtsPair, studioVoiceSettingsPatchSchema, voiceSettingsResponseSchema, describeVoiceSettingsIssue, deepAgentNameSchema, voiceCallIdSchema, voiceTagsSchema, VOICE_TTS_MARKUP_CONTEXT, VOICE_TTS_MARKUP_GUIDES, isVoiceBoxUrl, VOICE_BOX_URL_MAX, voiceBoxUrlSchema, VOICE_TRACES_PATH, VOICE_WORKER_AGENT_NAME_ENV, VOICE_WORKER_PORT_ENV, VOICE_CALL_TOKEN_MAX, voiceDispatchSchema, voiceRoomRequestSchema, voiceRoomResponseSchema, VOICE_RECORDING_STATUSES, voiceRecordingQuerySchema, VOICE_RECORDING_URL_TTL_SECONDS, voiceRecordingResponseSchema, voiceStatusResponseSchema, HEARD_TEXT_MAX, aguiHeardSchema, VOICE_CALLS_PATH, voiceCallBootstrapSchema, voiceCallKnowledgeSchema, voiceCallTranscriptSchema, VOICE_CALL_TURNS_MAX, VOICE_TURN_LATENCY_MAX_MS, turnLatencyMs, voiceCallTurnMetricsSchema, voiceCallTurnsSchema, readVoiceCallTurns, voiceCallCompleteSchema, voiceCallLifecycleResponseSchema;
3617
3672
  var init_studio_voice = __esmMin((() => {
3618
3673
  init_memory_identities();
3619
3674
  init_studio_rag();
@@ -3653,8 +3708,10 @@ var init_studio_voice = __esmMin((() => {
3653
3708
  mode: z.enum(["stt", "livekit"]),
3654
3709
  minEndpointingDelayMs: millisecondsSchema,
3655
3710
  maxEndpointingDelayMs: millisecondsSchema,
3656
- detectorThreshold: thresholdSchema
3711
+ detectorThreshold: thresholdSchema,
3712
+ eagerThreshold: z.number().min(.3).max(.7)
3657
3713
  });
3714
+ VOICE_EAGER_THRESHOLD_DEFAULT = .3;
3658
3715
  interruptionsSchema = z.strictObject({
3659
3716
  enabled: z.boolean(),
3660
3717
  minDurationMs: millisecondsSchema,
@@ -3695,7 +3752,7 @@ var init_studio_voice = __esmMin((() => {
3695
3752
  sttModel: z.enum(VOICE_STT_MODELS),
3696
3753
  keyterms: keytermsSchema.default(() => []),
3697
3754
  vad: vadSchema,
3698
- turn: turnSchema,
3755
+ turn: turnSchema.extend({ eagerThreshold: turnSchema.shape.eagerThreshold.default(VOICE_EAGER_THRESHOLD_DEFAULT) }),
3699
3756
  interruptions: interruptionsSchema,
3700
3757
  ttsModel: z.enum(VOICE_TTS_MODELS),
3701
3758
  /**
@@ -3774,6 +3831,7 @@ var init_studio_voice = __esmMin((() => {
3774
3831
  "turn.minEndpointingDelayMs",
3775
3832
  "turn.maxEndpointingDelayMs",
3776
3833
  "turn.detectorThreshold",
3834
+ "turn.eagerThreshold",
3777
3835
  "interruptions.enabled",
3778
3836
  "interruptions.minDurationMs",
3779
3837
  "interruptions.minWords",
@@ -3803,9 +3861,10 @@ var init_studio_voice = __esmMin((() => {
3803
3861
  "code",
3804
3862
  "default"
3805
3863
  ]);
3864
+ LEAVES_NEWER_THAN_SOME_BOXES = new Set(["turn.eagerThreshold"]);
3806
3865
  effectiveVoiceSettingsViewSchema = z.strictObject({
3807
3866
  value: effectiveVoiceSettingsObjectSchema,
3808
- origins: z.strictObject(Object.fromEntries(VOICE_SETTINGS_LEAF_PATHS.map((path) => [path, voiceSettingOriginSchema])))
3867
+ origins: z.strictObject(Object.fromEntries(VOICE_SETTINGS_LEAF_PATHS.map((path) => [path, LEAVES_NEWER_THAN_SOME_BOXES.has(path) ? voiceSettingOriginSchema.default("default") : voiceSettingOriginSchema])))
3809
3868
  });
3810
3869
  checkTtsPair = (value, ctx) => {
3811
3870
  if (value.ttsModel !== void 0 && value.ttsVoice === void 0) ctx.addIssue({
@@ -3915,6 +3974,7 @@ var init_studio_voice = __esmMin((() => {
3915
3974
  aguiHeardSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
3916
3975
  VOICE_CALLS_PATH = "/voice/v1/calls";
3917
3976
  voiceCallBootstrapSchema = z.strictObject({ identities: integrationIdentitiesSchema.optional() });
3977
+ voiceCallKnowledgeSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
3918
3978
  voiceCallTranscriptSchema = z.array(z.strictObject({
3919
3979
  role: z.enum(["user", "assistant"]),
3920
3980
  text: z.string().trim().min(1).max(16e3)
@@ -3929,7 +3989,9 @@ var init_studio_voice = __esmMin((() => {
3929
3989
  transcriptionMs: turnLatencyMs,
3930
3990
  llmTtftMs: turnLatencyMs,
3931
3991
  ttsTtfbMs: turnLatencyMs,
3932
- e2eMs: turnLatencyMs
3992
+ e2eMs: turnLatencyMs,
3993
+ /** How long before the final transcript the turn's last eager end of turn left for the box (STA3-94). */
3994
+ preflightLeadMs: turnLatencyMs
3933
3995
  });
3934
3996
  voiceCallTurnsSchema = z.array(voiceCallTurnMetricsSchema).max(VOICE_CALL_TURNS_MAX);
3935
3997
  readVoiceCallTurns = (value) => {
@@ -3965,7 +4027,8 @@ var init_effective_voice_settings = __esmMin((() => {
3965
4027
  mode: "stt",
3966
4028
  minEndpointingDelayMs: 0,
3967
4029
  maxEndpointingDelayMs: 1200,
3968
- detectorThreshold: .35
4030
+ detectorThreshold: .35,
4031
+ eagerThreshold: VOICE_EAGER_THRESHOLD_DEFAULT
3969
4032
  }),
3970
4033
  interruptions: Object.freeze({
3971
4034
  enabled: true,
@@ -6018,6 +6081,15 @@ var init_studio_catalog = __esmMin((() => {
6018
6081
  /** The provider routing an operator picked for this key, or `null` when nobody has. */
6019
6082
  selectedProviderSort: modelProviderSortSchema.nullable().default(null),
6020
6083
  /**
6084
+ * The full provider routing the code writes for this key, or `null` when it writes
6085
+ * none. `defaultProviderSort` is the same routing as a plain sort, for a Studio
6086
+ * older than the object, and `null` whenever the routing is more than one sort.
6087
+ * Absent from a box that predates it.
6088
+ */
6089
+ defaultProviderRouting: modelProviderRoutingSchema.nullable().optional(),
6090
+ /** The full provider routing an operator picked, as `defaultProviderRouting` is to the code's. */
6091
+ selectedProviderRouting: modelProviderRoutingSchema.nullable().optional(),
6092
+ /**
6021
6093
  * `declared` — the code declares this key. `unused` — only a pick exists, for a
6022
6094
  * key no declaration mentions any more; resetting it is the one thing left to do.
6023
6095
  * An entry NOTHING scanned never carries an `unused` row: see the box's
@@ -9479,7 +9551,8 @@ var init_model_routing = __esmMin((() => {
9479
9551
  ownerKind: text("owner_kind").notNull(),
9480
9552
  ownerName: text("owner_name").notNull(),
9481
9553
  key: text("key").notNull(),
9482
- sort: text("sort").notNull(),
9554
+ sort: text("sort"),
9555
+ routing: jsonb("routing"),
9483
9556
  /** The acting user's id, or null on the un-authenticated `stackbone dev` datapath. */
9484
9557
  updatedBy: text("updated_by"),
9485
9558
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
@@ -11406,7 +11479,7 @@ var init_json = __esmMin((() => {
11406
11479
  //#region ../../libs/shared/model-provider-wire/src/lib/common/provider-routing.ts
11407
11480
  var providerRoutingFor, withProviderRouting;
11408
11481
  var init_provider_routing = __esmMin((() => {
11409
- providerRoutingFor = (sort) => Object.freeze({ sort });
11482
+ providerRoutingFor = (routing) => Object.freeze({ ...routing });
11410
11483
  withProviderRouting = (body, kept) => {
11411
11484
  if (kept || !("provider" in body)) return body;
11412
11485
  const next = { ...body };
@@ -19613,6 +19686,7 @@ var init_learn_control_plane_identity = __esmMin((() => {
19613
19686
  }));
19614
19687
  //#endregion
19615
19688
  //#region ../../libs/runtime/workspace-runtime/src/lib/catalog/catalog-projector.ts
19689
+ init_src$26();
19616
19690
  /**
19617
19691
  * `${kind}:${name}` — the identity a catalog row, a declaration, a prompt owner and
19618
19692
  * a selection's owner all agree on. The kind is half of it, never decoration: a deep
@@ -19774,16 +19848,26 @@ var asDeclaredModel = (ref) => ({
19774
19848
  overridable: ref.overridable,
19775
19849
  file: ref.file,
19776
19850
  line: ref.line,
19777
- defaultProviderSort: ref.defaultProviderSort ?? null,
19851
+ ...defaultRoutingFields(declaredRouting(ref)),
19778
19852
  selectedModelId: null,
19779
19853
  selectedProviderId: null,
19780
- selectedProviderSort: null,
19854
+ ...selectedRoutingFields(null),
19781
19855
  state: "declared"
19782
19856
  });
19857
+ /** The routing the code writes; a manifest older than the object carries only its sort. */
19858
+ var declaredRouting = (ref) => readModelProviderRouting(ref.defaultProviderRouting ?? ref.defaultProviderSort);
19859
+ var defaultRoutingFields = (routing) => ({
19860
+ defaultProviderRouting: routing,
19861
+ defaultProviderSort: plainSortOf(routing)
19862
+ });
19863
+ var selectedRoutingFields = (routing) => ({
19864
+ selectedProviderRouting: routing,
19865
+ selectedProviderSort: plainSortOf(routing)
19866
+ });
19783
19867
  var selectedFields = (pick) => ({
19784
19868
  selectedModelId: pick?.modelId ?? null,
19785
19869
  selectedProviderId: pick?.providerId ?? null,
19786
- selectedProviderSort: pick?.providerSort ?? null
19870
+ ...selectedRoutingFields(pick?.providerRouting ?? null)
19787
19871
  });
19788
19872
  /**
19789
19873
  * Cross what the code DECLARES against what an operator PICKED, for every row at
@@ -19817,7 +19901,7 @@ var attachModelSelections = (entries, selections) => entries.map((entry) => {
19817
19901
  overridable: true,
19818
19902
  file: null,
19819
19903
  line: null,
19820
- defaultProviderSort: null,
19904
+ ...defaultRoutingFields(null),
19821
19905
  ...selectedFields(pick),
19822
19906
  state: "unused"
19823
19907
  }));
@@ -22674,7 +22758,7 @@ var init_role = __esmMin((() => {
22674
22758
  var STACKBONE_CLI_BUILD_VERSION, EMULATOR_CAPABILITY_ROUTES, EMULATOR_CAPABILITIES, buildContractResponse;
22675
22759
  var init_contract = __esmMin((() => {
22676
22760
  init_src$26();
22677
- STACKBONE_CLI_BUILD_VERSION = "0.9.1";
22761
+ STACKBONE_CLI_BUILD_VERSION = "0.9.2";
22678
22762
  EMULATOR_CAPABILITY_ROUTES = {
22679
22763
  "database.postgres_direct": "/api/db",
22680
22764
  "rag.basic": "/api/rag",
@@ -28263,7 +28347,7 @@ var init_api_docs = __esmMin((() => {
28263
28347
  },
28264
28348
  {
28265
28349
  name: VOICE_WORKER_TAG,
28266
- description: "What the LiveKit voice worker calls on the box besides AG-UI: identity bootstrap before output, heard-call completion after awaited closure, and the relay that takes a call's spans to the box's OTLP collector. It takes the call token the room route put in the call's dispatch, and nothing else. Never called by Studio or by the SDK."
28350
+ description: "What the LiveKit voice worker calls on the box besides AG-UI: identity bootstrap before output, the knowledge search started on the caller's eager end of turn, heard-call completion after awaited closure, and the relay that takes a call's spans to the box's OTLP collector. It takes the call token the room route put in the call's dispatch, and nothing else. Never called by Studio or by the SDK."
28267
28351
  }
28268
28352
  ];
28269
28353
  SURFACE_TAG_DESCRIPTIONS = {
@@ -41640,6 +41724,11 @@ var init_mutations_outside_catalog = __esmMin((() => {
41640
41724
  path: "/voice/v1/calls/{sessionKey}/bootstrap",
41641
41725
  reason: "The accepted call freezes structured identity and recalls memory using its own call token, without an operator seat."
41642
41726
  },
41727
+ {
41728
+ method: "POST",
41729
+ path: "/voice/v1/calls/{sessionKey}/knowledge",
41730
+ reason: "The call worker starts the knowledge search on the caller's eager end of turn using its own call token, without an operator seat. It persists nothing; its only caller is the LiveKit worker."
41731
+ },
41643
41732
  {
41644
41733
  method: "POST",
41645
41734
  path: "/voice/v1/calls/{sessionKey}/complete",
@@ -65522,12 +65611,12 @@ var init_registry = __esmMin((() => {
65522
65611
  };
65523
65612
  const instructions = read(() => def.resolvedInstructions?.());
65524
65613
  const model = read(() => def.resolvedModel?.());
65525
- const providerSort = read(() => def.resolvedProviderSort?.());
65526
- const { providerSort: _previous, ...rest } = metadata;
65614
+ const providerRouting = readModelProviderRouting(read(() => def.resolvedProviderRouting?.()) ?? read(() => def.resolvedProviderSort?.()));
65615
+ const { providerRouting: _previous, ...rest } = metadata;
65527
65616
  return {
65528
65617
  ...rest,
65529
65618
  ...typeof model === "string" && model !== "" ? { model } : {},
65530
- ...isModelProviderSort(providerSort) ? { providerSort } : {},
65619
+ ...providerRouting !== null ? { providerRouting } : {},
65531
65620
  ...typeof instructions === "string" && instructions !== "" ? { instructions } : {}
65532
65621
  };
65533
65622
  };
@@ -73127,7 +73216,7 @@ var selectionsSelect = (kinds, names) => sql`
73127
73216
  SELECT COALESCE(s.owner_kind, r.owner_kind) AS owner_kind,
73128
73217
  COALESCE(s.owner_name, r.owner_name) AS owner_name,
73129
73218
  COALESCE(s.key, r.key) AS key,
73130
- s.model_id, s.provider_id, r.sort
73219
+ s.model_id, s.provider_id, r.sort, r.routing
73131
73220
  FROM ${modelSelections} s
73132
73221
  FULL OUTER JOIN ${modelRouting} r
73133
73222
  ON r.owner_kind = s.owner_kind AND r.owner_name = s.owner_name AND r.key = s.key
@@ -73148,7 +73237,7 @@ var createModelSelectionsReader = (opts) => {
73148
73237
  keys.set(row.key, {
73149
73238
  modelId: row.model_id,
73150
73239
  providerId: row.provider_id,
73151
- providerSort: isModelProviderSort(row.sort) ? row.sort : null
73240
+ providerRouting: readModelProviderRouting(row.routing ?? row.sort)
73152
73241
  });
73153
73242
  byOwner.set(id, keys);
73154
73243
  }
@@ -86683,14 +86772,19 @@ var notFound$1 = {
86683
86772
  code: "studio_model_selection_not_found",
86684
86773
  message: "No model is picked for that key. Its code default already applies."
86685
86774
  };
86686
- var rowToRouting = (row) => ({
86687
- ownerKind: "agent",
86688
- ownerName: row.ownerName,
86689
- key: row.key,
86690
- sort: row.sort,
86691
- updatedBy: row.updatedBy,
86692
- updatedAt: row.updatedAt.toISOString()
86693
- });
86775
+ var rowToRouting = (row) => {
86776
+ const routing = readModelProviderRouting(row.routing ?? row.sort);
86777
+ if (!routing) throw new Error("model_routing_row_unreadable");
86778
+ return {
86779
+ ownerKind: "agent",
86780
+ ownerName: row.ownerName,
86781
+ key: row.key,
86782
+ routing,
86783
+ sort: plainSortOf(routing),
86784
+ updatedBy: row.updatedBy,
86785
+ updatedAt: row.updatedAt.toISOString()
86786
+ };
86787
+ };
86694
86788
  var routingNotFound = {
86695
86789
  code: "studio_model_routing_not_found",
86696
86790
  message: "No provider routing is picked for that key. The code's `modelRouting` already applies."
@@ -86820,10 +86914,10 @@ var createModelSelectionsRouter = (opts) => {
86820
86914
  tags: [MODEL_SELECTIONS_TAG],
86821
86915
  middleware: [requireCapability("config:write")],
86822
86916
  summary: "Pick the provider routing for one key",
86823
- description: "Sets which OpenRouter provider one model key of a deep agent prefers (`provider: { sort }` on its model calls): `latency`, `throughput` or `price`. It outranks the code's `modelRouting`, and a subagent key with none follows the agent's. The box drops the field on any gateway that is not OpenRouter. The agent is rebuilt to apply it.",
86917
+ description: "Sets which OpenRouter providers one model key of a deep agent goes to: OpenRouter's `provider` object on its model calls, e.g. `{ \"routing\": { \"only\": [\"deepinfra/fp8\"], \"allow_fallbacks\": false } }`, or `{ \"sort\": \"latency\" }` as shorthand for `{ \"routing\": { \"sort\": \"latency\" } }`. It outranks the code's `modelRouting`, and a subagent key with none follows the agent's. The box drops the field on any gateway that is not OpenRouter. The agent is rebuilt to apply it.",
86824
86918
  request: {
86825
86919
  params: studioModelRoutingPathSchema,
86826
- body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider sort.")
86920
+ body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider routing, or a plain sort.")
86827
86921
  },
86828
86922
  responses: {
86829
86923
  200: jsonResponse$1(named("StudioModelRouting", studioModelRoutingSchema), "The routing as it now stands."),
@@ -86832,12 +86926,14 @@ var createModelSelectionsRouter = (opts) => {
86832
86926
  }
86833
86927
  }), async (c) => {
86834
86928
  const path = c.req.valid("param");
86835
- const { sort } = c.req.valid("json");
86929
+ const routing = routingOfPutRequest(c.req.valid("json"));
86930
+ const sort = plainSortOf(routing);
86836
86931
  const actor = readActorUserId(c);
86837
86932
  const row = await opts.db.transaction(async (tx) => {
86838
86933
  const [written] = await tx.insert(modelRouting).values({
86839
86934
  ...path,
86840
86935
  sort,
86936
+ routing,
86841
86937
  updatedBy: actor
86842
86938
  }).onConflictDoUpdate({
86843
86939
  target: [
@@ -86847,6 +86943,7 @@ var createModelSelectionsRouter = (opts) => {
86847
86943
  ],
86848
86944
  set: {
86849
86945
  sort,
86946
+ routing,
86850
86947
  updatedBy: actor,
86851
86948
  updatedAt: sql`now()`
86852
86949
  }
@@ -86857,7 +86954,8 @@ var createModelSelectionsRouter = (opts) => {
86857
86954
  subjectType: "model_selection",
86858
86955
  payload: {
86859
86956
  ...selectionSubject(path),
86860
- sort
86957
+ ...sort !== null ? { sort } : {},
86958
+ routing
86861
86959
  }
86862
86960
  }, tx);
86863
86961
  return written;
@@ -93386,11 +93484,17 @@ var answerOf = async (storage, row) => {
93386
93484
  };
93387
93485
  //#endregion
93388
93486
  //#region ../../libs/runtime/workspace-runtime/src/lib/voice/voice-rag.ts
93389
- var WORD_CHARACTER, spokenWordCount, knowledgeQuery, selectKnowledge;
93487
+ var WORD_CHARACTER, spokenWordCount, knowledgeQuery, PUNCTUATION_OR_SYMBOL_RUN, prefetchSentence, prefetchKey, selectKnowledge;
93390
93488
  var init_voice_rag = __esmMin((() => {
93391
93489
  WORD_CHARACTER = /[\p{L}\p{N}]/u;
93392
93490
  spokenWordCount = (text) => text.trim().split(/\s+/u).filter((token) => WORD_CHARACTER.test(token)).length;
93393
93491
  knowledgeQuery = ({ exchanges, sentence }) => [...exchanges.flatMap(({ question, reply }) => [question, reply]), sentence].map((piece) => piece.trim()).filter((piece) => piece !== "").join("\n");
93492
+ PUNCTUATION_OR_SYMBOL_RUN = /[\p{P}\p{S}]+/gu;
93493
+ prefetchSentence = (text) => text.normalize("NFC").toLowerCase().replace(PUNCTUATION_OR_SYMBOL_RUN, " ").replace(/\s+/gu, " ").trim();
93494
+ prefetchKey = ({ exchanges, sentence }) => knowledgeQuery({
93495
+ exchanges,
93496
+ sentence: prefetchSentence(sentence)
93497
+ });
93394
93498
  selectKnowledge = (hits, { minScore, topK }) => {
93395
93499
  const passages = hits.filter((hit) => Number.isFinite(hit.score) && hit.score >= minScore).map((hit) => hit.content).filter((content) => typeof content === "string" && content.trim() !== "").slice(0, topK);
93396
93500
  return passages.length === 0 ? void 0 : JSON.stringify(passages);
@@ -93436,7 +93540,7 @@ var init_voice_calls = __esmMin((() => {
93436
93540
  const words = spokenWordCount(text);
93437
93541
  return words !== 0 && words >= rag.minWords;
93438
93542
  };
93439
- const searchKnowledge = async (call, text) => {
93543
+ const searchKnowledge = async (call, text, origin = "turn") => {
93440
93544
  const { rag } = call;
93441
93545
  if (!searchable(rag, text)) return void 0;
93442
93546
  const { collection } = rag;
@@ -93444,6 +93548,7 @@ var init_voice_calls = __esmMin((() => {
93444
93548
  exchanges: call.exchanges,
93445
93549
  sentence: text
93446
93550
  });
93551
+ const search = origin === "prefetch" ? "rag_prefetch" : "rag_search";
93447
93552
  const warn = (code) => options.logger.warn(`voice ${call.input.agent}/${call.input.sessionKey}: ${code}.`);
93448
93553
  try {
93449
93554
  const result = await withTimeout(retrieve({
@@ -93452,7 +93557,7 @@ var init_voice_calls = __esmMin((() => {
93452
93557
  topK: rag.topK
93453
93558
  }), rag.timeoutMs);
93454
93559
  if (result.error) {
93455
- warn(`rag_search_failed (${result.error.code})`);
93560
+ warn(`${search}_failed (${result.error.code})`);
93456
93561
  return;
93457
93562
  }
93458
93563
  if (result.data.length === 0 && !call.ragEmptyWarned) {
@@ -93461,11 +93566,31 @@ var init_voice_calls = __esmMin((() => {
93461
93566
  }
93462
93567
  return selectKnowledge(result.data, rag);
93463
93568
  } catch (error) {
93464
- if (error instanceof ActionTimeoutError) warn("rag_search_timeout");
93465
- else warn(`rag_search_failed (${error instanceof Error ? error.name : typeof error})`);
93569
+ if (error instanceof ActionTimeoutError) warn(`${search}_timeout`);
93570
+ else warn(`${search}_failed (${error instanceof Error ? error.name : typeof error})`);
93466
93571
  }
93467
93572
  };
93573
+ let inputGuard;
93468
93574
  return {
93575
+ useInputGuard: (guard) => {
93576
+ inputGuard = guard;
93577
+ },
93578
+ prefetchKnowledge: async (agent, sessionKey, text) => {
93579
+ const call = find(agent, sessionKey);
93580
+ const sequence = ++call.prefetches;
93581
+ const guard = inputGuard;
93582
+ if (!guard || call.completion || !searchable(call.rag, text)) return;
93583
+ const guarded = await Promise.resolve().then(() => guard(agent, text)).catch(() => "skip");
93584
+ if (sequence !== call.prefetches || call.completion) return;
93585
+ if (guarded === "skip" || guarded.blocked || !searchable(call.rag, guarded.content)) return;
93586
+ call.prefetch = {
93587
+ key: prefetchKey({
93588
+ exchanges: call.exchanges,
93589
+ sentence: guarded.content
93590
+ }),
93591
+ promise: searchKnowledge(call, guarded.content, "prefetch")
93592
+ };
93593
+ },
93469
93594
  accept: async (input) => {
93470
93595
  const registry = options.getRegistry();
93471
93596
  if (!registry) throw new Error("Voice call memory service is unavailable.");
@@ -93479,6 +93604,7 @@ var init_voice_calls = __esmMin((() => {
93479
93604
  rag: input.rag && Object.freeze({ ...input.rag }),
93480
93605
  refusalPhrase: input.refusalPhrase ?? VOICE_SETTINGS_DEFAULTS.refusalPhrase,
93481
93606
  exchanges: [],
93607
+ prefetches: 0,
93482
93608
  startedAt,
93483
93609
  expiresAt: startedAt + CALL_TOKEN_TTL_SECONDS * 1e3,
93484
93610
  work: /* @__PURE__ */ new Set()
@@ -93545,7 +93671,21 @@ var init_voice_calls = __esmMin((() => {
93545
93671
  done();
93546
93672
  };
93547
93673
  },
93548
- knowledgeFor: (text) => searchKnowledge(call, text)
93674
+ knowledgeFor: (text, notePrefetch) => {
93675
+ const { prefetch } = call;
93676
+ delete call.prefetch;
93677
+ call.prefetches++;
93678
+ if (!searchable(call.rag, text)) return Promise.resolve(void 0);
93679
+ if (prefetch?.key === prefetchKey({
93680
+ exchanges: call.exchanges,
93681
+ sentence: text
93682
+ })) {
93683
+ notePrefetch?.("hit");
93684
+ return prefetch.promise;
93685
+ }
93686
+ notePrefetch?.(prefetch ? "miss" : "none");
93687
+ return searchKnowledge(call, text);
93688
+ }
93549
93689
  });
93550
93690
  call.context = context;
93551
93691
  if (call.input.startRecording) Promise.resolve().then(() => call.input.startRecording(sessionAddressFor(agent, sessionKey, void 0, sessionIdentityFor(identity)))).catch(() => options.logger.warn(`voice ${agent}/${sessionKey}: recording_start_failed.`));
@@ -93634,7 +93774,7 @@ var createVoiceRoomsRouter = (options) => {
93634
93774
  middleware: [requireCapability(VOICE_CAPABILITY$1)],
93635
93775
  "x-stackbone-catalog": excludeFromCatalog("Answers a LiveKit participant token. The operation catalog never offers an operation whose answer is a credential."),
93636
93776
  summary: "Open a voice call with a deep agent",
93637
- 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 and heard completion at `/voice/v1/calls/{sessionKey}/bootstrap` and `/voice/v1/calls/{sessionKey}/complete`, nowhere else. The box freezes memory policy, the agent's knowledge settings (`rag`) and its refusal phrase (`refusalPhrase`, what the caller hears when a guardrail stops their sentence or the agent's reply), 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. 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.",
93777
+ 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`) and its refusal phrase (`refusalPhrase`, what the caller hears when a guardrail stops their sentence or the agent's reply), 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. 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.",
93638
93778
  request: { body: jsonBody(named("VoiceRoomRequest", voiceRoomRequestSchema), "The agent to call.") },
93639
93779
  responses: {
93640
93780
  200: jsonResponse$1(named("VoiceRoomResponse", voiceRoomResponseSchema), "The room, its participant token, and what the dispatch carries."),
@@ -94092,18 +94232,21 @@ var createVoiceCallsRouter = (options) => {
94092
94232
  options.logger.warn(principal?.type === "call" ? `voice ${principal.agent}/${principal.sessionKey}: lifecycle_failed.` : "voice lifecycle request failed.");
94093
94233
  return c.json(errorEnvelope("voice_lifecycle_failed", "The voice lifecycle operation failed."), 500);
94094
94234
  });
94235
+ const callResponses = {
94236
+ 200: jsonResponse$1(named("VoiceCallLifecycleResponse", voiceCallLifecycleResponseSchema), "The call operation completed."),
94237
+ 400: errorResponse("Invalid bounded call payload."),
94238
+ 401: errorResponse("Missing, invalid or expired call token."),
94239
+ 403: errorResponse("Only the call token for this session is admitted."),
94240
+ 404: errorResponse("No accepted call for this credential."),
94241
+ 413: errorResponse("The payload exceeds 1 MiB.")
94242
+ };
94095
94243
  const shared = {
94096
94244
  method: "post",
94097
94245
  tags: ["voice-worker"],
94098
94246
  security: [{ voiceCallToken: [] }],
94099
94247
  responses: {
94100
- 200: jsonResponse$1(named("VoiceCallLifecycleResponse", voiceCallLifecycleResponseSchema), "The call operation completed."),
94101
- 400: errorResponse("Invalid bounded call payload."),
94102
- 401: errorResponse("Missing, invalid or expired call token."),
94103
- 403: errorResponse("Only the call token for this session is admitted."),
94104
- 404: errorResponse("No accepted call for this credential."),
94248
+ ...callResponses,
94105
94249
  409: errorResponse("The call identity is frozen or the call is completing."),
94106
- 413: errorResponse("The payload exceeds 1 MiB."),
94107
94250
  500: errorResponse("Settings, recall or extraction failed.")
94108
94251
  }
94109
94252
  };
@@ -94121,6 +94264,22 @@ var createVoiceCallsRouter = (options) => {
94121
94264
  await options.calls.bootstrap(principal.agent, principal.sessionKey, c.req.valid("json").identities);
94122
94265
  return c.json({ ok: true }, 200);
94123
94266
  });
94267
+ app.openapi(createRoute({
94268
+ ...shared,
94269
+ responses: callResponses,
94270
+ path: "/{sessionKey}/knowledge",
94271
+ summary: "Start the knowledge search on the caller's eager end of turn",
94272
+ description: "Runs the agent's deterministic input guardrails over the sentence the speech model guessed ends the caller's turn, starts the knowledge search on the masked text and answers without waiting for it. The turn whose sentence matches it, ignoring case, spacing and punctuation, takes those passages instead of searching again. An agent without a knowledge collection or with a model-backed input guardrail, a sentence too short to search, a blocked sentence and a completing call answer the same and start nothing. Writes nothing to the conversation and calls no chat model.",
94273
+ request: {
94274
+ params: z$1.object({ sessionKey: z$1.string() }),
94275
+ body: jsonBody(named("VoiceCallKnowledge", voiceCallKnowledgeSchema), "The sentence the speech model guessed ends the caller's turn.")
94276
+ }
94277
+ }), async (c) => {
94278
+ const principal = readRequestPrincipal(c);
94279
+ if (principal.type !== "call") throw new Error("Missing call principal");
94280
+ await options.calls.prefetchKnowledge(principal.agent, principal.sessionKey, c.req.valid("json").text);
94281
+ return c.json({ ok: true }, 200);
94282
+ });
94124
94283
  app.openapi(createRoute({
94125
94284
  ...shared,
94126
94285
  path: "/{sessionKey}/complete",
@@ -94335,7 +94494,7 @@ var modelWarmupBody = (target) => ({
94335
94494
  content: WARMUP_USER_TEXT
94336
94495
  }],
94337
94496
  reasoning: { enabled: true },
94338
- ...target.providerSort ? { provider: providerRoutingFor(target.providerSort) } : {}
94497
+ ...target.providerRouting ? { provider: providerRoutingFor(target.providerRouting) } : {}
94339
94498
  });
94340
94499
  var createModelWarmup = (deps) => {
94341
94500
  const timeoutMs = deps.timeoutMs ?? 2e4;
@@ -94346,7 +94505,7 @@ var createModelWarmup = (deps) => {
94346
94505
  const body = modelWarmupBody({
94347
94506
  model: entry.metadata.model,
94348
94507
  instructions: entry.metadata.instructions,
94349
- providerSort: entry.metadata.providerSort
94508
+ providerRouting: entry.metadata.providerRouting
94350
94509
  });
94351
94510
  const signal = AbortSignal.timeout(timeoutMs);
94352
94511
  try {
@@ -95146,7 +95305,7 @@ async function probeDurableInterruptState(graph, threadId, logger) {
95146
95305
  return { supported: false };
95147
95306
  }
95148
95307
  }
95149
- var nowIso, CLIENT_MOVED_ON, CLIENT_TOOL_ANSWER_ROUNDS, createDeepAgentChat, hintDurableSessions, voiceCallConfigurable, guardrailHoldReply, toDecisionStep, toRedactionStep, toParkStep;
95308
+ var nowIso, CLIENT_MOVED_ON, CLIENT_TOOL_ANSWER_ROUNDS, createDeepAgentChat, hintDurableSessions, voiceCallConfigurable, guardrailHoldReply, toDecisionStep, toRedactionStep, toParkStep, agentPolicy, evaluateInput;
95150
95309
  var init_chat = __esmMin((() => {
95151
95310
  init_studio_continuation();
95152
95311
  init_abortable();
@@ -95218,10 +95377,7 @@ var init_chat = __esmMin((() => {
95218
95377
  async function readPolicy(agentName) {
95219
95378
  if (!opts.guardrails) return [];
95220
95379
  try {
95221
- return await opts.guardrails.effectiveGuardrails({
95222
- kind: "agent",
95223
- name: agentName
95224
- });
95380
+ return await agentPolicy(opts.guardrails, agentName);
95225
95381
  } catch (err) {
95226
95382
  logger.warn(`deep-agent guardrail policy read failed for "${agentName}" — the turn runs unguarded: ${messageOf$6(err)}`);
95227
95383
  return [];
@@ -95249,13 +95405,7 @@ var init_chat = __esmMin((() => {
95249
95405
  if (guardrails.length === 0) return unguarded;
95250
95406
  try {
95251
95407
  const original = lastUserMessage(turn.messages);
95252
- const classify = classifierFor(signal);
95253
- const result = await evaluateGuardrails({
95254
- guardrails,
95255
- content: original,
95256
- target: "input",
95257
- ...classify ? { classify } : {}
95258
- });
95408
+ const result = await evaluateInput(guardrails, original, classifierFor(signal));
95259
95409
  return {
95260
95410
  turn: result.content === original ? turn : withLastUserMessage(turn, result.content),
95261
95411
  blocked: result.allowed ? null : result.decision,
@@ -95268,6 +95418,27 @@ var init_chat = __esmMin((() => {
95268
95418
  }
95269
95419
  }
95270
95420
  /**
95421
+ * The input border's deterministic rules over one sentence that is not a turn yet:
95422
+ * the guess a voice call's knowledge prefetch searches (STA3-94 D2). It never asks
95423
+ * the classifier, which the guardrail ADR spends once per turn, and writes no run
95424
+ * step: the turn that follows evaluates the same rules and records them.
95425
+ */
95426
+ const guardInput = async (agentName, text) => {
95427
+ let guardrails = [];
95428
+ if (opts.guardrails) try {
95429
+ guardrails = await agentPolicy(opts.guardrails, agentName);
95430
+ } catch {
95431
+ return "skip";
95432
+ }
95433
+ const input = guardrails.filter((rule) => rule.enabled && rule.target === "input");
95434
+ if (input.some((rule) => isModelBackedCheck(rule.checkType))) return "skip";
95435
+ const result = await evaluateInput(input, text);
95436
+ return {
95437
+ blocked: !result.allowed,
95438
+ content: result.content
95439
+ };
95440
+ };
95441
+ /**
95271
95442
  * Wrap a turn's stream with the OUTPUT border: the reply is held until the policy
95272
95443
  * has judged it, then released, released masked, or dropped with a reason.
95273
95444
  *
@@ -95573,7 +95744,10 @@ var init_chat = __esmMin((() => {
95573
95744
  const guardedTurn = guarded.turn;
95574
95745
  const blocked = guarded.blocked;
95575
95746
  const userMessage = lastUserMessage(guardedTurn.messages);
95576
- const knowledge = registered && blocked === null ? voiceCall?.knowledgeFor(userMessage) : void 0;
95747
+ let prefetch;
95748
+ const knowledge = registered && blocked === null ? voiceCall?.knowledgeFor(userMessage, (outcome) => {
95749
+ prefetch = outcome;
95750
+ }) : void 0;
95577
95751
  const runInput = {
95578
95752
  trigger,
95579
95753
  input: { message: userMessage },
@@ -95583,6 +95757,7 @@ var init_chat = __esmMin((() => {
95583
95757
  memoryIdentity,
95584
95758
  ...voiceCall ? { voiceCall: true } : {},
95585
95759
  ...voiceCall?.searchesKnowledge ? { voiceKnowledgeRule: true } : {},
95760
+ ...prefetch ? { voiceKnowledgePrefetch: prefetch } : {},
95586
95761
  ...groupingKey !== void 0 ? { sessionKey: groupingKey } : {},
95587
95762
  ...turn.evalRunId !== void 0 ? evalRunStamp({
95588
95763
  evalRunId: turn.evalRunId,
@@ -96317,6 +96492,7 @@ var init_chat = __esmMin((() => {
96317
96492
  getMessageHistory,
96318
96493
  seedState,
96319
96494
  reconcileHeard,
96495
+ guardInput,
96320
96496
  runTurn: (turn) => ownedByAgent(turn.agent, runTurn(turn)),
96321
96497
  resumeTurn: ownedResumeTurn
96322
96498
  };
@@ -96343,6 +96519,16 @@ var init_chat = __esmMin((() => {
96343
96519
  ...guardrailParkRow(decision, approvalId),
96344
96520
  status: "running"
96345
96521
  });
96522
+ agentPolicy = (reader, agentName) => reader.effectiveGuardrails({
96523
+ kind: "agent",
96524
+ name: agentName
96525
+ });
96526
+ evaluateInput = (guardrails, content, classify) => evaluateGuardrails({
96527
+ guardrails,
96528
+ content,
96529
+ target: "input",
96530
+ ...classify ? { classify } : {}
96531
+ });
96346
96532
  }));
96347
96533
  //#endregion
96348
96534
  //#region ../../libs/runtime/workspace-runtime/src/lib/deep-agents/http/run-tags-header.ts
@@ -97001,10 +97187,12 @@ var init_mount$1 = __esmMin((() => {
97001
97187
  ...options.classify ? { classify: options.classify } : {},
97002
97188
  ...options.plantAmbientBridges ? { plantAmbientBridges: options.plantAmbientBridges } : {}
97003
97189
  });
97190
+ const voiceCalls = voiceCallsForApp(app);
97191
+ voiceCalls?.useInputGuard(chat.guardInput);
97004
97192
  const wire = {
97005
97193
  sessions: options.sessions,
97006
97194
  invocations: options.invocations,
97007
- voiceCalls: voiceCallsForApp(app),
97195
+ voiceCalls,
97008
97196
  chat,
97009
97197
  registry,
97010
97198
  logger,
@@ -104335,7 +104523,7 @@ var calls_workflow_ts_default = "import { z } from \"zod\";\nimport { streamDeep
104335
104523
  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";
104336
104524
  //#endregion
104337
104525
  //#region src/services/scaffold/template-loader.ts
104338
- var SDK_TEMPLATE_VERSION = "0.10.1";
104526
+ var SDK_TEMPLATE_VERSION = "0.10.2";
104339
104527
  var PREFIX = "./templates/";
104340
104528
  var templates = new Map(Object.entries(/* @__PURE__ */ Object.assign({
104341
104529
  "./templates/deep-agents/index.ts.tmpl": index_ts_default,
@@ -106849,6 +107037,57 @@ var literalText = (ts, node) => {
106849
107037
  return null;
106850
107038
  };
106851
107039
  /**
107040
+ * A value written down as JSON would be: strings, numbers (a leading `-` included),
107041
+ * booleans, `null`, and arrays and object literals of those. `undefined` for
107042
+ * anything else ANYWHERE inside it — an identifier, a spread, a computed key, an
107043
+ * interpolation — because a value read in part is a value guessed at. `as const`
107044
+ * and `satisfies` around it are type-only and looked through.
107045
+ */
107046
+ var literalJson = (ts, node) => {
107047
+ const K = ts.SyntaxKind;
107048
+ if (!node) return void 0;
107049
+ const text = literalText(ts, node);
107050
+ if (text !== null) return text;
107051
+ switch (node.kind) {
107052
+ case K["AsExpression"]:
107053
+ case K["SatisfiesExpression"]:
107054
+ case K["ParenthesizedExpression"]: return literalJson(ts, node.expression);
107055
+ case K["NumericLiteral"]: return Number(node.text);
107056
+ case K["TrueKeyword"]: return true;
107057
+ case K["FalseKeyword"]: return false;
107058
+ case K["NullKeyword"]: return null;
107059
+ case K["PrefixUnaryExpression"]: {
107060
+ const unary = node;
107061
+ if (unary.operator !== K["MinusToken"] || unary.operand.kind !== K["NumericLiteral"]) return;
107062
+ return -Number(unary.operand.text);
107063
+ }
107064
+ case K["ArrayLiteralExpression"]: {
107065
+ const items = [];
107066
+ for (const element of node.elements) {
107067
+ const item = literalJson(ts, element);
107068
+ if (item === void 0) return void 0;
107069
+ items.push(item);
107070
+ }
107071
+ return items;
107072
+ }
107073
+ case K["ObjectLiteralExpression"]: {
107074
+ const object = {};
107075
+ for (const property of node.properties) {
107076
+ if (property.kind !== K["PropertyAssignment"]) return void 0;
107077
+ const assignment = property;
107078
+ const key = literalKey(ts, assignment.name);
107079
+ const value = literalJson(ts, assignment.initializer);
107080
+ if (key === null || value === void 0) return void 0;
107081
+ object[key] = value;
107082
+ }
107083
+ return object;
107084
+ }
107085
+ default: return;
107086
+ }
107087
+ };
107088
+ /** An object literal key written as a name or a string, never a computed one. */
107089
+ var literalKey = (ts, name) => name.kind === ts.SyntaxKind["Identifier"] || name.kind === ts.SyntaxKind["StringLiteral"] ? name.text ?? null : null;
107090
+ /**
106852
107091
  * A plain string literal's text, and nothing else: narrower than `literalText`,
106853
107092
  * which also takes a template with no substitution.
106854
107093
  */
@@ -107080,6 +107319,14 @@ var MODELS_USE_METHOD = "use";
107080
107319
  var MODEL_PROPERTY = "model";
107081
107320
  /** The property an agent and each of its subagents names its provider routing in. */
107082
107321
  var ROUTING_PROPERTY = "modelRouting";
107322
+ /**
107323
+ * A key's routing as the manifest records it: the object, and its plain-sort twin,
107324
+ * which is all a runtime older than the object reads.
107325
+ */
107326
+ var routingFields = (routing) => ({
107327
+ defaultProviderRouting: routing,
107328
+ defaultProviderSort: plainSortOf(routing)
107329
+ });
107083
107330
  var MODEL_SCAN_MESSAGES = {
107084
107331
  noCompiler: "[models] cannot resolve typescript from the workspace, so the model inventory was not collected — Studio will not know which models this workspace declares. Install it as a dev dependency of the project.",
107085
107332
  unparsable: (file) => `[models] could not parse ${file}; the models it declares are missing from the inventory.`
@@ -107114,7 +107361,7 @@ var scanSource$1 = (source) => {
107114
107361
  key,
107115
107362
  defaultModelId: literalText(ts, call.arguments[1]),
107116
107363
  overridable: true,
107117
- defaultProviderSort: null,
107364
+ ...routingFields(null),
107118
107365
  file,
107119
107366
  line: lineOf(call)
107120
107367
  });
@@ -107138,7 +107385,7 @@ var recordModel = (existing, ref, warn) => {
107138
107385
  existing.push(ref);
107139
107386
  return;
107140
107387
  }
107141
- if (already.defaultModelId !== ref.defaultModelId || already.overridable !== ref.overridable || already.defaultProviderSort !== ref.defaultProviderSort) warn(`[models] \`${ref.key}\` is declared again in ${ref.file} with a different default. One key is one pick, so the first declaration (${already.file}:${already.line}) is the one an operator sees — make the two agree, or give them different keys.`, {
107388
+ if (already.defaultModelId !== ref.defaultModelId || already.overridable !== ref.overridable || JSON.stringify(already.defaultProviderRouting) !== JSON.stringify(ref.defaultProviderRouting)) warn(`[models] \`${ref.key}\` is declared again in ${ref.file} with a different default. One key is one pick, so the first declaration (${already.file}:${already.line}) is the one an operator sees — make the two agree, or give them different keys.`, {
107142
107389
  file: ref.file,
107143
107390
  line: ref.line
107144
107391
  });
@@ -107156,16 +107403,19 @@ var collectAgentModels = (source) => {
107156
107403
  const { ts, file, lineOf, at, warn } = source;
107157
107404
  const found = [];
107158
107405
  /**
107159
- * The `modelRouting` the config writes. `defineDeepAgent` refuses a value outside
107160
- * the sorts, so the agent will not load; one built at run time cannot be read
107161
- * here. Both are recorded as "no routing in the code", and the first is warned.
107406
+ * The `modelRouting` the config writes: a sort, or OpenRouter's `provider` object
107407
+ * written as a literal. `defineDeepAgent` refuses a value outside the schema, so
107408
+ * the agent will not load; one built at run time cannot be read here. Both are
107409
+ * recorded as "no routing in the code", and the first is warned.
107162
107410
  */
107163
107411
  const routingOf = (label, config) => {
107164
107412
  const value = propertyValue(ts, config, ROUTING_PROPERTY);
107165
107413
  if (value === void 0) return null;
107166
- const written = literalText(ts, value);
107167
- if (written === null || isModelProviderSort(written)) return written;
107168
- warn(`[models] ${label} in ${file} declares \`modelRouting\` '${written}', which is not one of ${MODEL_PROVIDER_SORTS_TEXT}. The agent is refused when it is defined.`, at(value));
107414
+ const written = literalJson(ts, value);
107415
+ if (written === void 0) return null;
107416
+ const parsed = modelRoutingInputSchema.safeParse(written);
107417
+ if (parsed.success) return toModelProviderRouting(parsed.data);
107418
+ warn(`[models] ${label} in ${file} declares \`modelRouting\` ${JSON.stringify(written)}, which is neither one of ${MODEL_PROVIDER_SORTS_TEXT} nor an OpenRouter \`provider\` object. The agent is refused when it is defined.`, at(value));
107169
107419
  return null;
107170
107420
  };
107171
107421
  /**
@@ -107177,13 +107427,13 @@ var collectAgentModels = (source) => {
107177
107427
  const declare = (args) => {
107178
107428
  const { label, key, config, node } = args;
107179
107429
  const declaration = propertyValue(ts, config, MODEL_PROPERTY);
107180
- const defaultProviderSort = routingOf(label, config);
107430
+ const routing = routingFields(routingOf(label, config));
107181
107431
  if (declaration === void 0) {
107182
107432
  found.push({
107183
107433
  key,
107184
107434
  defaultModelId: null,
107185
107435
  overridable: true,
107186
- defaultProviderSort,
107436
+ ...routing,
107187
107437
  file,
107188
107438
  line: lineOf(node)
107189
107439
  });
@@ -107196,7 +107446,7 @@ var collectAgentModels = (source) => {
107196
107446
  key,
107197
107447
  defaultModelId: blank ? null : written,
107198
107448
  overridable: written !== null,
107199
- defaultProviderSort,
107449
+ ...routing,
107200
107450
  file,
107201
107451
  line: lineOf(declaration)
107202
107452
  });