super-dsh 0.3.2 → 0.3.3-b

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.
Files changed (48) hide show
  1. package/agent-agy/dist/agent.d.ts +4 -1
  2. package/agent-agy/dist/agent.js +15 -1
  3. package/agent-agy/dist/index.js +5 -8
  4. package/agent-agy/package.json +6 -6
  5. package/agent-agy-sdk/dist/agent.d.ts +4 -1
  6. package/agent-agy-sdk/dist/agent.js +15 -1
  7. package/agent-agy-sdk/dist/index.js +5 -8
  8. package/agent-agy-sdk/package.json +5 -5
  9. package/agent-claude/dist/agent.js +15 -1
  10. package/agent-claude/package.json +8 -8
  11. package/agent-codex/dist/agent.js +15 -1
  12. package/agent-codex/package.json +7 -7
  13. package/agent-codex-sdk/dist/agent.js +15 -1
  14. package/agent-codex-sdk/dist/index.js +5 -11
  15. package/agent-codex-sdk/package.json +7 -7
  16. package/agent-hermes/dist/agent.js +15 -1
  17. package/agent-hermes/dist/index.js +5 -8
  18. package/agent-hermes/package.json +7 -7
  19. package/agent-omp/dist/agent.js +26 -2
  20. package/agent-omp/package.json +8 -8
  21. package/agent-omp-sdk/dist/agent.d.ts +4 -1
  22. package/agent-omp-sdk/dist/agent.js +15 -1
  23. package/agent-omp-sdk/package.json +8 -8
  24. package/agent-pi/dist/agent.js +20 -2
  25. package/agent-pi/dist/index.js +5 -11
  26. package/agent-pi/package.json +7 -7
  27. package/package.json +21 -21
  28. package/remote/hub-pty-pump.mjs +97 -0
  29. package/remote/plugin.d.ts +476 -200
  30. package/remote/plugin.js +1445 -664
  31. package/remote/provenance.json +22 -17
  32. package/superd-hub/dist/agent-model-face.d.ts +93 -0
  33. package/superd-hub/dist/agent-model-face.js +113 -0
  34. package/superd-hub/dist/exec.d.ts +3 -2
  35. package/superd-hub/dist/exec.js +6 -5
  36. package/superd-hub/dist/hub-tool.d.ts +1 -1
  37. package/superd-hub/dist/hub-tool.js +6 -6
  38. package/superd-hub/dist/index.d.ts +1 -0
  39. package/superd-hub/dist/index.js +1 -0
  40. package/superd-hub/dist/remote-provider.js +70 -36
  41. package/superd-hub/dist/world-entry.js +30 -0
  42. package/superd-hub/package.json +14 -10
  43. package/superd-hub/dist/exec-pty-channel.d.ts +0 -58
  44. package/superd-hub/dist/exec-pty-channel.js +0 -162
  45. package/superd-hub/dist/exec-pty-framing.d.ts +0 -34
  46. package/superd-hub/dist/exec-pty-framing.js +0 -89
  47. package/superd-hub/dist/exec-pty-session.d.ts +0 -78
  48. package/superd-hub/dist/exec-pty-session.js +0 -228
