super-dsh 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/README.md +9 -8
  2. package/agent-agy/cordis.patch.yml +8 -4
  3. package/agent-agy/dist/agent-preset-agy.d.ts +23 -43
  4. package/agent-agy/dist/agent-preset-agy.js +29 -166
  5. package/agent-agy/dist/agent.d.ts +2 -1
  6. package/agent-agy/dist/agent.js +3 -2
  7. package/agent-agy/dist/agy-sessions.d.ts +2 -1
  8. package/agent-agy/dist/agy-sessions.js +2 -11
  9. package/agent-agy/dist/index.js +13 -12
  10. package/agent-agy/dist/world-plugin.d.ts +2 -1
  11. package/agent-agy/dist/world-plugin.js +16 -21
  12. package/agent-agy/package.json +6 -6
  13. package/agent-claude/cordis.patch.yml +8 -4
  14. package/agent-claude/dist/agent-preset-claude.js +29 -166
  15. package/agent-claude/dist/agent.js +10 -10
  16. package/agent-claude/dist/claude-client.js +22 -3
  17. package/agent-claude/dist/claude-home.js +0 -8
  18. package/agent-claude/dist/index.js +12 -15
  19. package/agent-claude/dist/session-map.js +2 -5
  20. package/agent-claude/dist/world-plugin.js +3 -3
  21. package/agent-claude/package.json +9 -9
  22. package/agent-codex/README.md +8 -8
  23. package/agent-codex/cordis.patch.yml +9 -5
  24. package/agent-codex/dist/agent-preset-codex.js +29 -168
  25. package/agent-codex/dist/agent.js +17 -12
  26. package/agent-codex/dist/codex-store.js +0 -9
  27. package/agent-codex/dist/index.js +13 -16
  28. package/agent-codex/dist/session-map.js +2 -5
  29. package/agent-codex/dist/world-plugin.js +15 -20
  30. package/agent-codex/package.json +14 -11
  31. package/agent-hermes/README.md +9 -8
  32. package/agent-hermes/cordis.patch.yml +8 -4
  33. package/agent-hermes/dist/agent-preset-hermes.js +29 -168
  34. package/agent-hermes/dist/agent.js +33 -19
  35. package/agent-hermes/dist/hermes-client.js +5 -5
  36. package/agent-hermes/dist/hermes-store.js +2 -11
  37. package/agent-hermes/dist/index.js +13 -12
  38. package/agent-hermes/dist/world-plugin.js +16 -21
  39. package/agent-hermes/package.json +14 -11
  40. package/agent-omp/README.md +2 -2
  41. package/agent-omp/cordis.patch.yml +12 -7
  42. package/agent-omp/dist/agent-preset-omp.js +30 -169
  43. package/agent-omp/dist/agent.js +13 -5
  44. package/agent-omp/dist/index.js +12 -22
  45. package/agent-omp/dist/mobile/gesture.js +2 -2
  46. package/agent-omp/dist/mobile/zoom-guard.js +4 -4
  47. package/agent-omp/dist/mobile-boot.js +2 -2
  48. package/agent-omp/dist/omp-cli.js +1 -1
  49. package/agent-omp/dist/omp-disk-discovery.js +1 -1
  50. package/agent-omp/dist/protocol.js +1 -1
  51. package/agent-omp/dist/sidecar-client.js +1 -1
  52. package/agent-omp/dist/world-plugin.js +22 -19
  53. package/agent-omp/package.json +18 -14
  54. package/agent-omp/sidecar/main.ts +2 -2
  55. package/agent-pi/README.md +5 -5
  56. package/agent-pi/cordis.patch.yml +8 -4
  57. package/agent-pi/dist/agent-preset-pi.js +29 -166
  58. package/agent-pi/dist/agent.js +18 -12
  59. package/agent-pi/dist/index.js +14 -16
  60. package/agent-pi/dist/pi-home.js +2 -12
  61. package/agent-pi/dist/world-plugin.js +16 -21
  62. package/agent-pi/package.json +14 -11
  63. package/cordis.patch.yml +31 -21
  64. package/package.json +30 -38
  65. package/{agent-hub → superd-hub}/cordis.patch.yml +3 -3
  66. package/{agent-hub → superd-hub}/dist/carrier.d.ts +9 -3
  67. package/{agent-hub → superd-hub}/dist/carrier.js +29 -3
  68. package/{agent-hub → superd-hub}/dist/gateway.d.ts +2 -2
  69. package/{agent-hub → superd-hub}/dist/gateway.js +1 -1
  70. package/{agent-hub → superd-hub}/dist/index.d.ts +5 -3
  71. package/{agent-hub → superd-hub}/dist/index.js +5 -3
  72. package/{agent-hub → superd-hub}/dist/roster.d.ts +2 -2
  73. package/{agent-hub → superd-hub}/dist/rpc.js +1 -1
  74. package/{agent-hub → superd-hub}/dist/spawn-world.d.ts +27 -6
  75. package/superd-hub/dist/spawn-world.js +277 -0
  76. package/superd-hub/dist/system-head.d.ts +69 -0
  77. package/superd-hub/dist/system-head.js +194 -0
  78. package/superd-hub/dist/web-trust.d.ts +112 -0
  79. package/superd-hub/dist/web-trust.js +126 -0
  80. package/{agent-hub → superd-hub}/dist/world-entry.d.ts +2 -2
  81. package/{agent-hub → superd-hub}/dist/world-entry.js +4 -4
  82. package/{agent-hub → superd-hub}/dist/world-host.d.ts +1 -1
  83. package/{agent-hub → superd-hub}/dist/world-join.d.ts +2 -1
  84. package/{agent-hub → superd-hub}/dist/world-join.js +16 -21
  85. package/{agent-hub → superd-hub}/dist/world-mount.d.ts +14 -6
  86. package/{agent-hub → superd-hub}/dist/world-mount.js +28 -7
  87. package/{agent-hub → superd-hub}/dist/world-mux.d.ts +21 -6
  88. package/superd-hub/dist/world-mux.js +392 -0
  89. package/{agent-hub → superd-hub}/dist/world-provision.d.ts +20 -4
  90. package/superd-hub/dist/world-provision.js +196 -0
  91. package/{agent-hub → superd-hub}/dist/world-web-server.js +7 -1
  92. package/{agent-hub → superd-hub}/package.json +9 -16
  93. package/agent-hub/dist/spawn-world.js +0 -72
  94. package/agent-hub/dist/world-mux.js +0 -224
  95. package/agent-hub/dist/world-provision.js +0 -122
  96. package/agent-hub/lib/client/index.js +0 -242
  97. package/client/index.js +0 -242
  98. /package/{agent-hub → superd-hub}/dist/agent-roster.d.ts +0 -0
  99. /package/{agent-hub → superd-hub}/dist/agent-roster.js +0 -0
  100. /package/{agent-hub → superd-hub}/dist/client-shim.d.ts +0 -0
  101. /package/{agent-hub → superd-hub}/dist/client-shim.js +0 -0
  102. /package/{agent-hub → superd-hub}/dist/envelope.d.ts +0 -0
  103. /package/{agent-hub → superd-hub}/dist/envelope.js +0 -0
  104. /package/{agent-hub → superd-hub}/dist/index-pass.d.ts +0 -0
  105. /package/{agent-hub → superd-hub}/dist/index-pass.js +0 -0
  106. /package/{agent-hub → superd-hub}/dist/labels.d.ts +0 -0
  107. /package/{agent-hub → superd-hub}/dist/labels.js +0 -0
  108. /package/{agent-hub → superd-hub}/dist/ownership.d.ts +0 -0
  109. /package/{agent-hub → superd-hub}/dist/ownership.js +0 -0
  110. /package/{agent-hub → superd-hub}/dist/roster.js +0 -0
  111. /package/{agent-hub → superd-hub}/dist/rpc.d.ts +0 -0
  112. /package/{agent-hub → superd-hub}/dist/targets.d.ts +0 -0
  113. /package/{agent-hub → superd-hub}/dist/targets.js +0 -0
  114. /package/{agent-hub → superd-hub}/dist/world-host.js +0 -0
  115. /package/{agent-hub → superd-hub}/dist/world-web-server.d.ts +0 -0
