super-dsh 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-agy/dist/agent.d.ts +4 -1
- package/agent-agy/dist/agent.js +15 -1
- package/agent-agy/dist/index.js +5 -8
- package/agent-agy/package.json +6 -6
- package/agent-agy-sdk/dist/agent.d.ts +4 -1
- package/agent-agy-sdk/dist/agent.js +15 -1
- package/agent-agy-sdk/dist/index.js +5 -8
- package/agent-agy-sdk/package.json +5 -5
- package/agent-claude/dist/agent.js +15 -1
- package/agent-claude/package.json +8 -8
- package/agent-codex/dist/agent.js +15 -1
- package/agent-codex/package.json +7 -7
- package/agent-codex-sdk/dist/agent.js +15 -1
- package/agent-codex-sdk/dist/index.js +5 -11
- package/agent-codex-sdk/package.json +7 -7
- package/agent-hermes/dist/agent.js +15 -1
- package/agent-hermes/dist/index.js +5 -8
- package/agent-hermes/package.json +7 -7
- package/agent-omp/dist/agent.js +26 -2
- package/agent-omp/package.json +8 -8
- package/agent-omp-sdk/dist/agent.d.ts +4 -1
- package/agent-omp-sdk/dist/agent.js +15 -1
- package/agent-omp-sdk/package.json +8 -8
- package/agent-pi/dist/agent.js +20 -2
- package/agent-pi/dist/index.js +5 -11
- package/agent-pi/package.json +7 -7
- package/package.json +21 -21
- package/remote/hub-pty-pump.mjs +97 -0
- package/remote/plugin.d.ts +406 -203
- package/remote/plugin.js +631 -319
- package/remote/provenance.json +15 -12
- package/superd-hub/dist/agent-model-face.d.ts +93 -0
- package/superd-hub/dist/agent-model-face.js +113 -0
- package/superd-hub/dist/exec.d.ts +3 -2
- package/superd-hub/dist/exec.js +6 -5
- package/superd-hub/dist/hub-tool.d.ts +1 -1
- package/superd-hub/dist/hub-tool.js +6 -6
- package/superd-hub/dist/index.d.ts +1 -0
- package/superd-hub/dist/index.js +1 -0
- package/superd-hub/dist/remote-provider.js +70 -36
- package/superd-hub/dist/world-entry.js +30 -0
- package/superd-hub/package.json +14 -10
- package/superd-hub/dist/exec-pty-channel.d.ts +0 -58
- package/superd-hub/dist/exec-pty-channel.js +0 -162
- package/superd-hub/dist/exec-pty-framing.d.ts +0 -34
- package/superd-hub/dist/exec-pty-framing.js +0 -89
- package/superd-hub/dist/exec-pty-session.d.ts +0 -78
- package/superd-hub/dist/exec-pty-session.js +0 -228
package/remote/provenance.json
CHANGED
|
@@ -3,24 +3,25 @@
|
|
|
3
3
|
"entry": "better-dsh/src/remote/plugin.ts",
|
|
4
4
|
"workerEntry": "better-dsh/src/remote/file-worker.ts",
|
|
5
5
|
"sources": {
|
|
6
|
-
"better-dsh/src/remote/agent-registration.ts": "
|
|
6
|
+
"better-dsh/src/remote/agent-registration.ts": "cb08eb9f5d592201054410da69b3bf383e2604899d9494a8cbd8ba2f86b3f7ad",
|
|
7
7
|
"better-dsh/src/remote/archive.ts": "fd32f96dc63421be7d56eb545badf355c6ee2498599605ffa48958b9efca9a63",
|
|
8
8
|
"better-dsh/src/remote/copy.ts": "ab2aec5ee8df814f4bc568370977d229c402489248c738eff147431a52650c52",
|
|
9
|
-
"better-dsh/src/remote/driver.ts": "
|
|
9
|
+
"better-dsh/src/remote/driver.ts": "8bfa05e056b9099a1d9fcdf6cb67d15ec6e9a895ea061ea2e65cd3a513fe4fdf",
|
|
10
10
|
"better-dsh/src/remote/endpoints.ts": "e4ab0fa1afcee815ad78976eba4036e2ed2b21eaea11f7451874b56229f12eac",
|
|
11
11
|
"better-dsh/src/remote/file-worker.ts": "31ac019a944cf7fdcd0ff044f8b330d923bf4d384c77056f9b7e3032bc8c9598",
|
|
12
12
|
"better-dsh/src/remote/helper.ts": "789a802610fc480bb1c922dd284787ca22d7394fa906c729ee33d78ade79973c",
|
|
13
|
-
"better-dsh/src/remote/hub-contract.ts": "
|
|
13
|
+
"better-dsh/src/remote/hub-contract.ts": "a7ae5501c4ffe63ced1526c2d64d7d83798fc2025f9b4aea998e033a124fe598",
|
|
14
|
+
"better-dsh/src/remote/hub-pty-pump.mjs": "dfe0cb331cc585eb4d858fbb6d8f3ff22414f3ee55109c933df0fc14502aee5c",
|
|
14
15
|
"better-dsh/src/remote/input.ts": "dfe087d34e9e9aefc0d8ec2e33371dc86981b05723d436419ad1afb6ac8426b4",
|
|
15
|
-
"better-dsh/src/remote/jobs.ts": "
|
|
16
|
+
"better-dsh/src/remote/jobs.ts": "4556bd9f30580a8bf90800c7a945282e276abb548df6db7fe95e7091bb1543c6",
|
|
16
17
|
"better-dsh/src/remote/labels.ts": "d9dcfdc1c6bb292b37d3250e7897b239c416b55b3102943bcc4f1d0ce7d2e709",
|
|
17
18
|
"better-dsh/src/remote/native-copy.ts": "3f8691abb14dfb7a94e956956fb4d182b7732ffbe70f1a8ff7dec5dd2b9afb7c",
|
|
18
|
-
"better-dsh/src/remote/nonce-framing.ts": "
|
|
19
|
+
"better-dsh/src/remote/nonce-framing.ts": "2f2f80d6aa9934f96ba171d575dbe4cddbe2e127da3b0eec3032c5ea522cadc3",
|
|
19
20
|
"better-dsh/src/remote/oneshot.ts": "99f2c809ba1159c7865ec3cff2e6d7b1f165a7f03185a6fc9505c1a534ba57bc",
|
|
20
21
|
"better-dsh/src/remote/output-ring.ts": "a79104bd3dcddab06a3fe7e52d239a70becd4f2dcddd0c9b6513f5fe3c4ed0bc",
|
|
21
|
-
"better-dsh/src/remote/plugin.ts": "
|
|
22
|
+
"better-dsh/src/remote/plugin.ts": "da6b985cd013ffc253ee8cf5605dc2d729e26534139dd28269db5d246511fac9",
|
|
22
23
|
"better-dsh/src/remote/process.ts": "f4491c76457b662703e0b1e4a876299b9c8b7b3da5b304c6baee5576017de448",
|
|
23
|
-
"better-dsh/src/remote/pty-session.ts": "
|
|
24
|
+
"better-dsh/src/remote/pty-session.ts": "3cb07345b90f0e54cbcea00204ae0b5346d9bb6edf3a784f009fc1b07357f799",
|
|
24
25
|
"better-dsh/src/remote/roster.ts": "b84f75edc87550bb96803042487cfd7c39135b1710ff9715dd5ca402df0aa378",
|
|
25
26
|
"better-dsh/src/remote/rsync.ts": "f9c15279fdfa67b3265eeda216bcd0c46d8b4f05c6026cf2750a1c818ff333ba",
|
|
26
27
|
"better-dsh/src/remote/ssh-argv.ts": "e7f46ef3e53eaba6cfe92033afad046435db18b0b2f4b86832353d609c535662",
|
|
@@ -29,9 +30,10 @@
|
|
|
29
30
|
"better-dsh/src/remote/ssh-rsync-native-relay.ts": "5d9585ab30a11aeeba19ce7b43aa31f47e2ee0bde2e7c006ac3e811cf7940bdf",
|
|
30
31
|
"better-dsh/src/remote/ssh-targets.ts": "2a7e754fc26cf34e05ffb5d7f7406f35c09fd0770684db904dbf4c56d2c00beb",
|
|
31
32
|
"better-dsh/src/remote/status.ts": "e144c1cf98388dbaca8dab5622e6e37ceac85d898119e37d07f88e129113a82e",
|
|
32
|
-
"better-dsh/src/remote/target.ts": "
|
|
33
|
-
"better-dsh/src/remote/tool.ts": "
|
|
34
|
-
"better-dsh/src/remote/
|
|
33
|
+
"better-dsh/src/remote/target.ts": "bce47c6c61975a52e5cb77a6b727f68cbe78ecdcb3866c237fb932444ecc954e",
|
|
34
|
+
"better-dsh/src/remote/tool.ts": "ecb431c4b1dfa4981063ea78b90288ac9ac12070c08a113b51602f18887f194b",
|
|
35
|
+
"better-dsh/src/remote/transfer-tool.ts": "d02b216a31314e98fdf6933230b57adce58b9c8aebe9798aae7be808105b41e2",
|
|
36
|
+
"better-dsh/src/remote/transports.ts": "8a7190d6013f12901d9007e6b30708645baa4aa0770fcb1087ae6fcde656a8d0",
|
|
35
37
|
"better-dsh/third_party/schemastery/lib/index.cjs": "aca1c1a47c7b6034feee01501aefca3e6dd97a0bac4fe334fd30647ae5a9e480",
|
|
36
38
|
"better-dsh/third_party/schemastery/lib/index.d.mts": "66847f605af58c42575333c825eefdec4ad38278ad6cb260e37aa0cc63803bbd",
|
|
37
39
|
"better-dsh/third_party/schemastery/lib/index.mjs": "cfcbe46c7416bdb35ce891c252152bbf519e08199bede5930dd7cb880c65f1ef",
|
|
@@ -68,7 +70,8 @@
|
|
|
68
70
|
"helper-bin/x86_64-pc-windows-gnu/superd-run.exe": "1372450238c5bda702b4529f4148aa79e80a255c3c125d1a6229afd4693e9d90",
|
|
69
71
|
"helper-bin/x86_64-unknown-linux-musl/BUILT-VER": "8601d3c2ff79795c9657b02080afffe91c42775534dd6687c6dbdc0f4924db70",
|
|
70
72
|
"helper-bin/x86_64-unknown-linux-musl/superd-run": "05b428657942abf22a0ad609433d5cab6b6182fe389851345459a0274b5a6533",
|
|
71
|
-
"
|
|
72
|
-
"plugin.
|
|
73
|
+
"hub-pty-pump.mjs": "dfe0cb331cc585eb4d858fbb6d8f3ff22414f3ee55109c933df0fc14502aee5c",
|
|
74
|
+
"plugin.d.ts": "da9bfcbb1a0f37bd2994452ac4cfed284615e5cea45c05ff97569474a442e91f",
|
|
75
|
+
"plugin.js": "8dd1843f2c57097a1bbcfeddaf50193985bf25b942cc784ca501311d16aae7f3"
|
|
73
76
|
}
|
|
74
77
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-model-face.ts — the 0.2.2+ `Agent.model` face for foreign-runtime
|
|
3
|
+
* bridges.
|
|
4
|
+
*
|
|
5
|
+
* dsh 0.2.2 made `model` a required `Agent` member: the Web UI's model switcher
|
|
6
|
+
* reaches every agent — native and foreign alike — through
|
|
7
|
+
* `session/selectModel` → `agent.model.select()` (session-controller
|
|
8
|
+
* commands.ts). The native `AgentModel` (dsh-agent model-selection.ts) records
|
|
9
|
+
* a `model/selection` session event durably and resolves `current()` as
|
|
10
|
+
* pending ?? lastUsed ?? live options.
|
|
11
|
+
*
|
|
12
|
+
* This module hands the foreign bridges (claude/codex/hermes/pi/agy/omp) the
|
|
13
|
+
* same semantics without mounting the projection registry:
|
|
14
|
+
*
|
|
15
|
+
* - {@link recordedModelSelection} — the native `modelSelection` projection
|
|
16
|
+
* fold, replayed as one pass over the session's events. A recorded
|
|
17
|
+
* `model/selection` is the pending choice and survives non-matching
|
|
18
|
+
* `request/header`s; a matching header retires it; `lastUsed` is the last
|
|
19
|
+
* header's config. (The adapters that DO mount `sessionProjections` —
|
|
20
|
+
* claude, codex — may keep reading the projection directly; both produce
|
|
21
|
+
* the same answer.)
|
|
22
|
+
* - {@link createAgentModelFace} — the face itself: `current()` resolves
|
|
23
|
+
* pending → logged header → deployment default → agent options (each level
|
|
24
|
+
* must name both halves), `select()` appends the same durable
|
|
25
|
+
* `model/selection` event as the native face, deduped against the recorded
|
|
26
|
+
* choice.
|
|
27
|
+
*
|
|
28
|
+
* Structural session face on the system-head pattern: the real (generic-typed)
|
|
29
|
+
* `Session` satisfies it without this package taking a dsh-agent dependency.
|
|
30
|
+
*
|
|
31
|
+
* @module superd-hub/agent-model-face
|
|
32
|
+
*/
|
|
33
|
+
/** A model selection: both route halves, with an optional explicit effort. */
|
|
34
|
+
export interface ModelSelectionLike {
|
|
35
|
+
provider: string;
|
|
36
|
+
model: string;
|
|
37
|
+
reasoningEffort?: unknown;
|
|
38
|
+
}
|
|
39
|
+
/** The `Agent.model` shape (dsh-agent runtime-types `Model`), over the bridge's own selection type. */
|
|
40
|
+
export interface BridgeModelFace<Sel extends ModelSelectionLike> {
|
|
41
|
+
current(): Readonly<Sel> | undefined;
|
|
42
|
+
select(selection: Readonly<Sel>): void;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The session slice the fold reads. Deliberately loose so the real
|
|
46
|
+
* (generic-typed) `Session` satisfies it; the events the fold inspects arrive
|
|
47
|
+
* as `unknown` data and are narrowed inside this module only.
|
|
48
|
+
*/
|
|
49
|
+
export interface ModelSessionFace {
|
|
50
|
+
snapshotEvents(): ReadonlyArray<{
|
|
51
|
+
type: string;
|
|
52
|
+
data: unknown;
|
|
53
|
+
}>;
|
|
54
|
+
requestHeader(): {
|
|
55
|
+
readonly config: {
|
|
56
|
+
readonly provider: string;
|
|
57
|
+
readonly model: string;
|
|
58
|
+
};
|
|
59
|
+
} | undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The native `modelSelection` projection fold over one session's events.
|
|
63
|
+
* @param events - the session's full event snapshot (order is the log order).
|
|
64
|
+
* @returns the recorded choice — a pending selection, else the last request
|
|
65
|
+
* header's config — or `null` when the log names neither.
|
|
66
|
+
*/
|
|
67
|
+
export declare function recordedModelSelection(events: ReadonlyArray<{
|
|
68
|
+
type: string;
|
|
69
|
+
data: unknown;
|
|
70
|
+
}>): ModelSelectionLike | null;
|
|
71
|
+
/** Readers the face resolves through; each adapter wires its own sources. */
|
|
72
|
+
export interface AgentModelFaceParts<Sel extends ModelSelectionLike> {
|
|
73
|
+
/** The session whose events and request header back the fold. */
|
|
74
|
+
session: ModelSessionFace;
|
|
75
|
+
/** The agent's live route options (the options fallback names both halves or neither). */
|
|
76
|
+
options: {
|
|
77
|
+
readonly provider?: string;
|
|
78
|
+
readonly model?: string;
|
|
79
|
+
};
|
|
80
|
+
/** Appends the durable `model/selection` event (the adapter's own typed append). */
|
|
81
|
+
appendSelection: (selection: Sel) => void;
|
|
82
|
+
/** Reads the deployment default (`ctx.agentDefaultModel`); absent when the service is not mounted. */
|
|
83
|
+
readDefault?: () => {
|
|
84
|
+
provider: string;
|
|
85
|
+
model: string;
|
|
86
|
+
} | undefined;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Build the `Agent.model` face for one foreign-runtime agent.
|
|
90
|
+
* @param parts - the session, options, typed append, and optional default reader.
|
|
91
|
+
* @returns the face; assign it to the agent's `model` member.
|
|
92
|
+
*/
|
|
93
|
+
export declare function createAgentModelFace<Sel extends ModelSelectionLike>(parts: AgentModelFaceParts<Sel>): BridgeModelFace<Sel>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-model-face.ts — the 0.2.2+ `Agent.model` face for foreign-runtime
|
|
3
|
+
* bridges.
|
|
4
|
+
*
|
|
5
|
+
* dsh 0.2.2 made `model` a required `Agent` member: the Web UI's model switcher
|
|
6
|
+
* reaches every agent — native and foreign alike — through
|
|
7
|
+
* `session/selectModel` → `agent.model.select()` (session-controller
|
|
8
|
+
* commands.ts). The native `AgentModel` (dsh-agent model-selection.ts) records
|
|
9
|
+
* a `model/selection` session event durably and resolves `current()` as
|
|
10
|
+
* pending ?? lastUsed ?? live options.
|
|
11
|
+
*
|
|
12
|
+
* This module hands the foreign bridges (claude/codex/hermes/pi/agy/omp) the
|
|
13
|
+
* same semantics without mounting the projection registry:
|
|
14
|
+
*
|
|
15
|
+
* - {@link recordedModelSelection} — the native `modelSelection` projection
|
|
16
|
+
* fold, replayed as one pass over the session's events. A recorded
|
|
17
|
+
* `model/selection` is the pending choice and survives non-matching
|
|
18
|
+
* `request/header`s; a matching header retires it; `lastUsed` is the last
|
|
19
|
+
* header's config. (The adapters that DO mount `sessionProjections` —
|
|
20
|
+
* claude, codex — may keep reading the projection directly; both produce
|
|
21
|
+
* the same answer.)
|
|
22
|
+
* - {@link createAgentModelFace} — the face itself: `current()` resolves
|
|
23
|
+
* pending → logged header → deployment default → agent options (each level
|
|
24
|
+
* must name both halves), `select()` appends the same durable
|
|
25
|
+
* `model/selection` event as the native face, deduped against the recorded
|
|
26
|
+
* choice.
|
|
27
|
+
*
|
|
28
|
+
* Structural session face on the system-head pattern: the real (generic-typed)
|
|
29
|
+
* `Session` satisfies it without this package taking a dsh-agent dependency.
|
|
30
|
+
*
|
|
31
|
+
* @module superd-hub/agent-model-face
|
|
32
|
+
*/
|
|
33
|
+
/** One recorded selection read structurally; incomplete routes answer `null`. */
|
|
34
|
+
function selectionOf(value) {
|
|
35
|
+
if (typeof value !== 'object' || value === null)
|
|
36
|
+
return null;
|
|
37
|
+
const { provider, model } = value;
|
|
38
|
+
if (typeof provider !== 'string' || provider === '' || typeof model !== 'string' || model === '')
|
|
39
|
+
return null;
|
|
40
|
+
const reasoningEffort = value.reasoningEffort;
|
|
41
|
+
return {
|
|
42
|
+
provider,
|
|
43
|
+
model,
|
|
44
|
+
...(typeof reasoningEffort === 'string' && reasoningEffort !== ''
|
|
45
|
+
? { reasoningEffort }
|
|
46
|
+
: {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Route equality without the effort, and full equality with it (the fold's own rules). */
|
|
50
|
+
function sameRoute(left, right) {
|
|
51
|
+
return left.provider === right.provider && left.model === right.model;
|
|
52
|
+
}
|
|
53
|
+
function sameSelection(left, right) {
|
|
54
|
+
return sameRoute(left, right) && left.reasoningEffort === right.reasoningEffort;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The native `modelSelection` projection fold over one session's events.
|
|
58
|
+
* @param events - the session's full event snapshot (order is the log order).
|
|
59
|
+
* @returns the recorded choice — a pending selection, else the last request
|
|
60
|
+
* header's config — or `null` when the log names neither.
|
|
61
|
+
*/
|
|
62
|
+
export function recordedModelSelection(events) {
|
|
63
|
+
let pending = null;
|
|
64
|
+
let lastUsed = null;
|
|
65
|
+
for (const event of events) {
|
|
66
|
+
if (event.type === 'model/selection') {
|
|
67
|
+
const selection = selectionOf(event.data);
|
|
68
|
+
if (selection !== null && !(pending !== null && sameSelection(pending, selection)))
|
|
69
|
+
pending = selection;
|
|
70
|
+
}
|
|
71
|
+
else if (event.type === 'request/header') {
|
|
72
|
+
const header = event.data;
|
|
73
|
+
const config = header === null || typeof header !== 'object' ? undefined : header.header?.config;
|
|
74
|
+
const selection = selectionOf(config);
|
|
75
|
+
if (selection !== null) {
|
|
76
|
+
lastUsed = selection;
|
|
77
|
+
if (pending !== null && sameRoute(pending, selection))
|
|
78
|
+
pending = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return pending ?? lastUsed;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Build the `Agent.model` face for one foreign-runtime agent.
|
|
86
|
+
* @param parts - the session, options, typed append, and optional default reader.
|
|
87
|
+
* @returns the face; assign it to the agent's `model` member.
|
|
88
|
+
*/
|
|
89
|
+
export function createAgentModelFace(parts) {
|
|
90
|
+
const recorded = () => recordedModelSelection(parts.session.snapshotEvents());
|
|
91
|
+
const current = () => {
|
|
92
|
+
const fold = recorded();
|
|
93
|
+
if (fold !== null)
|
|
94
|
+
return { ...fold };
|
|
95
|
+
const preferred = parts.readDefault?.();
|
|
96
|
+
if (preferred !== undefined && preferred.provider !== '' && preferred.model !== '') {
|
|
97
|
+
return { provider: preferred.provider, model: preferred.model };
|
|
98
|
+
}
|
|
99
|
+
const { provider, model } = parts.options;
|
|
100
|
+
return provider !== undefined && provider !== '' && model !== undefined && model !== ''
|
|
101
|
+
? { provider, model }
|
|
102
|
+
: undefined;
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
current,
|
|
106
|
+
select(selection) {
|
|
107
|
+
const fold = recorded();
|
|
108
|
+
if (fold !== null && sameSelection(fold, selection))
|
|
109
|
+
return;
|
|
110
|
+
parts.appendSelection({ ...selection });
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
@@ -27,8 +27,9 @@ export interface OneShotExecResult {
|
|
|
27
27
|
*/
|
|
28
28
|
outcome: 'exited' | 'signaled' | 'timeout' | 'spawn-error';
|
|
29
29
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/** 0.3.2-e: the 30K returned-tail window is REMOVED — output returns in full up to
|
|
31
|
+
* retention and model-context sizing is the native agent runtime's job (spill policy
|
|
32
|
+
* or its foreign equivalent). Retention below stays the memory-safety bound. */
|
|
32
33
|
/** Hub oneshot per-stream raw-byte retention: 4MiB, distinct from native/PTy UTF16 caps. */
|
|
33
34
|
export declare const EXEC_RING_HARD_CAP: number;
|
|
34
35
|
export declare const EXEC_DEFAULT_TIMEOUT_SEC = 120;
|
package/superd-hub/dist/exec.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// tool); it never decides policy — auth is the shared /api fence's, and the
|
|
6
6
|
// initiator-side per-machine knob lives on the caller's registry row.
|
|
7
7
|
//
|
|
8
|
-
// Output discipline (
|
|
8
|
+
// Output discipline (0.3.2-e): return output IN FULL up to the 4MiB retention ring;
|
|
9
9
|
// each stream, retain at most 4M per stream while running (the tail of a
|
|
10
10
|
// capped ring is still the tail of the stream), mark `truncated` whenever a
|
|
11
11
|
// stream's total exceeded what is returned. Kill discipline: SIGTERM to the
|
|
@@ -15,8 +15,9 @@ import { spawn } from 'node:child_process';
|
|
|
15
15
|
import { ByteTailRing } from '../../remote/plugin.js';
|
|
16
16
|
/** Shared-channel Fetch route path this API owns. */
|
|
17
17
|
export const EXEC_API_BASE = '/api/superd/exec';
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/** 0.3.2-e: the 30K returned-tail window is REMOVED — output returns in full up to
|
|
19
|
+
* retention and model-context sizing is the native agent runtime's job (spill policy
|
|
20
|
+
* or its foreign equivalent). Retention below stays the memory-safety bound. */
|
|
20
21
|
/** Hub oneshot per-stream raw-byte retention: 4MiB, distinct from native/PTy UTF16 caps. */
|
|
21
22
|
export const EXEC_RING_HARD_CAP = 4 * 1024 * 1024;
|
|
22
23
|
export const EXEC_DEFAULT_TIMEOUT_SEC = 120;
|
|
@@ -109,7 +110,7 @@ export async function runOneShotExec(input, log) {
|
|
|
109
110
|
clearTimeout(killTimer);
|
|
110
111
|
if (graceTimer !== undefined)
|
|
111
112
|
clearTimeout(graceTimer);
|
|
112
|
-
const outTail = stdout.tail(
|
|
113
|
+
const outTail = stdout.tail(), errTail = stderr.tail();
|
|
113
114
|
const outText = outTail.toString('utf8');
|
|
114
115
|
const errText = spawnError !== undefined
|
|
115
116
|
? `exec: spawn failed — ${spawnError}${input.cwd === undefined ? '' : ` (cwd: ${input.cwd})`}`
|
|
@@ -127,7 +128,7 @@ export async function runOneShotExec(input, log) {
|
|
|
127
128
|
stdout: outText,
|
|
128
129
|
stderr: errText,
|
|
129
130
|
durationMs: Date.now() - started,
|
|
130
|
-
truncated: stdout.total > outTail.length || stderr.total > errTail.length,
|
|
131
|
+
truncated: stdout.total > outTail.length || stderr.total > errTail.length, // 0.3.2-e: true 仅当 retention 驱逐(>4MiB)
|
|
131
132
|
outputStats: { stdout: stdout.stats(outTail.length), stderr: stderr.stats(errTail.length) },
|
|
132
133
|
timedOut,
|
|
133
134
|
outcome,
|
|
@@ -19,7 +19,7 @@ export interface HubToolExecFace {
|
|
|
19
19
|
pty(sessionKey: string, machine: string, cmd: string, opts?: {
|
|
20
20
|
timeoutSec?: number;
|
|
21
21
|
stdin?: string;
|
|
22
|
-
}): Promise<import('
|
|
22
|
+
}): Promise<import('../../remote/plugin.js').PtyDispatchResult>;
|
|
23
23
|
}
|
|
24
24
|
import type { A2aAddress, A2aInitiator, A2aService } from './a2a-service.js';
|
|
25
25
|
/** The service face the tool consumes (structural subset of T2/T3's A2aService). */
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
* - `exec` is the P4 placeholder leg: `"not yet implemented: exec (P4)"`.
|
|
37
37
|
*/
|
|
38
38
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
39
|
-
import { parseOneShotExecBody
|
|
40
|
-
import {
|
|
39
|
+
import { parseOneShotExecBody } from './exec.js';
|
|
40
|
+
import { disclose } from '../../remote/plugin.js';
|
|
41
41
|
// F2 (0.1.7-n, PC 0.1.7-m first-person): the wire/registry sentinel stays
|
|
42
42
|
// '[undefined]', but the DISPLAY surface must not show it to a human — D17
|
|
43
43
|
// already sanctions display-layer beautification.
|
|
@@ -190,7 +190,7 @@ const DESCRIPTION = 'SuperD Hub — agent-to-agent messaging across the Super D
|
|
|
190
190
|
' {action:"send", agent?, conversationId?, title?, message, delivery?} → open-or-resume: omit conversationId to OPEN (then agent = the recipient, REQUIRED; title optional; message is the first turn); pass conversationId to RESUME (agent optional — the hub derives the route; a wrong agent is refused). Receipt {outcome: delivered|accepted|failed, messageId, conversationId, sessionId, created, error?, note?}: `delivered` = end-to-end complete; `accepted` = the hub took it and the SAME in-flight request keeps running (its `delivered`/`undelivered` outcome arrives as a notice — do not resend); `failed` = immediate rejection.\n' +
|
|
191
191
|
' {action:"read"} → sweep messages buffered for this session while it was not live.\n' +
|
|
192
192
|
' {action:"stop", conversationId, agent?, scope?} → cancel the conversation\'s ACTIVE turn (the session owner is derived from the conversation id; agent is optional). `stopped:true` means this call cancelled a live turn; a turn that finished on its own is IDLE, never `stopped` — so stop answers stopped:false with a note when nothing was running.\n' +
|
|
193
|
-
' {action:"exec", cmd, machine?, cwd?, stdin?, timeoutSec?, mode?} → run one shell command on a remote machine (machine required; self/local execution disabled unless localExec is explicitly enabled). mode "oneshot" (default): {exit, stdout (
|
|
193
|
+
' {action:"exec", cmd, machine?, cwd?, stdin?, timeoutSec?, mode?} → run one shell command on a remote machine (machine required; self/local execution disabled unless localExec is explicitly enabled). mode "oneshot" (default): {exit, stdout (full up to 4MiB retention; beyond it the retained tail plus outputStats), stderr, durationMs, truncated (retention eviction only), timedOut, outcome}; cwd sets its working directory. String stdin is piped and closed in oneshot; in PTY it is written immediately after cmd (REPL input, not script mode). Hub oneshot hard retention is 4MiB bytes per stream, PTY is 4,000,000 UTF-16 code units. outputStats reports real total/retained/dropped/returned and units; discarded head cannot be recovered by restarting. mode "pty": a PERSISTENT shell per (your session, machine) — the shell OWNS its cwd and env and they SURVIVE across dispatches (cd once inside the command, then keep working); {output (full up to 4M UTF-16 units; beyond it the retained tail plus outputStats), exit, cwd, timedOut, reconnected, durationMs}. cwd is oneshot-only: passing it with mode "pty" is refused (use `cd <dir> && …` inside the command). Prefer oneshot for independent commands; pty when shell state matters.\n' +
|
|
194
194
|
'Addresses are "<m4>-<a6>" (machine-agent); call agents first to list valid ids. delivery: "queue" (default) is injected into the peer\'s session as a waking turn — it never interrupts an active turn, it queues behind it; "steer" submits steering for the peer\'s nearest step boundary — use it only when urgency demands. NEVER poll for a reply: the peer\'s reply arrives in this conversation as a new message WHEN THE PEER SENDS one (peers that never use the channel get their last output at turn end delivered as their reply instead, form=transcript); a turn that ends with no channel artifact yields one notice whose reason= distinguishes peer-silent (ended, said nothing) / peer-error (stream died or turn aborted) / stopped-by-initiator (you stopped it). read only sweeps what was buffered while this session was down. stop is a hard stop of the peer\'s active turn (the UI stop button); scope "session" degrades to turn-only — no session-close exists upstream — and the conversation stays resumable: send again to resume it. agent_status reads a cache refreshed by the 5-minute sweep (collectedAt says when); the window is alive-first, ≤5 rows, newest first, and not being in it implies nothing; a `note` says why when the window has no collection behind it (the last collection failed, or none has happened yet). MESSAGING HAS NO STATUS QUERY: to check whether a peer is idle or running, call agent_status (a 5-minute cache — never a delivery receipt); if that is not enough and you cannot wait, send another message.';
|
|
195
195
|
/**
|
|
196
196
|
* Build the one SuperD Hub tool.
|
|
@@ -284,8 +284,8 @@ export function createSuperdHubTool(deps) {
|
|
|
284
284
|
count: { type: 'integer' },
|
|
285
285
|
messages: { type: 'json', description: 'read sweep: buffered messages with from as "<m4>-<a6>"' },
|
|
286
286
|
exit: { type: 'json', description: 'exec: the command exit code (null when killed by timeout/signal)' },
|
|
287
|
-
stdout: { type: 'json', description: 'exec:
|
|
288
|
-
stderr: { type: 'json', description: 'exec:
|
|
287
|
+
stdout: { type: 'json', description: 'exec: full stdout up to retention (4MiB bytes; PTY 4,000,000 UTF-16 units)' },
|
|
288
|
+
stderr: { type: 'json', description: 'exec: full stderr up to retention (4MiB bytes)' },
|
|
289
289
|
durationMs: { type: 'integer', description: 'exec: wall-clock duration' },
|
|
290
290
|
outputStats: { type: 'json', description: 'exec per-stream counters with explicit bytes/utf16 units; total, retained, dropped head, returned payload (excludes banners), retentionLimit. Absent on older peers: exact metrics unavailable.' },
|
|
291
291
|
truncated: { type: 'boolean', description: 'exec: a stream\'s total output exceeded the returned tail window' },
|
|
@@ -422,7 +422,7 @@ export function createSuperdHubTool(deps) {
|
|
|
422
422
|
const machine = args.machine ?? deps.exec.selfM4();
|
|
423
423
|
try {
|
|
424
424
|
const r = await deps.exec.pty(sessionKey, machine, args.cmd, { ...(args.timeoutSec === undefined ? {} : { timeoutSec: args.timeoutSec }), ...(args.stdin === undefined ? {} : { stdin: args.stdin }) });
|
|
425
|
-
const out =
|
|
425
|
+
const out = disclose(r.output, r.outputStats?.stdout); // 0.3.2-e: 全量返回,仅 retention 驱逐打 banner
|
|
426
426
|
return {
|
|
427
427
|
action,
|
|
428
428
|
mode: 'pty',
|
|
@@ -36,6 +36,7 @@ export { createOwnershipIndex, type OwnershipIndex, type OwnershipRow } from './
|
|
|
36
36
|
export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf, type HostMount } from './world-host.js';
|
|
37
37
|
export { worldMountPatches, superdTrustedHosts, WORLD_WAVE1_DISABLED_ROWS, WORLD_WAVE2_DISABLED_ROWS, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, type WorldPatchLayer, } from './world-mount.js';
|
|
38
38
|
export { withSystemHead, type HeadAwareSessionFace, type HeadPlaceholder, type WithSystemHead } from './system-head.js';
|
|
39
|
+
export { createAgentModelFace, recordedModelSelection, type AgentModelFaceParts, type BridgeModelFace, type ModelSelectionLike, type ModelSessionFace, } from './agent-model-face.js';
|
|
39
40
|
export { default as worldApply, name as worldPluginName, type Config as WorldEntryConfig } from './world-entry.js';
|
|
40
41
|
export { default as joinApply, name as worldJoinPluginName, type Config as WorldJoinConfig } from './world-join.js';
|
|
41
42
|
export { createA2aService, A2A_MESSAGE_TEMPLATE, A2A_UNTITLED, A2A_BUFFER_MAX, A2A_NO_SESSION_CLOSE_REASON, A2A_NO_TURN_ACTIVE_NOTE, renderA2aEnvelope, loadConversations, saveConversations, shortIdFor, isDegenerateShortId, migrateConversations, type A2aService, subscribeSessionCompletion, sessionRpc, turnLivenessOf, addressErrorBody, type A2aSessionSource, type A2aGateway, type A2aAddress, type A2aInitiator, type A2aDelivery, type A2aSendRequest, type A2aReceipt, type A2aReceiptOutcome, type A2aReceiptError, type A2aConversationRef, type A2aCompletion, type A2aCompletionCb, type A2aErrorCb, type A2aSubscribeOptions, type A2aStopScope, type A2aStopResult, type A2aBufferedMessage, type A2aBufferInput, type ConversationRow, type ConversationsFile, } from './a2a-service.js';
|
package/superd-hub/dist/index.js
CHANGED
|
@@ -36,6 +36,7 @@ export { createOwnershipIndex } from './ownership.js';
|
|
|
36
36
|
export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf } from './world-host.js';
|
|
37
37
|
export { worldMountPatches, superdTrustedHosts, WORLD_WAVE1_DISABLED_ROWS, WORLD_WAVE2_DISABLED_ROWS, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, } from './world-mount.js';
|
|
38
38
|
export { withSystemHead } from './system-head.js';
|
|
39
|
+
export { createAgentModelFace, recordedModelSelection, } from './agent-model-face.js';
|
|
39
40
|
export { default as worldApply, name as worldPluginName } from './world-entry.js';
|
|
40
41
|
export { default as joinApply, name as worldJoinPluginName } from './world-join.js';
|
|
41
42
|
export { createA2aService, A2A_MESSAGE_TEMPLATE, A2A_UNTITLED, A2A_BUFFER_MAX, A2A_NO_SESSION_CLOSE_REASON, A2A_NO_TURN_ACTIVE_NOTE, renderA2aEnvelope, loadConversations, saveConversations, shortIdFor, isDegenerateShortId, migrateConversations,
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
import { createRemoteFileEndpoint } from './remote-file-rpc.js';
|
|
2
2
|
import { startRemoteProcess } from './remote-job-client.js';
|
|
3
|
+
/** Hub transport implementation. HTTP/WS/pool mechanics stay in the existing exec modules. */
|
|
4
|
+
import { PtySession, buildHubPtyArgv, hubPtyAssets } from '../../remote/plugin.js';
|
|
5
|
+
// ws 模块绝对路径:在 superd-hub 上下文解析(分叉的 proven 链——闭包/泵子树解析不到,
|
|
6
|
+
// 0.3.2-c 定稿:按名解析只在 transport 侧做一次,经 ptyEndpoint/argv 下传)。
|
|
7
|
+
const wsModulePath = () => {
|
|
8
|
+
try {
|
|
9
|
+
return createRequire(import.meta.url).resolve('ws');
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
throw new Error(`[E_HUB_PTY_SETUP] superd-hub: the ws module could not be resolved from the hub tree — hub PTY spawns a local pump child that opens the peer WebSocket with ws; ${error instanceof Error ? error.message : String(error)}`);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
import { createRequire } from 'node:module';
|
|
3
17
|
import { ensureSelf, listRemotes } from './machines.js';
|
|
4
18
|
import { cookieOf } from './mesh-link.js';
|
|
5
19
|
import { runOneShotExec, postOneShotExec, parseOneShotExecBody } from './exec.js';
|
|
6
|
-
import { PtyPool } from './exec-pty-session.js';
|
|
7
|
-
import { LocalScriptChannel, WsPtyChannel } from './exec-pty-channel.js';
|
|
8
20
|
import { PTY_EXEC_PATH } from './exec-pty-rpc.js';
|
|
9
21
|
/** One shared initiator implementation for legacy tool and the optional remote service. */
|
|
10
22
|
export function createHubExec(home, log) {
|
|
23
|
+
// 0.3.2-c(Q2):分叉 exec-pty-session 已删——本 face 与 remote 工具共用 canonical
|
|
24
|
+
// PtySession(闭包导出)。per-owner 每机器一个持久 shell(A2A 语义保持:cwd/env 跨调用存活;
|
|
25
|
+
// dead → dispatch 内部重连 = 旧 markDead/重启;disposed → 下次调用重建)。
|
|
11
26
|
const owners = new Map();
|
|
12
27
|
let closed = false;
|
|
13
28
|
const assertOpen = () => { if (closed)
|
|
@@ -21,6 +36,16 @@ export function createHubExec(home, log) {
|
|
|
21
36
|
throw new Error(`exec is disabled for machine "${machine}" on this registry row (enabled/execEnabled=false)`);
|
|
22
37
|
return row;
|
|
23
38
|
};
|
|
39
|
+
const genCommandId = () => `remote-${randomUUID().replace(/-/g, '').slice(0, 16)}`;
|
|
40
|
+
const createPtySession = (machine, row, owner) => new PtySession({
|
|
41
|
+
key: `${owner}|hub-pty:${machine}`,
|
|
42
|
+
argv: row === undefined
|
|
43
|
+
? ['script', '-qfec', 'bash', '/dev/null']
|
|
44
|
+
: buildHubPtyArgv({ url: `${row.origin.replace(/^http/, 'ws')}${PTY_EXEC_PATH}`, cookie: cookieOf(row.id, home) ?? '', wsPath: wsModulePath() }, hubPtyAssets()),
|
|
45
|
+
id: `p-${randomUUID().slice(0, 8)}`,
|
|
46
|
+
connection: row === undefined ? 'local' : `hub:${machine}`,
|
|
47
|
+
idleTtlSec: 900,
|
|
48
|
+
});
|
|
24
49
|
const face = {
|
|
25
50
|
selfM4: () => ensureSelf(home, 'local').id,
|
|
26
51
|
run: input => { assertOpen(); return runOneShotExec(input, log); },
|
|
@@ -37,14 +62,34 @@ export function createHubExec(home, log) {
|
|
|
37
62
|
const row = machine === face.selfM4() ? undefined : remote(machine);
|
|
38
63
|
let pool = owners.get(owner);
|
|
39
64
|
if (!pool)
|
|
40
|
-
owners.set(owner, pool = new
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
owners.set(owner, pool = new Map());
|
|
66
|
+
const attempt = async (session) => {
|
|
67
|
+
const r = await session.dispatch(cmd, { stdin: opts.stdin, timeoutSec: opts.timeoutSec, commandId: genCommandId() });
|
|
68
|
+
// P4 handoff(超时不杀)对 A2A 面保持旧语义:显式两段 ^C 后等帧结算——分叉自动中断的等价物。
|
|
69
|
+
if (r.running === true && r.job !== undefined) {
|
|
70
|
+
r.job.interrupt();
|
|
71
|
+
const settled = await r.job.done;
|
|
72
|
+
return { output: settled.output, outputStats: settled.outputStats, exit: settled.exit, cwd: settled.cwd, timedOut: true, reconnected: r.reconnected, durationMs: r.durationMs, commandId: r.commandId };
|
|
73
|
+
}
|
|
74
|
+
return r;
|
|
75
|
+
};
|
|
76
|
+
const settle = (result) => {
|
|
77
|
+
log?.('info', 'exec.pty.run', { machine, cmd: cmd.slice(0, 120), exit: result.exit ?? 'none', durationMs: result.durationMs });
|
|
78
|
+
return result;
|
|
79
|
+
};
|
|
80
|
+
const existing = pool.get(machine);
|
|
81
|
+
if (existing !== undefined && existing.sessionState !== 'disposed')
|
|
82
|
+
return settle(await attempt(existing));
|
|
83
|
+
const session = createPtySession(machine, row, owner);
|
|
84
|
+
pool.set(machine, session);
|
|
85
|
+
try {
|
|
86
|
+
return settle(await attempt(session));
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
if (session.sessionState === 'disposed' && pool.get(machine) === session)
|
|
90
|
+
pool.delete(machine); // init 失败/auto-dispose 不留死项
|
|
91
|
+
throw err;
|
|
92
|
+
}
|
|
48
93
|
},
|
|
49
94
|
};
|
|
50
95
|
return {
|
|
@@ -52,12 +97,11 @@ export function createHubExec(home, log) {
|
|
|
52
97
|
async releaseOwner(owner) {
|
|
53
98
|
const pool = owners.get(owner);
|
|
54
99
|
if (pool) {
|
|
55
|
-
await pool.
|
|
56
|
-
|
|
57
|
-
owners.delete(owner);
|
|
100
|
+
await Promise.all([...pool.values()].map(s => s.dispose()));
|
|
101
|
+
owners.delete(owner);
|
|
58
102
|
}
|
|
59
103
|
},
|
|
60
|
-
async dispose() { closed = true; await Promise.all([...owners.values()].
|
|
104
|
+
async dispose() { closed = true; await Promise.all([...owners.values()].flatMap(p => [...p.values()].map(s => s.dispose()))); owners.clear(); },
|
|
61
105
|
};
|
|
62
106
|
}
|
|
63
107
|
export function createHubRemoteTransport(home, log) {
|
|
@@ -67,25 +111,10 @@ export function createHubRemoteTransport(home, log) {
|
|
|
67
111
|
const fileAbort = new AbortController();
|
|
68
112
|
const files = new Set();
|
|
69
113
|
const processes = new Map();
|
|
70
|
-
const pending = new Map();
|
|
71
114
|
const requireRemote = (machine) => {
|
|
72
115
|
if (!machine || machine === exec.face.selfM4())
|
|
73
116
|
throw new Error('Select a remote machine from the Hub roster; local execution is not available through remote');
|
|
74
117
|
};
|
|
75
|
-
const track = async (owner, work) => {
|
|
76
|
-
let set = pending.get(owner);
|
|
77
|
-
if (!set)
|
|
78
|
-
pending.set(owner, set = new Set());
|
|
79
|
-
set.add(work);
|
|
80
|
-
try {
|
|
81
|
-
return await work;
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
set.delete(work);
|
|
85
|
-
if (!set.size)
|
|
86
|
-
pending.delete(owner);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
118
|
return {
|
|
90
119
|
protocol: 1,
|
|
91
120
|
listTargets: () => listRemotes(home).filter(r => r.id !== exec.face.selfM4()).map(r => ({ id: r.id, label: r.name, execEnabled: r.enabled && r.execEnabled !== false })),
|
|
@@ -110,12 +139,17 @@ export function createHubRemoteTransport(home, log) {
|
|
|
110
139
|
processes.delete(owner); });
|
|
111
140
|
return process;
|
|
112
141
|
},
|
|
113
|
-
|
|
142
|
+
ptyEndpoint(machine) {
|
|
114
143
|
requireRemote(machine);
|
|
115
|
-
if (closed
|
|
116
|
-
throw new Error('Hub remote
|
|
117
|
-
const
|
|
118
|
-
|
|
144
|
+
if (closed)
|
|
145
|
+
throw new Error('Hub remote provider disposed');
|
|
146
|
+
const row = listRemotes(home).find(r => r.id === machine);
|
|
147
|
+
if (!row)
|
|
148
|
+
throw new Error(`unknown machine "${machine}"`);
|
|
149
|
+
if (!row.enabled || row.execEnabled === false)
|
|
150
|
+
throw new Error(`exec is disabled for machine "${machine}"`);
|
|
151
|
+
// P10:创建与每次 dispatch 都重取本端点(driver 侧调用)——pairing 复用不豁免。
|
|
152
|
+
return { url: `${row.origin.replace(/^http/, 'ws')}${PTY_EXEC_PATH}`, cookie: cookieOf(row.id, home) ?? '', wsPath: wsModulePath() };
|
|
119
153
|
},
|
|
120
154
|
fileEndpoint(machine) {
|
|
121
155
|
requireRemote(machine);
|
|
@@ -135,7 +169,7 @@ export function createHubRemoteTransport(home, log) {
|
|
|
135
169
|
const owned = [...processes.get(owner) ?? []];
|
|
136
170
|
for (const process of owned)
|
|
137
171
|
process.cancel('owner released');
|
|
138
|
-
await Promise.all([exec.releaseOwner(owner), ...owned.map(p => p.done)
|
|
172
|
+
await Promise.all([exec.releaseOwner(owner), ...owned.map(p => p.done)]);
|
|
139
173
|
},
|
|
140
174
|
async dispose() {
|
|
141
175
|
closed = true;
|
|
@@ -145,7 +179,7 @@ export function createHubRemoteTransport(home, log) {
|
|
|
145
179
|
for (const process of owned)
|
|
146
180
|
process.cancel('provider disposed');
|
|
147
181
|
await exec.dispose();
|
|
148
|
-
await Promise.allSettled([...owned.map(p => p.done)
|
|
182
|
+
await Promise.allSettled([...owned.map(p => p.done)]);
|
|
149
183
|
},
|
|
150
184
|
};
|
|
151
185
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { modelSelectionProjectionDefinition } from '@deepseek-ai/dsh-agent';
|
|
1
2
|
import { noteWorldServer, publishWorldFetch, takeHostMount } from './world-host.js';
|
|
2
3
|
import { WorldWebServer } from './world-web-server.js';
|
|
3
4
|
import { superdTrustedHosts } from './world-mount.js';
|
|
@@ -43,6 +44,35 @@ export function apply(ctx, config) {
|
|
|
43
44
|
dropWorldFetch = publishWorldFetch(key, connection.createSharedFetchHandler('/api'), mount);
|
|
44
45
|
}
|
|
45
46
|
});
|
|
47
|
+
// 0.2.2: register the `modelSelection` projection in THIS world's registry.
|
|
48
|
+
// Upstream's only registrant is the agent-loop row (agent-loop/src:334),
|
|
49
|
+
// which wave-1 minimization keeps out of world trees — without it the
|
|
50
|
+
// native model seat's projection frame never resolves and the trigger
|
|
51
|
+
// label stays "loading models…" forever (the catalog and selectModel RPC
|
|
52
|
+
// take different paths, which is why they keep working). The definition is
|
|
53
|
+
// a pure event fold (`model/selection` + `request/header`), no loop
|
|
54
|
+
// machinery; register() is ref-counted and duplicate-tolerant, so a future
|
|
55
|
+
// tree that also mounts agent-loop shares the registration cleanly.
|
|
56
|
+
ctx.inject(['sessionProjections'], (projCtx) => {
|
|
57
|
+
const projections = projCtx.get('sessionProjections');
|
|
58
|
+
if (projections === undefined)
|
|
59
|
+
return;
|
|
60
|
+
let disposed = false;
|
|
61
|
+
let handle;
|
|
62
|
+
void projections.register(modelSelectionProjectionDefinition).then((registered) => {
|
|
63
|
+
if (disposed) {
|
|
64
|
+
void registered.dispose();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
handle = registered;
|
|
68
|
+
}, (error) => {
|
|
69
|
+
ctx.logger?.warn?.(`superd-world-mount: modelSelection projection registration failed: ${String(error)}`);
|
|
70
|
+
});
|
|
71
|
+
return () => {
|
|
72
|
+
disposed = true;
|
|
73
|
+
void handle?.dispose();
|
|
74
|
+
};
|
|
75
|
+
});
|
|
46
76
|
ctx.effect(() => () => {
|
|
47
77
|
dropWorldFetch?.();
|
|
48
78
|
}, 'superd-world-mount: virtual webServer');
|