@@ -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",
3
+ "version": "0.3.3-b",
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,97 @@
1
+ #!/usr/bin/env node
2
+ // Hub PTY pump — the DUMB child of the canonical PTY session over a Hub
3
+ // machine (0.3.2-c). The session (better-dsh/src/remote/pty-session.ts)
4
+ // spawns `script -qfec 'stty -echo; … exec node <this file>' /dev/null`, so
5
+ // this process's stdin/stdout ride a host-side PTY; we forward those bytes
6
+ // over one WebSocket to the peer hub's dumb PTY executor
7
+ // (/api/superd/exec/pty: one WS = one `script -qfec bash /dev/null`, text
8
+ // frames = stream bytes, {"op":…} JSON frames = transport control, WS close
9
+ // = peer group-kill). All session intelligence (nonce framing, init
10
+ // handshake, env E-frames, two-stage interrupt, idle TTL) is in-band bytes
11
+ // produced by the SESSION — this pump never parses the stream, only the
12
+ // peer's control frames.
13
+ //
14
+ // Connection facts arrive via environment (never argv — credentials must not
15
+ // show up in `ps`):
16
+ // SUPERD_HUB_PTY_URL wss://…/api/superd/exec/pty (peer executor)
17
+ // SUPERD_HUB_PTY_COOKIE pairing cookie for the upgrade request
18
+ // SUPERD_HUB_PTY_WS ABSOLUTE path to a usable `ws` module (resolved by
19
+ // the caller before spawning; no resolution gamble
20
+ // here — a missing module is a creation-time error)
21
+ //
22
+ // Verbatim-asset discipline (helper-bin precedent): this file is copied
23
+ // byte-for-byte into lib/remote/ and the Super closure; no build step
24
+ // transforms it. Crash-proof by construction (mesh-gateway lesson): the
25
+ // 'error' listener is registered before anything can reject, and post-close
26
+ // sends are swallowed — the session's death path owns the teardown.
27
+ const PUMP_ENV_PREFIX = 'SUPERD_HUB_PTY_'
28
+ const [url, cookie, wsPath] = ['URL', 'COOKIE', 'WS'].map((k) => process.env[`SUPERD_HUB_PTY_${k}`])
29
+
30
+ // cookie may be legitimately empty (an unauthenticated test lane sends no header);
31
+ // the endpoint URL and the ws module path are the hard requirements.
32
+ if (url === undefined || url === '' || wsPath === undefined || wsPath === '') {
33
+ process.stderr.write(`hub-pty-pump: missing environment (${PUMP_ENV_PREFIX}URL/WS)\n`)
34
+ process.exit(1)
35
+ }
36
+
37
+ let WebSocket
38
+ try {
39
+ const require = (await import('node:module')).createRequire(import.meta.url)
40
+ const mod = require(wsPath)
41
+ WebSocket = typeof mod === 'function' ? mod : mod.default
42
+ } catch (err) {
43
+ process.stderr.write(`hub-pty-pump: cannot load ws module at ${wsPath} — ${err instanceof Error ? err.message : String(err)}\n`)
44
+ process.exit(1)
45
+ }
46
+ if (typeof WebSocket !== 'function') {
47
+ process.stderr.write('hub-pty-pump: the ws module did not yield a WebSocket constructor\n')
48
+ process.exit(1)
49
+ }
50
+
51
+ const socket = new WebSocket(url, { headers: cookie ? { cookie } : {} })
52
+ let opened = false
53
+ let forward = true
54
+
55
+ socket.on('error', (err) => {
56
+ // Always-on listener: an ECONNREFUSED or a rejected 401 upgrade must be a
57
+ // reported death, never an uncaught exception.
58
+ process.stderr.write(`hub-pty-pump: ${opened ? 'socket error' : 'connect failed'} — ${err instanceof Error ? err.message : String(err)}\n`)
59
+ })
60
+ socket.on('open', () => { opened = true; pumpStdin() })
61
+ socket.on('message', (data) => {
62
+ if (!forward) return
63
+ const text = typeof data === 'string' ? data : Array.isArray(data) ? Buffer.concat(data).toString('utf8') : Buffer.from(data).toString('utf8')
64
+ // Transport control frames (peer: {"op":"exit"} / {"op":"spawn-error"}) are
65
+ // addressed to THIS pump, not to the shell stream — the exit frame is
66
+ // followed by a socket close, which settles us below.
67
+ if (text.startsWith('{"op":')) {
68
+ try {
69
+ const parsed = JSON.parse(text)
70
+ if (typeof parsed.op === 'string') {
71
+ if (parsed.op === 'spawn-error' && typeof parsed.error === 'string') process.stderr.write(`hub-pty-pump: peer could not spawn the terminal — ${parsed.error}\n`)
72
+ return
73
+ }
74
+ } catch { /* not JSON after all: fall through and forward */ }
75
+ }
76
+ process.stdout.write(text)
77
+ })
78
+ socket.on('close', () => {
79
+ forward = false
80
+ // Before open this is a failed connection (diag already on stderr); after
81
+ // open it is the peer session ending — either way the child exiting is the
82
+ // session's dead signal.
83
+ process.exit(opened ? 0 : 1)
84
+ })
85
+
86
+ function pumpStdin() {
87
+ process.stdin.setEncoding('utf8')
88
+ process.stdin.on('data', (chunk) => {
89
+ if (!opened) return
90
+ try { socket.send(chunk) } catch { /* closing: the close handler exits us */ }
91
+ })
92
+ // The session's kill path tears down the process group; a plain stdin end
93
+ // (no kill) still means nobody will type more — close politely so the peer
94
+ // reaps its side.
95
+ process.stdin.on('end', () => { try { socket.close() } catch { /* already closing */ } })
96
+ process.stdin.on('error', () => { /* EPIPE on teardown: the close path owns the exit */ })
97
+ }