super-dsh 0.1.11-b → 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 +10 -15
- package/agent-agy/dist/agent.d.ts +2 -0
- package/agent-agy/dist/agent.js +27 -7
- package/agent-agy/dist/agy-cli-client.d.ts +3 -0
- package/agent-agy/dist/agy-cli-client.js +25 -5
- package/agent-codex/dist/index.js +3 -3
- package/agent-hermes/dist/index.js +4 -3
- package/agent-omp/dist/index.js +6 -3
- package/cordis.patch.yml +10 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ the foreign store.
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
dsh plugin --profile <name> add super-dsh@0.1.
|
|
17
|
+
dsh plugin --profile <name> add super-dsh@0.1.12
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
That single package composes the whole line (`@pgmi-builds/superd-hub` plus the
|
|
@@ -24,21 +24,16 @@ home under `<DSH_HOME>/agents/<label>/` on first boot.
|
|
|
24
24
|
|
|
25
25
|
## Remote web access
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
The shipped bundle enables `superd-web-trust` with `trustAllHosts: true`.
|
|
28
|
+
Authenticated requests may use any serving Host or Origin, and the browser's
|
|
29
|
+
native trusted-host / loopback checks see the page as Host-owned. No `--host`,
|
|
30
|
+
`--trusted-host`, or trusted-host environment variables are needed for trust.
|
|
31
|
+
Native token-to-cookie login, cookie expiry, and authority binding remain in effect.
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
This skips Host/Origin restrictions and enables browser ownership for every
|
|
37
|
-
hostname. Native token-to-cookie login, cookie expiry, and authority binding
|
|
38
|
-
remain in effect. The default is `false`, preserving declared-authority mode.
|
|
39
|
-
It does not change the server's listening address: keep loopback behind Caddy
|
|
40
|
-
and a LAN relay. The test launcher enables this mode without `--host` or
|
|
41
|
-
`--trusted-host` and starts the relay on every test port, including 4999.
|
|
33
|
+
This does not change the listening address: use the native loopback listener
|
|
34
|
+
behind your reverse proxy or LAN relay. Remove older manual host/trusted-host
|
|
35
|
+
patches and any profile override disabling `superd-web-trust`. To restore the
|
|
36
|
+
native address fence, explicitly set this row's `trustAllHosts: false`.
|
|
42
37
|
|
|
43
38
|
## Runtimes
|
|
44
39
|
|
|
@@ -101,6 +101,8 @@ export declare class AgyAgent implements Agent {
|
|
|
101
101
|
private deliver;
|
|
102
102
|
private deliverPrompt;
|
|
103
103
|
private sessionIdentityCommitted;
|
|
104
|
+
private seenToolCalls;
|
|
105
|
+
private stepOpen;
|
|
104
106
|
/** Stamp the single-preset selection once per session (projection seed). */
|
|
105
107
|
private bootstrapSessionIdentity;
|
|
106
108
|
private startTurn;
|
package/agent-agy/dist/agent.js
CHANGED
|
@@ -44,7 +44,7 @@ const trace = (...parts) => {
|
|
|
44
44
|
/** Classify a bridge turn failure into the stable {message, code} UI chip. */
|
|
45
45
|
export function classifyTurnError(error) {
|
|
46
46
|
const text = error instanceof Error ? error.message : String(error);
|
|
47
|
-
if (/429|prepayment|quota|rate limit/i.test(text))
|
|
47
|
+
if (/429|prepayment|quota|rate limit|billing/i.test(text))
|
|
48
48
|
return { message: text, code: "QUOTA" };
|
|
49
49
|
if (/api key|authentication|unauthenticated|API_KEY/i.test(text))
|
|
50
50
|
return { message: text, code: "AUTH" };
|
|
@@ -125,9 +125,15 @@ export class AgyAgent {
|
|
|
125
125
|
if (!this.turnOpen)
|
|
126
126
|
return;
|
|
127
127
|
if (ev.event === "tool_call") {
|
|
128
|
-
if (this.
|
|
129
|
-
|
|
130
|
-
this.
|
|
128
|
+
if (this.seenToolCalls.has(ev.id))
|
|
129
|
+
return;
|
|
130
|
+
this.seenToolCalls.add(ev.id);
|
|
131
|
+
// Concurrent tools share their originating step until every call resolves.
|
|
132
|
+
if (this.unpairedCalls.length === 0) {
|
|
133
|
+
if (this.step > 0)
|
|
134
|
+
this.closeStep();
|
|
135
|
+
this.commitStepStart();
|
|
136
|
+
}
|
|
131
137
|
const callId = ToolCallId(ev.id || `agy-${this.dashTurn}-${this.step}`);
|
|
132
138
|
const argumentsJson = ev.args || "{}";
|
|
133
139
|
// Advertisement-before-execution (validator invariant, 2026-10-04 dev3
|
|
@@ -156,6 +162,8 @@ export class AgyAgent {
|
|
|
156
162
|
else {
|
|
157
163
|
// ToolResult chunks: the SDK yields none today; if a future version
|
|
158
164
|
// does, pair them off immediately so no placeholder is written.
|
|
165
|
+
if (!this.unpairedCalls.some(c => c.callId === ev.id))
|
|
166
|
+
return;
|
|
159
167
|
this.unpairedCalls = this.unpairedCalls.filter((c) => c.callId !== (ev.id || ""));
|
|
160
168
|
this.session.append("tool/result", {
|
|
161
169
|
turn: this.dashTurn, step: this.step,
|
|
@@ -254,6 +262,8 @@ export class AgyAgent {
|
|
|
254
262
|
void this.startTurn(message);
|
|
255
263
|
}
|
|
256
264
|
sessionIdentityCommitted = false;
|
|
265
|
+
seenToolCalls = new Set();
|
|
266
|
+
stepOpen = false;
|
|
257
267
|
/** Stamp the single-preset selection once per session (projection seed). */
|
|
258
268
|
bootstrapSessionIdentity() {
|
|
259
269
|
if (this.sessionIdentityCommitted)
|
|
@@ -264,6 +274,7 @@ export class AgyAgent {
|
|
|
264
274
|
}
|
|
265
275
|
}
|
|
266
276
|
async startTurn(message) {
|
|
277
|
+
this.seenToolCalls.clear();
|
|
267
278
|
this.bootstrapSessionIdentity();
|
|
268
279
|
this.session.append("turn/start", { turn: this.dashTurn });
|
|
269
280
|
this.session.append("user/message", message, { surfaceOp: "append" });
|
|
@@ -277,7 +288,13 @@ export class AgyAgent {
|
|
|
277
288
|
trace("onboarding: key stored");
|
|
278
289
|
}
|
|
279
290
|
else {
|
|
280
|
-
this.
|
|
291
|
+
this.appendAssistantMessage(ONBOARDING_PROMPT);
|
|
292
|
+
this.closeStep();
|
|
293
|
+
this.closeTurn({ kind: "error", error: { code: "AUTH", message: ONBOARDING_PROMPT } });
|
|
294
|
+
this.markIdle();
|
|
295
|
+
this.endActivity();
|
|
296
|
+
this.flushFollowUps();
|
|
297
|
+
return;
|
|
281
298
|
}
|
|
282
299
|
this.closeTurn({ kind: "completed" });
|
|
283
300
|
this.markIdle();
|
|
@@ -410,18 +427,21 @@ export class AgyAgent {
|
|
|
410
427
|
closeTurn(reason) {
|
|
411
428
|
if (!this.turnOpen)
|
|
412
429
|
return;
|
|
430
|
+
this.closeStep();
|
|
413
431
|
this.turnOpen = false;
|
|
414
432
|
this.clearTurnDeadline();
|
|
415
433
|
this.session.append("turn/end", { turn: this.dashTurn, reason });
|
|
416
434
|
}
|
|
417
435
|
closeStep() {
|
|
418
|
-
if (!this.turnOpen || this.
|
|
436
|
+
if (!this.turnOpen || !this.stepOpen)
|
|
419
437
|
return;
|
|
438
|
+
this.stepOpen = false;
|
|
420
439
|
this.session.append("step/end", { turn: this.dashTurn, step: this.step });
|
|
421
440
|
}
|
|
422
441
|
commitStepStart() {
|
|
423
|
-
if (!this.turnOpen)
|
|
442
|
+
if (!this.turnOpen || this.stepOpen)
|
|
424
443
|
return;
|
|
444
|
+
this.stepOpen = true;
|
|
425
445
|
this.step += 1;
|
|
426
446
|
this.session.append("step/start", { turn: this.dashTurn, step: this.step });
|
|
427
447
|
}
|
|
@@ -114,6 +114,9 @@ export declare class AgyCliClient {
|
|
|
114
114
|
private dead;
|
|
115
115
|
private conversationIdValue;
|
|
116
116
|
private toolsSeen;
|
|
117
|
+
private toolCalls;
|
|
118
|
+
private toolResults;
|
|
119
|
+
private stepFailure?;
|
|
117
120
|
private hubPreparation?;
|
|
118
121
|
private hubSession?;
|
|
119
122
|
private prepare;
|
|
@@ -66,6 +66,9 @@ export class AgyCliClient {
|
|
|
66
66
|
dead = false;
|
|
67
67
|
conversationIdValue = "";
|
|
68
68
|
toolsSeen = 0;
|
|
69
|
+
toolCalls = new Set();
|
|
70
|
+
toolResults = new Set();
|
|
71
|
+
stepFailure;
|
|
69
72
|
hubPreparation;
|
|
70
73
|
hubSession;
|
|
71
74
|
async prepare() {
|
|
@@ -248,8 +251,8 @@ export class AgyCliClient {
|
|
|
248
251
|
const r = parsed.result;
|
|
249
252
|
if (r?.conversation_id)
|
|
250
253
|
this.conversationIdValue = r.conversation_id;
|
|
251
|
-
if (r?.status !== "SUCCESS") {
|
|
252
|
-
this.dispatch({ event: "error", error: this.withDiagnostics(r?.error ?? `agy result status=${r?.status}`), recoverable: false });
|
|
254
|
+
if (r?.status !== "SUCCESS" || this.stepFailure !== undefined) {
|
|
255
|
+
this.dispatch({ event: "error", error: this.withDiagnostics(r?.error ?? this.stepFailure ?? `agy result status=${r?.status}`), recoverable: false });
|
|
253
256
|
}
|
|
254
257
|
else {
|
|
255
258
|
this.dispatch({ event: "done", conversation_id: this.conversationIdValue, turn_ms: 0 });
|
|
@@ -259,6 +262,12 @@ export class AgyCliClient {
|
|
|
259
262
|
}
|
|
260
263
|
consumeStep(su) {
|
|
261
264
|
const st = su.step_type;
|
|
265
|
+
if (su.conversation_id)
|
|
266
|
+
this.conversationIdValue = su.conversation_id;
|
|
267
|
+
if (st === "error_message") {
|
|
268
|
+
this.stepFailure = su.text_delta || "Antigravity reported an error without details";
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
262
271
|
if (st === "system_message") {
|
|
263
272
|
this.onSystemMessage?.(su.text_delta ?? "");
|
|
264
273
|
return;
|
|
@@ -286,15 +295,22 @@ export class AgyCliClient {
|
|
|
286
295
|
return;
|
|
287
296
|
}
|
|
288
297
|
if (st === "tool") {
|
|
289
|
-
const
|
|
298
|
+
const wireName = String(su.tool_name ?? "");
|
|
299
|
+
const name = su.tool_info?.name || (/^\d+$/.test(wireName) ? "unknown" : wireName) || "unknown";
|
|
290
300
|
const id = `${this.conversationIdValue}:${su.step_index ?? 0}`;
|
|
291
|
-
if (su.state === "ACTIVE") {
|
|
301
|
+
if (su.state === "ACTIVE" && !this.toolCalls.has(id)) {
|
|
302
|
+
this.toolCalls.add(id);
|
|
292
303
|
this.dispatch({
|
|
293
304
|
event: "tool_call", id, name,
|
|
294
305
|
args: JSON.stringify(su.tool_info?.parameters ?? {}),
|
|
295
306
|
});
|
|
296
307
|
}
|
|
297
|
-
else if (su.state === "DONE" || su.state === "ERROR") {
|
|
308
|
+
else if ((su.state === "DONE" || su.state === "ERROR") && !this.toolResults.has(id)) {
|
|
309
|
+
if (!this.toolCalls.has(id)) {
|
|
310
|
+
this.toolCalls.add(id);
|
|
311
|
+
this.dispatch({ event: "tool_call", id, name, args: JSON.stringify(su.tool_info?.parameters ?? {}) });
|
|
312
|
+
}
|
|
313
|
+
this.toolResults.add(id);
|
|
298
314
|
const output = su.tool_info?.output ?? "";
|
|
299
315
|
const isError = su.state === "ERROR";
|
|
300
316
|
this.dispatch({
|
|
@@ -327,6 +343,10 @@ export class AgyCliClient {
|
|
|
327
343
|
await this.prepare();
|
|
328
344
|
this.spawnCli();
|
|
329
345
|
await this.expectReady();
|
|
346
|
+
this.toolCalls.clear();
|
|
347
|
+
this.toolResults.clear();
|
|
348
|
+
this.stepFailure = undefined;
|
|
349
|
+
this.diagnostics = [];
|
|
330
350
|
this.send({ event: "user", message: { content: text } });
|
|
331
351
|
let full = "";
|
|
332
352
|
for (;;) {
|
|
@@ -503,7 +503,7 @@ export class CodexProvider extends Service {
|
|
|
503
503
|
if (record === undefined) {
|
|
504
504
|
throw new CodexResumeError("SESSION_MAP_MISS", `cannot resume session "${id}": no Codex thread is recorded for this Dash session id`);
|
|
505
505
|
}
|
|
506
|
-
if (record.threadId === null) {
|
|
506
|
+
if (record.threadId === null && (await readStoredEvents(persistence, id, options.signal)).some(event => event.type === "turn/start" || event.type === "user/message" || event.type === "assistant/message")) {
|
|
507
507
|
// C2: never auto-start a fresh thread — the session's history is in the
|
|
508
508
|
// (missing) native thread, not in a blank one.
|
|
509
509
|
throw new CodexResumeError("NATIVE_THREAD_NEVER_STARTED", `cannot resume session "${id}": its Codex thread was never started (no thread id recorded)`);
|
|
@@ -518,7 +518,7 @@ export class CodexProvider extends Service {
|
|
|
518
518
|
// `<worldHome>/.codex` home (never migrated) and pruned rollouts alike.
|
|
519
519
|
// Without this gate the failure would surface only at the first turn
|
|
520
520
|
// (resumeThread is zero-IO).
|
|
521
|
-
if (readRolloutHead(resolveCodexHome(), record.threadId) === undefined) {
|
|
521
|
+
if (record.threadId !== null && readRolloutHead(resolveCodexHome(), record.threadId) === undefined) {
|
|
522
522
|
throw new CodexResumeError("ROLLOUT_MISSING", `cannot resume session "${id}": no rollout for Codex thread ${record.threadId} exists in the current Codex home (${resolveCodexHome()})`);
|
|
523
523
|
}
|
|
524
524
|
// Spawn cwd comes from the recorded pairing — re-realpath and re-verify so
|
|
@@ -545,7 +545,7 @@ export class CodexProvider extends Service {
|
|
|
545
545
|
// rollout). No process is spawned here — lazy-spawn gate, rules §14-i.
|
|
546
546
|
const client = CodexAppServerClient.create({
|
|
547
547
|
cwd: spawnCwd,
|
|
548
|
-
resumeThreadId: record.threadId,
|
|
548
|
+
...(record.threadId === null ? {} : { resumeThreadId: record.threadId }),
|
|
549
549
|
approvalPolicy,
|
|
550
550
|
sandboxMode,
|
|
551
551
|
worldHome: this.home,
|
|
@@ -364,7 +364,9 @@ export class HermesProvider extends Service {
|
|
|
364
364
|
if (record === undefined) {
|
|
365
365
|
throw new Error(`cannot resume session "${id}": no Hermes gateway session is recorded for this Dash session id`);
|
|
366
366
|
}
|
|
367
|
-
|
|
367
|
+
const persistence = this.requirePersistence("resume");
|
|
368
|
+
if ((record.gatewayStoredSessionId === null || record.gatewayStoredSessionId === undefined)
|
|
369
|
+
&& (await readStoredEvents(persistence, id, options.signal)).some(event => event.type === "turn/start" || event.type === "user/message" || event.type === "assistant/message")) {
|
|
368
370
|
throw new Error(`cannot resume session "${id}": no durable gateway session id is recorded (this record predates durable-id capture; re-create the session)`);
|
|
369
371
|
}
|
|
370
372
|
// Spawn cwd comes from the recorded pairing — re-realpath and re-verify.
|
|
@@ -372,7 +374,6 @@ export class HermesProvider extends Service {
|
|
|
372
374
|
if (spawnCwd === undefined) {
|
|
373
375
|
throw new Error(`cannot resume session "${id}": its recorded working directory no longer exists`);
|
|
374
376
|
}
|
|
375
|
-
const persistence = this.requirePersistence("resume");
|
|
376
377
|
// Preset (record-only): env override, else the recorded preset, else the
|
|
377
378
|
// persisted session log, else the permission service's default.
|
|
378
379
|
const envMode = envApprovalMode();
|
|
@@ -407,7 +408,7 @@ export class HermesProvider extends Service {
|
|
|
407
408
|
this.followGatewaySessionId(id, client);
|
|
408
409
|
handedOff = true;
|
|
409
410
|
const agentOptions = await this.sessionAgentOptions(options.agentOptions);
|
|
410
|
-
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(client, spawnCwd, record.gatewayStoredSessionId));
|
|
411
|
+
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(client, spawnCwd, record.gatewayStoredSessionId ?? undefined));
|
|
411
412
|
}
|
|
412
413
|
catch (error) {
|
|
413
414
|
trace(`resume ${id} THREW: ${String(error)} | stack=${error instanceof Error ? error.stack?.slice(0, 400) : "n/a"}`);
|
package/agent-omp/dist/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import { realpathSync, statSync } from "node:fs";
|
|
|
24
24
|
import { Service } from "@deepseek-ai/cordis";
|
|
25
25
|
import { homedir } from "node:os";
|
|
26
26
|
import { join } from "node:path";
|
|
27
|
-
import { SessionPreparation, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
27
|
+
import { interruptedTurnClosers, SessionPreparation, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
28
28
|
import { LazyOmpRpc } from "./lazy-rpc.js";
|
|
29
29
|
import { OmpAgent } from "./agent.js";
|
|
30
30
|
import { OmpLlmAdapter } from "./adapter.js";
|
|
@@ -438,10 +438,13 @@ export class OmpProvider extends Service {
|
|
|
438
438
|
// Both branches are LAZY and both resolve their launch flag at spawn.
|
|
439
439
|
let rpc;
|
|
440
440
|
try {
|
|
441
|
+
const closers = interruptedTurnClosers(coldRead.events);
|
|
442
|
+
if (closers.length > 0)
|
|
443
|
+
await stored.append(closers);
|
|
441
444
|
// Fresh attach: seed the dsh session from the dsh log (native replay),
|
|
442
445
|
// header identity folded from the stored header verbatim.
|
|
443
446
|
const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
|
|
444
|
-
seed: [...coldRead.events],
|
|
447
|
+
seed: [...coldRead.events, ...closers],
|
|
445
448
|
meta: structuredClone(stored.header),
|
|
446
449
|
inheritedEventCount: stored.inheritedEventCount,
|
|
447
450
|
eventState: coldRead.eventState,
|
|
@@ -465,7 +468,7 @@ export class OmpProvider extends Service {
|
|
|
465
468
|
return ["--approval-mode", spawnPermission().approvalMode, "--resume", ompFile, ...(extension === undefined ? [] : ["-e", extension])];
|
|
466
469
|
}, spawnCwd, () => hub.dispose());
|
|
467
470
|
}
|
|
468
|
-
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 } });
|
|
469
472
|
return this.registerHeld(id, ompFile ?? "", handle, rpc);
|
|
470
473
|
}
|
|
471
474
|
catch (error) {
|
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",
|