dsh-advisor 0.4.1 → 0.5.1
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/README.i18n.yaml +2 -2
- package/README.md +31 -22
- package/README.zh.md +31 -22
- package/lib/client/advisor-card.d.ts +18 -14
- package/lib/client/advisor-session.d.ts +75 -0
- package/lib/client/advisor-store.d.ts +131 -0
- package/lib/client/index.d.ts +18 -13
- package/lib/client/locales.d.ts +15 -0
- package/lib/client.js +375 -14
- package/lib/commands.d.ts +220 -27
- package/lib/commands.js +260 -34
- package/lib/commands.js.map +1 -1
- package/lib/config.d.ts +74 -20
- package/lib/config.js +74 -20
- package/lib/config.js.map +1 -1
- package/lib/delivery.d.ts +9 -9
- package/lib/delivery.js +10 -12
- package/lib/delivery.js.map +1 -1
- package/lib/gateway.d.ts +139 -24
- package/lib/gateway.js +138 -27
- package/lib/gateway.js.map +1 -1
- package/lib/index.d.ts +5 -5
- package/lib/index.js +483 -82
- package/lib/index.js.map +1 -1
- package/lib/kinds.d.ts +79 -51
- package/lib/kinds.js +60 -43
- package/lib/kinds.js.map +1 -1
- package/lib/settings.d.ts +56 -86
- package/lib/settings.js +48 -91
- package/lib/settings.js.map +1 -1
- package/lib/transcript.d.ts +11 -9
- package/lib/transcript.js +25 -16
- package/lib/transcript.js.map +1 -1
- package/lib/tui-settings.d.ts +10 -10
- package/lib/tui-settings.js +10 -10
- package/lib/tui-settings.js.map +1 -1
- package/lib/tui.js +39 -5
- package/lib/tui.js.map +1 -1
- package/package.json +25 -24
package/lib/config.js
CHANGED
|
@@ -4,10 +4,19 @@
|
|
|
4
4
|
* The exported schemastery `Config` schema is what the cordis Loader uses to
|
|
5
5
|
* validate the plugin row config: it applies defaults (`enabled` false,
|
|
6
6
|
* `immuneTurns` 3, `maxDeltaMessages` 60, `systemPrompt` "") and enforces
|
|
7
|
-
* types/bounds (integers ≥ 0).
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* types/bounds (integers ≥ 0). All six live fields are declared `.volatile()`:
|
|
8
|
+
* the Loader commits edits to them into the running fiber's references WITHOUT
|
|
9
|
+
* a remount (dsh 0.1.7-rc.1 — the settings.yaml user layer is gone), so
|
|
10
|
+
* `apply` receives each field as a `{ get() }` reference and every read must
|
|
11
|
+
* unwrap it first — {@link unwrapAdvisorConfig}, which tolerates plain values
|
|
12
|
+
* too (integration harnesses pass plain objects).
|
|
13
|
+
*
|
|
14
|
+
* `resolveAdvisorConfig(raw)` additionally enforces the explicit model gate:
|
|
15
|
+
* when `enabled` is true but `provider` or `model` is missing or empty, it
|
|
16
|
+
* resolves to a disabled-with-reason config — the advisor never starts a model
|
|
17
|
+
* call (hard gate, not a warning). The volatile unwrap happens BEFORE the gate:
|
|
18
|
+
* an unwrapped reference object is truthy, so the enabled/provider/model reads
|
|
19
|
+
* would silently pass the gate on the reference objects themselves.
|
|
11
20
|
*
|
|
12
21
|
* @module dsh-advisor/config
|
|
13
22
|
*/
|
|
@@ -32,17 +41,26 @@ const CONFIG_KEYS = new Set([
|
|
|
32
41
|
* stay optional so an enabled-without-pair config validates and then resolves
|
|
33
42
|
* to disabled-with-reason instead of failing to load.
|
|
34
43
|
*
|
|
44
|
+
* Volatile (dsh 0.1.7-rc.1): every field is a LIVE field. The Loader commits
|
|
45
|
+
* edits into the running fiber's references without remounting the plugin
|
|
46
|
+
* (same pattern as dsh `agent-default-model`), so flat, always-present fields
|
|
47
|
+
* are exactly what the settings forms surface. `.volatile()` requires a fixed
|
|
48
|
+
* object path with no enclosing volatile field — this flat schema qualifies.
|
|
49
|
+
*
|
|
35
50
|
* Type note: left to inference (`Schema<ObjectS, ObjectT>`), so calling the
|
|
36
51
|
* schema accepts partial input (each key optional, `| null`) and yields the
|
|
37
|
-
* fully-defaulted output — matching schemastery's runtime semantics.
|
|
52
|
+
* fully-defaulted output — matching schemastery's runtime semantics. With
|
|
53
|
+
* `.volatile()` the output type of each field is the reference shape; the
|
|
54
|
+
* resolver reads through {@link unwrapAdvisorConfig}, keeping this module's
|
|
55
|
+
* exported contracts plain-valued.
|
|
38
56
|
*/
|
|
39
57
|
export const Config = z.object({
|
|
40
|
-
enabled: z.boolean().default(false),
|
|
41
|
-
provider: z.string(),
|
|
42
|
-
model: z.string(),
|
|
43
|
-
systemPrompt: z.string().default(''),
|
|
44
|
-
immuneTurns: z.number().step(1).min(0).default(3),
|
|
45
|
-
maxDeltaMessages: z.number().step(1).min(0).default(60),
|
|
58
|
+
enabled: z.boolean().default(false).volatile(),
|
|
59
|
+
provider: z.string().volatile(),
|
|
60
|
+
model: z.string().volatile(),
|
|
61
|
+
systemPrompt: z.string().default('').volatile(),
|
|
62
|
+
immuneTurns: z.number().step(1).min(0).default(3).volatile(),
|
|
63
|
+
maxDeltaMessages: z.number().step(1).min(0).default(60).volatile(),
|
|
46
64
|
});
|
|
47
65
|
function isNonEmptyString(value) {
|
|
48
66
|
// Trim before checking: a whitespace-only value (" ") is empty in effect
|
|
@@ -50,6 +68,44 @@ function isNonEmptyString(value) {
|
|
|
50
68
|
// qc3 I-3 — a strict superset of dsh's own `length === 0` check).
|
|
51
69
|
return typeof value === 'string' && value.trim().length > 0;
|
|
52
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Read one volatile field: unwrap the `{ get() }` reference the Loader hands
|
|
73
|
+
* over, pass plain values through. Duck-typed on purpose — cosmokit's
|
|
74
|
+
* reference protocol is structural (`{ get }`), and importing the cosmokit
|
|
75
|
+
* types would add an undeclared dependency for one member.
|
|
76
|
+
*/
|
|
77
|
+
function unwrapReference(value) {
|
|
78
|
+
if (typeof value === 'object' && value !== null && typeof value.get === 'function') {
|
|
79
|
+
return value.get();
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Snapshot the live entry config into a plain {@link AdvisorConfig}: every
|
|
85
|
+
* schema-declared field is read through its volatile reference (or taken as
|
|
86
|
+
* the plain value it is on non-Loader paths), and `null` — which schemastery
|
|
87
|
+
* passes through for fields without a default — is normalized to `undefined`
|
|
88
|
+
* so the resolved contract is null-free and the gate treats null exactly like
|
|
89
|
+
* a missing value. Keys outside the schema ride along untouched (the
|
|
90
|
+
* schemastery object resolver merges unknown keys through; they are never
|
|
91
|
+
* volatile-declared, so they are never references) — the hard gate's
|
|
92
|
+
* unknown-key rejection must keep seeing them.
|
|
93
|
+
*
|
|
94
|
+
* This is a snapshot read, NOT a validation: the result is the RAW composed
|
|
95
|
+
* config `resolveAdvisorConfig` consumes.
|
|
96
|
+
*/
|
|
97
|
+
export function unwrapAdvisorConfig(raw) {
|
|
98
|
+
const { enabled, provider, model, systemPrompt, immuneTurns, maxDeltaMessages, ...rest } = raw;
|
|
99
|
+
return {
|
|
100
|
+
...rest,
|
|
101
|
+
enabled: unwrapReference(enabled),
|
|
102
|
+
provider: unwrapReference(provider) ?? undefined,
|
|
103
|
+
model: unwrapReference(model) ?? undefined,
|
|
104
|
+
systemPrompt: unwrapReference(systemPrompt),
|
|
105
|
+
immuneTurns: unwrapReference(immuneTurns),
|
|
106
|
+
maxDeltaMessages: unwrapReference(maxDeltaMessages),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
53
109
|
/**
|
|
54
110
|
* Resolve the raw config into the runtime contract.
|
|
55
111
|
*
|
|
@@ -57,6 +113,10 @@ function isNonEmptyString(value) {
|
|
|
57
113
|
* - Applies the explicit model gate (S4): `enabled: true` with `provider` or
|
|
58
114
|
* `model` missing/empty → disabled-with-reason, never throws, no model call.
|
|
59
115
|
* - `provider`/`model` are ignored while disabled.
|
|
116
|
+
*
|
|
117
|
+
* The volatile unwrap happens FIRST (before any gate read): a reference
|
|
118
|
+
* object is truthy regardless of the value behind it, so gating on the raw
|
|
119
|
+
* volatile fields would silently enable the advisor on `{ get() }` objects.
|
|
60
120
|
*/
|
|
61
121
|
export function resolveAdvisorConfig(raw) {
|
|
62
122
|
if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {
|
|
@@ -67,15 +127,9 @@ export function resolveAdvisorConfig(raw) {
|
|
|
67
127
|
throw new Error(`dsh-advisor: unknown config key "${key}"`);
|
|
68
128
|
}
|
|
69
129
|
}
|
|
70
|
-
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
// and the gate treats null exactly like a missing value.
|
|
74
|
-
const normalized = {
|
|
75
|
-
...config,
|
|
76
|
-
provider: config.provider ?? undefined,
|
|
77
|
-
model: config.model ?? undefined,
|
|
78
|
-
};
|
|
130
|
+
// Config(raw) resolves each `.volatile()` field to its reference; unwrap
|
|
131
|
+
// into the plain contract the gate (and every consumer) reads.
|
|
132
|
+
const normalized = unwrapAdvisorConfig(Config(raw));
|
|
79
133
|
if (!normalized.enabled)
|
|
80
134
|
return normalized;
|
|
81
135
|
const missing = [];
|
package/lib/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/lib/delivery.d.ts
CHANGED
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
* countdown and the KD-5 reset.
|
|
23
23
|
*
|
|
24
24
|
* Message shape (spec §6): a user-role message via `createUserMessage` whose
|
|
25
|
-
* source is
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
25
|
+
* source is this plugin's own producer kind (`kind: 'advisor'`, pinned to the
|
|
26
|
+
* `notice` form; src/kinds.ts) and whose content is self-describing
|
|
27
|
+
* `[advisor:{severity}] {note}` — the only cue the primary model gets about
|
|
28
|
+
* how to treat it ("weigh, don't blindly obey" spirit). The kind is claimed by
|
|
29
|
+
* declaration-merging `MessageSourceMap` in dsh-llm, and the V3→V4 format edge
|
|
30
|
+
* preserves direct source kinds, so this identity is cross-generation-safe —
|
|
31
|
+
* the property the retired `plugin` arm used to be borrowed for.
|
|
32
32
|
*
|
|
33
33
|
* Delivery is synchronous and fire-and-forget; the runtime path (T4 F1) is
|
|
34
34
|
* what contains a throwing `inject`/`steer` — this module lets agent-method
|
|
@@ -68,8 +68,8 @@ export interface AdvisorDeliveryOptions {
|
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Build the advisor message for one note (spec §6): a user-role message whose
|
|
71
|
-
* source carries this plugin's
|
|
72
|
-
*
|
|
71
|
+
* source carries this plugin's own producer kind and whose content is
|
|
72
|
+
* self-describing `[advisor:{severity}] {note}`.
|
|
73
73
|
*
|
|
74
74
|
* Bounds (qc3 F-2 / qc2 S-1): the note itself is already capped at
|
|
75
75
|
* `ADVISOR_NOTE_MAX_CHARS` by extraction; the collapsed-row summary is
|
package/lib/delivery.js
CHANGED
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
* countdown and the KD-5 reset.
|
|
23
23
|
*
|
|
24
24
|
* Message shape (spec §6): a user-role message via `createUserMessage` whose
|
|
25
|
-
* source is
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
25
|
+
* source is this plugin's own producer kind (`kind: 'advisor'`, pinned to the
|
|
26
|
+
* `notice` form; src/kinds.ts) and whose content is self-describing
|
|
27
|
+
* `[advisor:{severity}] {note}` — the only cue the primary model gets about
|
|
28
|
+
* how to treat it ("weigh, don't blindly obey" spirit). The kind is claimed by
|
|
29
|
+
* declaration-merging `MessageSourceMap` in dsh-llm, and the V3→V4 format edge
|
|
30
|
+
* preserves direct source kinds, so this identity is cross-generation-safe —
|
|
31
|
+
* the property the retired `plugin` arm used to be borrowed for.
|
|
32
32
|
*
|
|
33
33
|
* Delivery is synchronous and fire-and-forget; the runtime path (T4 F1) is
|
|
34
34
|
* what contains a throwing `inject`/`steer` — this module lets agent-method
|
|
@@ -37,11 +37,10 @@
|
|
|
37
37
|
* @module dsh-advisor/delivery
|
|
38
38
|
*/
|
|
39
39
|
import { boundContextSummary, createUserMessage } from '@deepseek-ai/dsh-llm';
|
|
40
|
-
import { ADVISOR_PLUGIN_ID } from './kinds.js';
|
|
41
40
|
/**
|
|
42
41
|
* Build the advisor message for one note (spec §6): a user-role message whose
|
|
43
|
-
* source carries this plugin's
|
|
44
|
-
*
|
|
42
|
+
* source carries this plugin's own producer kind and whose content is
|
|
43
|
+
* self-describing `[advisor:{severity}] {note}`.
|
|
45
44
|
*
|
|
46
45
|
* Bounds (qc3 F-2 / qc2 S-1): the note itself is already capped at
|
|
47
46
|
* `ADVISOR_NOTE_MAX_CHARS` by extraction; the collapsed-row summary is
|
|
@@ -57,8 +56,7 @@ export function buildAdvisorMessage(note) {
|
|
|
57
56
|
const text = `[advisor:${note.severity}] ${note.note}`;
|
|
58
57
|
const summary = `[${text.slice('[advisor:'.length)}`;
|
|
59
58
|
const source = {
|
|
60
|
-
kind: '
|
|
61
|
-
plugin: ADVISOR_PLUGIN_ID,
|
|
59
|
+
kind: 'advisor',
|
|
62
60
|
// n4 (user direction): declare the notice form + a collapsed-row summary so
|
|
63
61
|
// the web shell's ContextInjectionRow shows "… · advisor · [nit] <note>"
|
|
64
62
|
// instead of a bare producer label. The severity tag is part of the summary
|
package/lib/delivery.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"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"}
|
package/lib/gateway.d.ts
CHANGED
|
@@ -11,23 +11,24 @@
|
|
|
11
11
|
* exactly one plain-object `args` field whose keys are the method parameter
|
|
12
12
|
* names (`get()` → `{ args: {} }`; `set(patch)` → `{ args: { patch } }`).
|
|
13
13
|
*
|
|
14
|
-
* Data: `get` reads the `AdvisorSettingsBridge` source — the same live
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
14
|
+
* Data: `get` reads the `AdvisorSettingsBridge` source — the same live entry
|
|
15
|
+
* config the runtime reads (volatile references unwrapped), resolved through
|
|
16
|
+
* the `resolveAdvisorConfig` hard gate (the SSOT for enabled-without-pair
|
|
17
|
+
* disabled-with-reason). `set` validates the patch against the `Config`
|
|
18
|
+
* schema first (unknown-key rejection unchanged — the settings service itself
|
|
19
|
+
* is non-strict and would accept the unknown key), then writes the advisor
|
|
20
|
+
* ENTRY config in-process via `settings.update(ADVISOR_SETTINGS_NAMESPACE, ...)`
|
|
21
|
+
* (dsh 0.1.7-rc.1: the namespace key IS the profile entry id — the bundle row
|
|
22
|
+
* id `advisor` — and the write lands through the config editor into the
|
|
23
|
+
* Loader, which commits the volatile fields and emits `loader/volatile-update`,
|
|
24
|
+
* re-applying the runtime through the bridge with no restart), and returns
|
|
25
|
+
* the new composed value.
|
|
24
26
|
*
|
|
25
27
|
* The settings service is OPTIONAL (no settings service → the bridge source
|
|
26
28
|
* stays the entry, `get` still works; `set` fails with a clear error — KD-G5
|
|
27
29
|
* fallback). The gateway captures the service through a conditional
|
|
28
|
-
* `ctx.inject(['settings'], ...)` child
|
|
29
|
-
*
|
|
30
|
-
* fiber that declares it.
|
|
30
|
+
* `ctx.inject(['settings'], ...)` child, because `ctx.settings` is only
|
|
31
|
+
* resolvable from a fiber that declares it.
|
|
31
32
|
*
|
|
32
33
|
* The returned config is normalized to the typertGateway JSON wire boundary:
|
|
33
34
|
* absent keys (provider/model/disabledReason) are OMITTED, never
|
|
@@ -40,9 +41,84 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
40
41
|
import type { TypertContribution } from '@deepseek-ai/dsh-typert-registry';
|
|
41
42
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
42
43
|
import type { AdvisorSettingsBridge } from './settings.js';
|
|
44
|
+
import type { AdvisorModelSource } from './commands.js';
|
|
43
45
|
import type { AdvisorConfig, ResolvedAdvisorConfig } from './config.js';
|
|
44
46
|
/** Patch shape accepted by `advisor.set` — any subset of the config keys. */
|
|
45
47
|
export type AdvisorConfigPatch = Partial<AdvisorConfig>;
|
|
48
|
+
/** An atomic reviewer-route pair on the wire (JSON-safe; never undefined-valued). */
|
|
49
|
+
export interface AdvisorPairWire {
|
|
50
|
+
readonly provider: string;
|
|
51
|
+
readonly model: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The authoritative per-session advisor snapshot (spec §5.3) — the wire value
|
|
55
|
+
* of `advisor/getSession` and of a successful `advisor/setSessionModel`.
|
|
56
|
+
* Absent optional keys are OMITTED (the typertGateway result validation
|
|
57
|
+
* rejects undefined values), never present-as-undefined:
|
|
58
|
+
*
|
|
59
|
+
* - `modelOverride` — the session's pinned atomic pair; omitted while the
|
|
60
|
+
* session inherits the global defaults;
|
|
61
|
+
* - `modelSource` — where the effective route comes from (`session` = the
|
|
62
|
+
* pin, `global` = the composed global pair); present iff an effective pair
|
|
63
|
+
* exists at all;
|
|
64
|
+
* - `effectiveModel` — the effective route; omitted when neither level has a
|
|
65
|
+
* complete pair;
|
|
66
|
+
* - `disabledReason` — the S4 explicit-gate reason when it blocks.
|
|
67
|
+
*/
|
|
68
|
+
export interface AdvisorSessionSnapshotWire {
|
|
69
|
+
readonly sessionId: string;
|
|
70
|
+
readonly enabled: boolean;
|
|
71
|
+
/** Live-session lifetime marker (spec §5.3 — the snapshot is never durable). */
|
|
72
|
+
readonly lifetime: 'live-session';
|
|
73
|
+
readonly modelOverride?: AdvisorPairWire;
|
|
74
|
+
readonly modelSource?: AdvisorModelSource;
|
|
75
|
+
readonly effectiveModel?: AdvisorPairWire;
|
|
76
|
+
readonly disabledReason?: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Plugin-domain error tags carried in RETURNED DATA (`{ error: { tag,
|
|
80
|
+
* message } }`) — never new RemoteError codes (dsh's failure vocabulary stays
|
|
81
|
+
* frozen) and never a thrown coded failure for these business outcomes:
|
|
82
|
+
*
|
|
83
|
+
* - `advisor/session-unknown` — the target session is not (or no longer)
|
|
84
|
+
* live; rejected without allocating any state;
|
|
85
|
+
* - `advisor/unavailable` — this gateway fiber holds no elected owner (no
|
|
86
|
+
* controller face); the endpoints stay inert rather than guessing;
|
|
87
|
+
* - `advisor/rejected` — the selection failed pair validation (nothing was
|
|
88
|
+
* written);
|
|
89
|
+
* - `advisor/failed` — the pre-commit `resolveModelInfo` validation failed
|
|
90
|
+
* (previous selection untouched);
|
|
91
|
+
* - `advisor/superseded` — a newer set/reset superseded the attempt;
|
|
92
|
+
* - `advisor/cancelled` — the attempt was cancelled before commit.
|
|
93
|
+
*/
|
|
94
|
+
export type AdvisorSessionErrorTag = 'advisor/session-unknown' | 'advisor/unavailable' | 'advisor/rejected' | 'advisor/failed' | 'advisor/superseded' | 'advisor/cancelled';
|
|
95
|
+
/** One `advisor/getSession` / `advisor/setSessionModel` result: snapshot or tagged data error. */
|
|
96
|
+
export type AdvisorSessionRpcResult = {
|
|
97
|
+
readonly snapshot: AdvisorSessionSnapshotWire;
|
|
98
|
+
} | {
|
|
99
|
+
readonly error: {
|
|
100
|
+
readonly tag: AdvisorSessionErrorTag;
|
|
101
|
+
readonly message: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
/** Build one tagged data error (keeps every call site's shape identical). */
|
|
105
|
+
export declare function advisorSessionError(tag: AdvisorSessionErrorTag, message: string): AdvisorSessionRpcResult;
|
|
106
|
+
/**
|
|
107
|
+
* The elected owner's session face — implemented by the wiring (`index.ts`)
|
|
108
|
+
* against the SAME `AdvisorCommandController` the `/advisor model` commands
|
|
109
|
+
* drive (spec §5.3: one controller, no second state store, no divergence
|
|
110
|
+
* between the command face and the web face). The gateway consults it lazily
|
|
111
|
+
* per request: only the reviewer-claiming fiber assigns it, so a gateway on a
|
|
112
|
+
* non-owner fiber stays inert (`advisor/unavailable`) instead of guessing.
|
|
113
|
+
*/
|
|
114
|
+
export interface AdvisorSessionGatewayFace {
|
|
115
|
+
/** Authoritative snapshot for one live session (unknown → tagged error). */
|
|
116
|
+
getSession(sessionId: string): AdvisorSessionRpcResult;
|
|
117
|
+
/** Validate + commit a pair through the command controller (same fencing). */
|
|
118
|
+
setSessionModel(sessionId: string, provider: string, model: string): Promise<AdvisorSessionRpcResult>;
|
|
119
|
+
/** Drop the pin and re-inherit (selection: null); never touches the enable override. */
|
|
120
|
+
resetSessionModel(sessionId: string): AdvisorSessionRpcResult;
|
|
121
|
+
}
|
|
46
122
|
/**
|
|
47
123
|
* The host-side `advisor` config gateway (`/api/advisor/get` +
|
|
48
124
|
* `/api/advisor/set`). Registered as the cordis service key `'advisor'`
|
|
@@ -64,12 +140,24 @@ export declare class AdvisorConfigGateway extends TypertRemoteService {
|
|
|
64
140
|
private readonly bridge;
|
|
65
141
|
/** The live settings service once the optional inject child activates. */
|
|
66
142
|
private settings;
|
|
143
|
+
/**
|
|
144
|
+
* The elected owner's session face, resolved LAZILY per request. The
|
|
145
|
+
* reviewer-claiming fiber assigns it after its controller exists (the
|
|
146
|
+
* gateway is constructed before the single-reviewer claim); a `undefined`
|
|
147
|
+
* answer means this fiber owns the service key but not the reviewer role —
|
|
148
|
+
* the session endpoints answer `advisor/unavailable` in data and stay
|
|
149
|
+
* inert (no second controller, no surviving-fiber promotion).
|
|
150
|
+
*/
|
|
151
|
+
private readonly sessionControl;
|
|
67
152
|
/**
|
|
68
153
|
* @param ctx - owning context (the plugin fiber's ctx inside `apply`).
|
|
69
154
|
* @param bridge - the same `AdvisorSettingsBridge` the runtime reads, so
|
|
70
155
|
* get/set always operate on the live composed config.
|
|
156
|
+
* @param sessionControl - lazy access to the elected owner's session face
|
|
157
|
+
* (B2); defaults to "absent" so a direct construction without the wiring
|
|
158
|
+
* keeps the session endpoints cleanly unavailable.
|
|
71
159
|
*/
|
|
72
|
-
constructor(ctx: Context, bridge: AdvisorSettingsBridge);
|
|
160
|
+
constructor(ctx: Context, bridge: AdvisorSettingsBridge, sessionControl?: () => AdvisorSessionGatewayFace | undefined);
|
|
73
161
|
/**
|
|
74
162
|
* Read the current composed config (schema defaults → entry base → settings
|
|
75
163
|
* user layer) through the hard gate.
|
|
@@ -79,8 +167,35 @@ export declare class AdvisorConfigGateway extends TypertRemoteService {
|
|
|
79
167
|
config: ResolvedAdvisorConfig;
|
|
80
168
|
};
|
|
81
169
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
170
|
+
* B2 — the authoritative snapshot for one live session
|
|
171
|
+
* (`/api/advisor/getSession`, args `{ sessionId }`). Read-only: never
|
|
172
|
+
* allocates override state (the snapshot rides the wiring's `sessionStatus`
|
|
173
|
+
* readback). Unknown/disposed targets answer `advisor/session-unknown`
|
|
174
|
+
* without touching the controller; a gateway without an elected owner
|
|
175
|
+
* answers `advisor/unavailable`. Business outcomes live in the returned
|
|
176
|
+
* data (plugin-domain tags) — nothing here throws coded failures and the
|
|
177
|
+
* dsh failure vocabulary stays frozen.
|
|
178
|
+
*/
|
|
179
|
+
getSession(sessionId: string): AdvisorSessionRpcResult;
|
|
180
|
+
/**
|
|
181
|
+
* B2 — set (or reset) the per-session reviewer route for one live session
|
|
182
|
+
* (`/api/advisor/setSessionModel`, args `{ sessionId, selection }`).
|
|
183
|
+
* `selection: null` = reset (re-inherit the CURRENT global defaults; never
|
|
184
|
+
* touches the enable override). A non-null selection must be the atomic
|
|
185
|
+
* `{ provider, model }` pair and passes the SAME validation as the command
|
|
186
|
+
* face (`parseModelPair` — spec §5.3); the write itself routes through the
|
|
187
|
+
* elected owner's `AdvisorCommandController` (`setModel`/`resetModel`), so
|
|
188
|
+
* pre-commit `resolveModelInfo` validation (60 s, cancellable, no retry),
|
|
189
|
+
* per-session generation fencing, and the route-change semantics are
|
|
190
|
+
* INHERITED, not reimplemented. Unknown/disposed targets are rejected
|
|
191
|
+
* BEFORE the controller runs — no generation is allocated for a dead
|
|
192
|
+
* session. Returns the post-commit snapshot on success.
|
|
193
|
+
*/
|
|
194
|
+
setSessionModel(sessionId: string, selection: unknown): Promise<AdvisorSessionRpcResult>;
|
|
195
|
+
/**
|
|
196
|
+
* Validate a config patch and write it to the advisor ENTRY config (live —
|
|
197
|
+
* the Loader commits the volatile fields and the runtime re-applies through
|
|
198
|
+
* the bridge `onChange`; no restart needed).
|
|
84
199
|
* @param patch - any subset of the config keys; unknown keys are rejected
|
|
85
200
|
* by the `Config` schema before anything is written.
|
|
86
201
|
* @returns the NEW composed config after the write.
|
|
@@ -91,14 +206,14 @@ export declare class AdvisorConfigGateway extends TypertRemoteService {
|
|
|
91
206
|
config: ResolvedAdvisorConfig;
|
|
92
207
|
}>;
|
|
93
208
|
/**
|
|
94
|
-
* Resolve the live
|
|
95
|
-
* (qc2 W-1):
|
|
96
|
-
* survived the non-strict
|
|
97
|
-
* carrying the message — the gateway never fails the
|
|
98
|
-
*
|
|
99
|
-
* raw source is still readable, the fallback seeds its scalar
|
|
100
|
-
* (systemPrompt / immuneTurns / maxDeltaMessages) instead of
|
|
101
|
-
* defaults, so an invalid
|
|
209
|
+
* Resolve the live entry config through the hard gate. Containment
|
|
210
|
+
* (qc2 W-1): an entry config the resolver rejects (e.g. an unknown key
|
|
211
|
+
* that survived the non-strict schemastery object merge) resolves to
|
|
212
|
+
* disabled-with-reason carrying the message — the gateway never fails the
|
|
213
|
+
* RPC on a bad config, and gate semantics hold (no model call can start).
|
|
214
|
+
* S1: when the raw source is still readable, the fallback seeds its scalar
|
|
215
|
+
* latches (systemPrompt / immuneTurns / maxDeltaMessages) instead of
|
|
216
|
+
* hardcoded defaults, so an invalid config only drops the offending keys.
|
|
102
217
|
*/
|
|
103
218
|
private readConfig;
|
|
104
219
|
}
|