dsh-model-switch 0.4.40 → 0.4.42
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 +22 -0
- package/README.md +3 -1
- package/README.zh.md +4 -2
- package/cordis.patch.yml +7 -0
- package/lib/{auto-review-BDx3LjVL.js → auto-review-QDEFUjT2.js} +2 -332
- package/lib/auto-review.js +1 -1
- package/lib/automode-compat.js +266 -0
- package/lib/client.js +23 -1
- package/lib/index.js +5 -2
- package/lib/tool-allowlist-DlzB5GlQ.js +332 -0
- package/lib/types/automode-compat.d.ts +152 -0
- package/lib/types/capabilities-rpc.d.ts +8 -0
- package/lib/types/client/locales.d.ts +3 -0
- package/lib/types/client-contract.d.ts +7 -0
- package/package.json +7 -2
- package/lib/session-route-BCQFzqRP.js +0 -172
- package/lib/session-route-BjNPlliu.js +0 -172
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapt the third-party `@log.li/dsh-automode` row to this plugin's routes.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* `dsh-automode` owns a completely different decision pipeline: its own
|
|
7
|
+
* `auto-mode` permission preset, a `tools/pre-execute` gate, and a two-stage
|
|
8
|
+
* classifier that calls `ctx.llm.stream()` with
|
|
9
|
+
* `classifier.provider` / `classifier.model` (`resolveRoute`, falling back to
|
|
10
|
+
* the session header when both are empty). It reads the pure-read allowance from
|
|
11
|
+
* `config.readOnlyTools` with an exact `includes()` match
|
|
12
|
+
* (`src/pre-execute.ts`), so a call that hits it is allowed before the
|
|
13
|
+
* classifier runs — still after the plugin's own hard-deny regex band.
|
|
14
|
+
*
|
|
15
|
+
* Both knobs live in that row's own config. There is no public seam to inject a
|
|
16
|
+
* route or a tool list from outside, and the user asked for the adaptation to
|
|
17
|
+
* happen inside the plugin layer: no profile editing, no second plugin. This
|
|
18
|
+
* module is that adapter. It rewrites the automode row's **runtime** option
|
|
19
|
+
* object right before the loader mounts it and on every remount:
|
|
20
|
+
*
|
|
21
|
+
* - `classifier.provider` / `classifier.model` are filled from this plugin's
|
|
22
|
+
* Auto-review route **only when the row leaves them empty**; an explicit route
|
|
23
|
+
* in the profile always wins.
|
|
24
|
+
* - `readOnlyTools` is the union of the row's own list and this plugin's
|
|
25
|
+
* allowlist — but only while the "skip pure-read tools" switch is on. Entries
|
|
26
|
+
* that carry a `*` cannot transfer (the third-party check is exact-match) and
|
|
27
|
+
* are reported instead of silently dropped.
|
|
28
|
+
*
|
|
29
|
+
* Nothing is persisted: `entry.options` is the loader's live options object, and
|
|
30
|
+
* `Entry.update()` (which writes the profile layer) is deliberately never called
|
|
31
|
+
* here. Restarting the Host re-applies the same in-memory view.
|
|
32
|
+
*
|
|
33
|
+
* Every failure is contained. If the row shape or the loader internals change,
|
|
34
|
+
* this adapter logs one warning and the rest of the plugin keeps working.
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
39
|
+
import type { Loader } from '@deepseek-ai/cordis-plugin-loader';
|
|
40
|
+
import type { ModelSwitchSettings } from './host-settings.js';
|
|
41
|
+
/** Cordis plugin identity; matches the `cordis.patch.yml` insert row id. */
|
|
42
|
+
export declare const name = "model-switch-automode-compat";
|
|
43
|
+
/** The loader is the only seam this row needs besides this plugin's settings. */
|
|
44
|
+
export declare const inject: string[];
|
|
45
|
+
/** Third-party package this row adapts (strict package identity, never a prefix). */
|
|
46
|
+
export declare const AUTOMODE_PACKAGE = "@log.li/dsh-automode";
|
|
47
|
+
/**
|
|
48
|
+
* Why the route was not injected.
|
|
49
|
+
*
|
|
50
|
+
* - `user-set`: the row already names a provider and a model.
|
|
51
|
+
* - `no-route`: this plugin has no Auto-review route configured right now.
|
|
52
|
+
*/
|
|
53
|
+
export type AutomodeRouteSkip = 'user-set' | 'no-route';
|
|
54
|
+
/** Result of adapting one row's config object. */
|
|
55
|
+
export interface AutomodeCompatApplied {
|
|
56
|
+
/** `provider/model` written into the row, when it was empty before. */
|
|
57
|
+
readonly route?: string | undefined;
|
|
58
|
+
/** Set when the route was left alone. */
|
|
59
|
+
readonly routeSkipped?: AutomodeRouteSkip | undefined;
|
|
60
|
+
/** Allowlist entries merged into `readOnlyTools`. */
|
|
61
|
+
readonly added: readonly string[];
|
|
62
|
+
/** Allowlist entries that cannot transfer because they are pattern-shaped. */
|
|
63
|
+
readonly skipped: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
/** One adaptation result: the config to install plus what changed. */
|
|
66
|
+
export interface AutomodeCompatPlan {
|
|
67
|
+
/** The config object to assign to the row. */
|
|
68
|
+
readonly config: Record<string, unknown>;
|
|
69
|
+
/** Whether `config` differs from the input (false means "leave it alone"). */
|
|
70
|
+
readonly changed: boolean;
|
|
71
|
+
/** What was applied, for the status line and the log. */
|
|
72
|
+
readonly applied: AutomodeCompatApplied;
|
|
73
|
+
}
|
|
74
|
+
/** Live adapter state, surfaced next to the allowlist editor. */
|
|
75
|
+
export declare const automodeCompatStatus: {
|
|
76
|
+
/** A `@log.li/dsh-automode` row exists in this Host. */
|
|
77
|
+
installed: boolean;
|
|
78
|
+
/** `provider/model` this adapter wrote, when it wrote one. */
|
|
79
|
+
route: string | undefined;
|
|
80
|
+
/** Why the route was skipped, when it was. */
|
|
81
|
+
routeSkipped: AutomodeRouteSkip | undefined;
|
|
82
|
+
/** Tools merged into the row's `readOnlyTools`. */
|
|
83
|
+
added: string[];
|
|
84
|
+
/** Pattern-shaped entries that cannot transfer. */
|
|
85
|
+
skipped: string[];
|
|
86
|
+
/** How many rows were rewritten (remounts included). */
|
|
87
|
+
applied: number;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Whether one loader entry is the third-party auto-mode row.
|
|
91
|
+
* @param entry - anything the loader hands over (an entry, or an event payload).
|
|
92
|
+
* @returns true only for the exact package name.
|
|
93
|
+
*/
|
|
94
|
+
export declare function isAutomodeEntry(entry: unknown): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Decide the config one automode row should run with.
|
|
97
|
+
*
|
|
98
|
+
* Pure and side-effect free so the policy is unit-testable.
|
|
99
|
+
*
|
|
100
|
+
* @param config - the row's current config (any JSON-ish value).
|
|
101
|
+
* @param route - this plugin's Auto-review route; empty strings mean "unset".
|
|
102
|
+
* @param allowlist - tool names to merge into `readOnlyTools`; pass an empty
|
|
103
|
+
* list while the skip switch is off.
|
|
104
|
+
* @param adoptedRoute - a `provider/model` this adapter wrote on an earlier
|
|
105
|
+
* pass; seeing it again means the row is still ours to rewrite.
|
|
106
|
+
* @returns the config to install, whether it changed, and what was applied.
|
|
107
|
+
*/
|
|
108
|
+
export declare function planAutomodeCompat(config: unknown, route: {
|
|
109
|
+
readonly provider?: string;
|
|
110
|
+
readonly model?: string;
|
|
111
|
+
}, allowlist: readonly string[], adoptedRoute?: string): AutomodeCompatPlan;
|
|
112
|
+
/** One-line human summary of a plan, for the startup log. */
|
|
113
|
+
export declare function describeAutomodePlan(plan: AutomodeCompatPlan): string;
|
|
114
|
+
/** Shape the settings card renders for the third-party row, or undefined when absent. */
|
|
115
|
+
export interface AutomodeCompatView {
|
|
116
|
+
readonly installed: boolean;
|
|
117
|
+
/** `provider/model` written into the row, when one was. */
|
|
118
|
+
readonly route?: string | undefined;
|
|
119
|
+
/** Why no route was written, when none was. */
|
|
120
|
+
readonly routeSkipped?: AutomodeRouteSkip | undefined;
|
|
121
|
+
/** How many tools were merged in. */
|
|
122
|
+
readonly added: number;
|
|
123
|
+
/** How many pattern-shaped entries could not transfer. */
|
|
124
|
+
readonly skipped: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Snapshot for the RPC/UI layer.
|
|
128
|
+
* @returns the view, or undefined when this Host has no automode row.
|
|
129
|
+
*/
|
|
130
|
+
export declare function automodeCompatView(): AutomodeCompatView | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* Install the adapter: adapt live rows now, and every row the loader mounts later.
|
|
133
|
+
*
|
|
134
|
+
* The loader applies the profile patch layer and mounts the entry's plugin right
|
|
135
|
+
* after the `loader/patch-context` waterfall returns, so rewriting the entry's
|
|
136
|
+
* `options.config` from that waterfall is what makes the adapted values the ones
|
|
137
|
+
* the plugin activates with. No profile write happens here.
|
|
138
|
+
*
|
|
139
|
+
* @param ctx - this row's context.
|
|
140
|
+
* @param loader - the loader service, when the Host provides it.
|
|
141
|
+
* @param settings - reads the current plugin settings.
|
|
142
|
+
* @returns a disposer that detaches the listeners, plus a replay for live rows.
|
|
143
|
+
*/
|
|
144
|
+
export declare function installAutomodeCompat(ctx: Context, loader: Loader | undefined, settings: () => ModelSwitchSettings): {
|
|
145
|
+
dispose: () => void;
|
|
146
|
+
replay: () => boolean;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* Row entry point: adapt the third-party row on activation and on remount.
|
|
150
|
+
* @param ctx - this row's context.
|
|
151
|
+
*/
|
|
152
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -8,6 +8,14 @@ export interface ReviewStatus {
|
|
|
8
8
|
readonly held: boolean;
|
|
9
9
|
readonly allowedCalls: number;
|
|
10
10
|
readonly skippedReadOnly: number;
|
|
11
|
+
/** Third-party `@log.li/dsh-automode` row, when this Host mounts one. */
|
|
12
|
+
readonly automode?: {
|
|
13
|
+
readonly installed: boolean;
|
|
14
|
+
readonly route?: string | undefined;
|
|
15
|
+
readonly routeSkipped?: string | undefined;
|
|
16
|
+
readonly added: number;
|
|
17
|
+
readonly skipped: number;
|
|
18
|
+
} | undefined;
|
|
11
19
|
}
|
|
12
20
|
export declare const CAPABILITIES_RPC_METHOD = "plugin-rpc/model-switch";
|
|
13
21
|
export interface CapabilitiesSnapshot {
|
|
@@ -71,6 +71,9 @@ export declare const zh: {
|
|
|
71
71
|
readonly reviewAllowlistAvailable: "本机存在但未放行的工具:";
|
|
72
72
|
readonly reviewAllowlistRepo: "仓库";
|
|
73
73
|
readonly reviewAllowlistSkipped: "本 Host 进程已为纯读调用跳过 {count} 次审查。";
|
|
74
|
+
readonly reviewAutomodeLinked: "Auto mode 已接入:审查路由 {route},直通 +{added} 个纯读工具({skipped} 条通配未同步)。";
|
|
75
|
+
readonly reviewAutomodeRouteOwn: "沿用其自身配置";
|
|
76
|
+
readonly reviewAutomodeRouteUnset: "未配置(沿用会话模型)";
|
|
74
77
|
readonly 'reason.central-subagent-routing': "Alpha.4 没有全局子代理启动路由接口。";
|
|
75
78
|
readonly 'reason.packaged-preset-roots': "Alpha.4 不支持插件提供额外 preset root。";
|
|
76
79
|
readonly 'reason.tool-owner-suppression': "Alpha.4 没有工具所有者或来源抑制接口。";
|
|
@@ -94,6 +94,13 @@ export interface ReviewStatusView {
|
|
|
94
94
|
readonly held: boolean;
|
|
95
95
|
readonly allowedCalls: number;
|
|
96
96
|
readonly skippedReadOnly: number;
|
|
97
|
+
/** Third-party `@log.li/dsh-automode` row this Host adapted, when present. */
|
|
98
|
+
readonly automode?: {
|
|
99
|
+
readonly route?: string | undefined;
|
|
100
|
+
readonly routeSkipped?: string | undefined;
|
|
101
|
+
readonly added: number;
|
|
102
|
+
readonly skipped: number;
|
|
103
|
+
} | undefined;
|
|
97
104
|
}
|
|
98
105
|
/**
|
|
99
106
|
* Decode the reviewer counters from a `tools` RPC payload.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-model-switch",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.42",
|
|
4
4
|
"description": "Explicit model routing plus composer/Plan picker for DeepSeek Harness",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -58,6 +58,10 @@
|
|
|
58
58
|
"types": "./lib/types/adapter-registry.d.ts",
|
|
59
59
|
"default": "./lib/adapter-registry.js"
|
|
60
60
|
},
|
|
61
|
+
"./automode-compat": {
|
|
62
|
+
"types": "./lib/types/automode-compat.d.ts",
|
|
63
|
+
"default": "./lib/automode-compat.js"
|
|
64
|
+
},
|
|
61
65
|
"./package.json": "./package.json"
|
|
62
66
|
},
|
|
63
67
|
"files": [
|
|
@@ -107,7 +111,8 @@
|
|
|
107
111
|
"0.1.7-rc.1": "compatible",
|
|
108
112
|
"0.1.7-rc.2": "compatible",
|
|
109
113
|
"0.2.0-rc.2": "compatible",
|
|
110
|
-
"0.2.1-rc.2": "compatible"
|
|
114
|
+
"0.2.1-rc.2": "compatible",
|
|
115
|
+
"0.2.1-rc.3": "compatible"
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
},
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { t as groupOf } from "./provider-group-CwadImhH.js";
|
|
2
|
-
//#region lib/types/session-route.js
|
|
3
|
-
/**
|
|
4
|
-
* Per-session capability route derivation.
|
|
5
|
-
*
|
|
6
|
-
* Model Switch stores one deployment-wide route per capability (the flat
|
|
7
|
-
* settings fields) plus, per provider, a saved provider group. Nothing is
|
|
8
|
-
* written when a session changes provider: this module DERIVES the route a
|
|
9
|
-
* capability should use for one session from (a) the session's own provider and
|
|
10
|
-
* (b) the stored group table. Two sessions on different providers therefore
|
|
11
|
-
* resolve independently, and no session's actions rewrite another session's
|
|
12
|
-
* route.
|
|
13
|
-
*
|
|
14
|
-
* The session's provider is read from durable, official state in this order:
|
|
15
|
-
* the `modelSelection` session projection (`pending` before `lastUsed`, so a
|
|
16
|
-
* selection the user just made counts), then the latest logged request-context
|
|
17
|
-
* route, then the latest `request/header` route. Every read is optional: a
|
|
18
|
-
* session with no observable provider, an unregistered projection, or a
|
|
19
|
-
* profile without the projection package all resolve to the deployment default,
|
|
20
|
-
* which is exactly the pre-derivation behavior.
|
|
21
|
-
*
|
|
22
|
-
* All functions are pure except the readers, which never throw and never write.
|
|
23
|
-
*/
|
|
24
|
-
/** Flat deployment-default fields per capability, in group-storage field names. */
|
|
25
|
-
const CAPABILITY_FIELDS = Object.freeze({
|
|
26
|
-
compaction: {
|
|
27
|
-
provider: "compactProvider",
|
|
28
|
-
model: "compactModel"
|
|
29
|
-
},
|
|
30
|
-
autoReview: {
|
|
31
|
-
provider: "autoReviewProvider",
|
|
32
|
-
model: "autoReviewModel"
|
|
33
|
-
},
|
|
34
|
-
title: {
|
|
35
|
-
provider: "titleProvider",
|
|
36
|
-
model: "titleModel"
|
|
37
|
-
},
|
|
38
|
-
subagent: {
|
|
39
|
-
provider: "subagentProvider",
|
|
40
|
-
model: "subagentModel"
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
/** A non-blank string; blank values behave exactly like an absent field. */
|
|
44
|
-
function present(value) {
|
|
45
|
-
return typeof value === "string" && value.trim() !== "";
|
|
46
|
-
}
|
|
47
|
-
/** Read one capability's pair from a flat route record; undefined unless both halves are present. */
|
|
48
|
-
function pairOf(source, fields) {
|
|
49
|
-
if (source === void 0) return void 0;
|
|
50
|
-
const record = source;
|
|
51
|
-
const provider = record[fields.provider];
|
|
52
|
-
const model = record[fields.model];
|
|
53
|
-
if (!present(provider) || !present(model)) return void 0;
|
|
54
|
-
return {
|
|
55
|
-
provider,
|
|
56
|
-
model
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* The deployment-wide route for one capability: the flat settings pair.
|
|
61
|
-
* @param settings - live Model Switch settings; undefined means none are reachable.
|
|
62
|
-
* @param capability - capability whose flat pair is read.
|
|
63
|
-
* @returns The complete pair, or undefined when the capability stays at its official/derived default.
|
|
64
|
-
*/
|
|
65
|
-
function deploymentRoute(settings, capability) {
|
|
66
|
-
return pairOf(settings, CAPABILITY_FIELDS[capability]);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* The route a subagent spawn should use for one parent-session provider.
|
|
70
|
-
*
|
|
71
|
-
* A saved group for that provider pins the spawn; otherwise the flat Subagent
|
|
72
|
-
* card pins it, but only in `fixed` mode — `follow-main` IS the official inherit
|
|
73
|
-
* path, expressed by leaving the request untouched rather than by naming it.
|
|
74
|
-
* @param settings - live Model Switch settings.
|
|
75
|
-
* @param provider - the delegating session's provider, when one is observable.
|
|
76
|
-
* @returns The complete route, or undefined for official inherit.
|
|
77
|
-
*/
|
|
78
|
-
function subagentRouteOf(settings, provider) {
|
|
79
|
-
const group = groupRoute(settings, provider, "subagent");
|
|
80
|
-
if (group !== void 0) {
|
|
81
|
-
const effort = groupOf(settings?.providerGroups, provider ?? "")?.subagentReasoningEffort;
|
|
82
|
-
return present(effort) ? {
|
|
83
|
-
...group,
|
|
84
|
-
reasoningEffort: effort
|
|
85
|
-
} : group;
|
|
86
|
-
}
|
|
87
|
-
if (settings?.subagentMode !== "fixed") return void 0;
|
|
88
|
-
const fixed = deploymentRoute(settings, "subagent");
|
|
89
|
-
if (fixed === void 0) return void 0;
|
|
90
|
-
return present(settings.subagentReasoningEffort) ? {
|
|
91
|
-
...fixed,
|
|
92
|
-
reasoningEffort: settings.subagentReasoningEffort
|
|
93
|
-
} : fixed;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* One provider's saved route for one capability.
|
|
97
|
-
* @param settings - live Model Switch settings carrying the group table.
|
|
98
|
-
* @param provider - the session's provider; blank or unknown resolves to undefined.
|
|
99
|
-
* @param capability - capability whose group pin is read.
|
|
100
|
-
* @returns The complete group pair, or undefined when the group pins nothing here.
|
|
101
|
-
*/
|
|
102
|
-
function groupRoute(settings, provider, capability) {
|
|
103
|
-
if (!present(provider)) return void 0;
|
|
104
|
-
return pairOf(groupOf(settings?.providerGroups, provider), CAPABILITY_FIELDS[capability]);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* The route one capability should use for one session.
|
|
108
|
-
*
|
|
109
|
-
* A saved provider group for the session's provider wins; otherwise the flat
|
|
110
|
-
* deployment default applies. An unknown session provider skips every group, so
|
|
111
|
-
* a session Model Switch cannot attribute keeps the deployment default.
|
|
112
|
-
* @param settings - live Model Switch settings.
|
|
113
|
-
* @param provider - the session's provider, when one is observable.
|
|
114
|
-
* @param capability - capability being resolved.
|
|
115
|
-
* @returns The complete pair, or undefined for the capability's official default.
|
|
116
|
-
*/
|
|
117
|
-
function resolveCapabilityRoute(settings, provider, capability) {
|
|
118
|
-
return groupRoute(settings, provider, capability) ?? deploymentRoute(settings, capability);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Read the provider from the durable model-selection projection.
|
|
122
|
-
* @param ctx - context that may expose `sessionProjections`.
|
|
123
|
-
* @param session - the session whose selection is read.
|
|
124
|
-
* @returns The pending provider, else the last-used provider, else undefined.
|
|
125
|
-
*/
|
|
126
|
-
function projectionProvider(ctx, session) {
|
|
127
|
-
let state;
|
|
128
|
-
try {
|
|
129
|
-
state = ctx.get("sessionProjections", false)?.stateOf(session, "modelSelection");
|
|
130
|
-
} catch {
|
|
131
|
-
state = void 0;
|
|
132
|
-
}
|
|
133
|
-
if (state === void 0) return void 0;
|
|
134
|
-
if (present(state.pending?.provider)) return state.pending.provider;
|
|
135
|
-
if (present(state.lastUsed?.provider)) return state.lastUsed.provider;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* The provider one session is using, from official durable state.
|
|
139
|
-
*
|
|
140
|
-
* Preferred source is the session's own model selection (it covers a switch the
|
|
141
|
-
* client has sent but no request has consumed yet); the logged request-context
|
|
142
|
-
* and request-header routes cover profiles without the projection package and
|
|
143
|
-
* replayed sessions that never emitted a selection.
|
|
144
|
-
* @param ctx - context used for the optional projection read.
|
|
145
|
-
* @param session - the session to resolve; undefined resolves to undefined.
|
|
146
|
-
* @returns The provider, or undefined when nothing durable names one.
|
|
147
|
-
*/
|
|
148
|
-
function sessionProviderOf(ctx, session) {
|
|
149
|
-
if (session === void 0) return void 0;
|
|
150
|
-
const selected = projectionProvider(ctx, session);
|
|
151
|
-
if (selected !== void 0) return selected;
|
|
152
|
-
try {
|
|
153
|
-
const context = session.requestContext()?.provider;
|
|
154
|
-
if (present(context)) return context;
|
|
155
|
-
const header = session.requestHeader()?.config.provider;
|
|
156
|
-
if (present(header)) return header;
|
|
157
|
-
} catch {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* The subagent route one exact session's spawns should use.
|
|
163
|
-
* @param ctx - context used for the optional session reads.
|
|
164
|
-
* @param session - the delegating session; undefined keeps official inherit.
|
|
165
|
-
* @param settings - live Model Switch settings.
|
|
166
|
-
* @returns The complete route, or undefined for official inherit.
|
|
167
|
-
*/
|
|
168
|
-
function sessionSubagentRouteOf(ctx, session, settings) {
|
|
169
|
-
return subagentRouteOf(settings, sessionProviderOf(ctx, session));
|
|
170
|
-
}
|
|
171
|
-
//#endregion
|
|
172
|
-
export { sessionProviderOf as n, sessionSubagentRouteOf as r, resolveCapabilityRoute as t };
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { t as groupOf } from "./provider-group-CwadImhH.js";
|
|
2
|
-
//#region lib/types/session-route.js
|
|
3
|
-
/**
|
|
4
|
-
* Per-session capability route derivation.
|
|
5
|
-
*
|
|
6
|
-
* Model Switch stores one deployment-wide route per capability (the flat
|
|
7
|
-
* settings fields) plus, per provider, a saved provider group. Nothing is
|
|
8
|
-
* written when a session changes provider: this module DERIVES the route a
|
|
9
|
-
* capability should use for one session from (a) the session's own provider and
|
|
10
|
-
* (b) the stored group table. Two sessions on different providers therefore
|
|
11
|
-
* resolve independently, and no session's actions rewrite another session's
|
|
12
|
-
* route.
|
|
13
|
-
*
|
|
14
|
-
* The session's provider is read from durable, official state in this order:
|
|
15
|
-
* the `modelSelection` session projection (`pending` before `lastUsed`, so a
|
|
16
|
-
* selection the user just made counts), then the latest logged request-context
|
|
17
|
-
* route, then the latest `request/header` route. Every read is optional: a
|
|
18
|
-
* session with no observable provider, an unregistered projection, or a
|
|
19
|
-
* profile without the projection package all resolve to the deployment default,
|
|
20
|
-
* which is exactly the pre-derivation behavior.
|
|
21
|
-
*
|
|
22
|
-
* All functions are pure except the readers, which never throw and never write.
|
|
23
|
-
*/
|
|
24
|
-
/** Flat deployment-default fields per capability, in group-storage field names. */
|
|
25
|
-
const CAPABILITY_FIELDS = Object.freeze({
|
|
26
|
-
compaction: {
|
|
27
|
-
provider: "compactProvider",
|
|
28
|
-
model: "compactModel"
|
|
29
|
-
},
|
|
30
|
-
autoReview: {
|
|
31
|
-
provider: "autoReviewProvider",
|
|
32
|
-
model: "autoReviewModel"
|
|
33
|
-
},
|
|
34
|
-
title: {
|
|
35
|
-
provider: "titleProvider",
|
|
36
|
-
model: "titleModel"
|
|
37
|
-
},
|
|
38
|
-
subagent: {
|
|
39
|
-
provider: "subagentProvider",
|
|
40
|
-
model: "subagentModel"
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
/** A non-blank string; blank values behave exactly like an absent field. */
|
|
44
|
-
function present(value) {
|
|
45
|
-
return typeof value === "string" && value.trim() !== "";
|
|
46
|
-
}
|
|
47
|
-
/** Read one capability's pair from a flat route record; undefined unless both halves are present. */
|
|
48
|
-
function pairOf(source, fields) {
|
|
49
|
-
if (source === void 0) return void 0;
|
|
50
|
-
const record = source;
|
|
51
|
-
const provider = record[fields.provider];
|
|
52
|
-
const model = record[fields.model];
|
|
53
|
-
if (!present(provider) || !present(model)) return void 0;
|
|
54
|
-
return {
|
|
55
|
-
provider,
|
|
56
|
-
model
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* The deployment-wide route for one capability: the flat settings pair.
|
|
61
|
-
* @param settings - live Model Switch settings; undefined means none are reachable.
|
|
62
|
-
* @param capability - capability whose flat pair is read.
|
|
63
|
-
* @returns The complete pair, or undefined when the capability stays at its official/derived default.
|
|
64
|
-
*/
|
|
65
|
-
function deploymentRoute(settings, capability) {
|
|
66
|
-
return pairOf(settings, CAPABILITY_FIELDS[capability]);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* The route a subagent spawn should use for one parent-session provider.
|
|
70
|
-
*
|
|
71
|
-
* A saved group for that provider pins the spawn; otherwise the flat Subagent
|
|
72
|
-
* card pins it, but only in `fixed` mode — `follow-main` IS the official inherit
|
|
73
|
-
* path, expressed by leaving the request untouched rather than by naming it.
|
|
74
|
-
* @param settings - live Model Switch settings.
|
|
75
|
-
* @param provider - the delegating session's provider, when one is observable.
|
|
76
|
-
* @returns The complete route, or undefined for official inherit.
|
|
77
|
-
*/
|
|
78
|
-
function subagentRouteOf(settings, provider) {
|
|
79
|
-
const group = groupRoute(settings, provider, "subagent");
|
|
80
|
-
if (group !== void 0) {
|
|
81
|
-
const effort = groupOf(settings?.providerGroups, provider ?? "")?.subagentReasoningEffort;
|
|
82
|
-
return present(effort) ? {
|
|
83
|
-
...group,
|
|
84
|
-
reasoningEffort: effort
|
|
85
|
-
} : group;
|
|
86
|
-
}
|
|
87
|
-
if (settings?.subagentMode !== "fixed") return void 0;
|
|
88
|
-
const fixed = deploymentRoute(settings, "subagent");
|
|
89
|
-
if (fixed === void 0) return void 0;
|
|
90
|
-
return present(settings.subagentReasoningEffort) ? {
|
|
91
|
-
...fixed,
|
|
92
|
-
reasoningEffort: settings.subagentReasoningEffort
|
|
93
|
-
} : fixed;
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* One provider's saved route for one capability.
|
|
97
|
-
* @param settings - live Model Switch settings carrying the group table.
|
|
98
|
-
* @param provider - the session's provider; blank or unknown resolves to undefined.
|
|
99
|
-
* @param capability - capability whose group pin is read.
|
|
100
|
-
* @returns The complete group pair, or undefined when the group pins nothing here.
|
|
101
|
-
*/
|
|
102
|
-
function groupRoute(settings, provider, capability) {
|
|
103
|
-
if (!present(provider)) return void 0;
|
|
104
|
-
return pairOf(groupOf(settings?.providerGroups, provider), CAPABILITY_FIELDS[capability]);
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* The route one capability should use for one session.
|
|
108
|
-
*
|
|
109
|
-
* A saved provider group for the session's provider wins; otherwise the flat
|
|
110
|
-
* deployment default applies. An unknown session provider skips every group, so
|
|
111
|
-
* a session Model Switch cannot attribute keeps the deployment default.
|
|
112
|
-
* @param settings - live Model Switch settings.
|
|
113
|
-
* @param provider - the session's provider, when one is observable.
|
|
114
|
-
* @param capability - capability being resolved.
|
|
115
|
-
* @returns The complete pair, or undefined for the capability's official default.
|
|
116
|
-
*/
|
|
117
|
-
function resolveCapabilityRoute(settings, provider, capability) {
|
|
118
|
-
return groupRoute(settings, provider, capability) ?? deploymentRoute(settings, capability);
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* Read the provider from the durable model-selection projection.
|
|
122
|
-
* @param ctx - context that may expose `sessionProjections`.
|
|
123
|
-
* @param session - the session whose selection is read.
|
|
124
|
-
* @returns The pending provider, else the last-used provider, else undefined.
|
|
125
|
-
*/
|
|
126
|
-
function projectionProvider(ctx, session) {
|
|
127
|
-
let state;
|
|
128
|
-
try {
|
|
129
|
-
state = ctx.get("sessionProjections", false)?.stateOf(session, "modelSelection");
|
|
130
|
-
} catch {
|
|
131
|
-
state = void 0;
|
|
132
|
-
}
|
|
133
|
-
if (state === void 0) return void 0;
|
|
134
|
-
if (present(state.pending?.provider)) return state.pending.provider;
|
|
135
|
-
if (present(state.lastUsed?.provider)) return state.lastUsed.provider;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* The provider one session is using, from official durable state.
|
|
139
|
-
*
|
|
140
|
-
* Preferred source is the session's own model selection (it covers a switch the
|
|
141
|
-
* client has sent but no request has consumed yet); the logged request-context
|
|
142
|
-
* and request-header routes cover profiles without the projection package and
|
|
143
|
-
* replayed sessions that never emitted a selection.
|
|
144
|
-
* @param ctx - context used for the optional projection read.
|
|
145
|
-
* @param session - the session to resolve; undefined resolves to undefined.
|
|
146
|
-
* @returns The provider, or undefined when nothing durable names one.
|
|
147
|
-
*/
|
|
148
|
-
function sessionProviderOf(ctx, session) {
|
|
149
|
-
if (session === void 0) return void 0;
|
|
150
|
-
const selected = projectionProvider(ctx, session);
|
|
151
|
-
if (selected !== void 0) return selected;
|
|
152
|
-
try {
|
|
153
|
-
const context = session.requestContext()?.provider;
|
|
154
|
-
if (present(context)) return context;
|
|
155
|
-
const header = session.requestHeader()?.config.provider;
|
|
156
|
-
if (present(header)) return header;
|
|
157
|
-
} catch {
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* The subagent route one exact session's spawns should use.
|
|
163
|
-
* @param ctx - context used for the optional session reads.
|
|
164
|
-
* @param session - the delegating session; undefined keeps official inherit.
|
|
165
|
-
* @param settings - live Model Switch settings.
|
|
166
|
-
* @returns The complete route, or undefined for official inherit.
|
|
167
|
-
*/
|
|
168
|
-
function sessionSubagentRouteOf(ctx, session, settings) {
|
|
169
|
-
return subagentRouteOf(settings, sessionProviderOf(ctx, session));
|
|
170
|
-
}
|
|
171
|
-
//#endregion
|
|
172
|
-
export { sessionSubagentRouteOf as a, sessionProviderOf as i, groupRoute as n, resolveCapabilityRoute as r, deploymentRoute as t };
|