@@ -18,18 +18,23 @@
18
18
  * overrides (`vendor/include/src/index.ts:77-121`).
19
19
  */
20
20
  /** Id of the inserted world-mount row. */
21
- export declare const WORLD_MOUNT_ROW_ID = "aw-world-mount";
21
+ export declare const WORLD_MOUNT_ROW_ID = "superd-world-mount";
22
22
  /** Plugin name providing the world's virtual webServer. */
23
- export declare const WORLD_MOUNT_PLUGIN = "@pgmi-builds/agent-hub/world";
23
+ export declare const WORLD_MOUNT_PLUGIN = "@pgmi-builds/superd-hub/world";
24
24
  /** Id of the inserted hub row (the same plugin ctx0 itself composes). */
25
- export declare const WORLD_HUB_ROW_ID = "aw-agent-hub";
25
+ export declare const WORLD_HUB_ROW_ID = "superd-hub";
26
26
  /** Plugin name of the hub itself — one UI, one roster, every runtime. */
27
- export declare const WORLD_HUB_PLUGIN = "@pgmi-builds/agent-hub";
27
+ export declare const WORLD_HUB_PLUGIN = "@pgmi-builds/superd-hub";
28
+ /** Id of the inserted page-trust row (world trees; ctx0 composes its own). */
29
+ export declare const WORLD_WEB_TRUST_ROW_ID = "superd-web-trust";
30
+ /** Page-trust component subpath — world pages emit index-inject on their own root. */
31
+ export declare const WORLD_WEB_TRUST_PLUGIN = "@pgmi-builds/superd-hub/web-trust";
28
32
  /** One patch layer understood by the loader (structural; no app-boot import). */
