super-dsh 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -6
- package/agent-agy/dist/agent.d.ts +4 -0
- package/agent-agy/dist/agent.js +41 -7
- package/agent-agy/dist/agy-cli-client.d.ts +12 -0
- package/agent-agy/dist/agy-cli-client.js +81 -13
- package/agent-agy/dist/index.js +13 -7
- package/agent-agy/package.json +5 -1
- package/agent-claude/dist/agent.js +67 -4
- package/agent-claude/dist/claude-client.js +46 -1
- package/agent-claude/dist/index.js +3 -0
- package/agent-claude/dist/sdk-runtime.js +2 -2
- package/agent-codex/dist/agent.js +33 -8
- package/agent-codex/dist/app-server-client.js +26 -3
- package/agent-codex/dist/app-server-protocol.js +6 -2
- package/agent-codex/dist/app-server-wire.js +23 -6
- package/agent-codex/dist/codex-native-config.js +6 -1
- package/agent-codex/dist/codex-runtime-fetch.js +293 -0
- package/agent-codex/dist/codex-runtime.js +12 -12
- package/agent-codex/dist/index.js +10 -4
- package/agent-codex/package.json +8 -3
- package/agent-codex-sdk/dist/sdk-runtime.js +1 -1
- package/agent-hermes/dist/hermes-client.js +44 -5
- package/agent-hermes/dist/index.js +10 -5
- package/agent-omp/dist/agent.js +36 -5
- package/agent-omp/dist/index.js +29 -6
- package/agent-omp/dist/lazy-rpc.js +11 -2
- package/agent-omp/dist/rpc-client.js +6 -1
- package/agent-pi/dist/agent.js +36 -9
- package/agent-pi/dist/index.js +6 -2
- package/agent-pi/dist/pi-client.js +13 -3
- package/cordis.patch.yml +10 -9
- package/package.json +6 -4
- package/superd-hub/dist/a2a-row.js +20 -3
- package/superd-hub/dist/connection-trust.d.ts +8 -0
- package/superd-hub/dist/connection-trust.js +16 -0
- package/superd-hub/dist/foreign-agy.d.ts +3 -0
- package/superd-hub/dist/foreign-agy.js +110 -0
- package/superd-hub/dist/foreign-claude.d.ts +4 -0
- package/superd-hub/dist/foreign-claude.js +160 -0
- package/superd-hub/dist/foreign-extension.d.ts +10 -0
- package/superd-hub/dist/foreign-extension.js +27 -0
- package/superd-hub/dist/foreign-hub.d.ts +27 -0
- package/superd-hub/dist/foreign-hub.js +243 -0
- package/superd-hub/dist/foreign-native.d.ts +4 -0
- package/superd-hub/dist/foreign-native.js +115 -0
- package/superd-hub/dist/web-trust.d.ts +14 -42
- package/superd-hub/dist/web-trust.js +18 -43
- package/superd-hub/package.json +14 -2
- package/agent-codex/README.md +0 -191
- package/agent-codex-sdk/README.md +0 -79
- package/agent-hermes/README.md +0 -145
- package/agent-omp/README.md +0 -94
- package/agent-omp-sdk/README.md +0 -103
- package/agent-pi/README.md +0 -67
package/agent-omp/dist/agent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolCallRecovery } from "@deepseek-ai/dsh-session";
|
|
1
2
|
import { agentEvents } from "@deepseek-ai/dsh-agent";
|
|
2
3
|
import { Inbox } from "./inbox.js";
|
|
3
4
|
import { LlmAttemptId, ToolCallId, QUOTA_EXCEEDED_CODE, createAssistantMessage, createSystemMessage, createToolResultMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
@@ -265,6 +266,8 @@ export class OmpAgent {
|
|
|
265
266
|
#step = 0;
|
|
266
267
|
#lastTurn = 0;
|
|
267
268
|
#turnOpen = false;
|
|
269
|
+
#turnCommitted = false;
|
|
270
|
+
#stepOpen = false;
|
|
268
271
|
#cancelCause = null;
|
|
269
272
|
/** Terminal model failure of the open turn (`message_end` stopReason "error"); consumed by `agent_end`. */
|
|
270
273
|
#pendingFailure = null;
|
|
@@ -486,7 +489,7 @@ export class OmpAgent {
|
|
|
486
489
|
catch (error) {
|
|
487
490
|
trace(`#startTurn cold start failed: ${String(error)}`);
|
|
488
491
|
if (this.#turnOpen) {
|
|
489
|
-
this
|
|
492
|
+
this.#commitTurnStart();
|
|
490
493
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
491
494
|
this.#closeTurn({ kind: "error", error: { message: String(error), code: "UNKNOWN" } });
|
|
492
495
|
}
|
|
@@ -495,7 +498,7 @@ export class OmpAgent {
|
|
|
495
498
|
}
|
|
496
499
|
if (this.#disposed)
|
|
497
500
|
return;
|
|
498
|
-
this
|
|
501
|
+
this.#commitTurnStart();
|
|
499
502
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
500
503
|
void this.#rpc.prompt(userMessageText(message)).catch((error) => this.#fail(error));
|
|
501
504
|
}
|
|
@@ -709,12 +712,35 @@ export class OmpAgent {
|
|
|
709
712
|
return;
|
|
710
713
|
this.#reserveTurn();
|
|
711
714
|
this.#localUserPending = localUser;
|
|
712
|
-
this
|
|
715
|
+
this.#commitTurnStart();
|
|
713
716
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
714
717
|
}
|
|
718
|
+
#commitTurnStart() {
|
|
719
|
+
if (this.#turnCommitted)
|
|
720
|
+
return;
|
|
721
|
+
this.session.append("turn/start", { turn: this.#dashTurn });
|
|
722
|
+
this.#turnCommitted = true;
|
|
723
|
+
}
|
|
715
724
|
#closeTurn(reason) {
|
|
716
725
|
if (!this.#turnOpen)
|
|
717
726
|
return;
|
|
727
|
+
this.#commitTurnStart();
|
|
728
|
+
if (this.#stepOpen) {
|
|
729
|
+
const events = this.session.snapshotEvents();
|
|
730
|
+
const start = events.findLastIndex(event => event.type === "step/start");
|
|
731
|
+
const recovery = new ToolCallRecovery();
|
|
732
|
+
for (const event of events.slice(start))
|
|
733
|
+
recovery.observe(event);
|
|
734
|
+
for (const result of recovery.results()) {
|
|
735
|
+
this.session.append("tool/result", result.data, {
|
|
736
|
+
surfaceOp: "append",
|
|
737
|
+
...(result.sourceEventSeqs === undefined ? {} : { sourceEventSeqs: result.sourceEventSeqs }),
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
this.session.append("step/end", { turn: this.#dashTurn, step: this.#step });
|
|
741
|
+
this.#stepOpen = false;
|
|
742
|
+
}
|
|
743
|
+
this.#turnCommitted = false;
|
|
718
744
|
this.#turnOpen = false;
|
|
719
745
|
this.#localUserPending = false;
|
|
720
746
|
this.session.append("turn/end", { turn: this.#dashTurn, reason });
|
|
@@ -751,9 +777,12 @@ export class OmpAgent {
|
|
|
751
777
|
return;
|
|
752
778
|
this.#mirroredTitle = title;
|
|
753
779
|
try {
|
|
780
|
+
const input = this.session.snapshotEvents().find(event => event.type === "user/message" && event.data.source.kind === "user");
|
|
781
|
+
if (input === undefined)
|
|
782
|
+
return;
|
|
754
783
|
this.session.append("session/title", {
|
|
755
784
|
title,
|
|
756
|
-
messageSeqs: [],
|
|
785
|
+
messageSeqs: [input.seq],
|
|
757
786
|
source: { kind: "provider", provider: "omp" },
|
|
758
787
|
});
|
|
759
788
|
}
|
|
@@ -773,6 +802,7 @@ export class OmpAgent {
|
|
|
773
802
|
return;
|
|
774
803
|
this.#step += 1;
|
|
775
804
|
this.session.append("step/start", { turn: this.#dashTurn, step: this.#step });
|
|
805
|
+
this.#stepOpen = true;
|
|
776
806
|
}
|
|
777
807
|
/**
|
|
778
808
|
* Bridge an OMP `message_start(role=user)` that was NOT appended locally.
|
|
@@ -986,8 +1016,9 @@ export class OmpAgent {
|
|
|
986
1016
|
break;
|
|
987
1017
|
case "turn_end":
|
|
988
1018
|
trace("event turn_end");
|
|
989
|
-
if (this.#turnOpen) {
|
|
1019
|
+
if (this.#turnOpen && this.#stepOpen) {
|
|
990
1020
|
this.session.append("step/end", { turn: this.#dashTurn, step: this.#step });
|
|
1021
|
+
this.#stepOpen = false;
|
|
991
1022
|
}
|
|
992
1023
|
break;
|
|
993
1024
|
case "agent_end": {
|
package/agent-omp/dist/index.js
CHANGED
|
@@ -19,11 +19,12 @@
|
|
|
19
19
|
* exact transcript it rendered before the restart. The OMP transcript is
|
|
20
20
|
* replayed (`get_messages`) only when no Dash log was persisted.
|
|
21
21
|
*/
|
|
22
|
+
import { bindForeignHub } from '@pgmi-builds/superd-hub/foreign-hub';
|
|
22
23
|
import { realpathSync, statSync } from "node:fs";
|
|
23
24
|
import { Service } from "@deepseek-ai/cordis";
|
|
24
25
|
import { homedir } from "node:os";
|
|
25
26
|
import { join } from "node:path";
|
|
26
|
-
import { SessionPreparation, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
27
|
+
import { interruptedTurnClosers, SessionPreparation, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
27
28
|
import { LazyOmpRpc } from "./lazy-rpc.js";
|
|
28
29
|
import { OmpAgent } from "./agent.js";
|
|
29
30
|
import { OmpLlmAdapter } from "./adapter.js";
|
|
@@ -311,6 +312,13 @@ export class OmpProvider extends Service {
|
|
|
311
312
|
}
|
|
312
313
|
llm.registerAdapter(providers, new OmpLlmAdapter());
|
|
313
314
|
}
|
|
315
|
+
hubFor(sessionId) {
|
|
316
|
+
const fromBoot = this.runtime.ctx.get("dshHomePath");
|
|
317
|
+
const home = typeof fromBoot === "function" ? fromBoot() : fromBoot;
|
|
318
|
+
if (typeof home !== "string" || home === "")
|
|
319
|
+
throw new Error("OMP world has no boot home path");
|
|
320
|
+
return bindForeignHub('omp', sessionId, home, () => this.runtime.ctx.agents.get(sessionId));
|
|
321
|
+
}
|
|
314
322
|
async createAgent(ownerCtx, options) {
|
|
315
323
|
const loopCtx = this.runtime.ctx;
|
|
316
324
|
const id = options.sessionId;
|
|
@@ -347,7 +355,11 @@ export class OmpProvider extends Service {
|
|
|
347
355
|
}));
|
|
348
356
|
const session = preparation.session;
|
|
349
357
|
const spawnPermission = () => resolveSpawnPermission(session.snapshotEvents(), defaultPermissionPreset(loopCtx), envApprovalMode());
|
|
350
|
-
const
|
|
358
|
+
const hub = this.hubFor(id);
|
|
359
|
+
const rpc = new LazyOmpRpc(async () => {
|
|
360
|
+
const extension = await hub.extensionPath();
|
|
361
|
+
return ["--approval-mode", spawnPermission().approvalMode, ...(extension === undefined ? [] : ["-e", extension])];
|
|
362
|
+
}, cwd, () => hub.dispose());
|
|
351
363
|
// The prepared header is the single source of truth for the row the
|
|
352
364
|
// first-spawn adoption writes: v2 folds header identity across
|
|
353
365
|
// live/listed/loaded observations, so created_at/cwd must be copied from
|
|
@@ -426,10 +438,13 @@ export class OmpProvider extends Service {
|
|
|
426
438
|
// Both branches are LAZY and both resolve their launch flag at spawn.
|
|
427
439
|
let rpc;
|
|
428
440
|
try {
|
|
441
|
+
const closers = interruptedTurnClosers(coldRead.events);
|
|
442
|
+
if (closers.length > 0)
|
|
443
|
+
await stored.append(closers);
|
|
429
444
|
// Fresh attach: seed the dsh session from the dsh log (native replay),
|
|
430
445
|
// header identity folded from the stored header verbatim.
|
|
431
446
|
const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
|
|
432
|
-
seed: [...coldRead.events],
|
|
447
|
+
seed: [...coldRead.events, ...closers],
|
|
433
448
|
meta: structuredClone(stored.header),
|
|
434
449
|
inheritedEventCount: stored.inheritedEventCount,
|
|
435
450
|
eventState: coldRead.eventState,
|
|
@@ -438,14 +453,22 @@ export class OmpProvider extends Service {
|
|
|
438
453
|
trace(`resume id=${id} seeded from dsh session log (${coldRead.events.length} events), approval=resolve-at-spawn (row fallback ${rowPreset() ?? "none"})${freshOmp ? " fresh-omp" : ""}`);
|
|
439
454
|
const spawnPermission = () => resolveSpawnPermission(preparation.session.snapshotEvents(), rowPreset(), envApprovalMode());
|
|
440
455
|
if (ompFile === undefined) {
|
|
441
|
-
const
|
|
456
|
+
const hub = this.hubFor(id);
|
|
457
|
+
const lazy = new LazyOmpRpc(async () => {
|
|
458
|
+
const extension = await hub.extensionPath();
|
|
459
|
+
return ["--approval-mode", spawnPermission().approvalMode, ...(extension === undefined ? [] : ["-e", extension])];
|
|
460
|
+
}, spawnCwd, () => hub.dispose());
|
|
442
461
|
lazy.onSpawned((client) => { void adoptSpawnedChild(id, stored.header, client, spawnPermission().preset); });
|
|
443
462
|
rpc = lazy;
|
|
444
463
|
}
|
|
445
464
|
else {
|
|
446
|
-
|
|
465
|
+
const hub = this.hubFor(id);
|
|
466
|
+
rpc = new LazyOmpRpc(async () => {
|
|
467
|
+
const extension = await hub.extensionPath();
|
|
468
|
+
return ["--approval-mode", spawnPermission().approvalMode, "--resume", ompFile, ...(extension === undefined ? [] : ["-e", extension])];
|
|
469
|
+
}, spawnCwd, () => hub.dispose());
|
|
447
470
|
}
|
|
448
|
-
const handle = await setupAndPublish(loopCtx, ownerCtx, id, options.agentOptions ?? {}, options.setup, preparation.session, rpc, options.parentAgent, "resume", { enterSession: true, preparation, stored: { handle: stored, written: coldRead.events.length } });
|
|
471
|
+
const handle = await setupAndPublish(loopCtx, ownerCtx, id, options.agentOptions ?? {}, options.setup, preparation.session, rpc, options.parentAgent, "resume", { enterSession: true, preparation, stored: { handle: stored, written: coldRead.events.length + closers.length } });
|
|
449
472
|
return this.registerHeld(id, ompFile ?? "", handle, rpc);
|
|
450
473
|
}
|
|
451
474
|
catch (error) {
|
|
@@ -2,6 +2,7 @@ import { OmpRpcClient } from "./rpc-client.js";
|
|
|
2
2
|
/** Synthetic state for a not-yet-spawned child: quiescent by definition. */
|
|
3
3
|
const IDLE_STATE = { isStreaming: false };
|
|
4
4
|
export class LazyOmpRpc {
|
|
5
|
+
onClose;
|
|
5
6
|
#spawnArgs;
|
|
6
7
|
#cwd;
|
|
7
8
|
#client;
|
|
@@ -18,7 +19,8 @@ export class LazyOmpRpc {
|
|
|
18
19
|
* construction and the first prompt — the new-chat permission pick does.
|
|
19
20
|
* @param cwd - the spawn cwd.
|
|
20
21
|
*/
|
|
21
|
-
constructor(spawnArgs, cwd) {
|
|
22
|
+
constructor(spawnArgs, cwd, onClose) {
|
|
23
|
+
this.onClose = onClose;
|
|
22
24
|
this.#spawnArgs = spawnArgs;
|
|
23
25
|
this.#cwd = cwd;
|
|
24
26
|
}
|
|
@@ -42,7 +44,11 @@ export class LazyOmpRpc {
|
|
|
42
44
|
return Promise.resolve(this.#client);
|
|
43
45
|
if (this.#spawn === undefined) {
|
|
44
46
|
const args = typeof this.#spawnArgs === "function" ? this.#spawnArgs() : this.#spawnArgs;
|
|
45
|
-
this.#spawn =
|
|
47
|
+
this.#spawn = Promise.resolve(args).then((resolved) => {
|
|
48
|
+
if (this.#closed)
|
|
49
|
+
throw new Error('OMP client closed during extension preparation');
|
|
50
|
+
return OmpRpcClient.spawn(resolved, this.#cwd);
|
|
51
|
+
}).then((client) => {
|
|
46
52
|
// A close() that raced the in-flight spawn wins: dispose immediately.
|
|
47
53
|
if (this.#closed) {
|
|
48
54
|
client.close();
|
|
@@ -160,7 +166,10 @@ export class LazyOmpRpc {
|
|
|
160
166
|
this.#client.sendRaw(command);
|
|
161
167
|
}
|
|
162
168
|
close() {
|
|
169
|
+
if (this.#closed)
|
|
170
|
+
return;
|
|
163
171
|
this.#closed = true;
|
|
172
|
+
this.onClose?.();
|
|
164
173
|
const client = this.#client;
|
|
165
174
|
this.#client = undefined;
|
|
166
175
|
client?.close();
|
|
@@ -264,7 +264,12 @@ export class OmpRpcClient {
|
|
|
264
264
|
async getSystemPrompt() {
|
|
265
265
|
try {
|
|
266
266
|
const state = await this.getState();
|
|
267
|
-
|
|
267
|
+
const prompt = state.systemPrompt;
|
|
268
|
+
if (typeof prompt === "string")
|
|
269
|
+
return prompt;
|
|
270
|
+
if (Array.isArray(prompt) && prompt.every(part => typeof part === "string"))
|
|
271
|
+
return prompt.join("\n\n");
|
|
272
|
+
return undefined;
|
|
268
273
|
}
|
|
269
274
|
catch {
|
|
270
275
|
return undefined;
|
package/agent-pi/dist/agent.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ToolCallRecovery } from "@deepseek-ai/dsh-session";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
import { agentEvents } from "@deepseek-ai/dsh-agent";
|
|
3
4
|
import { Inbox } from "./inbox.js";
|
|
@@ -186,6 +187,8 @@ export class PiAgent {
|
|
|
186
187
|
#step = 0;
|
|
187
188
|
#lastTurn = 0;
|
|
188
189
|
#turnOpen = false;
|
|
190
|
+
#turnCommitted = false;
|
|
191
|
+
#stepOpen = false;
|
|
189
192
|
#cancelCause = null;
|
|
190
193
|
/** Terminal model failure of the open turn (`message_end` stopReason "error"); consumed by `agent_end`. */
|
|
191
194
|
#pendingFailure = null;
|
|
@@ -402,7 +405,7 @@ export class PiAgent {
|
|
|
402
405
|
catch (error) {
|
|
403
406
|
trace(`#startTurn cold start failed: ${String(error)}`);
|
|
404
407
|
if (this.#turnOpen) {
|
|
405
|
-
this
|
|
408
|
+
this.#commitTurnStart();
|
|
406
409
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
407
410
|
this.#closeTurn({ kind: "error", error: { message: String(error), code: "UNKNOWN" } });
|
|
408
411
|
}
|
|
@@ -411,7 +414,7 @@ export class PiAgent {
|
|
|
411
414
|
}
|
|
412
415
|
if (this.#disposed)
|
|
413
416
|
return;
|
|
414
|
-
this
|
|
417
|
+
this.#commitTurnStart();
|
|
415
418
|
this.#emitRequestContext();
|
|
416
419
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
417
420
|
void this.#client.prompt(userMessageText(message)).catch((error) => this.#fail(error));
|
|
@@ -562,13 +565,36 @@ export class PiAgent {
|
|
|
562
565
|
return;
|
|
563
566
|
this.#reserveTurn();
|
|
564
567
|
this.#localUserPending = localUser;
|
|
565
|
-
this
|
|
568
|
+
this.#commitTurnStart();
|
|
566
569
|
this.#emitRequestContext();
|
|
567
570
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
568
571
|
}
|
|
572
|
+
#commitTurnStart() {
|
|
573
|
+
if (this.#turnCommitted)
|
|
574
|
+
return;
|
|
575
|
+
this.session.append("turn/start", { turn: this.#dashTurn });
|
|
576
|
+
this.#turnCommitted = true;
|
|
577
|
+
}
|
|
569
578
|
#closeTurn(reason) {
|
|
570
579
|
if (!this.#turnOpen)
|
|
571
580
|
return;
|
|
581
|
+
this.#commitTurnStart();
|
|
582
|
+
if (this.#stepOpen) {
|
|
583
|
+
const events = this.session.snapshotEvents();
|
|
584
|
+
const start = events.findLastIndex(event => event.type === "step/start");
|
|
585
|
+
const recovery = new ToolCallRecovery();
|
|
586
|
+
for (const event of events.slice(start))
|
|
587
|
+
recovery.observe(event);
|
|
588
|
+
for (const result of recovery.results()) {
|
|
589
|
+
this.session.append("tool/result", result.data, {
|
|
590
|
+
surfaceOp: "append",
|
|
591
|
+
...(result.sourceEventSeqs === undefined ? {} : { sourceEventSeqs: result.sourceEventSeqs }),
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
this.session.append("step/end", { turn: this.#dashTurn, step: this.#step });
|
|
595
|
+
this.#stepOpen = false;
|
|
596
|
+
}
|
|
597
|
+
this.#turnCommitted = false;
|
|
572
598
|
this.#turnOpen = false;
|
|
573
599
|
this.#localUserPending = false;
|
|
574
600
|
this.session.append("turn/end", { turn: this.#dashTurn, reason });
|
|
@@ -585,6 +611,7 @@ export class PiAgent {
|
|
|
585
611
|
this.#stepStartPending = false;
|
|
586
612
|
this.#step += 1;
|
|
587
613
|
this.session.append("step/start", { turn: this.#dashTurn, step: this.#step });
|
|
614
|
+
this.#stepOpen = true;
|
|
588
615
|
trace(`step/start turn=${this.#dashTurn} step=${this.#step}`);
|
|
589
616
|
}
|
|
590
617
|
/**
|
|
@@ -695,10 +722,6 @@ export class PiAgent {
|
|
|
695
722
|
trace("event turn_start");
|
|
696
723
|
this.#stepStartPending = true;
|
|
697
724
|
this.#pendingFailure = null;
|
|
698
|
-
// Open the step immediately (pi may never send message_start)
|
|
699
|
-
// and try the system prompt now that the session exists.
|
|
700
|
-
this.#commitStepStart();
|
|
701
|
-
this.#emitSystemMessage();
|
|
702
725
|
break;
|
|
703
726
|
case "message_start": {
|
|
704
727
|
const message = event.message;
|
|
@@ -798,8 +821,9 @@ export class PiAgent {
|
|
|
798
821
|
// Last chance for the system prompt. Appended inside the still-open
|
|
799
822
|
// step (before step/end) so the step-scope invariant holds.
|
|
800
823
|
this.#emitSystemMessage();
|
|
801
|
-
if (this.#turnOpen && this.#
|
|
824
|
+
if (this.#turnOpen && this.#stepOpen) {
|
|
802
825
|
this.session.append("step/end", { turn: this.#dashTurn, step: this.#step });
|
|
826
|
+
this.#stepOpen = false;
|
|
803
827
|
}
|
|
804
828
|
break;
|
|
805
829
|
case "agent_end": {
|
|
@@ -839,9 +863,12 @@ export class PiAgent {
|
|
|
839
863
|
return;
|
|
840
864
|
this.#mirroredTitle = name;
|
|
841
865
|
try {
|
|
866
|
+
const input = this.session.snapshotEvents().find(event => event.type === "user/message" && event.data.source.kind === "user");
|
|
867
|
+
if (input === undefined)
|
|
868
|
+
return;
|
|
842
869
|
this.session.append("session/title", {
|
|
843
870
|
title: name,
|
|
844
|
-
messageSeqs: [],
|
|
871
|
+
messageSeqs: [input.seq],
|
|
845
872
|
source: { kind: "provider", provider: "pi" },
|
|
846
873
|
});
|
|
847
874
|
trace(`session/title ← pi: "${name}"`);
|
package/agent-pi/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
* duplicated by design: pi's native JSONL stays pi's authority; the DSH log
|
|
26
26
|
* is the WebUI copy.
|
|
27
27
|
*/
|
|
28
|
+
import { bindForeignHub } from '@pgmi-builds/superd-hub/foreign-hub';
|
|
28
29
|
import { realpathSync, statSync } from "node:fs";
|
|
29
30
|
import { join } from "node:path";
|
|
30
31
|
import { Service } from "@deepseek-ai/cordis";
|
|
@@ -288,6 +289,9 @@ export class PiProvider extends Service {
|
|
|
288
289
|
});
|
|
289
290
|
});
|
|
290
291
|
}
|
|
292
|
+
hubFor(sessionId) {
|
|
293
|
+
return bindForeignHub('pi', sessionId, this.home, () => this.runtime.ctx.agents.get(sessionId));
|
|
294
|
+
}
|
|
291
295
|
async createAgent(ownerCtx, options) {
|
|
292
296
|
const loopCtx = this.runtime.ctx;
|
|
293
297
|
const id = options.sessionId;
|
|
@@ -315,7 +319,7 @@ export class PiProvider extends Service {
|
|
|
315
319
|
const client = await PiSessionClient.spawn([], spawnCwd, {
|
|
316
320
|
kind: "create",
|
|
317
321
|
...(toolset === undefined ? {} : { tools: toolset }),
|
|
318
|
-
});
|
|
322
|
+
}, this.hubFor(id));
|
|
319
323
|
const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
|
|
320
324
|
...(options.seed === undefined ? {} : { seed: options.seed }),
|
|
321
325
|
...(meta === undefined ? {} : { meta }),
|
|
@@ -398,7 +402,7 @@ export class PiProvider extends Service {
|
|
|
398
402
|
// CREATE-shaped lazy shell — zero pi objects until the first prompt.
|
|
399
403
|
const client = await PiSessionClient.spawn([], spawnCwd, blank
|
|
400
404
|
? { kind: "create", ...(toolset === undefined ? {} : { tools: toolset }) }
|
|
401
|
-
: { kind: "resume", sessionFile: record.sessionFile });
|
|
405
|
+
: { kind: "resume", sessionFile: record.sessionFile }, this.hubFor(id));
|
|
402
406
|
let handle;
|
|
403
407
|
let stored;
|
|
404
408
|
let preparation;
|
|
@@ -23,14 +23,23 @@ import { projectSessionEvent } from "./pi-events.js";
|
|
|
23
23
|
import { getPiModelRuntime } from "./models.js";
|
|
24
24
|
import { trace } from "./knobs.js";
|
|
25
25
|
/** Default factory: the real SDK, over the shared ModelRuntime. */
|
|
26
|
-
let sessionFactory = async ({ cwd, mode }) => {
|
|
26
|
+
let sessionFactory = async ({ cwd, mode, hub }) => {
|
|
27
27
|
const sdk = (await import("@earendil-works/pi-coding-agent"));
|
|
28
28
|
const modelRuntime = await getPiModelRuntime();
|
|
29
29
|
const sessionManager = mode.kind === "resume" ? sdk.SessionManager.open(mode.sessionFile) : sdk.SessionManager.create(cwd);
|
|
30
|
+
const extension = await hub?.extensionPath();
|
|
31
|
+
let resourceLoader;
|
|
32
|
+
if (extension !== undefined) {
|
|
33
|
+
const { DefaultResourceLoader, getAgentDir } = await import('@earendil-works/pi-coding-agent');
|
|
34
|
+
const loader = new DefaultResourceLoader({ cwd, agentDir: getAgentDir(), additionalExtensionPaths: [extension] });
|
|
35
|
+
await loader.reload();
|
|
36
|
+
resourceLoader = loader;
|
|
37
|
+
}
|
|
30
38
|
const { session } = await sdk.createAgentSession({
|
|
31
39
|
modelRuntime,
|
|
32
40
|
sessionManager,
|
|
33
41
|
cwd,
|
|
42
|
+
...(resourceLoader === undefined ? {} : { resourceLoader }),
|
|
34
43
|
...(mode.kind === "create" && mode.tools !== undefined && mode.tools.length > 0 ? { tools: mode.tools } : {}),
|
|
35
44
|
});
|
|
36
45
|
return session;
|
|
@@ -57,11 +66,11 @@ export class PiSessionClient {
|
|
|
57
66
|
* `ensureStarted()`. `args` is accepted for omp-seam parity and must be
|
|
58
67
|
* empty — unknown values fail loud (the SDK line has no spawn flags).
|
|
59
68
|
*/
|
|
60
|
-
static async spawn(args, cwd, mode) {
|
|
69
|
+
static async spawn(args, cwd, mode, hub) {
|
|
61
70
|
if (args.length > 0) {
|
|
62
71
|
throw new Error(`PiSessionClient: unsupported spawn arg "${String(args[0])}" (SDK line)`);
|
|
63
72
|
}
|
|
64
|
-
return new PiSessionClient({ cwd, mode });
|
|
73
|
+
return new PiSessionClient({ cwd, mode, ...(hub === undefined ? {} : { hub }) });
|
|
65
74
|
}
|
|
66
75
|
/** Register a wire event listener; returns the unsubscriber. */
|
|
67
76
|
on(listener) {
|
|
@@ -183,6 +192,7 @@ export class PiSessionClient {
|
|
|
183
192
|
if (this.#closed)
|
|
184
193
|
return;
|
|
185
194
|
this.#closed = true;
|
|
195
|
+
this.config.hub?.dispose();
|
|
186
196
|
try {
|
|
187
197
|
this.#session?.dispose();
|
|
188
198
|
}
|
package/cordis.patch.yml
CHANGED
|
@@ -12,15 +12,14 @@
|
|
|
12
12
|
# zero-port sibling root, mounted under /<label>
|
|
13
13
|
# - super-dsh/join the generic hub joiner for the standalone
|
|
14
14
|
# claude adapter (R2)
|
|
15
|
-
# - super-dsh/web-trust
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# derives from the trusted-hosts env.
|
|
15
|
+
# - super-dsh/web-trust authentication-only admission (aligned with
|
|
16
|
+
# dashr): every serving Host/Origin is accepted
|
|
17
|
+
# after native cookie authentication, and the
|
|
18
|
+
# browser owns its Host on any authority. No
|
|
19
|
+
# trusted-host CLI flags or environment required.
|
|
20
|
+
# Set trustAllHosts: false to retain the native
|
|
21
|
+
# fence / declared-authorities mode, or disable
|
|
22
|
+
# this row explicitly.
|
|
24
23
|
# - super-dsh/a2a the agent-facing A2A tool face (mesh P3): the
|
|
25
24
|
# single `superd-hub` model tool on ctx0's tools
|
|
26
25
|
# service (dormant without tools; world trees
|
|
@@ -36,6 +35,8 @@
|
|
|
36
35
|
name: 'super-dsh'
|
|
37
36
|
- id: superd-web-trust
|
|
38
37
|
name: 'super-dsh/web-trust'
|
|
38
|
+
config:
|
|
39
|
+
trustAllHosts: true
|
|
39
40
|
# Native UI fusion transport (plan Task 3): the ctx0 page-level state atom
|
|
40
41
|
# + five call-time patches. Composing this row IS the switch — there is no
|
|
41
42
|
# environment variable in the decision (2026-09-27 user ruling: a release
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "super-dsh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Super D for the DeepSeek Harness (DSH) \u2014 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",
|
|
@@ -80,8 +80,6 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@earendil-works/pi-coding-agent": "0.84.2",
|
|
83
|
-
"@openai/codex": "0.160.0",
|
|
84
|
-
"ws": "^8.21.0",
|
|
85
83
|
"zod": "4.4.3",
|
|
86
84
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
87
85
|
},
|
|
@@ -100,7 +98,8 @@
|
|
|
100
98
|
"@deepseek-ai/dsh-tools": ">=0.1.5-0 <0.3.0-0",
|
|
101
99
|
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
|
|
102
100
|
"@deepseek-ai/dsh-user-approval": ">=0.1.5-0 <0.3.0-0",
|
|
103
|
-
"@deepseek-ai/dsh-user-questions": ">=0.1.5-0 <0.3.0-0"
|
|
101
|
+
"@deepseek-ai/dsh-user-questions": ">=0.1.5-0 <0.3.0-0",
|
|
102
|
+
"@deepseek-ai/dsh-client-connection": ">=0.1.5-0 <0.3.0-0"
|
|
104
103
|
},
|
|
105
104
|
"peerDependenciesMeta": {
|
|
106
105
|
"@deepseek-ai/cordis": {
|
|
@@ -147,6 +146,9 @@
|
|
|
147
146
|
},
|
|
148
147
|
"@deepseek-ai/dsh-user-questions": {
|
|
149
148
|
"optional": true
|
|
149
|
+
},
|
|
150
|
+
"@deepseek-ai/dsh-client-connection": {
|
|
151
|
+
"optional": true
|
|
150
152
|
}
|
|
151
153
|
},
|
|
152
154
|
"publishConfig": {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
1
3
|
import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
|
|
2
4
|
import { createSuperdHubTool } from './hub-tool.js';
|
|
3
5
|
import { createA2aIngest } from './a2a-ingest.js';
|
|
@@ -14,6 +16,10 @@ import { PtyPool } from './exec-pty-session.js';
|
|
|
14
16
|
import { LocalScriptChannel, WsPtyChannel } from './exec-pty-channel.js';
|
|
15
17
|
import { PTY_EXEC_PATH } from './exec-pty-rpc.js';
|
|
16
18
|
import { agentLinks } from './agent-roster.js';
|
|
19
|
+
import { foreignAgentHandle, registerForeignHubHost, foreignHubEndpoint } from './foreign-hub.js';
|
|
20
|
+
import { refreshHermesNativeEndpoint } from './foreign-native.js';
|
|
21
|
+
import { refreshClaudeNativeEndpoint } from './foreign-claude.js';
|
|
22
|
+
import { refreshAgyNativeEndpoint } from './foreign-agy.js';
|
|
17
23
|
/** Stable Cordis plugin (row) name. */
|
|
18
24
|
export const name = 'superd-a2a';
|
|
19
25
|
/** Zero static injects — the tools/typertGateway legs are dynamic (dormancy). */
|
|
@@ -69,7 +75,7 @@ export function wireHubToolRow(ctx, opts = {}) {
|
|
|
69
75
|
let ingestRef;
|
|
70
76
|
const service = createA2aService({ home, gatewayFor, ingest: () => ingestRef, log: alog });
|
|
71
77
|
const delivery = {
|
|
72
|
-
agents: { get: (sessionId) => liveAgentHandle(ctx, sessionId) },
|
|
78
|
+
agents: { get: (sessionId) => liveAgentHandle(ctx, sessionId) ?? foreignAgentHandle(home, sessionId) },
|
|
73
79
|
buffer: (initiatorSessionId, message) => { service.buffer(initiatorSessionId, message); },
|
|
74
80
|
};
|
|
75
81
|
ingestRef = createA2aIngest({ home, gatewayFor, delivery, log: alog });
|
|
@@ -141,7 +147,7 @@ export function wireHubToolRow(ctx, opts = {}) {
|
|
|
141
147
|
};
|
|
142
148
|
const execFaceFull = { ...execFace, pty: execPty };
|
|
143
149
|
// the a2a endpoints (POST /api/superd/a2a + GET /api/superd/a2a/outbox) ride the shared
|
|
144
|
-
|
|
150
|
+
const toolDeps = {
|
|
145
151
|
service,
|
|
146
152
|
delivery,
|
|
147
153
|
exec: execFaceFull,
|
|
@@ -152,7 +158,18 @@ export function wireHubToolRow(ctx, opts = {}) {
|
|
|
152
158
|
// native DSH session, so the initiator's a6 is the 'native' row's.
|
|
153
159
|
initiatorOf: (sessionId) => localA2aInitiator(home, 'native', sessionId),
|
|
154
160
|
onWatch: (dispose) => { ctx.effect(() => dispose, 'superd-a2a: a2a completion watch'); },
|
|
155
|
-
}
|
|
161
|
+
};
|
|
162
|
+
t.tools.register(createSuperdHubTool(toolDeps));
|
|
163
|
+
t.effect(() => registerForeignHubHost(home, toolDeps), 'superd-a2a: foreign session facility');
|
|
164
|
+
t.effect(() => {
|
|
165
|
+
const publication = Promise.all([
|
|
166
|
+
refreshHermesNativeEndpoint(() => foreignHubEndpoint(home), join(homedir(), '.hermes')),
|
|
167
|
+
refreshClaudeNativeEndpoint(() => foreignHubEndpoint(home), process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude')),
|
|
168
|
+
refreshAgyNativeEndpoint(() => foreignHubEndpoint(home), join(homedir(), '.gemini', 'antigravity-cli')),
|
|
169
|
+
])
|
|
170
|
+
.catch(error => ctx.logger.warn(`superd-a2a: native plugin endpoint refresh failed: ${String(error)}`));
|
|
171
|
+
return () => publication;
|
|
172
|
+
}, 'superd-a2a: native post office discovery');
|
|
156
173
|
});
|
|
157
174
|
}
|
|
158
175
|
/** MUST stay an arrow (RM0/better-dash note: function declarations get `new`-called, disposers lost). */
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HostConnectionHandle } from '@deepseek-ai/dsh-client-connection';
|
|
2
|
+
/**
|
|
3
|
+
* Use the native index authenticator as a cookie-only admission delegate.
|
|
4
|
+
* A fixed, query-free index path prevents token exchange or redirects here;
|
|
5
|
+
* original Host and Cookie headers preserve the native authority binding and
|
|
6
|
+
* lifetime. No upstream private fields or cookie-format copies are needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function installAuthenticationOnlyAdmission(connection: HostConnectionHandle): () => void;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use the native index authenticator as a cookie-only admission delegate.
|
|
3
|
+
* A fixed, query-free index path prevents token exchange or redirects here;
|
|
4
|
+
* original Host and Cookie headers preserve the native authority binding and
|
|
5
|
+
* lifetime. No upstream private fields or cookie-format copies are needed.
|
|
6
|
+
*/
|
|
7
|
+
export function installAuthenticationOnlyAdmission(connection) {
|
|
8
|
+
const original = connection.requestRejection;
|
|
9
|
+
const response = { writeHead() { }, end() { } };
|
|
10
|
+
connection.requestRejection = (request) => connection.authorizeIndex({
|
|
11
|
+
headers: request.headers,
|
|
12
|
+
method: 'GET',
|
|
13
|
+
url: '/index.html',
|
|
14
|
+
}, response) ? undefined : 401;
|
|
15
|
+
return () => { connection.requestRejection = original; };
|
|
16
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ForeignHubBinding } from './foreign-hub.js';
|
|
2
|
+
export declare function refreshAgyNativeEndpoint(endpoint: () => Promise<string | undefined>, nativeHome: string): Promise<void>;
|
|
3
|
+
export declare function prepareAgyNativePlugin(binding: Pick<ForeignHubBinding, 'transport'>, nativeHome: string, stagingDirectory: string, install: (directory: string) => Promise<unknown>): Promise<string | undefined>;
|