You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Would you like the advisor's provider and model to be per-session? If there's no plan for that on main, I'll just handle it locally on my end — I wanted to check with you first so I don't submit a change that wouldn't be useful.
Recommendation: yes, planned; a separate PR is welcome. Support an explicit provider/model pair for the current live session, retaining the existing persisted configuration as the global default. Different concurrent/interleaved sessions need different review costs, quality and trust boundaries. Selecting an advisor must not alter another session or the primary model.
Keep this separate from #87's card-seat migration and the maintainer's intended sequence: merge #87 after the dsh 0.1.6 RC, then release. This feature need not block that release.
Verified baseline and current behavior
Source notation below: A = omdsh-dev/dsh-advisor checkout 72fee953a561c32f00ad7be599c1f8ee88866ef0 (0.4.1, package.json:1-8); H = deepseek-harness checkout ddefc45fbc7f8e46dd73185e68295696d1297887 (0.1.6-alpha.2, package.json:1-9). References are inspected working-copy file:line ranges, not a claim about #87's diff or the future RC.
The bundle inserts a host-level row (A:cordis.patch.yml:1-6); defaults → row base → settings user layer feed one advisor namespace (A:src/settings.ts:5-12,52,113-137). Host settings registrations/document/write queues are namespace-keyed, not session-keyed (H:packages/settings/settings/src/index.ts:333-338,472-495).
There are already per-session runtimes, but only the enable override is session-local configuration: AdvisorSessionOverrides stores booleans (A:src/commands.ts:64-102); safeEffective overlays only enabled before runtime construction takes provider/model (A:src/index.ts:191-197,268-279). The live registry supports many agents (H:packages/core/agent/src/index.ts:245-256,558-585).
/advisor receives invocation.agent.session.id (A:src/commands.ts:303-306; exact invoking Agent contract: H:packages/interaction/commands/src/index.ts:40-77,421-425). /advisor config is intentionally global readback (A:src/commands.ts:344-346), while status currently also reads the global pair and must change (A:src/index.ts:492-503).
Global web config uses advisor/get and advisor/set → settings.update('advisor', ...) (A:src/gateway.ts:101-139,197-225; client calls: A:src/client/advisor-store.ts:370,647). The current card is settings.plugin.item (A:src/client/index.ts:151-173); the new plugin-manager config seats are root-scoped with only {view} owner props, not SessionId (H:packages/client/ui-plugin-manager/src/client/slot-contract.ts:16-45).
Host precedent: session.selectModel writes durable model/selection and its projection tracks pending/last-used choices (H:packages/api/session-controller/src/index.ts:249-256; src/agent.ts:283-335; src/model-selection-projection.ts:35-68, within that package). Do not call it for advisor selection: it changes the primary route and also attempts to save its default (H:packages/api/session-controller/src/commands.ts:133-159).
Session UI is available: conversation.session.header.actions is a list/session slot; its parent declares it and receives the bound SessionId (H:packages/client/ui-conversation/src/client/contract/slots.ts:142-147; src/client/apply.ts:306-324, within that package).
Arbitrary plugin-owned durable events are not a supported escape hatch: append creates a frozen envelope without an ignorable option (H:packages/core/session/src/index.ts:719-747); unknown external types are outside the generated catalog (known-event-types.ts:8-20 in that package), and restore rejects unknown non-ignorable records (H:packages/session/session-format-v1-to-v2/src/validation.ts:65-85, called by session-format-v2-to-v3/src/validation.ts:73). Projections derive from logs, not an independent plugin KV store (H:packages/session/session-projection/README.md:54-69,130-134).
Proposed contract
The selected lifetime is intentionally ephemeral, like /advisor on|off, not a temporary storage solution awaiting replacement. It survives navigation/reconnection while the same Agent and advisor owner remain live; disposal, cold resume, restart or owner reload clears it. A fork/new session inherits global defaults. Ordinary host command records may exist, but will not be replayed to restore advisor preferences (H:packages/interaction/commands/src/index.ts:70-77).
Precedence and schema
Validate the global config shape; malformed types/unknown keys still fail closed.
Enable = existing session enable override, otherwise raw global enabled.
Route = complete session pair, otherwise the composed global advisor pair. Never merge half-pairs across those two levels.
Global composition remains user settings → row base → schema defaults. Apply the no-call-without-both-fields gate after session resolution. There is no follow-primary mode or failure fallback to another route.
Persisted schema delta: none. Keep exactly enabled: boolean = false, provider?: string, model?: string, systemPrompt: string = "", immuneTurns: integer >= 0 = 3, maxDeltaMessages: integer >= 0 = 60 (0 unbounded); preserve explicit unknown-key rejection (A:src/config.ts:45-120). New runtime-only modelOverride?: {provider: string; model: string} is an atomic pair; absence inherits, reset deletes it. Reject extra fields, blank/partial pairs and whitespace-containing identifiers; trim outer whitespace and retain case. No sessionModels, inherit, thinking or reasoningEffort config keys.
A structurally valid default missing its pair may be satisfied for one session by a complete local pair. An invalid global schema cannot be bypassed. Setting/resetting a model never toggles enabled; off retains the local pair for later on. Pinning a pair equal to today's default still protects it from tomorrow's default edit.
Commands and web surface
/advisor model: show effective pair, source (session/global) and live-session lifetime.
/advisor model set <provider> <model>: set for the invoking live session. Separate arguments allow model IDs containing /; no arbitrary session-id command argument.
/advisor model reset: re-inherit current advisor defaults, without changing the enable override.
Existing toggle/on/off stay; status shows the effective pair/source/reason; config remains explicitly labeled global defaults.
TUI: extend the optional command-completion tree; its settings section stays global (A:src/tui.ts:54-60,103-141; A:src/tui-settings.ts:103-114,131-167). No TUI host picker is assumed.
Web: add an Advisor action to conversation.session.header.actions, not the singleton primary-model slot or root plugin card. Show pair/source/lifetime, set, and Use global default. The global card remains global-only.
Proposed plugin-owned endpoints: advisor/getSession({sessionId}) and advisor/setSessionModel({sessionId, selection: {provider, model} | null}), returning the authoritative session snapshot (sessionId, enabled, modelOverride, modelSource, effectiveModel, optional disabledReason, lifetime: 'live-session'). Keep existing advisor/get|set global. Use explicit typert registration as today (A:src/gateway.ts:197-225), not new first-party dsh methods or failure codes.
Reject unknown/disposed targets without allocating state or resuming them. SessionId is not authorization: retain Connection's Host/Origin and browser-authentication boundary (H:packages/client/connection/src/rpc-host.ts:96-100; src/index.ts:139-153, within that package), not a raw HTTP endpoint or invented session ACL support. Plugin-domain error tags can live in returned data without expanding dsh's frozen failure vocabulary.
Fetch on menu open, binding change, reconnect and focus/refresh; discard late responses for the old binding. Use a neutral closed action, not a model label falsely claiming continuous synchronization. An open menu is a refreshable snapshot. No speculative push-event contract or polling is needed; unavailable UI never falls back to global writes.
Runtime, failure and ownership requirements
Validate through the existing application-root LLM service's resolveModelInfo(provider, model, signal) before commit; catalog membership is advisory, so permit manual routable IDs (A:src/index.ts:280-285; H:packages/llm/llm/src/index.ts:724-747). Bound lookup to 60 seconds, matching the existing runtime deadline default (A:src/advisor-runtime.ts:105-111), with cancellation and no automatic retries. Failure leaves the previous selection/runtime untouched; lookup does not prove future credentials/quota success.
Fence async mutations by exact Agent identity and per-session generation; a newer set/reset supersedes unresolved older work. Dispose/reload cannot be undone by a delayed validation completion.
An effective-pair change aborts that session's old call, drops backlog, re-seeds to current session seq, and takes effect at the next new reviewable delta. Selection itself makes no generation call. Fence late note delivery by current runtime/generation; preserve delivery cooldown. Same effective pair needs no restart. Existing abort/queue behavior: A:src/advisor-runtime.ts:290-318,438-444.
Global pair edits affect inheritors, not pinned sessions; global prompt/window/cooldown remain global. Use one effective resolver for construction, status and signature/reapplication (A:src/index.ts:240-279,417-453,492-503); include enabled gate-blocked sessions with no runtime when defaults become usable.
Adapter/credential/quota failures after acceptance keep existing halt/pause behavior, name the selected route and never silently reroute; /advisor on remains recovery (A:src/index.ts:463-490; .mstar/specs/advisor-plugin.md:182). Reset to a missing global pair succeeds but reports gate-blocked/no call.
One elected owner must own settings source, override controller, gateway access and runtimes. Several fibers already exist; settings/gateway currently precede the reviewer claim (A:src/index.ts:80-99,113-152,311-330; A:src/settings.ts:98-111). Do not create a gateway-private map or depend on first-registrant coincidence. Validate before claiming; release on failure/unload; non-owners remain inert. No automatic surviving-fiber promotion is promised.
Clear entries/pending work on agent/session disposal and owner teardown; remove an empty entry on reset. Existing disposal hooks: A:src/index.ts:390-401. State is O(live overrides/pending work), with no historical SessionId accumulation, TTL or GC job. Persist only existing defaults, not overrides, in settings; no sidecar/localStorage/custom log record.
Alternatives considered
Alternative
User experience / resume
Blast radius, cost and decision
Per-session map in global settings
Durable only with same settings document/provider and SessionId; not session export/fork portability.
Feasible but rejected: new schema, shared document/revision contention, cleanup ownership. Dispose-time GC breaks resume; live-registry pruning deletes dormant sessions. mutate/replace can delete, so it is not API-append-only (H:packages/settings/settings/README.md:34-68).
In-memory command override
Explicit current-live-session choice, resets on cold resume.
Selected: smallest complete isolation contract, low cost, no durable accumulation; UI can layer on without changing storage.
Follow logged primary route
Reconstructible from primary selection/header, but couples review choice to primary choice.
Rejected: loses independent cost/trust selection; must handle pending selection, not only last header (H:packages/api/session-controller/src/agent.ts:283-309).
Session UI slot
Correctly binds the control to a session; supplies no persistence itself.
Selected presentation only, backed by the command controller, not a separate storage architecture.
Per-session row/composition
No such dimension in profile composition.
Not viable here: bundles/profile/launcher layers are process configuration (H:packages/boot/app-boot/src/profile.ts:5-13); presets join a standing mount and reject root-service leakage (H:packages/preset/agent-presets/src/index.ts:430-455; src/mount.ts:5-13). They do not isolate this singleton advisor.
Custom durable event / sidecar
Could aspire to resume preservation.
Custom append is not viable on this host (restore evidence above); a sidecar is technically possible but adds unnecessary storage/locking/GC and import/fork policy. Neither is a planned stepping stone.
Frozen contract amendment and host boundary
B0 must precede implementation. The authoritative spec is .mstar/specs/advisor-plugin.md, not a second spec under docs/ (A:.mstar/AGENTS.md:14-27). Amend §5 to distinguish persisted defaults from runtime overrides and apply its explicit pair gate after session resolution (spec:67-89); update command/verification and KD-5 lifecycle text (spec:178-183). Clarify §8.6 KD-6's no-new-plugin-config-keys freeze still holds for the same six persisted keys while permitting the separate runtime command/API (spec:185-194). Do not remove the freeze, alter the minimal request whitelist, add tools/thinking controls, or change the 768-token cap. Update README/consumer/configuration docs and global-vs-session labels together.
No host change is required for the selected command/runtime/UI design. Any new consumed host packages remain public-registry peers, never linked or patched (A:AGENTS.md:88-101). Host support would be required for a session-log-native durable independent advisor choice: a supported external append/admission contract with omission safety, or a separately approved first-party event/state contract with catalog/projection changes. Local settings/sidecar persistence is technically possible but rejected here. Do not repurpose model/selection, replay command strings, or pretend append(..., {ignorable:true}) exists.
Phased scope and contribution checklist
B0 — maintainer + architect: contract acceptance. Accept lifetime/precedence, amend frozen spec, file issue and answer contributor. Done: §5/KD-6 and lifecycle text no longer contradict the proposal. Unblocks a separate B1 PR, not fix: mount the Advisor card on the Plugins page (dsh 0.1.6-alpha.2) #87.
B1 — plugin contributor, after B0 and baseline coordination with fix: mount the Advisor card on the Plugins page (dsh 0.1.6-alpha.2) #87/RC: command/runtime feature. Shared owner, atomic override, command/status/TUI completion, validation/races and docs. Done: two sessions choose independently, inheritors track defaults, failed/disposed/superseded writes are harmless, no late old-route note, cold resume resets, multi-fiber composition has one owner/reviewer. Works without optional web/typert/settings. Independently releasable.
B2 — plugin frontend contributor, after B1 and fix: mount the Advisor card on the Plugins page (dsh 0.1.6-alpha.2) #87's final client seats: session web control. Same controller through plugin endpoints and session header action. Done: A's selector changes only A; B/global config remain unchanged; reset/unavailable/binding/reconnect paths are truthful and stale responses cannot target another session. Independently releasable; stopping after B1 is a complete command-based feature.
H0 — separate host design only if maintainers require persisted session choice. Define restore/fork/import/omission semantics and ship an approved public host seam before promising it. Not a hidden dependency or required follow-up for B1/B2.
Contributions must include scoped regression tests for atomic parsing/invalid inputs; A/B/inheritor isolation; global versus effective readback; missing-default-pair versus malformed-global gate; off/on/reset; equal-default explicit pin; timeout/cancel/dispose/reset during validation; no late delivery and no replay on route change; multiple fibers/owner unload; cold resume/fork lifetime. B2 adds gateway validation, session binding and stale-response/UI tests. Preserve the existing minimal-request behavior. Include spec, usage/configuration/consumer docs and completion changes. These are future acceptance requirements; this proposal did not run tests or change implementation.
Contributor response: recommend “yes, planned — here is the shape; separate PR welcome,” with this issue linked. This is useful upstream, not local-only.
Lifetime: recommend intentionally live-session-only, matching enable overrides. If durable cold resume is required, decide before B1 and scope H0 instead of silently adding a settings map.
Precedence/schema: recommend atomic local pair → global advisor pair, zero persisted-key delta, no primary-follow mode; keeps reviewer choice independent.
Route switch: recommend abort/drop/re-seed and next-new-delta effect, preserving cooldown; this avoids surprise history replay and stale advice, though already-submitted provider work may still be billed.
Context
Filed from the PR #87 discussion — the contributor asked:
Recommendation: yes, planned; a separate PR is welcome. Support an explicit provider/model pair for the current live session, retaining the existing persisted configuration as the global default. Different concurrent/interleaved sessions need different review costs, quality and trust boundaries. Selecting an advisor must not alter another session or the primary model.
Keep this separate from #87's card-seat migration and the maintainer's intended sequence: merge #87 after the dsh 0.1.6 RC, then release. This feature need not block that release.
Verified baseline and current behavior
Source notation below: A =
omdsh-dev/dsh-advisorcheckout72fee953a561c32f00ad7be599c1f8ee88866ef0(0.4.1,package.json:1-8); H =deepseek-harnesscheckoutddefc45fbc7f8e46dd73185e68295696d1297887(0.1.6-alpha.2,package.json:1-9). References are inspected working-copyfile:lineranges, not a claim about #87's diff or the future RC.A:cordis.patch.yml:1-6); defaults → row base → settings user layer feed oneadvisornamespace (A:src/settings.ts:5-12,52,113-137). Host settings registrations/document/write queues are namespace-keyed, not session-keyed (H:packages/settings/settings/src/index.ts:333-338,472-495).AdvisorSessionOverridesstores booleans (A:src/commands.ts:64-102);safeEffectiveoverlays only enabled before runtime construction takes provider/model (A:src/index.ts:191-197,268-279). The live registry supports many agents (H:packages/core/agent/src/index.ts:245-256,558-585)./advisorreceivesinvocation.agent.session.id(A:src/commands.ts:303-306; exact invoking Agent contract:H:packages/interaction/commands/src/index.ts:40-77,421-425)./advisor configis intentionally global readback (A:src/commands.ts:344-346), while status currently also reads the global pair and must change (A:src/index.ts:492-503).advisor/getandadvisor/set→settings.update('advisor', ...)(A:src/gateway.ts:101-139,197-225; client calls:A:src/client/advisor-store.ts:370,647). The current card issettings.plugin.item(A:src/client/index.ts:151-173); the new plugin-manager config seats are root-scoped with only{view}owner props, not SessionId (H:packages/client/ui-plugin-manager/src/client/slot-contract.ts:16-45).session.selectModelwrites durablemodel/selectionand its projection tracks pending/last-used choices (H:packages/api/session-controller/src/index.ts:249-256;src/agent.ts:283-335;src/model-selection-projection.ts:35-68, within that package). Do not call it for advisor selection: it changes the primary route and also attempts to save its default (H:packages/api/session-controller/src/commands.ts:133-159).conversation.session.header.actionsis a list/session slot; its parent declares it and receives the bound SessionId (H:packages/client/ui-conversation/src/client/contract/slots.ts:142-147;src/client/apply.ts:306-324, within that package).ignorableoption (H:packages/core/session/src/index.ts:719-747); unknown external types are outside the generated catalog (known-event-types.ts:8-20in that package), and restore rejects unknown non-ignorable records (H:packages/session/session-format-v1-to-v2/src/validation.ts:65-85, called bysession-format-v2-to-v3/src/validation.ts:73). Projections derive from logs, not an independent plugin KV store (H:packages/session/session-projection/README.md:54-69,130-134).Proposed contract
The selected lifetime is intentionally ephemeral, like
/advisor on|off, not a temporary storage solution awaiting replacement. It survives navigation/reconnection while the same Agent and advisor owner remain live; disposal, cold resume, restart or owner reload clears it. A fork/new session inherits global defaults. Ordinary host command records may exist, but will not be replayed to restore advisor preferences (H:packages/interaction/commands/src/index.ts:70-77).Precedence and schema
Persisted schema delta: none. Keep exactly
enabled: boolean = false,provider?: string,model?: string,systemPrompt: string = "",immuneTurns: integer >= 0 = 3,maxDeltaMessages: integer >= 0 = 60(0unbounded); preserve explicit unknown-key rejection (A:src/config.ts:45-120). New runtime-onlymodelOverride?: {provider: string; model: string}is an atomic pair; absence inherits, reset deletes it. Reject extra fields, blank/partial pairs and whitespace-containing identifiers; trim outer whitespace and retain case. NosessionModels,inherit,thinkingorreasoningEffortconfig keys.A structurally valid default missing its pair may be satisfied for one session by a complete local pair. An invalid global schema cannot be bypassed. Setting/resetting a model never toggles enabled; off retains the local pair for later on. Pinning a pair equal to today's default still protects it from tomorrow's default edit.
Commands and web surface
/advisor model: show effective pair, source (session/global) and live-session lifetime./advisor model set <provider> <model>: set for the invoking live session. Separate arguments allow model IDs containing/; no arbitrary session-id command argument./advisor model reset: re-inherit current advisor defaults, without changing the enable override.A:src/tui.ts:54-60,103-141;A:src/tui-settings.ts:103-114,131-167). No TUI host picker is assumed.conversation.session.header.actions, not the singleton primary-model slot or root plugin card. Show pair/source/lifetime, set, and Use global default. The global card remains global-only.advisor/getSession({sessionId})andadvisor/setSessionModel({sessionId, selection: {provider, model} | null}), returning the authoritative session snapshot (sessionId,enabled,modelOverride,modelSource,effectiveModel, optionaldisabledReason,lifetime: 'live-session'). Keep existingadvisor/get|setglobal. Use explicit typert registration as today (A:src/gateway.ts:197-225), not new first-party dsh methods or failure codes.H:packages/client/connection/src/rpc-host.ts:96-100;src/index.ts:139-153, within that package), not a raw HTTP endpoint or invented session ACL support. Plugin-domain error tags can live in returned data without expanding dsh's frozen failure vocabulary.Runtime, failure and ownership requirements
resolveModelInfo(provider, model, signal)before commit; catalog membership is advisory, so permit manual routable IDs (A:src/index.ts:280-285;H:packages/llm/llm/src/index.ts:724-747). Bound lookup to 60 seconds, matching the existing runtime deadline default (A:src/advisor-runtime.ts:105-111), with cancellation and no automatic retries. Failure leaves the previous selection/runtime untouched; lookup does not prove future credentials/quota success.A:src/advisor-runtime.ts:290-318,438-444.A:src/index.ts:240-279,417-453,492-503); include enabled gate-blocked sessions with no runtime when defaults become usable./advisor onremains recovery (A:src/index.ts:463-490;.mstar/specs/advisor-plugin.md:182). Reset to a missing global pair succeeds but reports gate-blocked/no call.A:src/index.ts:80-99,113-152,311-330;A:src/settings.ts:98-111). Do not create a gateway-private map or depend on first-registrant coincidence. Validate before claiming; release on failure/unload; non-owners remain inert. No automatic surviving-fiber promotion is promised.A:src/index.ts:390-401. State is O(live overrides/pending work), with no historical SessionId accumulation, TTL or GC job. Persist only existing defaults, not overrides, in settings; no sidecar/localStorage/custom log record.Alternatives considered
mutate/replacecan delete, so it is not API-append-only (H:packages/settings/settings/README.md:34-68).H:packages/api/session-controller/src/agent.ts:283-309).H:packages/boot/app-boot/src/profile.ts:5-13); presets join a standing mount and reject root-service leakage (H:packages/preset/agent-presets/src/index.ts:430-455;src/mount.ts:5-13). They do not isolate this singleton advisor.Frozen contract amendment and host boundary
B0 must precede implementation. The authoritative spec is
.mstar/specs/advisor-plugin.md, not a second spec underdocs/(A:.mstar/AGENTS.md:14-27). Amend §5 to distinguish persisted defaults from runtime overrides and apply its explicit pair gate after session resolution (spec:67-89); update command/verification and KD-5 lifecycle text (spec:178-183). Clarify §8.6 KD-6's no-new-plugin-config-keys freeze still holds for the same six persisted keys while permitting the separate runtime command/API (spec:185-194). Do not remove the freeze, alter the minimal request whitelist, add tools/thinking controls, or change the 768-token cap. Update README/consumer/configuration docs and global-vs-session labels together.No host change is required for the selected command/runtime/UI design. Any new consumed host packages remain public-registry peers, never linked or patched (
A:AGENTS.md:88-101). Host support would be required for a session-log-native durable independent advisor choice: a supported external append/admission contract with omission safety, or a separately approved first-party event/state contract with catalog/projection changes. Local settings/sidecar persistence is technically possible but rejected here. Do not repurposemodel/selection, replay command strings, or pretendappend(..., {ignorable:true})exists.Phased scope and contribution checklist
Contributions must include scoped regression tests for atomic parsing/invalid inputs; A/B/inheritor isolation; global versus effective readback; missing-default-pair versus malformed-global gate; off/on/reset; equal-default explicit pin; timeout/cancel/dispose/reset during validation; no late delivery and no replay on route change; multiple fibers/owner unload; cold resume/fork lifetime. B2 adds gateway validation, session binding and stale-response/UI tests. Preserve the existing minimal-request behavior. Include spec, usage/configuration/consumer docs and completion changes. These are future acceptance requirements; this proposal did not run tests or change implementation.
Non-goals: durable override restoration, per-session plugin rows, primary-model changes/follow mode, per-session prompt/window/cooldown, multi-process synchronization, tools, advisor transcripts, cost statistics, full omp parity, host patches or first-party vocabulary additions.
Maintainer decisions