@stackbone/cli 0.9.0 → 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/CHANGELOG.md +37 -0
- package/main.js +343 -84
- package/migrations/0122_model_routing_object.sql +16 -0
- package/package.json +1 -1
- package/stackbone-cli-0.9.2.tgz +0 -0
- package/voice-worker/main.js +215 -58
- package/voice-worker/main.js.map +4 -4
- package/stackbone-cli-0.9.0.tgz +0 -0
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,
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
@@ -3673,13 +3730,21 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3673
3730
|
ambientSound: z.enum(VOICE_AMBIENT_SOUNDS),
|
|
3674
3731
|
ambientVolume: volumeSchema,
|
|
3675
3732
|
thinkingSound: z.enum(VOICE_THINKING_SOUNDS),
|
|
3676
|
-
thinkingVolume: volumeSchema
|
|
3733
|
+
thinkingVolume: volumeSchema,
|
|
3734
|
+
/**
|
|
3735
|
+
* How long a reply's wait runs before the thinking sound starts, when no box tool
|
|
3736
|
+
* call is open (a tool call starts it at once). Every turn waits for the knowledge
|
|
3737
|
+
* search and the model's first token, about 1.4 s at the median on a voice box
|
|
3738
|
+
* (2026-11-09), so a delay under that plays the sound on nearly every reply.
|
|
3739
|
+
*/
|
|
3740
|
+
thinkingDelayMs: millisecondsSchema
|
|
3677
3741
|
});
|
|
3678
3742
|
VOICE_BACKGROUND_AUDIO_DEFAULTS = Object.freeze({
|
|
3679
3743
|
ambientSound: "office-ambience",
|
|
3680
3744
|
ambientVolume: .7,
|
|
3681
3745
|
thinkingSound: "keyboard-typing",
|
|
3682
|
-
thinkingVolume: .6
|
|
3746
|
+
thinkingVolume: .6,
|
|
3747
|
+
thinkingDelayMs: 2e3
|
|
3683
3748
|
});
|
|
3684
3749
|
effectiveVoiceSettingsObjectSchema = z.strictObject({
|
|
3685
3750
|
/** BCP-47-ish tag the STT is hinted with and the TTS speaks (`es`, `en-US`), or `multi`. */
|
|
@@ -3687,7 +3752,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3687
3752
|
sttModel: z.enum(VOICE_STT_MODELS),
|
|
3688
3753
|
keyterms: keytermsSchema.default(() => []),
|
|
3689
3754
|
vad: vadSchema,
|
|
3690
|
-
turn: turnSchema,
|
|
3755
|
+
turn: turnSchema.extend({ eagerThreshold: turnSchema.shape.eagerThreshold.default(VOICE_EAGER_THRESHOLD_DEFAULT) }),
|
|
3691
3756
|
interruptions: interruptionsSchema,
|
|
3692
3757
|
ttsModel: z.enum(VOICE_TTS_MODELS),
|
|
3693
3758
|
/**
|
|
@@ -3749,7 +3814,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3749
3814
|
recording: true,
|
|
3750
3815
|
rag: true,
|
|
3751
3816
|
refusalPhrase: true
|
|
3752
|
-
}).extend({ backgroundAudio: backgroundAudioSchema.default(() => ({ ...VOICE_BACKGROUND_AUDIO_DEFAULTS })) });
|
|
3817
|
+
}).extend({ backgroundAudio: backgroundAudioSchema.extend({ thinkingDelayMs: millisecondsSchema.default(VOICE_BACKGROUND_AUDIO_DEFAULTS.thinkingDelayMs) }).default(() => ({ ...VOICE_BACKGROUND_AUDIO_DEFAULTS })) });
|
|
3753
3818
|
callVoiceSettingsSchema = callVoiceSettingsObjectSchema.superRefine(checkCrossFieldRules);
|
|
3754
3819
|
toCallVoiceSettings = ({ recording: _recording, rag: _rag, refusalPhrase: _refusalPhrase, ...call }) => ({
|
|
3755
3820
|
...call,
|
|
@@ -3766,6 +3831,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3766
3831
|
"turn.minEndpointingDelayMs",
|
|
3767
3832
|
"turn.maxEndpointingDelayMs",
|
|
3768
3833
|
"turn.detectorThreshold",
|
|
3834
|
+
"turn.eagerThreshold",
|
|
3769
3835
|
"interruptions.enabled",
|
|
3770
3836
|
"interruptions.minDurationMs",
|
|
3771
3837
|
"interruptions.minWords",
|
|
@@ -3779,6 +3845,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3779
3845
|
"backgroundAudio.ambientVolume",
|
|
3780
3846
|
"backgroundAudio.thinkingSound",
|
|
3781
3847
|
"backgroundAudio.thinkingVolume",
|
|
3848
|
+
"backgroundAudio.thinkingDelayMs",
|
|
3782
3849
|
"recording",
|
|
3783
3850
|
"rag.collection",
|
|
3784
3851
|
"rag.minScore",
|
|
@@ -3794,9 +3861,10 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3794
3861
|
"code",
|
|
3795
3862
|
"default"
|
|
3796
3863
|
]);
|
|
3864
|
+
LEAVES_NEWER_THAN_SOME_BOXES = new Set(["turn.eagerThreshold"]);
|
|
3797
3865
|
effectiveVoiceSettingsViewSchema = z.strictObject({
|
|
3798
3866
|
value: effectiveVoiceSettingsObjectSchema,
|
|
3799
|
-
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])))
|
|
3800
3868
|
});
|
|
3801
3869
|
checkTtsPair = (value, ctx) => {
|
|
3802
3870
|
if (value.ttsModel !== void 0 && value.ttsVoice === void 0) ctx.addIssue({
|
|
@@ -3906,6 +3974,7 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3906
3974
|
aguiHeardSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
|
|
3907
3975
|
VOICE_CALLS_PATH = "/voice/v1/calls";
|
|
3908
3976
|
voiceCallBootstrapSchema = z.strictObject({ identities: integrationIdentitiesSchema.optional() });
|
|
3977
|
+
voiceCallKnowledgeSchema = z.strictObject({ text: z.string().min(1).max(HEARD_TEXT_MAX) });
|
|
3909
3978
|
voiceCallTranscriptSchema = z.array(z.strictObject({
|
|
3910
3979
|
role: z.enum(["user", "assistant"]),
|
|
3911
3980
|
text: z.string().trim().min(1).max(16e3)
|
|
@@ -3920,7 +3989,9 @@ var init_studio_voice = __esmMin((() => {
|
|
|
3920
3989
|
transcriptionMs: turnLatencyMs,
|
|
3921
3990
|
llmTtftMs: turnLatencyMs,
|
|
3922
3991
|
ttsTtfbMs: turnLatencyMs,
|
|
3923
|
-
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
|
|
3924
3995
|
});
|
|
3925
3996
|
voiceCallTurnsSchema = z.array(voiceCallTurnMetricsSchema).max(VOICE_CALL_TURNS_MAX);
|
|
3926
3997
|
readVoiceCallTurns = (value) => {
|
|
@@ -3956,7 +4027,8 @@ var init_effective_voice_settings = __esmMin((() => {
|
|
|
3956
4027
|
mode: "stt",
|
|
3957
4028
|
minEndpointingDelayMs: 0,
|
|
3958
4029
|
maxEndpointingDelayMs: 1200,
|
|
3959
|
-
detectorThreshold: .35
|
|
4030
|
+
detectorThreshold: .35,
|
|
4031
|
+
eagerThreshold: VOICE_EAGER_THRESHOLD_DEFAULT
|
|
3960
4032
|
}),
|
|
3961
4033
|
interruptions: Object.freeze({
|
|
3962
4034
|
enabled: true,
|
|
@@ -6009,6 +6081,15 @@ var init_studio_catalog = __esmMin((() => {
|
|
|
6009
6081
|
/** The provider routing an operator picked for this key, or `null` when nobody has. */
|
|
6010
6082
|
selectedProviderSort: modelProviderSortSchema.nullable().default(null),
|
|
6011
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
|
+
/**
|
|
6012
6093
|
* `declared` — the code declares this key. `unused` — only a pick exists, for a
|
|
6013
6094
|
* key no declaration mentions any more; resetting it is the one thing left to do.
|
|
6014
6095
|
* An entry NOTHING scanned never carries an `unused` row: see the box's
|
|
@@ -9470,7 +9551,8 @@ var init_model_routing = __esmMin((() => {
|
|
|
9470
9551
|
ownerKind: text("owner_kind").notNull(),
|
|
9471
9552
|
ownerName: text("owner_name").notNull(),
|
|
9472
9553
|
key: text("key").notNull(),
|
|
9473
|
-
sort: text("sort")
|
|
9554
|
+
sort: text("sort"),
|
|
9555
|
+
routing: jsonb("routing"),
|
|
9474
9556
|
/** The acting user's id, or null on the un-authenticated `stackbone dev` datapath. */
|
|
9475
9557
|
updatedBy: text("updated_by"),
|
|
9476
9558
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
@@ -11397,7 +11479,7 @@ var init_json = __esmMin((() => {
|
|
|
11397
11479
|
//#region ../../libs/shared/model-provider-wire/src/lib/common/provider-routing.ts
|
|
11398
11480
|
var providerRoutingFor, withProviderRouting;
|
|
11399
11481
|
var init_provider_routing = __esmMin((() => {
|
|
11400
|
-
providerRoutingFor = (
|
|
11482
|
+
providerRoutingFor = (routing) => Object.freeze({ ...routing });
|
|
11401
11483
|
withProviderRouting = (body, kept) => {
|
|
11402
11484
|
if (kept || !("provider" in body)) return body;
|
|
11403
11485
|
const next = { ...body };
|
|
@@ -19604,6 +19686,7 @@ var init_learn_control_plane_identity = __esmMin((() => {
|
|
|
19604
19686
|
}));
|
|
19605
19687
|
//#endregion
|
|
19606
19688
|
//#region ../../libs/runtime/workspace-runtime/src/lib/catalog/catalog-projector.ts
|
|
19689
|
+
init_src$26();
|
|
19607
19690
|
/**
|
|
19608
19691
|
* `${kind}:${name}` — the identity a catalog row, a declaration, a prompt owner and
|
|
19609
19692
|
* a selection's owner all agree on. The kind is half of it, never decoration: a deep
|
|
@@ -19765,16 +19848,26 @@ var asDeclaredModel = (ref) => ({
|
|
|
19765
19848
|
overridable: ref.overridable,
|
|
19766
19849
|
file: ref.file,
|
|
19767
19850
|
line: ref.line,
|
|
19768
|
-
|
|
19851
|
+
...defaultRoutingFields(declaredRouting(ref)),
|
|
19769
19852
|
selectedModelId: null,
|
|
19770
19853
|
selectedProviderId: null,
|
|
19771
|
-
|
|
19854
|
+
...selectedRoutingFields(null),
|
|
19772
19855
|
state: "declared"
|
|
19773
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
|
+
});
|
|
19774
19867
|
var selectedFields = (pick) => ({
|
|
19775
19868
|
selectedModelId: pick?.modelId ?? null,
|
|
19776
19869
|
selectedProviderId: pick?.providerId ?? null,
|
|
19777
|
-
|
|
19870
|
+
...selectedRoutingFields(pick?.providerRouting ?? null)
|
|
19778
19871
|
});
|
|
19779
19872
|
/**
|
|
19780
19873
|
* Cross what the code DECLARES against what an operator PICKED, for every row at
|
|
@@ -19808,7 +19901,7 @@ var attachModelSelections = (entries, selections) => entries.map((entry) => {
|
|
|
19808
19901
|
overridable: true,
|
|
19809
19902
|
file: null,
|
|
19810
19903
|
line: null,
|
|
19811
|
-
|
|
19904
|
+
...defaultRoutingFields(null),
|
|
19812
19905
|
...selectedFields(pick),
|
|
19813
19906
|
state: "unused"
|
|
19814
19907
|
}));
|
|
@@ -22665,7 +22758,7 @@ var init_role = __esmMin((() => {
|
|
|
22665
22758
|
var STACKBONE_CLI_BUILD_VERSION, EMULATOR_CAPABILITY_ROUTES, EMULATOR_CAPABILITIES, buildContractResponse;
|
|
22666
22759
|
var init_contract = __esmMin((() => {
|
|
22667
22760
|
init_src$26();
|
|
22668
|
-
STACKBONE_CLI_BUILD_VERSION = "0.9.
|
|
22761
|
+
STACKBONE_CLI_BUILD_VERSION = "0.9.2";
|
|
22669
22762
|
EMULATOR_CAPABILITY_ROUTES = {
|
|
22670
22763
|
"database.postgres_direct": "/api/db",
|
|
22671
22764
|
"rag.basic": "/api/rag",
|
|
@@ -28254,7 +28347,7 @@ var init_api_docs = __esmMin((() => {
|
|
|
28254
28347
|
},
|
|
28255
28348
|
{
|
|
28256
28349
|
name: VOICE_WORKER_TAG,
|
|
28257
|
-
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."
|
|
28258
28351
|
}
|
|
28259
28352
|
];
|
|
28260
28353
|
SURFACE_TAG_DESCRIPTIONS = {
|
|
@@ -41631,6 +41724,11 @@ var init_mutations_outside_catalog = __esmMin((() => {
|
|
|
41631
41724
|
path: "/voice/v1/calls/{sessionKey}/bootstrap",
|
|
41632
41725
|
reason: "The accepted call freezes structured identity and recalls memory using its own call token, without an operator seat."
|
|
41633
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
|
+
},
|
|
41634
41732
|
{
|
|
41635
41733
|
method: "POST",
|
|
41636
41734
|
path: "/voice/v1/calls/{sessionKey}/complete",
|
|
@@ -65513,12 +65611,12 @@ var init_registry = __esmMin((() => {
|
|
|
65513
65611
|
};
|
|
65514
65612
|
const instructions = read(() => def.resolvedInstructions?.());
|
|
65515
65613
|
const model = read(() => def.resolvedModel?.());
|
|
65516
|
-
const
|
|
65517
|
-
const {
|
|
65614
|
+
const providerRouting = readModelProviderRouting(read(() => def.resolvedProviderRouting?.()) ?? read(() => def.resolvedProviderSort?.()));
|
|
65615
|
+
const { providerRouting: _previous, ...rest } = metadata;
|
|
65518
65616
|
return {
|
|
65519
65617
|
...rest,
|
|
65520
65618
|
...typeof model === "string" && model !== "" ? { model } : {},
|
|
65521
|
-
...
|
|
65619
|
+
...providerRouting !== null ? { providerRouting } : {},
|
|
65522
65620
|
...typeof instructions === "string" && instructions !== "" ? { instructions } : {}
|
|
65523
65621
|
};
|
|
65524
65622
|
};
|
|
@@ -73118,7 +73216,7 @@ var selectionsSelect = (kinds, names) => sql`
|
|
|
73118
73216
|
SELECT COALESCE(s.owner_kind, r.owner_kind) AS owner_kind,
|
|
73119
73217
|
COALESCE(s.owner_name, r.owner_name) AS owner_name,
|
|
73120
73218
|
COALESCE(s.key, r.key) AS key,
|
|
73121
|
-
s.model_id, s.provider_id, r.sort
|
|
73219
|
+
s.model_id, s.provider_id, r.sort, r.routing
|
|
73122
73220
|
FROM ${modelSelections} s
|
|
73123
73221
|
FULL OUTER JOIN ${modelRouting} r
|
|
73124
73222
|
ON r.owner_kind = s.owner_kind AND r.owner_name = s.owner_name AND r.key = s.key
|
|
@@ -73139,7 +73237,7 @@ var createModelSelectionsReader = (opts) => {
|
|
|
73139
73237
|
keys.set(row.key, {
|
|
73140
73238
|
modelId: row.model_id,
|
|
73141
73239
|
providerId: row.provider_id,
|
|
73142
|
-
|
|
73240
|
+
providerRouting: readModelProviderRouting(row.routing ?? row.sort)
|
|
73143
73241
|
});
|
|
73144
73242
|
byOwner.set(id, keys);
|
|
73145
73243
|
}
|
|
@@ -86674,14 +86772,19 @@ var notFound$1 = {
|
|
|
86674
86772
|
code: "studio_model_selection_not_found",
|
|
86675
86773
|
message: "No model is picked for that key. Its code default already applies."
|
|
86676
86774
|
};
|
|
86677
|
-
var rowToRouting = (row) =>
|
|
86678
|
-
|
|
86679
|
-
|
|
86680
|
-
|
|
86681
|
-
|
|
86682
|
-
|
|
86683
|
-
|
|
86684
|
-
|
|
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
|
+
};
|
|
86685
86788
|
var routingNotFound = {
|
|
86686
86789
|
code: "studio_model_routing_not_found",
|
|
86687
86790
|
message: "No provider routing is picked for that key. The code's `modelRouting` already applies."
|
|
@@ -86811,10 +86914,10 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86811
86914
|
tags: [MODEL_SELECTIONS_TAG],
|
|
86812
86915
|
middleware: [requireCapability("config:write")],
|
|
86813
86916
|
summary: "Pick the provider routing for one key",
|
|
86814
|
-
description: "Sets which OpenRouter
|
|
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.",
|
|
86815
86918
|
request: {
|
|
86816
86919
|
params: studioModelRoutingPathSchema,
|
|
86817
|
-
body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider sort.")
|
|
86920
|
+
body: jsonBody(named("PutStudioModelRoutingRequest", putStudioModelRoutingRequestSchema), "The provider routing, or a plain sort.")
|
|
86818
86921
|
},
|
|
86819
86922
|
responses: {
|
|
86820
86923
|
200: jsonResponse$1(named("StudioModelRouting", studioModelRoutingSchema), "The routing as it now stands."),
|
|
@@ -86823,12 +86926,14 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86823
86926
|
}
|
|
86824
86927
|
}), async (c) => {
|
|
86825
86928
|
const path = c.req.valid("param");
|
|
86826
|
-
const
|
|
86929
|
+
const routing = routingOfPutRequest(c.req.valid("json"));
|
|
86930
|
+
const sort = plainSortOf(routing);
|
|
86827
86931
|
const actor = readActorUserId(c);
|
|
86828
86932
|
const row = await opts.db.transaction(async (tx) => {
|
|
86829
86933
|
const [written] = await tx.insert(modelRouting).values({
|
|
86830
86934
|
...path,
|
|
86831
86935
|
sort,
|
|
86936
|
+
routing,
|
|
86832
86937
|
updatedBy: actor
|
|
86833
86938
|
}).onConflictDoUpdate({
|
|
86834
86939
|
target: [
|
|
@@ -86838,6 +86943,7 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86838
86943
|
],
|
|
86839
86944
|
set: {
|
|
86840
86945
|
sort,
|
|
86946
|
+
routing,
|
|
86841
86947
|
updatedBy: actor,
|
|
86842
86948
|
updatedAt: sql`now()`
|
|
86843
86949
|
}
|
|
@@ -86848,7 +86954,8 @@ var createModelSelectionsRouter = (opts) => {
|
|
|
86848
86954
|
subjectType: "model_selection",
|
|
86849
86955
|
payload: {
|
|
86850
86956
|
...selectionSubject(path),
|
|
86851
|
-
sort
|
|
86957
|
+
...sort !== null ? { sort } : {},
|
|
86958
|
+
routing
|
|
86852
86959
|
}
|
|
86853
86960
|
}, tx);
|
|
86854
86961
|
return written;
|
|
@@ -91331,9 +91438,9 @@ var lookUpLiveKitCredentials = async (opts) => {
|
|
|
91331
91438
|
credentials = await readLiveKitCredentials(opts);
|
|
91332
91439
|
} catch (err) {
|
|
91333
91440
|
if (!(err instanceof SecretUndecryptableError)) throw err;
|
|
91334
|
-
return voiceNotConfigured("This box cannot read the saved LiveKit credentials: they were saved with another key. Open the Voice
|
|
91441
|
+
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.");
|
|
91335
91442
|
}
|
|
91336
|
-
if (!credentials) return voiceNotConfigured("This box has no LiveKit credentials. Save the URL, API key and API secret on the Voice
|
|
91443
|
+
if (!credentials) return voiceNotConfigured("This box has no LiveKit credentials. Save the URL, API key and API secret on the Voice provider screen.");
|
|
91337
91444
|
return { credentials };
|
|
91338
91445
|
};
|
|
91339
91446
|
/**
|
|
@@ -93377,11 +93484,17 @@ var answerOf = async (storage, row) => {
|
|
|
93377
93484
|
};
|
|
93378
93485
|
//#endregion
|
|
93379
93486
|
//#region ../../libs/runtime/workspace-runtime/src/lib/voice/voice-rag.ts
|
|
93380
|
-
var WORD_CHARACTER, spokenWordCount, knowledgeQuery, selectKnowledge;
|
|
93487
|
+
var WORD_CHARACTER, spokenWordCount, knowledgeQuery, PUNCTUATION_OR_SYMBOL_RUN, prefetchSentence, prefetchKey, selectKnowledge;
|
|
93381
93488
|
var init_voice_rag = __esmMin((() => {
|
|
93382
93489
|
WORD_CHARACTER = /[\p{L}\p{N}]/u;
|
|
93383
93490
|
spokenWordCount = (text) => text.trim().split(/\s+/u).filter((token) => WORD_CHARACTER.test(token)).length;
|
|
93384
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
|
+
});
|
|
93385
93498
|
selectKnowledge = (hits, { minScore, topK }) => {
|
|
93386
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);
|
|
93387
93500
|
return passages.length === 0 ? void 0 : JSON.stringify(passages);
|
|
@@ -93427,7 +93540,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93427
93540
|
const words = spokenWordCount(text);
|
|
93428
93541
|
return words !== 0 && words >= rag.minWords;
|
|
93429
93542
|
};
|
|
93430
|
-
const searchKnowledge = async (call, text) => {
|
|
93543
|
+
const searchKnowledge = async (call, text, origin = "turn") => {
|
|
93431
93544
|
const { rag } = call;
|
|
93432
93545
|
if (!searchable(rag, text)) return void 0;
|
|
93433
93546
|
const { collection } = rag;
|
|
@@ -93435,6 +93548,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93435
93548
|
exchanges: call.exchanges,
|
|
93436
93549
|
sentence: text
|
|
93437
93550
|
});
|
|
93551
|
+
const search = origin === "prefetch" ? "rag_prefetch" : "rag_search";
|
|
93438
93552
|
const warn = (code) => options.logger.warn(`voice ${call.input.agent}/${call.input.sessionKey}: ${code}.`);
|
|
93439
93553
|
try {
|
|
93440
93554
|
const result = await withTimeout(retrieve({
|
|
@@ -93443,7 +93557,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93443
93557
|
topK: rag.topK
|
|
93444
93558
|
}), rag.timeoutMs);
|
|
93445
93559
|
if (result.error) {
|
|
93446
|
-
warn(
|
|
93560
|
+
warn(`${search}_failed (${result.error.code})`);
|
|
93447
93561
|
return;
|
|
93448
93562
|
}
|
|
93449
93563
|
if (result.data.length === 0 && !call.ragEmptyWarned) {
|
|
@@ -93452,11 +93566,31 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93452
93566
|
}
|
|
93453
93567
|
return selectKnowledge(result.data, rag);
|
|
93454
93568
|
} catch (error) {
|
|
93455
|
-
if (error instanceof ActionTimeoutError) warn(
|
|
93456
|
-
else warn(
|
|
93569
|
+
if (error instanceof ActionTimeoutError) warn(`${search}_timeout`);
|
|
93570
|
+
else warn(`${search}_failed (${error instanceof Error ? error.name : typeof error})`);
|
|
93457
93571
|
}
|
|
93458
93572
|
};
|
|
93573
|
+
let inputGuard;
|
|
93459
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
|
+
},
|
|
93460
93594
|
accept: async (input) => {
|
|
93461
93595
|
const registry = options.getRegistry();
|
|
93462
93596
|
if (!registry) throw new Error("Voice call memory service is unavailable.");
|
|
@@ -93470,6 +93604,7 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93470
93604
|
rag: input.rag && Object.freeze({ ...input.rag }),
|
|
93471
93605
|
refusalPhrase: input.refusalPhrase ?? VOICE_SETTINGS_DEFAULTS.refusalPhrase,
|
|
93472
93606
|
exchanges: [],
|
|
93607
|
+
prefetches: 0,
|
|
93473
93608
|
startedAt,
|
|
93474
93609
|
expiresAt: startedAt + CALL_TOKEN_TTL_SECONDS * 1e3,
|
|
93475
93610
|
work: /* @__PURE__ */ new Set()
|
|
@@ -93536,7 +93671,21 @@ var init_voice_calls = __esmMin((() => {
|
|
|
93536
93671
|
done();
|
|
93537
93672
|
};
|
|
93538
93673
|
},
|
|
93539
|
-
knowledgeFor: (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
|
+
}
|
|
93540
93689
|
});
|
|
93541
93690
|
call.context = context;
|
|
93542
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.`));
|
|
@@ -93625,7 +93774,7 @@ var createVoiceRoomsRouter = (options) => {
|
|
|
93625
93774
|
middleware: [requireCapability(VOICE_CAPABILITY$1)],
|
|
93626
93775
|
"x-stackbone-catalog": excludeFromCatalog("Answers a LiveKit participant token. The operation catalog never offers an operation whose answer is a credential."),
|
|
93627
93776
|
summary: "Open a voice call with a deep agent",
|
|
93628
|
-
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.",
|
|
93629
93778
|
request: { body: jsonBody(named("VoiceRoomRequest", voiceRoomRequestSchema), "The agent to call.") },
|
|
93630
93779
|
responses: {
|
|
93631
93780
|
200: jsonResponse$1(named("VoiceRoomResponse", voiceRoomResponseSchema), "The room, its participant token, and what the dispatch carries."),
|
|
@@ -94083,18 +94232,21 @@ var createVoiceCallsRouter = (options) => {
|
|
|
94083
94232
|
options.logger.warn(principal?.type === "call" ? `voice ${principal.agent}/${principal.sessionKey}: lifecycle_failed.` : "voice lifecycle request failed.");
|
|
94084
94233
|
return c.json(errorEnvelope("voice_lifecycle_failed", "The voice lifecycle operation failed."), 500);
|
|
94085
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
|
+
};
|
|
94086
94243
|
const shared = {
|
|
94087
94244
|
method: "post",
|
|
94088
94245
|
tags: ["voice-worker"],
|
|
94089
94246
|
security: [{ voiceCallToken: [] }],
|
|
94090
94247
|
responses: {
|
|
94091
|
-
|
|
94092
|
-
400: errorResponse("Invalid bounded call payload."),
|
|
94093
|
-
401: errorResponse("Missing, invalid or expired call token."),
|
|
94094
|
-
403: errorResponse("Only the call token for this session is admitted."),
|
|
94095
|
-
404: errorResponse("No accepted call for this credential."),
|
|
94248
|
+
...callResponses,
|
|
94096
94249
|
409: errorResponse("The call identity is frozen or the call is completing."),
|
|
94097
|
-
413: errorResponse("The payload exceeds 1 MiB."),
|
|
94098
94250
|
500: errorResponse("Settings, recall or extraction failed.")
|
|
94099
94251
|
}
|
|
94100
94252
|
};
|
|
@@ -94112,6 +94264,22 @@ var createVoiceCallsRouter = (options) => {
|
|
|
94112
94264
|
await options.calls.bootstrap(principal.agent, principal.sessionKey, c.req.valid("json").identities);
|
|
94113
94265
|
return c.json({ ok: true }, 200);
|
|
94114
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
|
+
});
|
|
94115
94283
|
app.openapi(createRoute({
|
|
94116
94284
|
...shared,
|
|
94117
94285
|
path: "/{sessionKey}/complete",
|
|
@@ -94326,7 +94494,7 @@ var modelWarmupBody = (target) => ({
|
|
|
94326
94494
|
content: WARMUP_USER_TEXT
|
|
94327
94495
|
}],
|
|
94328
94496
|
reasoning: { enabled: true },
|
|
94329
|
-
...target.
|
|
94497
|
+
...target.providerRouting ? { provider: providerRoutingFor(target.providerRouting) } : {}
|
|
94330
94498
|
});
|
|
94331
94499
|
var createModelWarmup = (deps) => {
|
|
94332
94500
|
const timeoutMs = deps.timeoutMs ?? 2e4;
|
|
@@ -94337,7 +94505,7 @@ var createModelWarmup = (deps) => {
|
|
|
94337
94505
|
const body = modelWarmupBody({
|
|
94338
94506
|
model: entry.metadata.model,
|
|
94339
94507
|
instructions: entry.metadata.instructions,
|
|
94340
|
-
|
|
94508
|
+
providerRouting: entry.metadata.providerRouting
|
|
94341
94509
|
});
|
|
94342
94510
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
94343
94511
|
try {
|
|
@@ -95137,7 +95305,7 @@ async function probeDurableInterruptState(graph, threadId, logger) {
|
|
|
95137
95305
|
return { supported: false };
|
|
95138
95306
|
}
|
|
95139
95307
|
}
|
|
95140
|
-
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;
|
|
95141
95309
|
var init_chat = __esmMin((() => {
|
|
95142
95310
|
init_studio_continuation();
|
|
95143
95311
|
init_abortable();
|
|
@@ -95209,10 +95377,7 @@ var init_chat = __esmMin((() => {
|
|
|
95209
95377
|
async function readPolicy(agentName) {
|
|
95210
95378
|
if (!opts.guardrails) return [];
|
|
95211
95379
|
try {
|
|
95212
|
-
return await opts.guardrails
|
|
95213
|
-
kind: "agent",
|
|
95214
|
-
name: agentName
|
|
95215
|
-
});
|
|
95380
|
+
return await agentPolicy(opts.guardrails, agentName);
|
|
95216
95381
|
} catch (err) {
|
|
95217
95382
|
logger.warn(`deep-agent guardrail policy read failed for "${agentName}" — the turn runs unguarded: ${messageOf$6(err)}`);
|
|
95218
95383
|
return [];
|
|
@@ -95240,13 +95405,7 @@ var init_chat = __esmMin((() => {
|
|
|
95240
95405
|
if (guardrails.length === 0) return unguarded;
|
|
95241
95406
|
try {
|
|
95242
95407
|
const original = lastUserMessage(turn.messages);
|
|
95243
|
-
const
|
|
95244
|
-
const result = await evaluateGuardrails({
|
|
95245
|
-
guardrails,
|
|
95246
|
-
content: original,
|
|
95247
|
-
target: "input",
|
|
95248
|
-
...classify ? { classify } : {}
|
|
95249
|
-
});
|
|
95408
|
+
const result = await evaluateInput(guardrails, original, classifierFor(signal));
|
|
95250
95409
|
return {
|
|
95251
95410
|
turn: result.content === original ? turn : withLastUserMessage(turn, result.content),
|
|
95252
95411
|
blocked: result.allowed ? null : result.decision,
|
|
@@ -95259,6 +95418,27 @@ var init_chat = __esmMin((() => {
|
|
|
95259
95418
|
}
|
|
95260
95419
|
}
|
|
95261
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
|
+
/**
|
|
95262
95442
|
* Wrap a turn's stream with the OUTPUT border: the reply is held until the policy
|
|
95263
95443
|
* has judged it, then released, released masked, or dropped with a reason.
|
|
95264
95444
|
*
|
|
@@ -95564,7 +95744,10 @@ var init_chat = __esmMin((() => {
|
|
|
95564
95744
|
const guardedTurn = guarded.turn;
|
|
95565
95745
|
const blocked = guarded.blocked;
|
|
95566
95746
|
const userMessage = lastUserMessage(guardedTurn.messages);
|
|
95567
|
-
|
|
95747
|
+
let prefetch;
|
|
95748
|
+
const knowledge = registered && blocked === null ? voiceCall?.knowledgeFor(userMessage, (outcome) => {
|
|
95749
|
+
prefetch = outcome;
|
|
95750
|
+
}) : void 0;
|
|
95568
95751
|
const runInput = {
|
|
95569
95752
|
trigger,
|
|
95570
95753
|
input: { message: userMessage },
|
|
@@ -95574,6 +95757,7 @@ var init_chat = __esmMin((() => {
|
|
|
95574
95757
|
memoryIdentity,
|
|
95575
95758
|
...voiceCall ? { voiceCall: true } : {},
|
|
95576
95759
|
...voiceCall?.searchesKnowledge ? { voiceKnowledgeRule: true } : {},
|
|
95760
|
+
...prefetch ? { voiceKnowledgePrefetch: prefetch } : {},
|
|
95577
95761
|
...groupingKey !== void 0 ? { sessionKey: groupingKey } : {},
|
|
95578
95762
|
...turn.evalRunId !== void 0 ? evalRunStamp({
|
|
95579
95763
|
evalRunId: turn.evalRunId,
|
|
@@ -96308,6 +96492,7 @@ var init_chat = __esmMin((() => {
|
|
|
96308
96492
|
getMessageHistory,
|
|
96309
96493
|
seedState,
|
|
96310
96494
|
reconcileHeard,
|
|
96495
|
+
guardInput,
|
|
96311
96496
|
runTurn: (turn) => ownedByAgent(turn.agent, runTurn(turn)),
|
|
96312
96497
|
resumeTurn: ownedResumeTurn
|
|
96313
96498
|
};
|
|
@@ -96334,6 +96519,16 @@ var init_chat = __esmMin((() => {
|
|
|
96334
96519
|
...guardrailParkRow(decision, approvalId),
|
|
96335
96520
|
status: "running"
|
|
96336
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
|
+
});
|
|
96337
96532
|
}));
|
|
96338
96533
|
//#endregion
|
|
96339
96534
|
//#region ../../libs/runtime/workspace-runtime/src/lib/deep-agents/http/run-tags-header.ts
|
|
@@ -96992,10 +97187,12 @@ var init_mount$1 = __esmMin((() => {
|
|
|
96992
97187
|
...options.classify ? { classify: options.classify } : {},
|
|
96993
97188
|
...options.plantAmbientBridges ? { plantAmbientBridges: options.plantAmbientBridges } : {}
|
|
96994
97189
|
});
|
|
97190
|
+
const voiceCalls = voiceCallsForApp(app);
|
|
97191
|
+
voiceCalls?.useInputGuard(chat.guardInput);
|
|
96995
97192
|
const wire = {
|
|
96996
97193
|
sessions: options.sessions,
|
|
96997
97194
|
invocations: options.invocations,
|
|
96998
|
-
voiceCalls
|
|
97195
|
+
voiceCalls,
|
|
96999
97196
|
chat,
|
|
97000
97197
|
registry,
|
|
97001
97198
|
logger,
|
|
@@ -104326,7 +104523,7 @@ var calls_workflow_ts_default = "import { z } from \"zod\";\nimport { streamDeep
|
|
|
104326
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";
|
|
104327
104524
|
//#endregion
|
|
104328
104525
|
//#region src/services/scaffold/template-loader.ts
|
|
104329
|
-
var SDK_TEMPLATE_VERSION = "0.10.
|
|
104526
|
+
var SDK_TEMPLATE_VERSION = "0.10.2";
|
|
104330
104527
|
var PREFIX = "./templates/";
|
|
104331
104528
|
var templates = new Map(Object.entries(/* @__PURE__ */ Object.assign({
|
|
104332
104529
|
"./templates/deep-agents/index.ts.tmpl": index_ts_default,
|
|
@@ -106449,7 +106646,7 @@ var buildWorkspaceBundle = async (options) => {
|
|
|
106449
106646
|
const artifactPath = resolve(outDir, CONFIG_SCHEMA_ARTIFACT_PATH);
|
|
106450
106647
|
await mkdir(dirname(artifactPath), { recursive: true });
|
|
106451
106648
|
await writeFile(artifactPath, JSON.stringify(configSchema), "utf8");
|
|
106452
|
-
} else logger?.warn(`This workspace declares ${CONFIG_SCHEMA_SOURCE}, but no config JSON Schema was produced for the bundle. Studio
|
|
106649
|
+
} else logger?.warn(`This workspace declares ${CONFIG_SCHEMA_SOURCE}, but no config JSON Schema was produced for the bundle. Studio shows no Dynamic config section (edit the values with \`stackbone config\`) and writes will not be validated.`);
|
|
106453
106650
|
}
|
|
106454
106651
|
const manifest = deriveWorkspaceManifest({
|
|
106455
106652
|
workspace,
|
|
@@ -106552,7 +106749,7 @@ var extractConfigSchema = async (projectRoot, opts = {}) => {
|
|
|
106552
106749
|
try {
|
|
106553
106750
|
loaded = await loader(projectRoot);
|
|
106554
106751
|
} catch (err) {
|
|
106555
|
-
emit(`[config-schema] config schema extraction bundle failed; no config schema is available —
|
|
106752
|
+
emit(`[config-schema] config schema extraction bundle failed; no config schema is available — Studio shows no Dynamic config section; edit the values with \`stackbone config\`. Cause: ${errMessage$3(err)}`);
|
|
106556
106753
|
return {
|
|
106557
106754
|
schema: null,
|
|
106558
106755
|
warnings
|
|
@@ -106568,7 +106765,7 @@ var extractConfigSchema = async (projectRoot, opts = {}) => {
|
|
|
106568
106765
|
warnings
|
|
106569
106766
|
};
|
|
106570
106767
|
} catch (err) {
|
|
106571
|
-
emit(`[config-schema] config.schema.ts could not be converted to JSON Schema —
|
|
106768
|
+
emit(`[config-schema] config.schema.ts could not be converted to JSON Schema — Studio shows no Dynamic config section; edit the values with \`stackbone config\`. Cause: ${errMessage$3(err)}`);
|
|
106572
106769
|
return {
|
|
106573
106770
|
schema: null,
|
|
106574
106771
|
warnings
|
|
@@ -106840,6 +107037,57 @@ var literalText = (ts, node) => {
|
|
|
106840
107037
|
return null;
|
|
106841
107038
|
};
|
|
106842
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
|
+
/**
|
|
106843
107091
|
* A plain string literal's text, and nothing else: narrower than `literalText`,
|
|
106844
107092
|
* which also takes a template with no substitution.
|
|
106845
107093
|
*/
|
|
@@ -107071,6 +107319,14 @@ var MODELS_USE_METHOD = "use";
|
|
|
107071
107319
|
var MODEL_PROPERTY = "model";
|
|
107072
107320
|
/** The property an agent and each of its subagents names its provider routing in. */
|
|
107073
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
|
+
});
|
|
107074
107330
|
var MODEL_SCAN_MESSAGES = {
|
|
107075
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.",
|
|
107076
107332
|
unparsable: (file) => `[models] could not parse ${file}; the models it declares are missing from the inventory.`
|
|
@@ -107105,7 +107361,7 @@ var scanSource$1 = (source) => {
|
|
|
107105
107361
|
key,
|
|
107106
107362
|
defaultModelId: literalText(ts, call.arguments[1]),
|
|
107107
107363
|
overridable: true,
|
|
107108
|
-
|
|
107364
|
+
...routingFields(null),
|
|
107109
107365
|
file,
|
|
107110
107366
|
line: lineOf(call)
|
|
107111
107367
|
});
|
|
@@ -107129,7 +107385,7 @@ var recordModel = (existing, ref, warn) => {
|
|
|
107129
107385
|
existing.push(ref);
|
|
107130
107386
|
return;
|
|
107131
107387
|
}
|
|
107132
|
-
if (already.defaultModelId !== ref.defaultModelId || already.overridable !== ref.overridable || already.
|
|
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.`, {
|
|
107133
107389
|
file: ref.file,
|
|
107134
107390
|
line: ref.line
|
|
107135
107391
|
});
|
|
@@ -107147,16 +107403,19 @@ var collectAgentModels = (source) => {
|
|
|
107147
107403
|
const { ts, file, lineOf, at, warn } = source;
|
|
107148
107404
|
const found = [];
|
|
107149
107405
|
/**
|
|
107150
|
-
* The `modelRouting` the config writes
|
|
107151
|
-
*
|
|
107152
|
-
*
|
|
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.
|
|
107153
107410
|
*/
|
|
107154
107411
|
const routingOf = (label, config) => {
|
|
107155
107412
|
const value = propertyValue(ts, config, ROUTING_PROPERTY);
|
|
107156
107413
|
if (value === void 0) return null;
|
|
107157
|
-
const written =
|
|
107158
|
-
if (written ===
|
|
107159
|
-
|
|
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));
|
|
107160
107419
|
return null;
|
|
107161
107420
|
};
|
|
107162
107421
|
/**
|
|
@@ -107168,13 +107427,13 @@ var collectAgentModels = (source) => {
|
|
|
107168
107427
|
const declare = (args) => {
|
|
107169
107428
|
const { label, key, config, node } = args;
|
|
107170
107429
|
const declaration = propertyValue(ts, config, MODEL_PROPERTY);
|
|
107171
|
-
const
|
|
107430
|
+
const routing = routingFields(routingOf(label, config));
|
|
107172
107431
|
if (declaration === void 0) {
|
|
107173
107432
|
found.push({
|
|
107174
107433
|
key,
|
|
107175
107434
|
defaultModelId: null,
|
|
107176
107435
|
overridable: true,
|
|
107177
|
-
|
|
107436
|
+
...routing,
|
|
107178
107437
|
file,
|
|
107179
107438
|
line: lineOf(node)
|
|
107180
107439
|
});
|
|
@@ -107187,7 +107446,7 @@ var collectAgentModels = (source) => {
|
|
|
107187
107446
|
key,
|
|
107188
107447
|
defaultModelId: blank ? null : written,
|
|
107189
107448
|
overridable: written !== null,
|
|
107190
|
-
|
|
107449
|
+
...routing,
|
|
107191
107450
|
file,
|
|
107192
107451
|
line: lineOf(declaration)
|
|
107193
107452
|
});
|