29
33
  export type WorldPatchLayer = {
30
34
  id: string;
31
35
  disabled?: boolean;
32
36
  inject?: readonly string[];
37
+ config?: Record<string, unknown>;
33
38
  } | {
34
39
  insert: ReadonlyArray<{
35
40
  id: string;
@@ -43,9 +48,12 @@ export type WorldPatchLayer = {
43
48
  * 1. `webserver` off — the world owns no socket (the hub serves its face);
44
49
  * 2. `modules` waits for the virtual `webServer` the entry row provides;
45
50
  * 3. the entry row itself (the world's mount identity);
46
- * 4. the HUB ROW — the same `@pgmi-builds/agent-hub` native ctx0 composes, so
51
+ * 4. the HUB ROW — the same `@pgmi-builds/superd-hub` native ctx0 composes, so
47
52
  * the foreign agent gets the *identical* selector UI, and the roster it
48
- * renders is read from the hub over the world's own `/api` channel.
53
+ * renders is read from the hub over the world's own `/api` channel;
54
+ * 5. the PAGE-TRUST row — the same component ctx0 composes through the
55
+ * composite bundle, so a trusted non-loopback page (Caddy domain / LAN
56
+ * IP) keeps its loopback-equivalent verdict in the WORLD's UI too.
49
57
  * @param key - runtime key (`omp`, `codex`, …) — the mount label.
50
58
  * @returns patch layers to append to the world's composition.
51
59
  */
@@ -18,22 +18,29 @@
18
18
  * overrides (`vendor/include/src/index.ts:77-121`).
19
19
  */
20
20
  /** Id of the inserted world-mount row. */
21
- export const WORLD_MOUNT_ROW_ID = 'aw-world-mount';
21
+ export const WORLD_MOUNT_ROW_ID = 'superd-world-mount';
22
22
  /** Plugin name providing the world's virtual webServer. */
23
- export const WORLD_MOUNT_PLUGIN = '@pgmi-builds/agent-hub/world';
23
+ export const WORLD_MOUNT_PLUGIN = '@pgmi-builds/superd-hub/world';
24
24
  /** Id of the inserted hub row (the same plugin ctx0 itself composes). */
25
- export const WORLD_HUB_ROW_ID = 'aw-agent-hub';
25
+ export const WORLD_HUB_ROW_ID = 'superd-hub';
26
26
  /** Plugin name of the hub itself — one UI, one roster, every runtime. */
27
- export const WORLD_HUB_PLUGIN = '@pgmi-builds/agent-hub';
27
+ export const WORLD_HUB_PLUGIN = '@pgmi-builds/superd-hub';
28
+ /** Id of the inserted page-trust row (world trees; ctx0 composes its own). */
29
+ export const WORLD_WEB_TRUST_ROW_ID = 'superd-web-trust';
30
+ /** Page-trust component subpath — world pages emit index-inject on their own root. */
31
+ export const WORLD_WEB_TRUST_PLUGIN = '@pgmi-builds/superd-hub/web-trust';
28
32
  /**
29
33
  * The layers that turn a listener-owning world into a mounted one:
30
34
  *
31
35
  * 1. `webserver` off — the world owns no socket (the hub serves its face);
32
36
  * 2. `modules` waits for the virtual `webServer` the entry row provides;
33
37
  * 3. the entry row itself (the world's mount identity);
34
- * 4. the HUB ROW — the same `@pgmi-builds/agent-hub` native ctx0 composes, so
38
+ * 4. the HUB ROW — the same `@pgmi-builds/superd-hub` native ctx0 composes, so
35
39
  * the foreign agent gets the *identical* selector UI, and the roster it
36
- * renders is read from the hub over the world's own `/api` channel.
40
+ * renders is read from the hub over the world's own `/api` channel;
41
+ * 5. the PAGE-TRUST row — the same component ctx0 composes through the
42
+ * composite bundle, so a trusted non-loopback page (Caddy domain / LAN
43
+ * IP) keeps its loopback-equivalent verdict in the WORLD's UI too.
37
44
  * @param key - runtime key (`omp`, `codex`, …) — the mount label.
38
45
  * @returns patch layers to append to the world's composition.
39
46
  */
@@ -42,11 +49,25 @@ export function worldMountPatches(key) {
42
49
  throw new Error(`world-mount: invalid key ${JSON.stringify(key)}`);
43
50
  }
44
51
  return [
45
- { id: 'webserver', disabled: true },
52
+ // 0.1.7 belt-and-braces: the composition provably carries `disabled: true`
53
+ // (SUPERD_TRACE dump), yet the live world audit still saw the row activate
54
+ // and bind the default 3080 — so pin the config to an EPHEMERAL loopback
55
+ // port too (the audited AW fixture shape: loopback-only, never
56
+ // user-facing). Honored disables bind nothing; ignored disables bind a
57
+ // throwaway socket instead of colliding with a real deployment port.
58
+ { id: 'webserver', disabled: true, config: { host: '127.0.0.1', port: 0 } },
59
+ // 0.1.7: with a profileContext provided (settings requires it), the base
60
+ // hmr row ENABLES itself in world trees (`disabled: !!js
61
+ // !ctx.get('profileContext')`) and its config reload then recomposes the
62
+ // tree from the profile layers alone — DROPPING the spawn-time
63
+ // worldMountPatches (webserver re-enabled on 3080, world rows gone).
64
+ // Worlds must stay hmr-less: their composition is a spawn-time fact.
65
+ { id: 'hmr', disabled: true },
46
66
  { id: 'modules', inject: ['webServer'] },
47
67
  { insert: [
48
68
  { id: WORLD_MOUNT_ROW_ID, name: WORLD_MOUNT_PLUGIN, config: { key } },
49
69
  { id: WORLD_HUB_ROW_ID, name: WORLD_HUB_PLUGIN },
70
+ { id: WORLD_WEB_TRUST_ROW_ID, name: WORLD_WEB_TRUST_PLUGIN },
50
71
  ] },
51
72
  ];
52
73
  }
@@ -4,20 +4,26 @@
4
4
  * Upstream's `RemoteStreamMuxServer` owns `/api/remote.mux` for the native
5
5
  * gateway and is NOT reachable from outside its package (root exports omit it;
6
6
  * `./src/*` cannot be imported at runtime). So this is our own wire-compatible
7
- * implementation of the same protocol, bound to the ADDRESSED world's
8
- * `openWireStream`:
7
+ * implementation of the same protocol (dsh 0.1.7 `stream-protocol.ts`), bound
8
+ * to the ADDRESSED world's `openWireStream`:
9
9
  *
10
10
  * browser → server { type:'open', streamId, endpoint, payload }
11
+ * { type:'item', streamId, value? } (uplink)
12
+ * { type:'end', streamId } (uplink half-close)
11
13
  * { type:'cancel', streamId }
12
14
  * server → browser { type:'item', streamId, value }
13
15
  * { type:'end', streamId }
14
16
  * { type:'error', streamId, error:{code,message,details} }
15
17
  *
16
- * Heartbeat: ping every interval, terminate after two missed pongs (same
17
- * policy as upstream).
18
+ * Dispatch semantics mirror upstream `RemoteStreamMuxConnection`: `item`,
19
+ * `end`, and `cancel` for a stream this connection no longer owns are dropped;
20
+ * a duplicate `open` is the one violation that closes the socket. Uplink items
21
+ * wait in a bounded inbox created before the opener resolves, so items sent
22
+ * right after `open` are never lost. Heartbeat: ping every interval, terminate
23
+ * after two missed pongs.
18
24
  */
19
25
  /** Open one world stream (the world gateway's `openWireStream`). */
20
- export type MuxOpen = (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<AsyncIterable<unknown>>;
26
+ export type MuxOpen = (endpoint: string, payload: unknown, uplink: AsyncIterable<unknown>, control: AbortController) => Promise<AsyncIterable<unknown>> | AsyncIterable<unknown>;
21
27
  /** Wire failure body (upstream `RemoteStreamFailure`). */
22
28
  export interface MuxFailure {
23
29
  code: string;
@@ -26,8 +32,10 @@ export interface MuxFailure {
26
32
  }
27
33
  /** Options. */
28
34
  export interface WorldMuxOptions {
29
- /** Ping interval; upstream default is 30s. */
35
+ /** Ping interval; upstream default is 2s. */
30
36
  heartbeatIntervalMs?: number;
37
+ /** Buffered uplink frame bytes one logical stream may hold before it fails; upstream default 256KiB. */
38
+ streamInboxBytes?: number;
31
39
  /** Error → wire failure mapper. */
32
40
  failure?: (error: unknown) => MuxFailure;
33
41
  }
@@ -37,6 +45,13 @@ type ClientMessage = {
37
45
  streamId: string;
38
46
  endpoint: string;
39
47
  payload: unknown;
48
+ } | {
49
+ type: 'item';
50
+ streamId: string;
51
+ value?: unknown;
52
+ } | {
53
+ type: 'end';
54
+ streamId: string;
40
55
  } | {
41
56
  type: 'cancel';
42
57
  streamId: string;
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Per-world Remote stream mux server (AW-B DL6).
3
+ *
4
+ * Upstream's `RemoteStreamMuxServer` owns `/api/remote.mux` for the native
5
+ * gateway and is NOT reachable from outside its package (root exports omit it;
6
+ * `./src/*` cannot be imported at runtime). So this is our own wire-compatible
7
+ * implementation of the same protocol (dsh 0.1.7 `stream-protocol.ts`), bound
8
+ * to the ADDRESSED world's `openWireStream`:
9
+ *
10
+ * browser → server { type:'open', streamId, endpoint, payload }
11
+ * { type:'item', streamId, value? } (uplink)
12
+ * { type:'end', streamId } (uplink half-close)
13
+ * { type:'cancel', streamId }
14
+ * server → browser { type:'item', streamId, value }
15
+ * { type:'end', streamId }
16
+ * { type:'error', streamId, error:{code,message,details} }
17
+ *
18
+ * Dispatch semantics mirror upstream `RemoteStreamMuxConnection`: `item`,
19
+ * `end`, and `cancel` for a stream this connection no longer owns are dropped;
20
+ * a duplicate `open` is the one violation that closes the socket. Uplink items
21
+ * wait in a bounded inbox created before the opener resolves, so items sent
22
+ * right after `open` are never lost. Heartbeat: ping every interval, terminate
23
+ * after two missed pongs.
24
+ */
25
+ /** Specifier is widened on purpose: `ws` is a runtime peer, not a typed dep. */
26
+ const WS_MODULE = 'ws';
27
+ const MAX_MISSED_HEARTBEATS = 2;
28
+ const DEFAULT_HEARTBEAT_MS = 2_000;
29
+ const DEFAULT_STREAM_INBOX_BYTES = 262_144;
30
+ const UPLINK_DONE = { value: undefined, done: true };
31
+ function isRecord(value) {
32
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
33
+ }
34
+ function exactKeys(value, keys) {
35
+ const own = Object.keys(value);
36
+ return own.length === keys.length && keys.every(key => Object.hasOwn(value, key));
37
+ }
38
+ function validId(value) {
39
+ return typeof value === 'string' && value.length > 0;
40
+ }
41
+ /** An abort reason carrying wire failure fields (upstream `remoteErrorOf`). */
42
+ function isRemoteFailure(reason) {
43
+ return isRecord(reason) && typeof reason['code'] === 'string';
44
+ }
45
+ /**
46
+ * Parse one browser text frame.
47
+ * @param text - complete WebSocket text message.
48
+ * @returns the validated request.
49
+ * @throws {Error} on a malformed frame.
50
+ */
51
+ export function parseClientMessage(text) {
52
+ let raw;
53
+ try {
54
+ raw = JSON.parse(text);
55
+ }
56
+ catch {
57
+ throw new Error('world-mux: invalid client message');
58
+ }
59
+ if (!isRecord(raw))
60
+ throw new Error('world-mux: invalid client message');
61
+ if ((raw['type'] === 'cancel' || raw['type'] === 'end')
62
+ && exactKeys(raw, ['type', 'streamId']) && validId(raw['streamId'])) {
63
+ return { type: raw['type'], streamId: raw['streamId'] };
64
+ }
65
+ if (raw['type'] === 'item'
66
+ && (exactKeys(raw, ['type', 'streamId']) || exactKeys(raw, ['type', 'streamId', 'value']))
67
+ && validId(raw['streamId'])) {
68
+ return Object.hasOwn(raw, 'value')
69
+ ? { type: 'item', streamId: raw['streamId'], value: raw['value'] }
70
+ : { type: 'item', streamId: raw['streamId'] };
71
+ }
72
+ if (raw['type'] === 'open'
73
+ && exactKeys(raw, ['type', 'streamId', 'endpoint', 'payload'])
74
+ && validId(raw['streamId'])
75
+ && typeof raw['endpoint'] === 'string'
76
+ && raw['endpoint'].length > 0) {
77
+ return { type: 'open', streamId: raw['streamId'], endpoint: raw['endpoint'], payload: raw['payload'] };
78
+ }
79
+ throw new Error('world-mux: invalid client message');
80
+ }
81
+ /** Default failure mapper: keep the error code when it looks like one. */
82
+ export function defaultFailure(error) {
83
+ if (isRecord(error)) {
84
+ const code = typeof error['code'] === 'string' ? error['code'] : undefined;
85
+ const message = typeof error['message'] === 'string' ? error['message'] : undefined;
86
+ if (code !== undefined) {
87
+ const details = isRecord(error['details']) ? error['details'] : {};
88
+ return { code, message: message ?? code, details };
89
+ }
90
+ }
91
+ return { code: 'gateway/stream-error', message: error instanceof Error ? error.message : String(error), details: {} };
92
+ }
93
+ /** One uplink violation: an abort reason that must reach the client as a failure frame. */
94
+ function uplinkViolation(code, message, endpoint) {
95
+ return Object.assign(new Error(message), { code, details: { endpoint } });
96
+ }
97
+ /**
98
+ * Bounded single-consumer uplink queue of one logical stream, the source the
99
+ * world method reads as its uplink iterable. Mirrors upstream `UplinkInbox`:
100
+ * overflow, and an item after the client's `end`, fail the queue and abort the
101
+ * logical stream with a wire failure.
102
+ */
103
+ class UplinkInbox {
104
+ maxBytes;
105
+ endpoint;
106
+ onViolation;
107
+ queue = [];
108
+ bytes = 0;
109
+ ended = false;
110
+ closed = false;
111
+ taken = false;
112
+ failure;
113
+ wake;
114
+ constructor(maxBytes, endpoint, onViolation) {
115
+ this.maxBytes = maxBytes;
116
+ this.endpoint = endpoint;
117
+ this.onViolation = onViolation;
118
+ }
119
+ push(value, frameBytes) {
120
+ if (this.failure !== undefined || this.closed)
121
+ return;
122
+ if (this.ended) {
123
+ this.violate(uplinkViolation('gateway/protocol', 'world-mux: Remote stream uplink item after end', this.endpoint));
124
+ return;
125
+ }
126
+ if (this.bytes + frameBytes > this.maxBytes) {
127
+ this.violate(uplinkViolation('gateway/uplink-overflow', `world-mux: Remote stream uplink exceeded ${String(this.maxBytes)} buffered bytes`, this.endpoint));
128
+ return;
129
+ }
130
+ this.queue.push({ value, bytes: frameBytes });
131
+ this.bytes += frameBytes;
132
+ this.signal();
133
+ }
134
+ /** Client half-close; idempotent. */
135
+ end() {
136
+ if (this.ended)
137
+ return;
138
+ this.ended = true;
139
+ this.signal();
140
+ }
141
+ /** End the consumer's next read with `error`; idempotent, drops buffered items. */
142
+ fail(error) {
143
+ if (this.failure !== undefined)
144
+ return;
145
+ this.failure = error;
146
+ this.queue.length = 0;
147
+ this.bytes = 0;
148
+ this.signal();
149
+ }
150
+ [Symbol.asyncIterator]() {
151
+ if (this.taken)
152
+ throw new Error('world-mux: Remote stream uplink inbox already has a consumer');
153
+ this.taken = true;
154
+ return this;
155
+ }
156
+ async next() {
157
+ while (true) {
158
+ if (this.closed)
159
+ return UPLINK_DONE;
160
+ const entry = this.queue.shift();
161
+ if (entry !== undefined) {
162
+ this.bytes -= entry.bytes;
163
+ return { value: entry.value, done: false };
164
+ }
165
+ if (this.failure !== undefined)
166
+ throw this.failure;
167
+ if (this.ended)
168
+ return UPLINK_DONE;
169
+ if (this.wake !== undefined)
170
+ throw new Error('world-mux: Remote stream uplink inbox has one pending read');
171
+ await new Promise((resolve) => { this.wake = resolve; });
172
+ }
173
+ }
174
+ /** Consumer stopped reading: later items are dropped, a pending read ends. */
175
+ return() {
176
+ this.closed = true;
177
+ this.queue.length = 0;
178
+ this.bytes = 0;
179
+ this.signal();
180
+ return Promise.resolve(UPLINK_DONE);
181
+ }
182
+ violate(error) {
183
+ this.fail(error);
184
+ this.onViolation(error);
185
+ }
186
+ signal() {
187
+ const wake = this.wake;
188
+ this.wake = undefined;
189
+ wake?.();
190
+ }
191
+ }
192
+ /** One world's mux endpoint. */
193
+ export class WorldMuxServer {
194
+ open;
195
+ options;
196
+ server;
197
+ ws;
198
+ heartbeat;
199
+ connections = new Set();
200
+ missed = new WeakMap();
201
+ closed = false;
202
+ constructor(open, options = {}) {
203
+ this.open = open;
204
+ this.options = options;
205
+ }
206
+ /** Take over one accepted upgrade request. */
207
+ async handleUpgrade(req, socket, head) {
208
+ const { server, ws } = await this.load();
209
+ server.handleUpgrade(req, socket, head, (client) => {
210
+ this.missed.set(client, 0);
211
+ client.on('pong', () => { this.missed.set(client, 0); });
212
+ this.startHeartbeat();
213
+ const done = this.runConnection(client, ws);
214
+ this.connections.add(done);
215
+ void done.then(() => { this.connections.delete(done); });
216
+ });
217
+ }
218
+ /** Terminate every socket and wait for the active streams to unwind. */
219
+ async close() {
220
+ this.closed = true;
221
+ clearInterval(this.heartbeat);
222
+ this.heartbeat = undefined;
223
+ const server = this.server;
224
+ if (server !== undefined) {
225
+ for (const client of server.clients)
226
+ client.terminate();
227
+ await new Promise((resolve) => { server.close(() => resolve()); });
228
+ }
229
+ await Promise.all([...this.connections]);
230
+ }
231
+ async load() {
232
+ if (this.server !== undefined && this.ws !== undefined)
233
+ return { server: this.server, ws: this.ws };
234
+ const mod = (await import(WS_MODULE));
235
+ const ws = typeof mod.WebSocketServer === 'function' ? mod : mod.default;
236
+ const server = new ws.WebSocketServer({ noServer: true });
237
+ this.server = server;
238
+ this.ws = ws;
239
+ return { server, ws };
240
+ }
241
+ async runConnection(client, ws) {
242
+ const streams = new Map();
243
+ let writes = Promise.resolve();
244
+ const send = (message) => {
245
+ let text;
246
+ try {
247
+ text = JSON.stringify(message);
248
+ }
249
+ catch (cause) {
250
+ return Promise.reject(new Error('world-mux: item is not JSON serializable', { cause }));
251
+ }
252
+ const delivery = writes.then(() => new Promise((resolve, reject) => {
253
+ if (client.readyState !== ws.WebSocket.OPEN) {
254
+ reject(new Error('world-mux: socket is closed'));
255
+ return;
256
+ }
257
+ client.send(text, (error) => { if (error)
258
+ reject(error);
259
+ else
260
+ resolve(); });
261
+ }));
262
+ writes = delivery.catch(() => undefined);
263
+ return delivery;
264
+ };
265
+ const failureOf = this.options.failure ?? defaultFailure;
266
+ const sendFailure = async (streamId, error) => {
267
+ if (client.readyState !== ws.WebSocket.OPEN)
268
+ return;
269
+ try {
270
+ await send({ type: 'error', streamId, error: failureOf(error) });
271
+ }
272
+ catch {
273
+ client.close(1011, 'Remote stream failure could not be delivered');
274
+ }
275
+ };
276
+ const pump = async (streamId, endpoint, payload, active) => {
277
+ let failed;
278
+ try {
279
+ const source = await this.open(endpoint, payload, active.inbox, active.abort);
280
+ for await (const value of source) {
281
+ await send({ type: 'item', streamId, value });
282
+ }
283
+ }
284
+ catch (error) {
285
+ failed = { error };
286
+ }
287
+ // The downlink has settled; later uplink frames cannot change the outcome.
288
+ active.inbox.fail(new Error('world-mux: Remote stream ended'));
289
+ if (active.abort.signal.aborted) {
290
+ // A wire failure as the abort reason is this mux failing the stream (a
291
+ // rejected or overflowing uplink item, or an item after end); the client
292
+ // is still waiting for that terminal frame. Any other abort is a
293
+ // cancellation and stays silent.
294
+ const reason = active.abort.signal.reason;
295
+ if (isRemoteFailure(reason))
296
+ await sendFailure(streamId, reason);
297
+ return;
298
+ }
299
+ if (failed !== undefined) {
300
+ await sendFailure(streamId, failed.error);
301
+ return;
302
+ }
303
+ try {
304
+ await send({ type: 'end', streamId });
305
+ }
306
+ catch (error) {
307
+ await sendFailure(streamId, error);
308
+ }
309
+ };
310
+ await new Promise((resolve) => {
311
+ client.once('close', () => { resolve(); });
312
+ client.on('error', () => { client.terminate(); });
313
+ client.on('message', (data, isBinary) => {
314
+ if (isBinary === true) {
315
+ client.close(1003, 'text messages required');
316
+ return;
317
+ }
318
+ const text = String(data);
319
+ let message;
320
+ try {
321
+ message = parseClientMessage(text);
322
+ }
323
+ catch {
324
+ client.close(1008, 'invalid Remote stream request');
325
+ return;
326
+ }
327
+ if (message.type === 'open') {
328
+ if (streams.has(message.streamId)) {
329
+ client.close(1008, 'duplicate Remote stream id');
330
+ return;
331
+ }
332
+ const abort = new AbortController();
333
+ // Created before the opener resolves so items the client sends right
334
+ // after `open` wait in the inbox instead of being lost.
335
+ const inbox = new UplinkInbox(this.options.streamInboxBytes ?? DEFAULT_STREAM_INBOX_BYTES, message.endpoint, (error) => { abort.abort(error); });
336
+ const active = {
337
+ abort,
338
+ inbox,
339
+ stop: (reason) => {
340
+ abort.abort(reason);
341
+ inbox.fail(reason);
342
+ },
343
+ done: Promise.resolve(),
344
+ };
345
+ streams.set(message.streamId, active);
346
+ const done = pump(message.streamId, message.endpoint, message.payload, active);
347
+ active.done = done;
348
+ void done.then(() => streams.delete(message.streamId), () => streams.delete(message.streamId));
349
+ return;
350
+ }
351
+ // `item`, `end`, and `cancel` for a stream this connection no longer
352
+ // owns are dropped: a finished stream leaves the table while the
353
+ // client's in-flight frames are still arriving.
354
+ if (message.type === 'item') {
355
+ streams.get(message.streamId)?.inbox.push(message.value, Buffer.byteLength(text, 'utf8'));
356
+ return;
357
+ }
358
+ if (message.type === 'end') {
359
+ streams.get(message.streamId)?.inbox.end();
360
+ return;
361
+ }
362
+ streams.get(message.streamId)?.stop(new Error('world-mux: Remote stream cancelled'));
363
+ });
364
+ });
365
+ const active = [...streams.values()];
366
+ for (const stream of active)
367
+ stream.stop(new Error('world-mux: Remote stream socket closed'));
368
+ await Promise.all(active.map(stream => stream.done));
369
+ }
370
+ startHeartbeat() {
371
+ if (this.heartbeat !== undefined || this.closed)
372
+ return;
373
+ const interval = this.options.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS;
374
+ this.heartbeat = setInterval(() => {
375
+ const server = this.server;
376
+ if (server === undefined)
377
+ return;
378
+ for (const client of server.clients) {
379
+ if (client.readyState !== (this.ws?.WebSocket.OPEN ?? 1))
380
+ continue;
381
+ const missed = this.missed.get(client) ?? 0;
382
+ if (missed >= MAX_MISSED_HEARTBEATS) {
383
+ client.terminate();
384
+ continue;
385
+ }
386
+ this.missed.set(client, missed + 1);
387
+ client.ping();
388
+ }
389
+ }, interval);
390
+ this.heartbeat.unref();
391
+ }
392
+ }
@@ -1,3 +1,19 @@
1
+ /**
2
+ * Resolve the running dsh installation's package.json — the anchor worlds
3
+ * resolve `@deepseek-ai` bundles and the fallback closure from.
4
+ *
5
+ * Precedence: `$SUPERD_DSH_ANCHOR` (the repo dev lines' contract — the
6
+ * launcher pins the checkout build), then a walk UP from the
7
+ * `@deepseek-ai/dsh-app-boot` this module resolved (the host always provides
8
+ * it) to the nearest ancestor package named `@deepseek-ai/dsh` — the
9
+ * packaged-install layout (`<install>/@deepseek-ai/dsh/node_modules/
10
+ * @deepseek-ai/dsh-app-boot`) lands on the install root in two hops. No
11
+ * machine-specific path ships in code.
12
+ * @throws when neither yields an installation (mis-launched composition).
13
+ */
14
+ export declare function resolveInstallAnchor(host?: {
15
+ get(name: string): unknown;
16
+ }): string;
1
17
  export interface ProvisionWorldOptions {
2
18
  /** Runtime key — also the mount label and the `agents/<key>` home segment. */
3
19
  key: string;
@@ -10,11 +26,11 @@ export interface ProvisionWorldOptions {
10
26
  }
11
27
  /**
12
28
  * Provision one world's nested profile (idempotent: rewrites profile files,
13
- * re-links packages — lstat-safe against dangling links). Returns the
14
- * `bareModuleBaseUrl` for {@link spawnWorld}, or null when unresolvable.
29
+ * re-links packages — lstat-safe against dangling links). Returns whether
30
+ * the layout landed.
15
31
  *
16
- * Never throws: any provisioning failure is logged and returns null so the
32
+ * Never throws: any provisioning failure is logged and returns false so the
17
33
  * calling world plugin degrades to a not-ready roster row instead of failing
18
34
  * the loader entry — a failed apply would take down the whole host boot.
19
35
  */
20
- export declare function provisionWorldProfile(opts: ProvisionWorldOptions): string | null;
36
+ export declare function provisionWorldProfile(opts: ProvisionWorldOptions): boolean;