super-dsh 0.1.11 → 0.1.12-a
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/client/index.js +434 -307
- package/cordis.patch.yml +10 -9
- package/package.json +8 -5
- package/superd-hub/assets/icons/README.md +20 -0
- package/superd-hub/assets/icons/amp.png +0 -0
- package/superd-hub/assets/icons/ante.png +0 -0
- package/superd-hub/assets/icons/antigravity.png +0 -0
- package/superd-hub/assets/icons/aug.png +0 -0
- package/superd-hub/assets/icons/autohand.png +0 -0
- package/superd-hub/assets/icons/claude.svg +1 -0
- package/superd-hub/assets/icons/cline.png +0 -0
- package/superd-hub/assets/icons/codebuddy.png +0 -0
- package/superd-hub/assets/icons/codebuff.png +0 -0
- package/superd-hub/assets/icons/codex.svg +1 -0
- package/superd-hub/assets/icons/command-code.png +0 -0
- package/superd-hub/assets/icons/continue.png +0 -0
- package/superd-hub/assets/icons/copilot.png +0 -0
- package/superd-hub/assets/icons/crush.png +0 -0
- package/superd-hub/assets/icons/cursor.png +0 -0
- package/superd-hub/assets/icons/devin.png +0 -0
- package/superd-hub/assets/icons/droid.png +0 -0
- package/superd-hub/assets/icons/dsh-fish.svg +3 -0
- package/superd-hub/assets/icons/dsh.png +0 -0
- package/superd-hub/assets/icons/freebuff.png +0 -0
- package/superd-hub/assets/icons/gemini.png +0 -0
- package/superd-hub/assets/icons/goose.png +0 -0
- package/superd-hub/assets/icons/grok.png +0 -0
- package/superd-hub/assets/icons/hermes.png +0 -0
- package/superd-hub/assets/icons/jcode.png +0 -0
- package/superd-hub/assets/icons/kilo.png +0 -0
- package/superd-hub/assets/icons/kimi.png +0 -0
- package/superd-hub/assets/icons/kiro.png +0 -0
- package/superd-hub/assets/icons/manifest.json +20 -0
- package/superd-hub/assets/icons/mimo-code.png +0 -0
- package/superd-hub/assets/icons/mistral-vibe.png +0 -0
- package/superd-hub/assets/icons/muse.png +0 -0
- package/superd-hub/assets/icons/omp.svg +1 -0
- package/superd-hub/assets/icons/openclaude.png +0 -0
- package/superd-hub/assets/icons/openclaw.png +0 -0
- package/superd-hub/assets/icons/opencode.png +0 -0
- package/superd-hub/assets/icons/pi.svg +1 -0
- package/superd-hub/assets/icons/prime-agent.png +0 -0
- package/superd-hub/assets/icons/qoder-cn.png +0 -0
- package/superd-hub/assets/icons/qoder.png +0 -0
- package/superd-hub/assets/icons/qwen-code.png +0 -0
- package/superd-hub/assets/icons/rovo.png +0 -0
- package/superd-hub/assets/icons/trae.png +0 -0
- package/superd-hub/assets/icons/zcode.png +0 -0
- package/superd-hub/dist/a2a-row.js +21 -4
- package/superd-hub/dist/agent-roster.d.ts +8 -3
- package/superd-hub/dist/agent-roster.js +19 -7
- package/superd-hub/dist/client/icon-assets.gen.d.ts +16 -0
- package/superd-hub/dist/client/icon-assets.gen.js +54 -0
- package/superd-hub/dist/client/mesh-api.d.ts +28 -17
- package/superd-hub/dist/client/mesh-api.js +44 -31
- package/superd-hub/dist/client/mesh-groups.d.ts +13 -11
- package/superd-hub/dist/client/mesh-groups.js +30 -10
- package/superd-hub/dist/client/roster.d.ts +7 -16
- package/superd-hub/dist/client/roster.js +5 -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/fusion-transport.d.ts +5 -2
- package/superd-hub/dist/fusion-transport.js +15 -3
- package/superd-hub/dist/machines.d.ts +6 -3
- package/superd-hub/dist/machines.js +15 -3
- package/superd-hub/dist/mesh-federate.d.ts +18 -8
- package/superd-hub/dist/mesh-federate.js +14 -9
- package/superd-hub/dist/mesh-link.d.ts +37 -3
- package/superd-hub/dist/mesh-link.js +49 -4
- package/superd-hub/dist/mesh-rpc.d.ts +48 -16
- package/superd-hub/dist/mesh-rpc.js +139 -14
- package/superd-hub/dist/roster.d.ts +20 -0
- package/superd-hub/dist/roster.js +24 -0
- package/superd-hub/dist/switch-core.d.ts +3 -2
- package/superd-hub/dist/switch-core.js +1 -1
- package/superd-hub/dist/web-trust.d.ts +14 -42
- package/superd-hub/dist/web-trust.js +18 -43
- package/superd-hub/lib/client/index.js +434 -307
- package/superd-hub/package.json +17 -3
- 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/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
|
|
@@ -22,17 +22,63 @@ six primary `@pgmi-builds/agent-*` packages and three OPTIONAL SDK halves).
|
|
|
22
22
|
Restart the profile and the
|
|
23
23
|
home under `<DSH_HOME>/agents/<label>/` on first boot.
|
|
24
24
|
|
|
25
|
+
## Remote web access
|
|
26
|
+
|
|
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.
|
|
32
|
+
|
|
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`.
|
|
37
|
+
|
|
25
38
|
## Runtimes
|
|
26
39
|
|
|
27
40
|
| Label | Runtime | Integration |
|
|
28
41
|
|---|---|---|
|
|
29
42
|
| OMP | OMP (`~/.omp`) | host `omp` binary over RPC (`omp --mode rpc`, NDJSON v1), lazy per session |
|
|
30
|
-
| Codex | Codex app-server (`~/.codex`) |
|
|
43
|
+
| Codex | Codex app-server (`~/.codex`) | CLI first, desktop core and lazy world-home fetch fallback |
|
|
31
44
|
| Claude | Claude Code (`~/.claude`) | standalone adapter, joined by the hub |
|
|
32
45
|
| Pi | pi coding agent (`~/.pi`) | in-process SDK |
|
|
33
46
|
| Hermes | Hermes (`~/.hermes`) | gateway child |
|
|
34
47
|
| Antigravity | agy SDK | Python bridge (`bridge/agy_bridge.py`) |
|
|
35
48
|
|
|
49
|
+
### Foreign-agent Hub facility
|
|
50
|
+
|
|
51
|
+
Super D sessions in Pi, OMP, and Codex can use the same `superd-hub` facility
|
|
52
|
+
for agent roster/status, messaging, and shell execution. Tool descriptions,
|
|
53
|
+
argument validation, receipts, and reply guidance come from the existing Hub.
|
|
54
|
+
Replies wake the initiating session automatically; do not poll for them.
|
|
55
|
+
|
|
56
|
+
Pi and OMP share one native extension. It is loaded incrementally on the first
|
|
57
|
+
prompt, preserving normal extension discovery and native tools. Its per-session
|
|
58
|
+
wrapper lives under the Super D world home (`hub-extensions`, mode 0600), with a
|
|
59
|
+
session capability for a loopback-only HTTP listener. No global extension is
|
|
60
|
+
installed in `~/.pi` or `~/.omp`. Some OMP versions present registered extension
|
|
61
|
+
tools as devices: read `xd://superd-hub` for instructions and write action JSON
|
|
62
|
+
to that device.
|
|
63
|
+
|
|
64
|
+
Codex uses the official experimental app-server `dynamicTools` interface on
|
|
65
|
+
new threads. Callbacks bind to that thread and the Super D session; existing
|
|
66
|
+
native tools/plugins remain available. No global plugin is installed in
|
|
67
|
+
`~/.codex`. Resuming a thread created through this integration restores its
|
|
68
|
+
saved tool definition. Older threads without that definition need a new
|
|
69
|
+
Super D conversation to gain the facility. A native Codex client resuming one
|
|
70
|
+
of these threads does not supply Super D's callback host.
|
|
71
|
+
|
|
72
|
+
Pi/OMP extension closures cannot transparently renew an already-loaded listener
|
|
73
|
+
capability when the Hub row is unmounted/remounted. Restart Super D and resume
|
|
74
|
+
the conversation after that operation; the tool reports this recovery path
|
|
75
|
+
when its connection becomes unavailable. Whole-app restart/resume is tested.
|
|
76
|
+
|
|
77
|
+
Draft creation and passive history viewing create no extension file, listener,
|
|
78
|
+
or agent process. Closing a session invalidates its capability. Interrupted
|
|
79
|
+
tool operations retain conservative upstream recovery guidance about unknown
|
|
80
|
+
outcomes rather than encouraging blind retries.
|
|
81
|
+
|
|
36
82
|
### Codex in 0.1.11
|
|
37
83
|
|
|
38
84
|
Codex discovers the local OpenAI OAuth login as a read-only provider in the
|
|
@@ -44,10 +90,12 @@ The adapter prefers the installed desktop core, then the CLI, then the bundled
|
|
|
44
90
|
0.160.0 core (`CODEX_BIN` overrides discovery). Desktop host browser and computer
|
|
45
91
|
use services are separate and are not attached by this adapter.
|
|
46
92
|
|
|
47
|
-
**
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
93
|
+
**Provider compatibility:** each provider defines its own message, reasoning,
|
|
94
|
+
and encrypted-state formats. Selecting another provider does not guarantee
|
|
95
|
+
that it can consume the existing session history. Successful continuation
|
|
96
|
+
across endpoints is an observed convenience, not an adapter guarantee.
|
|
97
|
+
The adapter surfaces provider errors; if the selected provider rejects the
|
|
98
|
+
history, start a new session with that provider.
|
|
51
99
|
|
|
52
100
|
### Opt-in SDK halves
|
|
53
101
|
|
|
@@ -35,6 +35,7 @@ import { type WithSystemHead } from "../../superd-hub/dist/index.js";
|
|
|
35
35
|
/** The CLI stream transport the agent drives (SDK bridge moved to agent-agy-sdk). */
|
|
36
36
|
export type LiveAgyClient = Pick<AgyCliClient, "conversationId" | "lastUsage" | "onThinkingDelta" | "onToolEvent" | "setApprovalHandler" | "turn" | "close"> & {
|
|
37
37
|
setSystemMessageHandler?(handler: (text: string) => void): void;
|
|
38
|
+
interrupt?: () => Promise<void>;
|
|
38
39
|
};
|
|
39
40
|
/** Classify a bridge turn failure into the stable {message, code} UI chip. */
|
|
40
41
|
export declare function classifyTurnError(error: unknown): {
|
|
@@ -100,6 +101,8 @@ export declare class AgyAgent implements Agent {
|
|
|
100
101
|
private deliver;
|
|
101
102
|
private deliverPrompt;
|
|
102
103
|
private sessionIdentityCommitted;
|
|
104
|
+
private seenToolCalls;
|
|
105
|
+
private stepOpen;
|
|
103
106
|
/** Stamp the single-preset selection once per session (projection seed). */
|
|
104
107
|
private bootstrapSessionIdentity;
|
|
105
108
|
private startTurn;
|
|
@@ -119,6 +122,7 @@ export declare class AgyAgent implements Agent {
|
|
|
119
122
|
/** Flush ONE parked follow-up as a fresh prompt (never submit while busy). */
|
|
120
123
|
private flushFollowUps;
|
|
121
124
|
private pendingThinking;
|
|
125
|
+
private stopInFlight?;
|
|
122
126
|
/** Calls from this turn still lacking a result (SDK 0.1.17 exposes none). */
|
|
123
127
|
private unpairedCalls;
|
|
124
128
|
private armTurnDeadline;
|
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();
|
|
@@ -293,6 +310,8 @@ export class AgyAgent {
|
|
|
293
310
|
this.ensureStreamBridge().push({ type: "text-delta", index: full.length, text: chunk });
|
|
294
311
|
full += chunk;
|
|
295
312
|
}
|
|
313
|
+
await this.stopInFlight;
|
|
314
|
+
this.stopInFlight = undefined;
|
|
296
315
|
this.clearTurnDeadline();
|
|
297
316
|
// SDK 0.1.17 streams ToolCall but NEVER ToolResult (verified live and
|
|
298
317
|
// in SDK source: receive_chunks yields Thought/Text/ToolCall only; the
|
|
@@ -327,6 +346,8 @@ export class AgyAgent {
|
|
|
327
346
|
this.flushFollowUps();
|
|
328
347
|
}
|
|
329
348
|
catch (error) {
|
|
349
|
+
await this.stopInFlight;
|
|
350
|
+
this.stopInFlight = undefined;
|
|
330
351
|
this.clearTurnDeadline();
|
|
331
352
|
this.abandonStreamBridge();
|
|
332
353
|
const failure = classifyTurnError(error);
|
|
@@ -345,8 +366,10 @@ export class AgyAgent {
|
|
|
345
366
|
if (this.turnOpen && this.step > 0)
|
|
346
367
|
this.closeStep();
|
|
347
368
|
this.closeTurn({ kind: "error", error: failure });
|
|
369
|
+
this.cancelCause = null;
|
|
348
370
|
this.markIdle();
|
|
349
371
|
this.endActivity();
|
|
372
|
+
this.flushFollowUps();
|
|
350
373
|
}
|
|
351
374
|
}
|
|
352
375
|
appendSystemNotice(text) {
|
|
@@ -404,18 +427,21 @@ export class AgyAgent {
|
|
|
404
427
|
closeTurn(reason) {
|
|
405
428
|
if (!this.turnOpen)
|
|
406
429
|
return;
|
|
430
|
+
this.closeStep();
|
|
407
431
|
this.turnOpen = false;
|
|
408
432
|
this.clearTurnDeadline();
|
|
409
433
|
this.session.append("turn/end", { turn: this.dashTurn, reason });
|
|
410
434
|
}
|
|
411
435
|
closeStep() {
|
|
412
|
-
if (!this.turnOpen || this.
|
|
436
|
+
if (!this.turnOpen || !this.stepOpen)
|
|
413
437
|
return;
|
|
438
|
+
this.stepOpen = false;
|
|
414
439
|
this.session.append("step/end", { turn: this.dashTurn, step: this.step });
|
|
415
440
|
}
|
|
416
441
|
commitStepStart() {
|
|
417
|
-
if (!this.turnOpen)
|
|
442
|
+
if (!this.turnOpen || this.stepOpen)
|
|
418
443
|
return;
|
|
444
|
+
this.stepOpen = true;
|
|
419
445
|
this.step += 1;
|
|
420
446
|
this.session.append("step/start", { turn: this.dashTurn, step: this.step });
|
|
421
447
|
}
|
|
@@ -452,6 +478,7 @@ export class AgyAgent {
|
|
|
452
478
|
this.deliverPrompt(entry.message);
|
|
453
479
|
}
|
|
454
480
|
pendingThinking = [];
|
|
481
|
+
stopInFlight;
|
|
455
482
|
/** Calls from this turn still lacking a result (SDK 0.1.17 exposes none). */
|
|
456
483
|
unpairedCalls = [];
|
|
457
484
|
armTurnDeadline() {
|
|
@@ -462,6 +489,13 @@ export class AgyAgent {
|
|
|
462
489
|
if (!this.turnOpen)
|
|
463
490
|
return;
|
|
464
491
|
trace(`turn deadline exceeded after ${AGY_TURN_DEADLINE_MS}ms — failing open turn`);
|
|
492
|
+
if (this.client.interrupt !== undefined) {
|
|
493
|
+
// Keep ownership until the old CLI stops. Its catch pairs unresolved
|
|
494
|
+
// calls and only then starts the next queued message.
|
|
495
|
+
this.cancelCause = { kind: "hook", reason: `agy turn deadline exceeded after ${AGY_TURN_DEADLINE_MS}ms` };
|
|
496
|
+
this.stopInFlight = this.client.interrupt().catch(error => trace(`deadline interrupt failed: ${String(error)}`));
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
465
499
|
this.abandonStreamBridge();
|
|
466
500
|
if (this.step > 0)
|
|
467
501
|
this.closeStep();
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Same surface as AgyBridgeClient so AgyAgent needs no changes.
|
|
17
17
|
*/
|
|
18
|
+
import type { ForeignHubBinding } from "@pgmi-builds/superd-hub/foreign-hub";
|
|
18
19
|
import { type AgyAuthMode, type AgySettingsMutation } from "./agy-auth.js";
|
|
19
20
|
export interface AgyCliOptions {
|
|
20
21
|
/** Key or a resolver (onboarding-saved keys become visible at first spawn). */
|
|
@@ -49,6 +50,9 @@ export interface AgyCliOptions {
|
|
|
49
50
|
authMode?: AgyAuthMode;
|
|
50
51
|
spawnAttempts?: number;
|
|
51
52
|
trace?: (...parts: unknown[]) => void;
|
|
53
|
+
hubBinding?: ForeignHubBinding;
|
|
54
|
+
hubStagingDirectory?: string;
|
|
55
|
+
prepareHub?: () => Promise<string | undefined>;
|
|
52
56
|
}
|
|
53
57
|
export type CliEvent = {
|
|
54
58
|
event: "ready";
|
|
@@ -110,6 +114,12 @@ export declare class AgyCliClient {
|
|
|
110
114
|
private dead;
|
|
111
115
|
private conversationIdValue;
|
|
112
116
|
private toolsSeen;
|
|
117
|
+
private toolCalls;
|
|
118
|
+
private toolResults;
|
|
119
|
+
private stepFailure?;
|
|
120
|
+
private hubPreparation?;
|
|
121
|
+
private hubSession?;
|
|
122
|
+
private prepare;
|
|
113
123
|
onThinkingDelta?: (text: string) => void;
|
|
114
124
|
onToolEvent?: (ev: {
|
|
115
125
|
event: "tool_call";
|
|
@@ -163,5 +173,7 @@ export declare class AgyCliClient {
|
|
|
163
173
|
private readyReceived;
|
|
164
174
|
private expectReady;
|
|
165
175
|
ping(): Promise<void>;
|
|
176
|
+
/** Stop one CLI execution, preserving its conversation and Hub capability. */
|
|
177
|
+
interrupt(): Promise<void>;
|
|
166
178
|
close(): Promise<void>;
|
|
167
179
|
}
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
* Same surface as AgyBridgeClient so AgyAgent needs no changes.
|
|
17
17
|
*/
|
|
18
18
|
import { spawn } from "node:child_process";
|
|
19
|
+
import { execFile } from "node:child_process";
|
|
20
|
+
import { promisify } from "node:util";
|
|
21
|
+
import { prepareAgyNativePlugin } from "@pgmi-builds/superd-hub/foreign-agy";
|
|
19
22
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
23
|
import { join } from "node:path";
|
|
21
24
|
import { authChildEnv, authSettingsMutation, nativeSettingsPath, } from "./agy-auth.js";
|
|
@@ -63,6 +66,27 @@ export class AgyCliClient {
|
|
|
63
66
|
dead = false;
|
|
64
67
|
conversationIdValue = "";
|
|
65
68
|
toolsSeen = 0;
|
|
69
|
+
toolCalls = new Set();
|
|
70
|
+
toolResults = new Set();
|
|
71
|
+
stepFailure;
|
|
72
|
+
hubPreparation;
|
|
73
|
+
hubSession;
|
|
74
|
+
async prepare() {
|
|
75
|
+
if (this.dead)
|
|
76
|
+
throw new Error("agy client is closed");
|
|
77
|
+
this.hubPreparation ??= (async () => {
|
|
78
|
+
if (this.opts.hubBinding === undefined)
|
|
79
|
+
return;
|
|
80
|
+
this.hubSession = await (this.opts.prepareHub?.() ?? prepareAgyNativePlugin(this.opts.hubBinding, join(this.realHome(), ".gemini", "antigravity-cli"), this.opts.hubStagingDirectory, async (directory) => {
|
|
81
|
+
const exec = promisify(execFile);
|
|
82
|
+
await exec(this.opts.agyBin ?? "agy", ["plugin", "install", directory], { timeout: 60_000 });
|
|
83
|
+
await exec(this.opts.agyBin ?? "agy", ["plugin", "enable", "superd-hub"], { timeout: 60_000 });
|
|
84
|
+
}));
|
|
85
|
+
})();
|
|
86
|
+
await this.hubPreparation;
|
|
87
|
+
if (this.dead)
|
|
88
|
+
throw new Error("agy client is closed");
|
|
89
|
+
}
|
|
66
90
|
onThinkingDelta;
|
|
67
91
|
onToolEvent;
|
|
68
92
|
setApprovalHandler(_handler) {
|
|
@@ -90,6 +114,8 @@ export class AgyCliClient {
|
|
|
90
114
|
spawnCli() {
|
|
91
115
|
if (this.child || this.dead)
|
|
92
116
|
return;
|
|
117
|
+
this.pending.length = 0;
|
|
118
|
+
this.buffer = "";
|
|
93
119
|
const mode = this.opts.authMode ?? "apiKey";
|
|
94
120
|
// Only key mode patches settings (`modelProvider: "gemini"`); ADC and OAuth
|
|
95
121
|
// need the DEFAULT backend, where that key must be ABSENT — the binary
|
|
@@ -107,8 +133,9 @@ export class AgyCliClient {
|
|
|
107
133
|
"--input-format=stream-json", "--output-format=stream-json",
|
|
108
134
|
"--disable-slash-commands",
|
|
109
135
|
];
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
const conversationId = this.conversationIdValue || this.opts.conversationId;
|
|
137
|
+
if (conversationId)
|
|
138
|
+
args.push(`--conversation=${conversationId}`);
|
|
112
139
|
if (this.opts.model)
|
|
113
140
|
args.push(`--model=${this.opts.model}`);
|
|
114
141
|
if (this.opts.effort)
|
|
@@ -127,6 +154,9 @@ export class AgyCliClient {
|
|
|
127
154
|
// ADC, the key for key mode, and the auto-update pin on EVERY mode.
|
|
128
155
|
// NO_PROXY keeps the dev3 loopback bridge reachable.
|
|
129
156
|
const env = { ...process.env, NO_PROXY: "127.0.0.1,localhost", ...authChildEnv(mode, key) };
|
|
157
|
+
delete env.SUPERD_HUB_SESSION;
|
|
158
|
+
if (this.hubSession !== undefined)
|
|
159
|
+
env.SUPERD_HUB_SESSION = this.hubSession;
|
|
130
160
|
// No HOME override, ever: the settings we just maintained live at
|
|
131
161
|
// `$HOME/.gemini`, ADC at `$HOME/.config/gcloud`, and the sign-in token
|
|
132
162
|
// in the keyring — isolating HOME destroys all three. That was the
|
|
@@ -155,6 +185,8 @@ export class AgyCliClient {
|
|
|
155
185
|
});
|
|
156
186
|
child.on("exit", (code) => {
|
|
157
187
|
this.child = undefined;
|
|
188
|
+
this.readyReceived = false;
|
|
189
|
+
this.buffer = "";
|
|
158
190
|
// The CLI reports its refusals on stderr / as non-JSON stdout lines and
|
|
159
191
|
// then exits non-zero. Those lines ARE the diagnosis ("--effort is not
|
|
160
192
|
// supported for model …", "Eligibility check failed: …"), so the exit
|
|
@@ -206,6 +238,8 @@ export class AgyCliClient {
|
|
|
206
238
|
continue;
|
|
207
239
|
}
|
|
208
240
|
if (parsed.event === "init") {
|
|
241
|
+
if (typeof parsed.conversation_id === "string")
|
|
242
|
+
this.conversationIdValue = parsed.conversation_id;
|
|
209
243
|
const init = parsed.init;
|
|
210
244
|
this.toolsSeen = Array.isArray(init?.tools) ? init.tools.length : 0;
|
|
211
245
|
this.dispatch({ event: "ready", tools: this.toolsSeen, permissionMode: init?.permission_mode });
|
|
@@ -217,8 +251,8 @@ export class AgyCliClient {
|
|
|
217
251
|
const r = parsed.result;
|
|
218
252
|
if (r?.conversation_id)
|
|
219
253
|
this.conversationIdValue = r.conversation_id;
|
|
220
|
-
if (r?.status !== "SUCCESS") {
|
|
221
|
-
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 });
|
|
222
256
|
}
|
|
223
257
|
else {
|
|
224
258
|
this.dispatch({ event: "done", conversation_id: this.conversationIdValue, turn_ms: 0 });
|
|
@@ -228,6 +262,12 @@ export class AgyCliClient {
|
|
|
228
262
|
}
|
|
229
263
|
consumeStep(su) {
|
|
230
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
|
+
}
|
|
231
271
|
if (st === "system_message") {
|
|
232
272
|
this.onSystemMessage?.(su.text_delta ?? "");
|
|
233
273
|
return;
|
|
@@ -255,26 +295,28 @@ export class AgyCliClient {
|
|
|
255
295
|
return;
|
|
256
296
|
}
|
|
257
297
|
if (st === "tool") {
|
|
258
|
-
const
|
|
298
|
+
const wireName = String(su.tool_name ?? "");
|
|
299
|
+
const name = su.tool_info?.name || (/^\d+$/.test(wireName) ? "unknown" : wireName) || "unknown";
|
|
259
300
|
const id = `${this.conversationIdValue}:${su.step_index ?? 0}`;
|
|
260
|
-
if (su.state === "ACTIVE") {
|
|
301
|
+
if (su.state === "ACTIVE" && !this.toolCalls.has(id)) {
|
|
302
|
+
this.toolCalls.add(id);
|
|
261
303
|
this.dispatch({
|
|
262
304
|
event: "tool_call", id, name,
|
|
263
305
|
args: JSON.stringify(su.tool_info?.parameters ?? {}),
|
|
264
306
|
});
|
|
265
|
-
this.onToolEvent?.({ event: "tool_call", id, name, args: JSON.stringify(su.tool_info?.parameters ?? {}) });
|
|
266
307
|
}
|
|
267
|
-
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);
|
|
268
314
|
const output = su.tool_info?.output ?? "";
|
|
269
315
|
const isError = su.state === "ERROR";
|
|
270
316
|
this.dispatch({
|
|
271
317
|
event: "tool_result", id, name, is_error: isError,
|
|
272
318
|
result: output || (isError ? "tool failed" : "[agy CLI reported no output summary]"),
|
|
273
319
|
});
|
|
274
|
-
this.onToolEvent?.({
|
|
275
|
-
event: "tool_result", id, name, is_error: isError,
|
|
276
|
-
result: output || (isError ? "tool failed" : "[agy CLI reported no output summary]"),
|
|
277
|
-
});
|
|
278
320
|
}
|
|
279
321
|
}
|
|
280
322
|
}
|
|
@@ -298,8 +340,13 @@ export class AgyCliClient {
|
|
|
298
340
|
}
|
|
299
341
|
/** One turn. First call spawns the CLI (lazy per §14-i) and keeps it resident. */
|
|
300
342
|
async *turn(text) {
|
|
343
|
+
await this.prepare();
|
|
301
344
|
this.spawnCli();
|
|
302
345
|
await this.expectReady();
|
|
346
|
+
this.toolCalls.clear();
|
|
347
|
+
this.toolResults.clear();
|
|
348
|
+
this.stepFailure = undefined;
|
|
349
|
+
this.diagnostics = [];
|
|
303
350
|
this.send({ event: "user", message: { content: text } });
|
|
304
351
|
let full = "";
|
|
305
352
|
for (;;) {
|
|
@@ -343,13 +390,34 @@ export class AgyCliClient {
|
|
|
343
390
|
}
|
|
344
391
|
}
|
|
345
392
|
async ping() {
|
|
393
|
+
await this.prepare();
|
|
346
394
|
this.spawnCli();
|
|
347
395
|
await this.expectReady();
|
|
348
396
|
}
|
|
397
|
+
/** Stop one CLI execution, preserving its conversation and Hub capability. */
|
|
398
|
+
async interrupt() {
|
|
399
|
+
const child = this.child;
|
|
400
|
+
if (child === undefined)
|
|
401
|
+
return;
|
|
402
|
+
this.readyReceived = false;
|
|
403
|
+
const exited = new Promise(resolve => child.once("exit", () => resolve()));
|
|
404
|
+
child.kill("SIGTERM");
|
|
405
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), 2000);
|
|
406
|
+
timer.unref();
|
|
407
|
+
try {
|
|
408
|
+
await exited;
|
|
409
|
+
}
|
|
410
|
+
finally {
|
|
411
|
+
clearTimeout(timer);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
349
414
|
async close() {
|
|
350
|
-
if (
|
|
415
|
+
if (this.dead)
|
|
351
416
|
return;
|
|
352
417
|
this.dead = true;
|
|
418
|
+
await this.opts.hubBinding?.dispose();
|
|
419
|
+
if (!this.child)
|
|
420
|
+
return;
|
|
353
421
|
const child = this.child;
|
|
354
422
|
try {
|
|
355
423
|
child.stdin.end();
|
package/agent-agy/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
* `sessionPersistence` service (hermes create/resume transaction shape).
|
|
19
19
|
*/
|
|
20
20
|
import { realpathSync, statSync } from "node:fs";
|
|
21
|
+
import { bindForeignHub } from "@pgmi-builds/superd-hub/foreign-hub";
|
|
21
22
|
import { join } from "node:path";
|
|
22
23
|
import { Service } from "@deepseek-ai/cordis";
|
|
23
24
|
import { installModelSelection } from "@deepseek-ai/dsh-agent";
|
|
@@ -149,6 +150,8 @@ export class AgyProvider extends Service {
|
|
|
149
150
|
hasAuthEvidence() {
|
|
150
151
|
if (this.resolveApiKey() !== undefined)
|
|
151
152
|
return true;
|
|
153
|
+
if (hasSigninCredential(this.opts.realHome ?? process.env.HOME ?? ""))
|
|
154
|
+
return true;
|
|
152
155
|
return hasAdcEvidence(process.env, process.env.HOME ?? "");
|
|
153
156
|
}
|
|
154
157
|
/** Default model id: adapter state → native TUI default (slug-normalized) → catalog head. */
|
|
@@ -260,7 +263,8 @@ export class AgyProvider extends Service {
|
|
|
260
263
|
trace(`create id=${id} spawnCwd=${spawnCwd}`);
|
|
261
264
|
// Full-lazy (§14-i): construct the client (spawns NOTHING); the bridge
|
|
262
265
|
// child materializes on the first real prompt.
|
|
263
|
-
const
|
|
266
|
+
const agentRef = {};
|
|
267
|
+
const client = this.createLiveClient(spawnCwd, agentOptionsModel(options.agentOptions), options.agentOptions?.reasoningEffort, bindForeignHub("agy", id, this.home, () => agentRef.current));
|
|
264
268
|
const preparation = SessionPreparation.create(loopCtx.sessions.prepare(id, {
|
|
265
269
|
...(options.seed === undefined ? {} : { seed: options.seed }),
|
|
266
270
|
...(meta === undefined ? {} : { meta }),
|
|
@@ -273,7 +277,7 @@ export class AgyProvider extends Service {
|
|
|
273
277
|
upsertSession(this.home, String(id), { conversationId: null, cwd: spawnCwd, createdAt: Date.now(), preset: null });
|
|
274
278
|
handedOff = true;
|
|
275
279
|
const agentOptions = await this.sessionAgentOptions(options.agentOptions);
|
|
276
|
-
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "startup", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client));
|
|
280
|
+
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "startup", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client), agentRef);
|
|
277
281
|
}
|
|
278
282
|
catch (error) {
|
|
279
283
|
trace(`create ${id} THREW: ${String(error)}`);
|
|
@@ -318,7 +322,8 @@ export class AgyProvider extends Service {
|
|
|
318
322
|
}
|
|
319
323
|
return verified;
|
|
320
324
|
})();
|
|
321
|
-
const
|
|
325
|
+
const agentRef = {};
|
|
326
|
+
const client = this.createLiveClient(spawnCwd, agentOptionsModel(options.agentOptions), options.agentOptions?.reasoningEffort, bindForeignHub("agy", id, this.home, () => agentRef.current), blank ? undefined : record.conversationId);
|
|
322
327
|
let handle;
|
|
323
328
|
let stored;
|
|
324
329
|
let preparation;
|
|
@@ -340,7 +345,7 @@ export class AgyProvider extends Service {
|
|
|
340
345
|
handle = undefined;
|
|
341
346
|
handedOff = true;
|
|
342
347
|
const agentOptions = await this.sessionAgentOptions(options.agentOptions);
|
|
343
|
-
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client));
|
|
348
|
+
return await setupAndPublish(loopCtx, ownerCtx, id, agentOptions, options.setup, preparation.session, client, options.parentAgent, "resume", { enterSession: true, preparation }, stored, this.agentRuntime(spawnCwd), () => this.followConversationId(id, client), agentRef);
|
|
344
349
|
}
|
|
345
350
|
catch (error) {
|
|
346
351
|
trace(`resume ${id} THREW: ${String(error)}`);
|
|
@@ -363,8 +368,8 @@ export class AgyProvider extends Service {
|
|
|
363
368
|
* environment for free while the SDK is an opt-in extra. Auth-form selection
|
|
364
369
|
* still rides the agy-auth ladder.
|
|
365
370
|
*/
|
|
366
|
-
createLiveClient(cwd, modelId, reasoningEffort) {
|
|
367
|
-
return new AgyCliClient(this.cliOptions(cwd, modelId, reasoningEffort));
|
|
371
|
+
createLiveClient(cwd, modelId, reasoningEffort, hubBinding, conversationId) {
|
|
372
|
+
return new AgyCliClient({ ...this.cliOptions(cwd, modelId, reasoningEffort), conversationId, hubBinding, hubStagingDirectory: join(this.home, "hub-native-agy") });
|
|
368
373
|
}
|
|
369
374
|
/**
|
|
370
375
|
* The auth form this machine should use: an explicitly selected, actually
|
|
@@ -457,7 +462,7 @@ async function appendUnstoredSuffix(stored, session) {
|
|
|
457
462
|
stored.storedCount += suffix.length;
|
|
458
463
|
}
|
|
459
464
|
/** Shared create/resume transaction (module-level: Cordis tracing proxy). */
|
|
460
|
-
async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, session, client, parentAgent, source, opts, stored, runtimeInfo, onTurnDone) {
|
|
465
|
+
async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, session, client, parentAgent, source, opts, stored, runtimeInfo, onTurnDone, agentRef) {
|
|
461
466
|
const runtime = { ...runtimeInfo, onTurnDone };
|
|
462
467
|
let detachSession;
|
|
463
468
|
let detachAgent;
|
|
@@ -465,6 +470,7 @@ async function setupAndPublish(loopCtx, ownerCtx, id, agentOptions, setup, sessi
|
|
|
465
470
|
let idleExit;
|
|
466
471
|
try {
|
|
467
472
|
agent = new AgyAgent(loopCtx, id, agentOptions, session, client, () => idleExit?.(), runtime);
|
|
473
|
+
agentRef.current = agent;
|
|
468
474
|
// Pin THIS session to the agy route (static resolution — synchronous).
|
|
469
475
|
try {
|
|
470
476
|
const selection = { provider: agentOptions.provider ?? "agy", model: agentOptions.model ?? "" };
|
package/agent-agy/package.json
CHANGED
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"@deepseek-ai/dsh-scope": ">=0.1.5-0 <0.3.0-0",
|
|
31
31
|
"@deepseek-ai/dsh-session": ">=0.1.5-0 <0.3.0-0",
|
|
32
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"
|
|
33
|
+
"@deepseek-ai/dsh-typert-protocol": ">=0.1.5-0 <0.3.0-0",
|
|
34
|
+
"@pgmi-builds/superd-hub": "*"
|
|
34
35
|
},
|
|
35
36
|
"peerDependenciesMeta": {
|
|
36
37
|
"@deepseek-ai/cordis": {
|
|
@@ -51,6 +52,9 @@
|
|
|
51
52
|
"@deepseek-ai/dsh-session-persistence": {
|
|
52
53
|
"optional": true
|
|
53
54
|
},
|
|
55
|
+
"@pgmi-builds/superd-hub": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
54
58
|
"@deepseek-ai/dsh-typert-protocol": {
|
|
55
59
|
"optional": true
|
|
56
60
|
}
|