super-dsh 0.3.2-e → 0.3.3

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.
@@ -26,7 +26,7 @@
26
26
  * `onApiKey` (adapter state, world home only) and confirmed in the same turn.
27
27
  */
28
28
  import type { Context } from "@deepseek-ai/cordis";
29
- import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from "@deepseek-ai/dsh-agent";
29
+ import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget, Model } from "@deepseek-ai/dsh-agent";
30
30
  import { Inbox } from "./inbox.js";
31
31
  import type { AgentCancelCause, Session, SessionId, UserMessage } from "@deepseek-ai/dsh-session";
32
32
  import type { TokenUsage } from "@deepseek-ai/dsh-llm";
@@ -65,6 +65,9 @@ export declare class AgyAgent implements Agent {
65
65
  readonly session: WithSystemHead<Session>;
66
66
  readonly inbox: Inbox;
67
67
  readonly ctx: Context;
68
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
69
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
70
+ readonly model: Model;
68
71
  private readonly client;
69
72
  private readonly loopCtx;
70
73
  private readonly scope;
@@ -2,7 +2,7 @@ import { agentEvents } from "@deepseek-ai/dsh-agent";
2
2
  import { Inbox } from "./inbox.js";
3
3
  import { LlmAttemptId, ToolCallId, createAssistantMessage, createSystemMessage, createToolResultMessage } from "@deepseek-ai/dsh-llm";
4
4
  import { createScope } from "@deepseek-ai/dsh-scope";
5
- import { withSystemHead } from "../../superd-hub/dist/index.js";
5
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
6
6
  /** v2 live assistant-stream publication (same frame protocol as hermes). */
7
7
  class AssistantStreamBridge {
8
8
  attemptId;
@@ -80,6 +80,9 @@ export class AgyAgent {
80
80
  session;
81
81
  inbox;
82
82
  ctx;
83
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
84
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
85
+ model;
83
86
  client;
84
87
  loopCtx;
85
88
  scope;
@@ -106,6 +109,17 @@ export class AgyAgent {
106
109
  this.id = id;
107
110
  this.options = options;
108
111
  this.session = withSystemHead(session, () => createSystemMessage(""));
112
+ this.model = createAgentModelFace({
113
+ session: this.session,
114
+ options: this.options,
115
+ appendSelection: (selection) => {
116
+ this.session.append("model/selection", selection);
117
+ },
118
+ readDefault: () => {
119
+ const service = this.ctx.get("agentDefaultModel");
120
+ return service?.currentSelection?.();
121
+ },
122
+ });
109
123
  this.client = client;
110
124
  this.onIdleExit = onIdleExit;
111
125
  this.runtimeInfo = runtimeInfo;
@@ -21,7 +21,6 @@ import { realpathSync, statSync } from "node:fs";
21
21
  import { bindForeignHub } from "@pgmi-builds/superd-hub/foreign-hub";
22
22
  import { join } from "node:path";
23
23
  import { Service } from "@deepseek-ai/cordis";
24
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
25
24
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
26
25
  import { AgyCliClient } from "./agy-cli-client.js";
27
26
  import { hasSigninCredential, resolveAuthMode } from "./agy-auth.js";
@@ -475,13 +474,11 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
475
474
  try {
476
475
  const selection = { provider: agentOptions.provider ?? "agy", model: agentOptions.model ?? "" };
477
476
  if (selection.model !== "") {
478
- let picked = selection;
479
- installModelSelection(agent.ctx, {
480
- get current() { return picked; },
481
- set current(next) { picked = next; },
482
- assembled: undefined,
483
- });
484
- session.append("model/selection", selection);
477
+ // 0.2.2: installModelSelection dropped its injected-ref parameter — the
478
+ // durable face (`agent.model`, the same mechanism session/selectModel
479
+ // uses) carries the pin: select() appends the typed, deduped
480
+ // `model/selection` event.
481
+ agent.model.select(selection);
485
482
  trace(`session route pinned: ${selection.provider}/${selection.model}`);
486
483
  }
487
484
  }
@@ -25,12 +25,12 @@
25
25
  },
26
26
  "peerDependencies": {
27
27
  "@deepseek-ai/cordis": "^4.0.1",
28
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
29
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
30
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
28
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
29
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
30
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
34
34
  "@pgmi-builds/superd-hub": "*"
35
35
  },
36
36
  "peerDependenciesMeta": {
@@ -25,7 +25,7 @@
25
25
  * `onApiKey` (adapter state, world home only) and confirmed in the same turn.
26
26
  */
27
27
  import type { Context } from "@deepseek-ai/cordis";
28
- import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from "@deepseek-ai/dsh-agent";
28
+ import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget, Model } from "@deepseek-ai/dsh-agent";
29
29
  import { Inbox } from "./inbox.js";
30
30
  import type { AgentCancelCause, Session, SessionId, UserMessage } from "@deepseek-ai/dsh-session";
31
31
  import type { TokenUsage } from "@deepseek-ai/dsh-llm";
@@ -63,6 +63,9 @@ export declare class AgyAgent implements Agent {
63
63
  readonly session: WithSystemHead<Session>;
64
64
  readonly inbox: Inbox;
65
65
  readonly ctx: Context;
66
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
67
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
68
+ readonly model: Model;
66
69
  private readonly client;
67
70
  private readonly loopCtx;
68
71
  private readonly scope;
@@ -2,7 +2,7 @@ import { agentEvents } from "@deepseek-ai/dsh-agent";
2
2
  import { Inbox } from "./inbox.js";
3
3
  import { LlmAttemptId, ToolCallId, createAssistantMessage, createSystemMessage, createToolResultMessage } from "@deepseek-ai/dsh-llm";
4
4
  import { createScope } from "@deepseek-ai/dsh-scope";
5
- import { withSystemHead } from "../../superd-hub/dist/index.js";
5
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
6
6
  /** v2 live assistant-stream publication (same frame protocol as hermes). */
7
7
  class AssistantStreamBridge {
8
8
  attemptId;
@@ -80,6 +80,9 @@ export class AgyAgent {
80
80
  session;
81
81
  inbox;
82
82
  ctx;
83
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
84
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
85
+ model;
83
86
  client;
84
87
  loopCtx;
85
88
  scope;
@@ -106,6 +109,17 @@ export class AgyAgent {
106
109
  this.id = id;
107
110
  this.options = options;
108
111
  this.session = withSystemHead(session, () => createSystemMessage(""));
112
+ this.model = createAgentModelFace({
113
+ session: this.session,
114
+ options: this.options,
115
+ appendSelection: (selection) => {
116
+ this.session.append("model/selection", selection);
117
+ },
118
+ readDefault: () => {
119
+ const service = this.ctx.get("agentDefaultModel");
120
+ return service?.currentSelection?.();
121
+ },
122
+ });
109
123
  this.client = client;
110
124
  this.onIdleExit = onIdleExit;
111
125
  this.runtimeInfo = runtimeInfo;
@@ -26,7 +26,6 @@
26
26
  import { realpathSync, statSync } from "node:fs";
27
27
  import { join } from "node:path";
28
28
  import { Service } from "@deepseek-ai/cordis";
29
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
30
29
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
31
30
  import { AgyBridgeClient } from "./agy-client.js";
32
31
  import { registerAntigravityPreset } from "./agent-preset-agy.js";
@@ -413,13 +412,11 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
413
412
  try {
414
413
  const selection = { provider: agentOptions.provider ?? "agy", model: agentOptions.model ?? "" };
415
414
  if (selection.model !== "") {
416
- let picked = selection;
417
- installModelSelection(agent.ctx, {
418
- get current() { return picked; },
419
- set current(next) { picked = next; },
420
- assembled: undefined,
421
- });
422
- session.append("model/selection", selection);
415
+ // 0.2.2: installModelSelection dropped its injected-ref parameter — the
416
+ // durable face (`agent.model`, the same mechanism session/selectModel
417
+ // uses) carries the pin: select() appends the typed, deduped
418
+ // `model/selection` event.
419
+ agent.model.select(selection);
423
420
  trace(`session route pinned: ${selection.provider}/${selection.model}`);
424
421
  }
425
422
  }
@@ -27,11 +27,11 @@
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@deepseek-ai/cordis": "^4.0.1",
30
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
34
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0"
30
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
34
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0"
35
35
  },
36
36
  "peerDependenciesMeta": {
37
37
  "@deepseek-ai/cordis": {
@@ -50,7 +50,7 @@ import { resolveRoute, routeSeed } from "./claude-route.js";
50
50
  import { CLAUDE_PROVIDER_ID } from "./adapter.js";
51
51
  import { claudePermissionMode, presetFromEvents } from "./permission.js";
52
52
  import { catalogFromInit, modelEntryFromSdk, readModelCatalog, setModelCatalog } from "./models.js";
53
- import { withSystemHead } from "../../superd-hub/dist/index.js";
53
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
54
54
  import { registerClaudeCommands, slashCommandsFromReported, } from "./commands.js";
55
55
  /** Diagnostic trace (set CLAUDE_TRACE=1 on the dsh process to enable). */
56
56
  const TRACE = process.env.CLAUDE_TRACE === "1";
@@ -237,6 +237,9 @@ export class ClaudeAgent {
237
237
  session;
238
238
  inbox;
239
239
  ctx;
240
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
241
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
242
+ model;
240
243
  client;
241
244
  /** Unsubscriber for the LIVE client's wire listener — released before a swap closes the old child. */
242
245
  clientUnsubscribe = undefined;
@@ -313,6 +316,17 @@ export class ClaudeAgent {
313
316
  this.id = id;
314
317
  this.options = options;
315
318
  this.session = withSystemHead(session, () => createSystemMessage(""));
319
+ this.model = createAgentModelFace({
320
+ session: this.session,
321
+ options: this.options,
322
+ appendSelection: (selection) => {
323
+ this.session.append("model/selection", selection);
324
+ },
325
+ readDefault: () => {
326
+ const service = this.ctx.get("agentDefaultModel");
327
+ return service?.currentSelection?.();
328
+ },
329
+ });
316
330
  this.client = client;
317
331
  this.clientFactory = clientFactory;
318
332
  this.onIdleExit = onIdleExit;
@@ -23,14 +23,14 @@
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@deepseek-ai/cordis": "^4.0.1",
26
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
27
- "@deepseek-ai/dsh-commands": ">=0.1.5-0 <0.3.0-0",
28
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
29
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
30
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-user-approval": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-user-questions": ">=0.1.5-0 <0.3.0-0"
26
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
27
+ "@deepseek-ai/dsh-commands": ">=0.2.2-0 <1.0.0-0",
28
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
29
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
30
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-user-approval": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-user-questions": ">=0.2.2-0 <1.0.0-0"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@deepseek-ai/cordis": {
@@ -6,7 +6,7 @@ import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage,
6
6
  import { createScope } from "@deepseek-ai/dsh-scope";
7
7
  import { makeApprovalHandler, parseApprovalTimeout } from "./app-server-approvals.js";
8
8
  import { presetFromEvents, turnPolicyFromEvents } from "./permission.js";
9
- import { withSystemHead } from "../../superd-hub/dist/index.js";
9
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
10
10
  import { NATIVE_LAUNCH } from "./codex-launch.js";
11
11
  import { resolveRoute } from "./codex-route.js";
12
12
  /**
@@ -257,6 +257,9 @@ export class CodexAgent {
257
257
  session;
258
258
  inbox;
259
259
  ctx;
260
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
261
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
262
+ model;
260
263
  #client;
261
264
  #loopCtx;
262
265
  #scope;
@@ -341,6 +344,17 @@ export class CodexAgent {
341
344
  this.id = id;
342
345
  this.options = options;
343
346
  this.session = withSystemHead(session, () => createSystemMessage(""));
347
+ this.model = createAgentModelFace({
348
+ session: this.session,
349
+ options: this.options,
350
+ appendSelection: (selection) => {
351
+ this.session.append("model/selection", selection);
352
+ },
353
+ readDefault: () => {
354
+ const service = this.ctx.get("agentDefaultModel");
355
+ return service?.currentSelection?.();
356
+ },
357
+ });
344
358
  this.#client = client;
345
359
  this.#onIdleExit = onIdleExit;
346
360
  this.#runtimeInfo = runtimeInfo;
@@ -32,13 +32,13 @@
32
32
 
33
33
  "peerDependencies": {
34
34
  "@deepseek-ai/cordis": "^4.0.1",
35
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
36
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
37
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
38
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
39
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
40
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
41
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
35
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
36
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
37
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
38
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
39
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
40
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
41
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
42
42
  "@pgmi-builds/superd-hub": "*"
43
43
  },
44
44
  "peerDependenciesMeta": {
@@ -4,7 +4,7 @@ import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage,
4
4
  import { createScope } from "@deepseek-ai/dsh-scope";
5
5
  import { NATIVE_SDK_LAUNCH } from "./codex-sdk-launch.js";
6
6
  import { resolveRoute } from "./codex-route.js";
7
- import { withSystemHead } from "../../superd-hub/dist/index.js";
7
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
8
8
  /**
9
9
  * v2 live assistant-stream publication for the Codex bridge — the same frame
10
10
  * protocol the reference loop's AssistantStreamAttempt speaks (start marker,
@@ -243,6 +243,9 @@ export class CodexAgent {
243
243
  session;
244
244
  inbox;
245
245
  ctx;
246
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
247
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
248
+ model;
246
249
  #client;
247
250
  #loopCtx;
248
251
  #scope;
@@ -312,6 +315,17 @@ export class CodexAgent {
312
315
  this.id = id;
313
316
  this.options = options;
314
317
  this.session = withSystemHead(session, () => createSystemMessage(""));
318
+ this.model = createAgentModelFace({
319
+ session: this.session,
320
+ options: this.options,
321
+ appendSelection: (selection) => {
322
+ this.session.append("model/selection", selection);
323
+ },
324
+ readDefault: () => {
325
+ const service = this.ctx.get("agentDefaultModel");
326
+ return service?.currentSelection?.();
327
+ },
328
+ });
315
329
  this.#client = client;
316
330
  this.#onIdleExit = onIdleExit;
317
331
  this.#runtimeInfo = runtimeInfo;
@@ -23,7 +23,6 @@ import { OPENAI_OAUTH_ID, readEffectiveProviderStore } from "./codex-oauth.js";
23
23
  import { existsSync, realpathSync, statSync } from "node:fs";
24
24
  import { join } from "node:path";
25
25
  import { Service } from "@deepseek-ai/cordis";
26
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
27
26
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
28
27
  import { CodexSdkClient } from "./codex-sdk-client.js";
29
28
  import { CodexAgent } from "./agent.js";
@@ -659,16 +658,11 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
659
658
  // `ModelSelectionRef` (same shape the controller installs) that the
660
659
  // next prompt's route check reads. Synchronous on purpose — the first
661
660
  // prompt must not depend on the global default's late flush.
662
- let picked = selection;
663
- installModelSelection(agent.ctx, {
664
- get current() { return picked; },
665
- set current(next) { picked = next; },
666
- assembled: undefined,
667
- });
668
- // `model/selection` is contributed to `SessionEventMap` by the upstream
669
- // session-controller package (declaration merging); this adapter does
670
- // not depend on it, so the append goes through a narrow local cast.
671
- session.append("model/selection", selection);
661
+ // 0.2.2: installModelSelection dropped its injected-ref parameter — the
662
+ // durable face (`agent.model`, the same mechanism session/selectModel
663
+ // uses) carries the pin: select() appends the typed, deduped
664
+ // `model/selection` event.
665
+ agent.model.select(selection);
672
666
  trace(`session route pinned: ${route.provider}/${route.model}`);
673
667
  }
674
668
  }
@@ -26,13 +26,13 @@
26
26
  "dependencies": {},
27
27
  "peerDependencies": {
28
28
  "@deepseek-ai/cordis": "^4.0.1",
29
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
30
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
34
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
35
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
29
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
30
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
34
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
35
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
36
36
  "@pgmi-builds/superd-hub": "*"
37
37
  },
38
38
  "peerDependenciesMeta": {
@@ -4,7 +4,7 @@ import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage,
4
4
  import { createScope } from "@deepseek-ai/dsh-scope";
5
5
  import { GatewayRpcError, HERMES_SPAWN_ATTEMPTS } from "./hermes-client.js";
6
6
  import { approvalChoiceFromOutcome } from "./permission.js";
7
- import { withSystemHead } from "../../superd-hub/dist/index.js";
7
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
8
8
  /**
9
9
  * v2 live assistant-stream publication for the Hermes bridge — the same frame
10
10
  * protocol the reference loop's AssistantStreamAttempt speaks (start marker,
@@ -259,6 +259,9 @@ export class HermesAgent {
259
259
  session;
260
260
  inbox;
261
261
  ctx;
262
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
263
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
264
+ model;
262
265
  #client;
263
266
  #loopCtx;
264
267
  #scope;
@@ -319,6 +322,17 @@ export class HermesAgent {
319
322
  this.id = id;
320
323
  this.options = options;
321
324
  this.session = withSystemHead(session, () => createSystemMessage(""));
325
+ this.model = createAgentModelFace({
326
+ session: this.session,
327
+ options: this.options,
328
+ appendSelection: (selection) => {
329
+ this.session.append("model/selection", selection);
330
+ },
331
+ readDefault: () => {
332
+ const service = this.ctx.get("agentDefaultModel");
333
+ return service?.currentSelection?.();
334
+ },
335
+ });
322
336
  this.#client = client;
323
337
  this.#onIdleExit = onIdleExit;
324
338
  this.#runtimeInfo = runtimeInfo;
@@ -24,7 +24,6 @@
24
24
  import { realpathSync, statSync } from "node:fs";
25
25
  import { join } from "node:path";
26
26
  import { Service } from "@deepseek-ai/cordis";
27
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
28
27
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
29
28
  import { bindForeignHub } from "@pgmi-builds/superd-hub/foreign-hub";
30
29
  import { HermesGatewayClient, HERMES_SPAWN_GRACE_MS } from "./hermes-client.js";
@@ -482,13 +481,11 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
482
481
  const route = await runtimeInfo?.routeContext({ provider: agentOptions.provider, model: agentOptions.model });
483
482
  if (route !== undefined) {
484
483
  const selection = { provider: route.provider, model: route.model };
485
- let picked = selection;
486
- installModelSelection(agent.ctx, {
487
- get current() { return picked; },
488
- set current(next) { picked = next; },
489
- assembled: undefined,
490
- });
491
- session.append("model/selection", selection);
484
+ // 0.2.2: installModelSelection dropped its injected-ref parameter — the
485
+ // durable face (`agent.model`, the same mechanism session/selectModel
486
+ // uses) carries the pin: select() appends the typed, deduped
487
+ // `model/selection` event.
488
+ agent.model.select(selection);
492
489
  trace(`session route pinned: ${route.provider}/${route.model}`);
493
490
  }
494
491
  }
@@ -26,13 +26,13 @@
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@deepseek-ai/cordis": "^4.0.1",
29
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
30
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
34
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
35
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
29
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
30
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
34
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
35
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
36
36
  "@pgmi-builds/superd-hub": "*"
37
37
  },
38
38
  "peerDependenciesMeta": {
@@ -5,7 +5,7 @@ import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage,
5
5
  import { createScope } from "@deepseek-ai/dsh-scope";
6
6
  import { ompPermissionCommand } from "./permission.js";
7
7
  import { getBridgeStore } from "./store/index.js";
8
- import { withSystemHead } from "../../superd-hub/dist/index.js";
8
+ import { createAgentModelFace, recordedModelSelection, withSystemHead } from "../../superd-hub/dist/index.js";
9
9
  /**
10
10
  * v2 live assistant-stream publication for the OMP bridge — the same frame
11
11
  * protocol the reference loop's AssistantStreamAttempt speaks (start marker,
@@ -255,6 +255,11 @@ export class OmpAgent {
255
255
  session;
256
256
  inbox;
257
257
  ctx;
258
+ /** Durable model selection (0.2.2 Agent face): recorded via `model/selection`,
259
+ * resolved with the same precedence as the native AgentModel — see
260
+ * {@link #recordedModelSelection}. Pushes to OMP at the next turn boundary
261
+ * through the existing {@link #syncModelSelection} `set_model` path. */
262
+ model;
258
263
  #rpc;
259
264
  #loopCtx;
260
265
  #scope;
@@ -311,6 +316,14 @@ export class OmpAgent {
311
316
  this.#onIdleExit = onIdleExit;
312
317
  this.#dispatch = agentEvents(loopCtx, this);
313
318
  this.inbox = new Inbox();
319
+ this.model = createAgentModelFace({
320
+ session: this.session,
321
+ options: this.options,
322
+ appendSelection: (selection) => {
323
+ this.session.append("model/selection", selection);
324
+ },
325
+ readDefault: () => this.ctx.get("agentDefaultModel")?.currentSelection(),
326
+ });
314
327
  this.#scope = createScope(loopCtx, this);
315
328
  this.ctx = this.#scope.ctx.extend({ agent: this });
316
329
  this.#lastTurn = session.snapshotEvents().findLast((event) => event.type === "turn/start")?.data.turn ?? 0;
@@ -649,8 +662,19 @@ export class OmpAgent {
649
662
  }
650
663
  return base;
651
664
  }
652
- /** Tier-2/3 base: the session's logged header config, else the global default, else agent options. */
665
+ // The durable-selection fold and the `Model` face moved to the shared hub
666
+ // module (superd-hub agent-model-face.ts): the constructor builds `this.model`
667
+ // with `createAgentModelFace`, and {@link #baseModelSelection} reads the fold
668
+ // through the thin wrapper below.
669
+ #recordedModelSelection() {
670
+ return recordedModelSelection(this.session.snapshotEvents());
671
+ }
672
+ /** Tier-0..3 base: pending selection, else the session's logged header config, else the global default, else agent options. */
653
673
  #baseModelSelection() {
674
+ const pending = this.#recordedModelSelection();
675
+ if (pending !== null && pending.provider !== "" && pending.model !== "") {
676
+ return { provider: pending.provider, model: pending.model };
677
+ }
654
678
  const persisted = this.session.requestHeader()?.config;
655
679
  if (persisted !== undefined && persisted.provider !== "" && persisted.model !== "") {
656
680
  return { provider: persisted.provider, model: persisted.model };
@@ -34,14 +34,14 @@
34
34
  "dependencies": {},
35
35
  "peerDependencies": {
36
36
  "@deepseek-ai/cordis": "^4.0.1",
37
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
38
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
39
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
40
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
41
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
42
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
43
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
44
- "@deepseek-ai/dsh-home-paths": ">=0.1.5-0 <0.3.0-0",
37
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
38
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
39
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
40
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
41
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
42
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
43
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
44
+ "@deepseek-ai/dsh-home-paths": ">=0.2.2-0 <1.0.0-0",
45
45
  "@pgmi-builds/superd-hub": "*"
46
46
  },
47
47
  "peerDependenciesMeta": {
@@ -20,7 +20,7 @@
20
20
  * are synthesized around the OMP stream.
21
21
  */
22
22
  import type { Context } from "@deepseek-ai/cordis";
23
- import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget } from "@deepseek-ai/dsh-agent";
23
+ import type { Agent, AgentOptions, AgentStatus, CancelOptions, InboxTarget, Model } from "@deepseek-ai/dsh-agent";
24
24
  import { Inbox } from "./inbox.js";
25
25
  import type { AgentCancelCause, Session, SessionId, UserMessage } from "@deepseek-ai/dsh-session";
26
26
  import type { ContentBlock, TokenUsage } from "@deepseek-ai/dsh-llm";
@@ -61,6 +61,9 @@ export declare class OmpAgent implements Agent {
61
61
  readonly session: WithSystemHead<Session>;
62
62
  readonly inbox: Inbox;
63
63
  readonly ctx: Context;
64
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
65
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
66
+ readonly model: Model;
64
67
  constructor(loopCtx: Context, id: SessionId, options: AgentOptions, session: Session, rpc: OmpAgentRpc, onIdleExit?: () => void);
65
68
  get status(): AgentStatus;
66
69
  send(message: UserMessage, target: InboxTarget, wakeup: boolean): void;
@@ -4,7 +4,7 @@ import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage,
4
4
  import { createScope } from "@deepseek-ai/dsh-scope";
5
5
  import { ompPermissionCommand } from "./permission.js";
6
6
  import { getBridgeStore } from "./store/index.js";
7
- import { withSystemHead } from "../../superd-hub/dist/index.js";
7
+ import { createAgentModelFace, withSystemHead } from "../../superd-hub/dist/index.js";
8
8
  /**
9
9
  * v2 live assistant-stream publication for the OMP bridge — the same frame
10
10
  * protocol the reference loop's AssistantStreamAttempt speaks (start marker,
@@ -254,6 +254,9 @@ export class OmpAgent {
254
254
  session;
255
255
  inbox;
256
256
  ctx;
257
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
258
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
259
+ model;
257
260
  #rpc;
258
261
  #loopCtx;
259
262
  #scope;
@@ -304,6 +307,17 @@ export class OmpAgent {
304
307
  this.id = id;
305
308
  this.options = options;
306
309
  this.session = withSystemHead(session, () => createSystemMessage(""));
310
+ this.model = createAgentModelFace({
311
+ session: this.session,
312
+ options: this.options,
313
+ appendSelection: (selection) => {
314
+ this.session.append("model/selection", selection);
315
+ },
316
+ readDefault: () => {
317
+ const service = this.ctx.get("agentDefaultModel");
318
+ return service?.currentSelection?.();
319
+ },
320
+ });
307
321
  this.#rpc = rpc;
308
322
  this.#onIdleExit = onIdleExit;
309
323
  this.#dispatch = agentEvents(loopCtx, this);
@@ -26,14 +26,14 @@
26
26
  "dependencies": {},
27
27
  "peerDependencies": {
28
28
  "@deepseek-ai/cordis": "^4.0.1",
29
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
30
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
34
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
35
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
36
- "@deepseek-ai/dsh-home-paths": ">=0.1.5-0 <0.3.0-0",
29
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
30
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
34
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
35
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
36
+ "@deepseek-ai/dsh-home-paths": ">=0.2.2-0 <1.0.0-0",
37
37
  "@pgmi-builds/superd-hub": "*"
38
38
  },
39
39
  "peerDependenciesMeta": {
@@ -3,7 +3,7 @@ import { randomUUID } from "node:crypto";
3
3
  import { agentEvents } from "@deepseek-ai/dsh-agent";
4
4
  import { Inbox } from "./inbox.js";
5
5
  import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage, createSystemMessage, createToolResultMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
6
- import { withSystemHead } from "../../superd-hub/dist/index.js";
6
+ import { createAgentModelFace, recordedModelSelection, withSystemHead } from "../../superd-hub/dist/index.js";
7
7
  import { createScope } from "@deepseek-ai/dsh-scope";
8
8
  import { PI_IDLE_EXIT_MS } from "./knobs.js";
9
9
  import { trace } from "./knobs.js";
@@ -176,6 +176,9 @@ export class PiAgent {
176
176
  session;
177
177
  inbox;
178
178
  ctx;
179
+ /** Durable model selection (0.2.2 Agent face): the shared hub face over
180
+ * `model/selection` events — see superd-hub agent-model-face.ts. */
181
+ model;
179
182
  #client;
180
183
  #loopCtx;
181
184
  #scope;
@@ -237,6 +240,17 @@ export class PiAgent {
237
240
  this.id = id;
238
241
  this.options = options;
239
242
  this.session = withSystemHead(session, () => createSystemMessage(""));
243
+ this.model = createAgentModelFace({
244
+ session: this.session,
245
+ options: this.options,
246
+ appendSelection: (selection) => {
247
+ this.session.append("model/selection", selection);
248
+ },
249
+ readDefault: () => {
250
+ const service = this.ctx.get("agentDefaultModel");
251
+ return service?.currentSelection?.();
252
+ },
253
+ });
240
254
  this.#client = client;
241
255
  this.#onIdleExit = onIdleExit;
242
256
  this.#runtimeInfo = runtimeInfo;
@@ -522,7 +536,11 @@ export class PiAgent {
522
536
  * LIVE (an upgrade over the codex SDK line's next-turn switch).
523
537
  */
524
538
  async #syncModelSelection() {
525
- const persisted = this.session.requestHeader()?.config;
539
+ // 0.2.2: the recorded-selection fold (pending ?? last-used) leads the
540
+ // waterfall, so a `model.select` switch lands on the NEXT turn — the
541
+ // native precedence (pending retires only on a matching request header).
542
+ const recorded = recordedModelSelection(this.session.snapshotEvents());
543
+ const persisted = recorded ?? this.session.requestHeader()?.config;
526
544
  const service = this.ctx.get("agentDefaultModel");
527
545
  const selection = service?.currentSelection();
528
546
  const target = persisted !== undefined && persisted.provider !== "" && persisted.model !== ""
@@ -29,7 +29,6 @@ import { bindForeignHub } from '@pgmi-builds/superd-hub/foreign-hub';
29
29
  import { realpathSync, statSync } from "node:fs";
30
30
  import { join } from "node:path";
31
31
  import { Service } from "@deepseek-ai/cordis";
32
- import { installModelSelection } from "@deepseek-ai/dsh-agent";
33
32
  import { SessionLogOffset, SessionPreparation, interruptedTurnClosers, } from "@deepseek-ai/dsh-session";
34
33
  import { PiSessionClient } from "./pi-client.js";
35
34
  import { PiAgent } from "./agent.js";
@@ -514,16 +513,11 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
514
513
  const route = runtimeInfo?.routeContext(agentOptions);
515
514
  if (route !== undefined) {
516
515
  const selection = { provider: route.provider, model: route.model };
517
- let picked = selection;
518
- installModelSelection(agent.ctx, {
519
- get current() { return picked; },
520
- set current(next) { picked = next; },
521
- assembled: undefined,
522
- });
523
- // `model/selection` is contributed to `SessionEventMap` by the upstream
524
- // session-controller package (declaration merging); this adapter does
525
- // not depend on it, so the append goes through a narrow local cast.
526
- session.append("model/selection", selection);
516
+ // 0.2.2: installModelSelection dropped its injected-ref parameter — the
517
+ // durable face (`agent.model`, the same mechanism session/selectModel
518
+ // uses) carries the pin: select() appends the typed, deduped
519
+ // `model/selection` event.
520
+ agent.model.select(selection);
527
521
  trace(`session route pinned: ${route.provider}/${route.model}`);
528
522
  }
529
523
  }
@@ -27,13 +27,13 @@
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@deepseek-ai/cordis": "^4.0.1",
30
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
31
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
32
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
33
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
34
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
35
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
36
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
30
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
31
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
32
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
33
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
34
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
35
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
36
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
37
37
  "@pgmi-builds/superd-hub": "*"
38
38
  },
39
39
  "peerDependenciesMeta": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "super-dsh",
3
- "version": "0.3.2-e",
3
+ "version": "0.3.3",
4
4
  "description": "Super D for the DeepSeek Harness (DSH) — one dsh plugin that mounts the superd hub (runtime selector, roster, per-runtime agents) and joins local foreign-agent runtimes (OMP, Codex, Claude, Pi, Hermes, Antigravity) into the DSH Web UI. Each runtime keeps its native home and session store; the hub only bridges.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -93,26 +93,26 @@
93
93
  },
94
94
  "peerDependencies": {
95
95
  "@deepseek-ai/cordis": "^4.0.1",
96
- "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",
97
- "@deepseek-ai/dsh-agent-presets": ">=0.1.5-0 <0.3.0-0",
98
- "@deepseek-ai/dsh-api-gateway": ">=0.1.5-0 <0.3.0-0",
99
- "@deepseek-ai/dsh-commands": ">=0.1.5-0 <0.3.0-0",
100
- "@deepseek-ai/dsh-home-paths": ">=0.1.5-0 <0.3.0-0",
101
- "@deepseek-ai/dsh-host-webserver": ">=0.1.5-0 <0.3.0-0",
102
- "@deepseek-ai/dsh-llm": ">=0.1.5-0 <0.3.0-0",
103
- "@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
104
- "@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
105
- "@deepseek-ai/dsh-session-persistence": ">=0.1.5-0 <0.3.0-0",
106
- "@deepseek-ai/dsh-tools": ">=0.1.5-0 <0.3.0-0",
107
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
108
- "@deepseek-ai/dsh-user-approval": ">=0.1.5-0 <0.3.0-0",
109
- "@deepseek-ai/dsh-user-questions": ">=0.1.5-0 <0.3.0-0",
110
- "@deepseek-ai/dsh-client-connection": ">=0.1.5-0 <0.3.0-0",
111
- "@deepseek-ai/dsh-app-boot": ">=0.1.5-0 <0.3.0-0",
112
- "@deepseek-ai/dsh-atomic-write": ">=0.1.5-0 <0.3.0-0",
113
- "@deepseek-ai/dsh-cmdline": ">=0.1.5-0 <0.3.0-0",
114
- "@deepseek-ai/dsh-plugin-manager": ">=0.1.5-0 <0.3.0-0",
115
- "@deepseek-ai/dsh-jobs": ">=0.1.5-0 <0.3.0-0"
96
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
97
+ "@deepseek-ai/dsh-agent-presets": ">=0.2.2-0 <1.0.0-0",
98
+ "@deepseek-ai/dsh-api-gateway": ">=0.2.2-0 <1.0.0-0",
99
+ "@deepseek-ai/dsh-commands": ">=0.2.2-0 <1.0.0-0",
100
+ "@deepseek-ai/dsh-home-paths": ">=0.2.2-0 <1.0.0-0",
101
+ "@deepseek-ai/dsh-host-webserver": ">=0.2.2-0 <1.0.0-0",
102
+ "@deepseek-ai/dsh-llm": ">=0.2.2-0 <1.0.0-0",
103
+ "@deepseek-ai/dsh-scope": ">=0.2.2-0 <1.0.0-0",
104
+ "@deepseek-ai/dsh-session": ">=0.2.2-0 <1.0.0-0",
105
+ "@deepseek-ai/dsh-session-persistence": ">=0.2.2-0 <1.0.0-0",
106
+ "@deepseek-ai/dsh-tools": ">=0.2.2-0 <1.0.0-0",
107
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
108
+ "@deepseek-ai/dsh-user-approval": ">=0.2.2-0 <1.0.0-0",
109
+ "@deepseek-ai/dsh-user-questions": ">=0.2.2-0 <1.0.0-0",
110
+ "@deepseek-ai/dsh-client-connection": ">=0.2.2-0 <1.0.0-0",
111
+ "@deepseek-ai/dsh-app-boot": ">=0.2.2-0 <1.0.0-0",
112
+ "@deepseek-ai/dsh-atomic-write": ">=0.2.2-0 <1.0.0-0",
113
+ "@deepseek-ai/dsh-cmdline": ">=0.2.2-0 <1.0.0-0",
114
+ "@deepseek-ai/dsh-plugin-manager": ">=0.2.2-0 <1.0.0-0",
115
+ "@deepseek-ai/dsh-jobs": ">=0.2.2-0 <1.0.0-0"
116
116
  },
117
117
  "peerDependenciesMeta": {
118
118
  "@deepseek-ai/cordis": {
@@ -0,0 +1,93 @@
1
+ /**
2
+ * agent-model-face.ts — the 0.2.2+ `Agent.model` face for foreign-runtime
3
+ * bridges.
4
+ *
5
+ * dsh 0.2.2 made `model` a required `Agent` member: the Web UI's model switcher
6
+ * reaches every agent — native and foreign alike — through
7
+ * `session/selectModel` → `agent.model.select()` (session-controller
8
+ * commands.ts). The native `AgentModel` (dsh-agent model-selection.ts) records
9
+ * a `model/selection` session event durably and resolves `current()` as
10
+ * pending ?? lastUsed ?? live options.
11
+ *
12
+ * This module hands the foreign bridges (claude/codex/hermes/pi/agy/omp) the
13
+ * same semantics without mounting the projection registry:
14
+ *
15
+ * - {@link recordedModelSelection} — the native `modelSelection` projection
16
+ * fold, replayed as one pass over the session's events. A recorded
17
+ * `model/selection` is the pending choice and survives non-matching
18
+ * `request/header`s; a matching header retires it; `lastUsed` is the last
19
+ * header's config. (The adapters that DO mount `sessionProjections` —
20
+ * claude, codex — may keep reading the projection directly; both produce
21
+ * the same answer.)
22
+ * - {@link createAgentModelFace} — the face itself: `current()` resolves
23
+ * pending → logged header → deployment default → agent options (each level
24
+ * must name both halves), `select()` appends the same durable
25
+ * `model/selection` event as the native face, deduped against the recorded
26
+ * choice.
27
+ *
28
+ * Structural session face on the system-head pattern: the real (generic-typed)
29
+ * `Session` satisfies it without this package taking a dsh-agent dependency.
30
+ *
31
+ * @module superd-hub/agent-model-face
32
+ */
33
+ /** A model selection: both route halves, with an optional explicit effort. */
34
+ export interface ModelSelectionLike {
35
+ provider: string;
36
+ model: string;
37
+ reasoningEffort?: unknown;
38
+ }
39
+ /** The `Agent.model` shape (dsh-agent runtime-types `Model`), over the bridge's own selection type. */
40
+ export interface BridgeModelFace<Sel extends ModelSelectionLike> {
41
+ current(): Readonly<Sel> | undefined;
42
+ select(selection: Readonly<Sel>): void;
43
+ }
44
+ /**
45
+ * The session slice the fold reads. Deliberately loose so the real
46
+ * (generic-typed) `Session` satisfies it; the events the fold inspects arrive
47
+ * as `unknown` data and are narrowed inside this module only.
48
+ */
49
+ export interface ModelSessionFace {
50
+ snapshotEvents(): ReadonlyArray<{
51
+ type: string;
52
+ data: unknown;
53
+ }>;
54
+ requestHeader(): {
55
+ readonly config: {
56
+ readonly provider: string;
57
+ readonly model: string;
58
+ };
59
+ } | undefined;
60
+ }
61
+ /**
62
+ * The native `modelSelection` projection fold over one session's events.
63
+ * @param events - the session's full event snapshot (order is the log order).
64
+ * @returns the recorded choice — a pending selection, else the last request
65
+ * header's config — or `null` when the log names neither.
66
+ */
67
+ export declare function recordedModelSelection(events: ReadonlyArray<{
68
+ type: string;
69
+ data: unknown;
70
+ }>): ModelSelectionLike | null;
71
+ /** Readers the face resolves through; each adapter wires its own sources. */
72
+ export interface AgentModelFaceParts<Sel extends ModelSelectionLike> {
73
+ /** The session whose events and request header back the fold. */
74
+ session: ModelSessionFace;
75
+ /** The agent's live route options (the options fallback names both halves or neither). */
76
+ options: {
77
+ readonly provider?: string;
78
+ readonly model?: string;
79
+ };
80
+ /** Appends the durable `model/selection` event (the adapter's own typed append). */
81
+ appendSelection: (selection: Sel) => void;
82
+ /** Reads the deployment default (`ctx.agentDefaultModel`); absent when the service is not mounted. */
83
+ readDefault?: () => {
84
+ provider: string;
85
+ model: string;
86
+ } | undefined;
87
+ }
88
+ /**
89
+ * Build the `Agent.model` face for one foreign-runtime agent.
90
+ * @param parts - the session, options, typed append, and optional default reader.
91
+ * @returns the face; assign it to the agent's `model` member.
92
+ */
93
+ export declare function createAgentModelFace<Sel extends ModelSelectionLike>(parts: AgentModelFaceParts<Sel>): BridgeModelFace<Sel>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * agent-model-face.ts — the 0.2.2+ `Agent.model` face for foreign-runtime
3
+ * bridges.
4
+ *
5
+ * dsh 0.2.2 made `model` a required `Agent` member: the Web UI's model switcher
6
+ * reaches every agent — native and foreign alike — through
7
+ * `session/selectModel` → `agent.model.select()` (session-controller
8
+ * commands.ts). The native `AgentModel` (dsh-agent model-selection.ts) records
9
+ * a `model/selection` session event durably and resolves `current()` as
10
+ * pending ?? lastUsed ?? live options.
11
+ *
12
+ * This module hands the foreign bridges (claude/codex/hermes/pi/agy/omp) the
13
+ * same semantics without mounting the projection registry:
14
+ *
15
+ * - {@link recordedModelSelection} — the native `modelSelection` projection
16
+ * fold, replayed as one pass over the session's events. A recorded
17
+ * `model/selection` is the pending choice and survives non-matching
18
+ * `request/header`s; a matching header retires it; `lastUsed` is the last
19
+ * header's config. (The adapters that DO mount `sessionProjections` —
20
+ * claude, codex — may keep reading the projection directly; both produce
21
+ * the same answer.)
22
+ * - {@link createAgentModelFace} — the face itself: `current()` resolves
23
+ * pending → logged header → deployment default → agent options (each level
24
+ * must name both halves), `select()` appends the same durable
25
+ * `model/selection` event as the native face, deduped against the recorded
26
+ * choice.
27
+ *
28
+ * Structural session face on the system-head pattern: the real (generic-typed)
29
+ * `Session` satisfies it without this package taking a dsh-agent dependency.
30
+ *
31
+ * @module superd-hub/agent-model-face
32
+ */
33
+ /** One recorded selection read structurally; incomplete routes answer `null`. */
34
+ function selectionOf(value) {
35
+ if (typeof value !== 'object' || value === null)
36
+ return null;
37
+ const { provider, model } = value;
38
+ if (typeof provider !== 'string' || provider === '' || typeof model !== 'string' || model === '')
39
+ return null;
40
+ const reasoningEffort = value.reasoningEffort;
41
+ return {
42
+ provider,
43
+ model,
44
+ ...(typeof reasoningEffort === 'string' && reasoningEffort !== ''
45
+ ? { reasoningEffort }
46
+ : {}),
47
+ };
48
+ }
49
+ /** Route equality without the effort, and full equality with it (the fold's own rules). */
50
+ function sameRoute(left, right) {
51
+ return left.provider === right.provider && left.model === right.model;
52
+ }
53
+ function sameSelection(left, right) {
54
+ return sameRoute(left, right) && left.reasoningEffort === right.reasoningEffort;
55
+ }
56
+ /**
57
+ * The native `modelSelection` projection fold over one session's events.
58
+ * @param events - the session's full event snapshot (order is the log order).
59
+ * @returns the recorded choice — a pending selection, else the last request
60
+ * header's config — or `null` when the log names neither.
61
+ */
62
+ export function recordedModelSelection(events) {
63
+ let pending = null;
64
+ let lastUsed = null;
65
+ for (const event of events) {
66
+ if (event.type === 'model/selection') {
67
+ const selection = selectionOf(event.data);
68
+ if (selection !== null && !(pending !== null && sameSelection(pending, selection)))
69
+ pending = selection;
70
+ }
71
+ else if (event.type === 'request/header') {
72
+ const header = event.data;
73
+ const config = header === null || typeof header !== 'object' ? undefined : header.header?.config;
74
+ const selection = selectionOf(config);
75
+ if (selection !== null) {
76
+ lastUsed = selection;
77
+ if (pending !== null && sameRoute(pending, selection))
78
+ pending = null;
79
+ }
80
+ }
81
+ }
82
+ return pending ?? lastUsed;
83
+ }
84
+ /**
85
+ * Build the `Agent.model` face for one foreign-runtime agent.
86
+ * @param parts - the session, options, typed append, and optional default reader.
87
+ * @returns the face; assign it to the agent's `model` member.
88
+ */
89
+ export function createAgentModelFace(parts) {
90
+ const recorded = () => recordedModelSelection(parts.session.snapshotEvents());
91
+ const current = () => {
92
+ const fold = recorded();
93
+ if (fold !== null)
94
+ return { ...fold };
95
+ const preferred = parts.readDefault?.();
96
+ if (preferred !== undefined && preferred.provider !== '' && preferred.model !== '') {
97
+ return { provider: preferred.provider, model: preferred.model };
98
+ }
99
+ const { provider, model } = parts.options;
100
+ return provider !== undefined && provider !== '' && model !== undefined && model !== ''
101
+ ? { provider, model }
102
+ : undefined;
103
+ };
104
+ return {
105
+ current,
106
+ select(selection) {
107
+ const fold = recorded();
108
+ if (fold !== null && sameSelection(fold, selection))
109
+ return;
110
+ parts.appendSelection({ ...selection });
111
+ },
112
+ };
113
+ }
@@ -36,6 +36,7 @@ export { createOwnershipIndex, type OwnershipIndex, type OwnershipRow } from './
36
36
  export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf, type HostMount } from './world-host.js';
37
37
  export { worldMountPatches, superdTrustedHosts, WORLD_WAVE1_DISABLED_ROWS, WORLD_WAVE2_DISABLED_ROWS, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, type WorldPatchLayer, } from './world-mount.js';
38
38
  export { withSystemHead, type HeadAwareSessionFace, type HeadPlaceholder, type WithSystemHead } from './system-head.js';
39
+ export { createAgentModelFace, recordedModelSelection, type AgentModelFaceParts, type BridgeModelFace, type ModelSelectionLike, type ModelSessionFace, } from './agent-model-face.js';
39
40
  export { default as worldApply, name as worldPluginName, type Config as WorldEntryConfig } from './world-entry.js';
40
41
  export { default as joinApply, name as worldJoinPluginName, type Config as WorldJoinConfig } from './world-join.js';
41
42
  export { createA2aService, A2A_MESSAGE_TEMPLATE, A2A_UNTITLED, A2A_BUFFER_MAX, A2A_NO_SESSION_CLOSE_REASON, A2A_NO_TURN_ACTIVE_NOTE, renderA2aEnvelope, loadConversations, saveConversations, shortIdFor, isDegenerateShortId, migrateConversations, type A2aService, subscribeSessionCompletion, sessionRpc, turnLivenessOf, addressErrorBody, type A2aSessionSource, type A2aGateway, type A2aAddress, type A2aInitiator, type A2aDelivery, type A2aSendRequest, type A2aReceipt, type A2aReceiptOutcome, type A2aReceiptError, type A2aConversationRef, type A2aCompletion, type A2aCompletionCb, type A2aErrorCb, type A2aSubscribeOptions, type A2aStopScope, type A2aStopResult, type A2aBufferedMessage, type A2aBufferInput, type ConversationRow, type ConversationsFile, } from './a2a-service.js';
@@ -36,6 +36,7 @@ export { createOwnershipIndex } from './ownership.js';
36
36
  export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf } from './world-host.js';
37
37
  export { worldMountPatches, superdTrustedHosts, WORLD_WAVE1_DISABLED_ROWS, WORLD_WAVE2_DISABLED_ROWS, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, } from './world-mount.js';
38
38
  export { withSystemHead } from './system-head.js';
39
+ export { createAgentModelFace, recordedModelSelection, } from './agent-model-face.js';
39
40
  export { default as worldApply, name as worldPluginName } from './world-entry.js';
40
41
  export { default as joinApply, name as worldJoinPluginName } from './world-join.js';
41
42
  export { createA2aService, A2A_MESSAGE_TEMPLATE, A2A_UNTITLED, A2A_BUFFER_MAX, A2A_NO_SESSION_CLOSE_REASON, A2A_NO_TURN_ACTIVE_NOTE, renderA2aEnvelope, loadConversations, saveConversations, shortIdFor, isDegenerateShortId, migrateConversations,
@@ -1,3 +1,4 @@
1
+ import { modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent';
1
2
  import { noteWorldServer, publishWorldFetch, takeHostMount } from './world-host.js';
2
3
  import { WorldWebServer } from './world-web-server.js';
3
4
  import { superdTrustedHosts } from './world-mount.js';
@@ -43,6 +44,35 @@ export function apply(ctx, config) {
43
44
  dropWorldFetch = publishWorldFetch(key, connection.createSharedFetchHandler('/api'), mount);
44
45
  }
45
46
  });
47
+ // 0.2.2: register the `modelSelection` projection in THIS world's registry.
48
+ // Upstream's only registrant is the agent-loop row (agent-loop/src:334),
49
+ // which wave-1 minimization keeps out of world trees — without it the
50
+ // native model seat's projection frame never resolves and the trigger
51
+ // label stays "loading models…" forever (the catalog and selectModel RPC
52
+ // take different paths, which is why they keep working). The definition is
53
+ // a pure event fold (`model/selection` + `request/header`), no loop
54
+ // machinery; register() is ref-counted and duplicate-tolerant, so a future
55
+ // tree that also mounts agent-loop shares the registration cleanly.
56
+ ctx.inject(['sessionProjections'], (projCtx) => {
57
+ const projections = projCtx.get('sessionProjections');
58
+ if (projections === undefined)
59
+ return;
60
+ let disposed = false;
61
+ let handle;
62
+ void projections.register(modelSelectionProjectionDefinition).then((registered) => {
63
+ if (disposed) {
64
+ void registered.dispose();
65
+ return;
66
+ }
67
+ handle = registered;
68
+ }, (error) => {
69
+ ctx.logger?.warn?.(`superd-world-mount: modelSelection projection registration failed: ${String(error)}`);
70
+ });
71
+ return () => {
72
+ disposed = true;
73
+ void handle?.dispose();
74
+ };
75
+ });
46
76
  ctx.effect(() => () => {
47
77
  dropWorldFetch?.();
48
78
  }, 'superd-world-mount: virtual webServer');
@@ -45,16 +45,17 @@
45
45
  "peerDependencies": {
46
46
  "@earendil-works/pi-coding-agent": "0.84.2",
47
47
  "@deepseek-ai/cordis": "*",
48
- "@deepseek-ai/dsh-api-gateway": ">=0.1.5-0 <0.3.0-0",
49
- "@deepseek-ai/dsh-home-paths": ">=0.1.5-0 <0.3.0-0",
50
- "@deepseek-ai/dsh-host-webserver": ">=0.1.5-0 <0.3.0-0",
51
- "@deepseek-ai/dsh-tools": ">=0.1.5-0 <0.3.0-0",
52
- "@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
53
- "@deepseek-ai/dsh-client-connection": ">=0.1.5-0 <0.3.0-0",
54
- "@deepseek-ai/dsh-app-boot": ">=0.1.5-0 <0.3.0-0",
55
- "@deepseek-ai/dsh-atomic-write": ">=0.1.5-0 <0.3.0-0",
56
- "@deepseek-ai/dsh-cmdline": ">=0.1.5-0 <0.3.0-0",
57
- "@deepseek-ai/dsh-plugin-manager": ">=0.1.5-0 <0.3.0-0"
48
+ "@deepseek-ai/dsh-agent": ">=0.2.2-0 <1.0.0-0",
49
+ "@deepseek-ai/dsh-api-gateway": ">=0.2.2-0 <1.0.0-0",
50
+ "@deepseek-ai/dsh-home-paths": ">=0.2.2-0 <1.0.0-0",
51
+ "@deepseek-ai/dsh-host-webserver": ">=0.2.2-0 <1.0.0-0",
52
+ "@deepseek-ai/dsh-tools": ">=0.2.2-0 <1.0.0-0",
53
+ "@deepseek-ai/dsh-typert-protocol": ">=0.2.2-0 <1.0.0-0",
54
+ "@deepseek-ai/dsh-client-connection": ">=0.2.2-0 <1.0.0-0",
55
+ "@deepseek-ai/dsh-app-boot": ">=0.2.2-0 <1.0.0-0",
56
+ "@deepseek-ai/dsh-atomic-write": ">=0.2.2-0 <1.0.0-0",
57
+ "@deepseek-ai/dsh-cmdline": ">=0.2.2-0 <1.0.0-0",
58
+ "@deepseek-ai/dsh-plugin-manager": ">=0.2.2-0 <1.0.0-0"
58
59
  },
59
60
  "peerDependenciesMeta": {
60
61
  "@earendil-works/pi-coding-agent": {
@@ -66,6 +67,9 @@
66
67
  "@deepseek-ai/dsh-api-gateway": {
67
68
  "optional": true
68
69
  },
70
+ "@deepseek-ai/dsh-agent": {
71
+ "optional": true
72
+ },
69
73
  "@deepseek-ai/dsh-home-paths": {
70
74
  "optional": true
71
75
  },