super-dsh 0.1.5 → 0.1.6

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 (35) hide show
  1. package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
  2. package/agent-agy/dist/agent-preset-projection.js +33 -0
  3. package/agent-claude/dist/agent-preset-projection.js +30 -0
  4. package/agent-codex/dist/agent-preset-projection.js +37 -0
  5. package/agent-codex/dist/app-home.js +65 -0
  6. package/agent-codex/dist/spike.js +46 -0
  7. package/agent-hermes/dist/agent-preset-projection.js +34 -0
  8. package/agent-omp/dist/agent-preset-projection.js +36 -0
  9. package/agent-omp/dist/app-home.js +86 -0
  10. package/agent-pi/dist/agent-preset-projection.js +29 -0
  11. package/client/index.js +273 -93
  12. package/cordis.patch.yml +6 -5
  13. package/package.json +2 -2
  14. package/superd-hub/dist/agent-roster.d.ts +4 -0
  15. package/superd-hub/dist/agent-roster.js +2 -2
  16. package/superd-hub/dist/client-shim.d.ts +28 -0
  17. package/superd-hub/dist/client-shim.js +251 -0
  18. package/superd-hub/dist/fusion-transport.d.ts +23 -21
  19. package/superd-hub/dist/fusion-transport.js +12 -40
  20. package/superd-hub/dist/index-pass.d.ts +22 -0
  21. package/superd-hub/dist/index-pass.js +47 -0
  22. package/superd-hub/dist/index.d.ts +3 -3
  23. package/superd-hub/dist/index.js +3 -3
  24. package/superd-hub/dist/labels.d.ts +45 -0
  25. package/superd-hub/dist/labels.js +83 -0
  26. package/superd-hub/dist/roster.d.ts +4 -0
  27. package/superd-hub/dist/roster.js +9 -1
  28. package/superd-hub/dist/switch-core.d.ts +40 -0
  29. package/superd-hub/dist/switch-core.js +72 -0
  30. package/superd-hub/dist/toast-core.d.ts +43 -0
  31. package/superd-hub/dist/toast-core.js +63 -0
  32. package/superd-hub/dist/world-join.js +8 -3
  33. package/superd-hub/dist/world-mount.d.ts +27 -0
  34. package/superd-hub/dist/world-mount.js +67 -0
  35. package/superd-hub/lib/client/index.js +273 -93
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Client mount shim (AW-B DL8).
3
+ *
4
+ * ONE head script installs every client-side break point:
5
+ * - `__DSH_TRANSPORT__.fetch` → the Connection RPC fetch (URL object input)
6
+ * - `WebSocket` / `EventSource` / `XMLHttpRequest` constructors → path rewrite
7
+ * - `__DSH_FILE_UPLOAD__` → uploads bypass the blob worker + its XHR
8
+ * - Storage.prototype get/set/remove → per-world key namespace
9
+ * - `globalThis.fetch` → global-fetch calls asking for ctx0-root `/api`
10
+ * URLs (2026-09-17 H1: upstream session-log-export resolves the GLOBAL
11
+ * fetch at call time; narrow `/api`-only rule, query + hash preserved)
12
+ * - `HTMLAnchorElement.prototype.click` → download-anchor clicks re-rooted
13
+ * (2026-09-18 H1 r2: upstream hands the UNREWRITTEN absolute URL to a
14
+ * DETACHED `<a download>` and clicks it — detached clicks never reach
15
+ * document, so a document-level listener cannot see them, and a blanket
16
+ * rewrite breaks the runtime selector's root-absolute anchors
17
+ * (live-found 2026-09-18). Narrow rule: ONLY anchors carrying a
18
+ * `download` attribute AND a same-authority root-absolute `/api` href.)
19
+ * The rule is namespace-agnostic: a same-origin root-absolute URL is re-rooted
20
+ * under the mount (`/omp`), nothing else is touched. That covers every prefix
21
+ * a plugin may declare later — no list to keep in sync.
22
+ */
23
+ /** Escape a label for embedding in generated JavaScript. */
24
+ function js(value) {
25
+ return JSON.stringify(value);
26
+ }
27
+ /**
28
+ * Render the shim as a classic script (parser-blocking head injection).
29
+ * @param labelPath - mount root, e.g. `/omp`.
30
+ * @returns JavaScript source.
31
+ */
32
+ export function renderClientShim(labelPath) {
33
+ if (typeof labelPath !== 'string' || !labelPath.startsWith('/') || labelPath === '/' || labelPath.endsWith('/')) {
34
+ throw new Error(`client-shim: labelPath must start with "/" and carry no trailing slash, got ${JSON.stringify(labelPath)}`);
35
+ }
36
+ const prefix = `${labelPath}/`;
37
+ return `(() => {
38
+ const LABEL = ${js(labelPath)}
39
+ const PREFIX = ${js(prefix)}
40
+
41
+ /** Re-root one pathname when it is same-origin root-absolute and not mounted yet. */
42
+ const rewritePath = (path) => {
43
+ if (typeof path !== 'string' || !path.startsWith('/') || path.startsWith('//')) return path
44
+ if (path === LABEL || path.startsWith(PREFIX)) return path
45
+ return PREFIX + path.slice(1)
46
+ }
47
+
48
+ const origin = () => {
49
+ const loc = globalThis.location
50
+ if (loc !== undefined && typeof loc.origin === 'string' && loc.origin !== 'null') return loc.origin
51
+ return 'http://dsh.internal'
52
+ }
53
+
54
+ /** Same authority as the page (scheme-agnostic: ws/wss/http/https share host). */
55
+ const sameAuthority = (url) => {
56
+ try {
57
+ return url.host === new URL(origin()).host
58
+ } catch {
59
+ return false
60
+ }
61
+ }
62
+
63
+ /** Rewrite a full URL (absolute or relative); other authorities are untouched. */
64
+ const rewriteUrl = (input) => {
65
+ if (typeof input === 'string') {
66
+ if (input.startsWith('/')) return rewritePath(input)
67
+ try {
68
+ const url = new URL(input, origin())
69
+ if (!sameAuthority(url)) return input
70
+ const next = rewritePath(url.pathname)
71
+ if (next === url.pathname) return input
72
+ url.pathname = next
73
+ return url.href
74
+ } catch {
75
+ return input
76
+ }
77
+ }
78
+ if (input !== null && typeof input === 'object' && typeof input.href === 'string') {
79
+ try {
80
+ const url = new URL(input.href)
81
+ if (!sameAuthority(url)) return input
82
+ const next = rewritePath(url.pathname)
83
+ if (next === url.pathname) return input
84
+ url.pathname = next
85
+ return url
86
+ } catch {
87
+ return input
88
+ }
89
+ }
90
+ return input
91
+ }
92
+
93
+ const nativeFetch = globalThis.fetch
94
+ const shimFetch = (input, init) => {
95
+ if (typeof nativeFetch !== 'function') throw new Error('client-shim: fetch is unavailable')
96
+ if (typeof Request === 'function' && input instanceof Request) {
97
+ const url = rewriteUrl(input.url)
98
+ return nativeFetch(url === input.url ? input : new Request(url, input), init)
99
+ }
100
+ return nativeFetch(rewriteUrl(input), init)
101
+ }
102
+
103
+ const transport = (globalThis.__DSH_TRANSPORT__ = globalThis.__DSH_TRANSPORT__ || {})
104
+ transport.rewrite = rewritePath
105
+ transport.fetch = shimFetch
106
+
107
+ // Construct through a Proxy, never a hand-rolled wrapper function: the
108
+ // upstream client compares socket.readyState against WebSocket.OPEN, so the
109
+ // native statics (CONNECTING/OPEN/CLOSING/CLOSED), prototype and instanceof
110
+ // must survive the interception.
111
+ const NativeWebSocket = globalThis.WebSocket
112
+ if (typeof NativeWebSocket === 'function') {
113
+ globalThis.WebSocket = new Proxy(NativeWebSocket, {
114
+ construct: (Target, args) => new Target(rewriteUrl(args[0]), ...args.slice(1)),
115
+ })
116
+ }
117
+
118
+ const NativeEventSource = globalThis.EventSource
119
+
120
+ if (typeof NativeEventSource === 'function') {
121
+ globalThis.EventSource = new Proxy(NativeEventSource, {
122
+ construct: (Target, args) => new Target(rewriteUrl(args[0]), ...args.slice(1)),
123
+ })
124
+ }
125
+
126
+ const NativeXhr = globalThis.XMLHttpRequest
127
+ if (typeof NativeXhr === 'function' && NativeXhr.prototype !== undefined && typeof NativeXhr.prototype.open === 'function') {
128
+ const nativeOpen = NativeXhr.prototype.open
129
+ NativeXhr.prototype.open = function (method, url, ...rest) {
130
+ return nativeOpen.call(this, method, rewriteUrl(url), ...rest)
131
+ }
132
+ }
133
+
134
+ // Read by the file-upload service at construction time: present ⇒ the page
135
+ // fetch carries uploads (no blob worker, and therefore no worker-scope XHR).
136
+ globalThis.__DSH_FILE_UPLOAD__ = { fetch: shimFetch }
137
+
138
+ // Narrow '/api' re-root helpers (2026-09-17 session-log 404, H1): upstream
139
+ // client code (session-log-export) resolves the GLOBAL fetch at call time
140
+ // and hands absolute ctx0-root URLs both to fetch and to a DETACHED
141
+ // download anchor — neither is visible to the transport faces above.
142
+ // Narrow rule: only root-absolute '/api' paths move under the mount;
143
+ // relative, same-mount, and foreign URLs pass through untouched. Query and
144
+ // hash survive because only the pathname is rewritten. The mount root comes
145
+ // from this shim's argument, never from location.pathname. NOTE: the '/api'
146
+ // literals below stay SINGLE-quoted on purpose — index-pass.ts rewrites
147
+ // every DOUBLE-quoted root-absolute string in the served HTML, and a
148
+ // doubled literal would silently disable these rewrites (regression tests
149
+ // survive the mount HTML pass).
150
+ const rewriteApiPath = (path) => {
151
+ if (path === '/api' || path.startsWith('/api/')) return PREFIX + path.slice(1)
152
+ return path
153
+ }
154
+ const rewriteApiUrl = (input) => {
155
+ if (typeof input === 'string') {
156
+ if (input.startsWith('/') && !input.startsWith('//')) return rewriteApiPath(input)
157
+ try {
158
+ const url = new URL(input, origin())
159
+ if (!sameAuthority(url)) return input
160
+ const next = rewriteApiPath(url.pathname)
161
+ if (next === url.pathname) return input
162
+ url.pathname = next
163
+ return url.href
164
+ } catch {
165
+ return input
166
+ }
167
+ }
168
+ if (input !== null && typeof input === 'object' && typeof input.href === 'string') {
169
+ try {
170
+ const url = new URL(input.href)
171
+ if (!sameAuthority(url)) return input
172
+ const next = rewriteApiPath(url.pathname)
173
+ if (next === url.pathname) return input
174
+ url.pathname = next
175
+ return url
176
+ } catch {
177
+ return input
178
+ }
179
+ }
180
+ return input
181
+ }
182
+ if (typeof nativeFetch === 'function' && globalThis.__DSH_FETCH_REWRITE__ !== true) {
183
+ globalThis.__DSH_FETCH_REWRITE__ = true
184
+ globalThis.fetch = (input, init) => nativeFetch(rewriteApiUrl(input), init)
185
+ }
186
+
187
+ // Download anchors (2026-09-18 H1 r2): upstream session-log-export hands
188
+ // the UNREWRITTEN absolute URL to a DETACHED '<a download>' element and
189
+ // calls .click() — detached clicks never propagate to document, so the r1
190
+ // document-level capture listener could not see them, while its BLANKET
191
+ // rewrite re-rooted the runtime selector's root-absolute anchors and broke
192
+ // cross-world navigation (live-found 2026-09-18). Narrow seam: wrap
193
+ // HTMLAnchorElement.prototype.click itself and rewrite ONLY anchors that
194
+ // carry a 'download' attribute AND a same-authority root-absolute '/api'
195
+ // href. Everything else — the runtime selector's links included — passes
196
+ // through untouched.
197
+ if (globalThis.HTMLAnchorElement !== undefined
198
+ && globalThis.HTMLAnchorElement.prototype !== undefined
199
+ && typeof globalThis.HTMLAnchorElement.prototype.click === 'function'
200
+ && globalThis.__DSH_ANCHOR_CLICK_REWRITE__ !== true) {
201
+ globalThis.__DSH_ANCHOR_CLICK_REWRITE__ = true
202
+ const nativeAnchorClick = globalThis.HTMLAnchorElement.prototype.click
203
+ globalThis.HTMLAnchorElement.prototype.click = function (...args) {
204
+ try {
205
+ if (this.hasAttribute('download')) {
206
+ const current = this.getAttribute('href')
207
+ if (typeof current === 'string' && current !== '') {
208
+ const next = rewriteApiUrl(current)
209
+ if (typeof next === 'string' && next !== current) this.setAttribute('href', next)
210
+ }
211
+ }
212
+ } catch {
213
+ /* best-effort: never break the click itself */
214
+ }
215
+ return nativeAnchorClick.apply(this, args)
216
+ }
217
+ }
218
+
219
+ // Storage namespace (2026-09-16 storage findings P1): all worlds share one
220
+
221
+ // origin, so bare localStorage keys would overwrite each other across
222
+ // mounts. Prefix every key with the label; root '/' never injects this
223
+ // shim and keeps bare keys. Never throws: attachPersistence only
224
+ // console-errors on failure, so a throwing shim would silently disable
225
+ // persistence.
226
+ try {
227
+ const StorageProto = globalThis.Storage && globalThis.Storage.prototype
228
+ if (globalThis.localStorage !== undefined && StorageProto !== undefined
229
+ && !globalThis.__DSH_STORAGE_NS__
230
+ && typeof StorageProto.getItem === 'function'
231
+ && typeof StorageProto.setItem === 'function'
232
+ && typeof StorageProto.removeItem === 'function') {
233
+ // NS derives from PREFIX, never LABEL: the mount HTML pass (index-pass)
234
+ // rewrites every quoted root-absolute string EXCEPT "<label>/"-prefixed
235
+ // ones, so the shim's LABEL="/omp" literal doubles to "/omp/omp" while
236
+ // PREFIX="/omp/" is immune (live-found 2026-09-17, acceptance A2/A5).
237
+ // The NS-from-PREFIX coupling is DELIBERATE: index-pass.ts rewrites double-quoted root-absolute strings except "<label>/"-prefixed ones, so LABEL="/omp" doubles while PREFIX="/omp/" survives; do NOT derive NS from LABEL.
238
+ const NS = PREFIX.slice(1, -1) + ':'
239
+ globalThis.__DSH_STORAGE_NS__ = NS
240
+ const nativeGetItem = StorageProto.getItem
241
+ const nativeSetItem = StorageProto.setItem
242
+ const nativeRemoveItem = StorageProto.removeItem
243
+ StorageProto.getItem = function (name) { return nativeGetItem.call(this, NS + name) }
244
+ StorageProto.setItem = function (name, value) { return nativeSetItem.call(this, NS + name, value) }
245
+ StorageProto.removeItem = function (name) { return nativeRemoveItem.call(this, NS + name) }
246
+ }
247
+ } catch {
248
+ /* storage namespace is best-effort: never break the shim */
249
+ }
250
+ })()`;
251
+ }
@@ -48,9 +48,9 @@
48
48
  * so cordis's FIFO effect order guarantees trust pushes first and fusion
49
49
  * pushes last — the non-merging `if(!window.__DSH_TRANSPORT__)` verdict in the
50
50
  * trust script is always evaluated before anything could own that global.
51
- * Flag gate (minimal, Task 5 formalizes the config face): row config
52
- * `enabled`, defaulting OFF from the `SUPERD_UI_FUSION` env — while the flag
53
- * is off the component stays dormant and injects no row at all.
51
+ * Row gate: the row config `enabled`, ON by default — the composite patch
52
+ * composing this row is the switch. `enabled: false` (or `disabled: true`)
53
+ * leaves the component dormant: no listener, no script, no flag global.
54
54
  *
55
55
  * @module @pgmi-builds/superd-hub/fusion-transport
56
56
  */
@@ -64,27 +64,25 @@ import z from '@deepseek-ai/schemastery';
64
64
  * @returns JavaScript source.
65
65
  */
66
66
  export declare function renderFusionTransport(): string;
67
- /** The env name the minimal Task-3 gate reads; Task 5 builds the config face on it. */
68
- export declare const FUSION_FLAG_ENV = "SUPERD_UI_FUSION";
69
67
  /**
70
- * The page global the ctx0 index carries when the gate is on: the client
68
+ * The page global the ctx0 index carries when this row is on: the client
71
69
  * half's fusion-branch discriminator (the Hub UI switch surfaces read its
72
70
  * presence to intercept agent clicks into the switch orchestration instead
73
71
  * of letting the hyperlinks navigate — the sole switch surface since the T-B
74
72
  * RuntimeSeat retirement; world pages never carry it, so they keep the real
75
- * navigation half — spec §5 双半边). Baked by the SAME gated listener as the
73
+ * navigation half — spec §5 双半边). Baked by the SAME listener as the
76
74
  * transport script, so the two halves can never disagree.
77
75
  */
78
76
  export declare const FUSION_FLAG_GLOBAL = "__SUPERD_UI_FUSION__";
79
77
  /**
80
- * The flag's SEMANTICS after the path-separation Part 2 closeout (T-C/T-D
78
+ * The row's SEMANTICS after the path-separation Part 2 closeout (T-C/T-D
81
79
  * landed; this block is the T-E closeout record) — native-ui-only, there is
82
80
  * no fallback route anywhere:
83
81
  *
84
82
  * - ON: the ctx0 page fuses. The transport script arms the in-place switch
85
83
  * and `/hub` is the sole surface for every runtime. The per-label URL
86
84
  * family (`/<label>/` ctx0 pages, T-C) and the world web UIs (T-D) are
87
- * RETIRED from the product, not gated by this flag — there is nothing
85
+ * RETIRED from the product, not gated by this row — there is nothing
88
86
  * navigational behind them to fall back to.
89
87
  * - OFF: NO foreign entry exists AT ALL. The served product is the bare
90
88
  * native UI plus the Hub panel as an information surface: the roster
@@ -94,20 +92,24 @@ export declare const FUSION_FLAG_GLOBAL = "__SUPERD_UI_FUSION__";
94
92
  * has no /<label>/ page to reach and the Hub UI ships no hrefs to reach
95
93
  * one with.
96
94
  *
97
- * The shipped default stays OFF (truthy env opt-in). Moving the default ON is
98
- * the user's 放行 (acceptance/release) round — a two-gate decision per repo
99
- * §〇.1, never a code-side choice.
100
- *
101
- * Derivation: truthy spellings only (`1` / `true` / `yes` / `on`,
102
- * case-insensitive, trimmed). Anything else — unset included — is OFF.
95
+ * The switch is ROW COMPOSITION, exactly like every other row here: the
96
+ * composite patch composes this row, so the fused surface is what the product
97
+ * ships. A deployment that does not want it turns the row off where cordis
98
+ * already owns that decision — a profile layer restating the row with
99
+ * `config: { enabled: false }` (整行重述: same id, same name) or
100
+ * `disabled: true`, or dropping `super-dsh/fusion-transport` from the
101
+ * composition entirely. There is deliberately no env-derived default: a
102
+ * release gate living in an environment variable is deployment state that no
103
+ * patch layer can see, read, or diff.
103
104
  */
104
- export declare function deriveFusionFlag(env: string | undefined): boolean;
105
- /** Row config schema face. */
106
105
  export interface FusionConfig {
107
- /** Master gate: inject the transport script on ctx0's index. Default off. */
106
+ /**
107
+ * Whether this row injects the transport script and the flag global on
108
+ * ctx0's index. Default on — the row being composed is the switch.
109
+ */
108
110
  enabled?: boolean;
109
111
  }
110
- /** Row config (schema-level default derives from the {@link FUSION_FLAG_ENV} env). */
112
+ /** Row config: ON unless a patch layer restates the row with `enabled: false`. */
111
113
  export declare const Config: z<Schemastery.ObjectS<NoInfer<{
112
114
  enabled: z<boolean, boolean, "defined">;
113
115
  }>>, Schemastery.ObjectT<NoInfer<{
@@ -129,10 +131,10 @@ export declare const inject: string[];
129
131
  * the {@link FUSION_FLAG_GLOBAL} baked global row (Task 4): one gate, both
130
132
  * halves.
131
133
  *
132
- * Flag off → the component registers nothing: no listener, no row, no script,
134
+ * Off → the component registers nothing: no listener, no row, no script,
133
135
  * no flag global.
134
136
  * @param ctx - host plugin context.
135
- * @param config - the row config.
137
+ * @param config - the row config (`enabled`, default on).
136
138
  */
137
139
  export declare function apply(ctx: Context, config: FusionConfig | undefined): void;
138
140
  declare const _default: {
@@ -48,9 +48,9 @@
48
48
  * so cordis's FIFO effect order guarantees trust pushes first and fusion
49
49
  * pushes last — the non-merging `if(!window.__DSH_TRANSPORT__)` verdict in the
50
50
  * trust script is always evaluated before anything could own that global.
51
- * Flag gate (minimal, Task 5 formalizes the config face): row config
52
- * `enabled`, defaulting OFF from the `SUPERD_UI_FUSION` env — while the flag
53
- * is off the component stays dormant and injects no row at all.
51
+ * Row gate: the row config `enabled`, ON by default — the composite patch
52
+ * composing this row is the switch. `enabled: false` (or `disabled: true`)
53
+ * leaves the component dormant: no listener, no script, no flag global.
54
54
  *
55
55
  * @module @pgmi-builds/superd-hub/fusion-transport
56
56
  */
@@ -319,49 +319,19 @@ try {
319
319
  } catch { /* the whole install is best-effort: a partial page is still a page */ }
320
320
  })()`;
321
321
  }
322
- /** The env name the minimal Task-3 gate reads; Task 5 builds the config face on it. */
323
- export const FUSION_FLAG_ENV = 'SUPERD_UI_FUSION';
324
322
  /**
325
- * The page global the ctx0 index carries when the gate is on: the client
323
+ * The page global the ctx0 index carries when this row is on: the client
326
324
  * half's fusion-branch discriminator (the Hub UI switch surfaces read its
327
325
  * presence to intercept agent clicks into the switch orchestration instead
328
326
  * of letting the hyperlinks navigate — the sole switch surface since the T-B
329
327
  * RuntimeSeat retirement; world pages never carry it, so they keep the real
330
- * navigation half — spec §5 双半边). Baked by the SAME gated listener as the
328
+ * navigation half — spec §5 双半边). Baked by the SAME listener as the
331
329
  * transport script, so the two halves can never disagree.
332
330
  */
333
331
  export const FUSION_FLAG_GLOBAL = '__SUPERD_UI_FUSION__';
334
- /**
335
- * The flag's SEMANTICS after the path-separation Part 2 closeout (T-C/T-D
336
- * landed; this block is the T-E closeout record) — native-ui-only, there is
337
- * no fallback route anywhere:
338
- *
339
- * - ON: the ctx0 page fuses. The transport script arms the in-place switch
340
- * and `/hub` is the sole surface for every runtime. The per-label URL
341
- * family (`/<label>/` ctx0 pages, T-C) and the world web UIs (T-D) are
342
- * RETIRED from the product, not gated by this flag — there is nothing
343
- * navigational behind them to fall back to.
344
- * - OFF: NO foreign entry exists AT ALL. The served product is the bare
345
- * native UI plus the Hub panel as an information surface: the roster
346
- * still lists the runtimes (the control plane is not gated), but the
347
- * switch affordances render inert with the degradation hint
348
- * (`switchAffordance` in switch-core) instead of dead controls — a row
349
- * has no /<label>/ page to reach and the Hub UI ships no hrefs to reach
350
- * one with.
351
- *
352
- * The shipped default stays OFF (truthy env opt-in). Moving the default ON is
353
- * the user's 放行 (acceptance/release) round — a two-gate decision per repo
354
- * §〇.1, never a code-side choice.
355
- *
356
- * Derivation: truthy spellings only (`1` / `true` / `yes` / `on`,
357
- * case-insensitive, trimmed). Anything else — unset included — is OFF.
358
- */
359
- export function deriveFusionFlag(env) {
360
- return ['1', 'true', 'yes', 'on'].includes((env ?? '').trim().toLowerCase());
361
- }
362
- /** Row config (schema-level default derives from the {@link FUSION_FLAG_ENV} env). */
332
+ /** Row config: ON unless a patch layer restates the row with `enabled: false`. */
363
333
  export const Config = z.object({
364
- enabled: z.boolean().default(deriveFusionFlag(process.env[FUSION_FLAG_ENV])),
334
+ enabled: z.boolean().default(true),
365
335
  });
366
336
  /** Cordis plugin name. */
367
337
  export const name = 'superd-fusion-transport';
@@ -379,13 +349,15 @@ export const inject = [];
379
349
  * the {@link FUSION_FLAG_GLOBAL} baked global row (Task 4): one gate, both
380
350
  * halves.
381
351
  *
382
- * Flag off → the component registers nothing: no listener, no row, no script,
352
+ * Off → the component registers nothing: no listener, no row, no script,
383
353
  * no flag global.
384
354
  * @param ctx - host plugin context.
385
- * @param config - the row config.
355
+ * @param config - the row config (`enabled`, default on).
386
356
  */
387
357
  export function apply(ctx, config) {
388
- if (config?.enabled !== true)
358
+ // Composed row = on. Only an explicit `enabled: false` turns it off, so a
359
+ // composition that omits the config key cannot silently disable the surface.
360
+ if (config?.enabled === false)
389
361
  return;
390
362
  ctx.inject(['webServer'], () => {
391
363
  ctx.on('webserver/index-inject', (table) => {
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Mount HTML pass (AW-B DL1/DL8 server half).
3
+ *
4
+ * The world dist is built with RELATIVE asset URLs (`./assets/...`), so a
5
+ * `<base>` pointing at the mount makes every relative reference resolve under
6
+ * `/<label>/`. Root-absolute strings (the boot graph's `/plugins/...` URLs,
7
+ * injected script sources, `href="/"`-style anchors) do NOT follow `<base>`,
8
+ * so they are re-rooted explicitly — one generic rule, no namespace list.
9
+ */
10
+ /**
11
+ * Rewrite a world index for serving under `<labelPath>/`.
12
+ *
13
+ * - pins `<base>` at the mount (inserting one after `<head>` when absent);
14
+ * - prefixes every double-quoted root-absolute string that is not already
15
+ * mounted and not protocol-relative (`//host/...`);
16
+ * - idempotent (a second pass changes nothing).
17
+ *
18
+ * @param html - the world's index.html (already rendered with its own rows).
19
+ * @param labelPath - mount root, e.g. `/omp`.
20
+ * @returns the mount-ready HTML.
21
+ */
22
+ export declare function rewriteIndexHtml(html: string, labelPath: string): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Mount HTML pass (AW-B DL1/DL8 server half).
3
+ *
4
+ * The world dist is built with RELATIVE asset URLs (`./assets/...`), so a
5
+ * `<base>` pointing at the mount makes every relative reference resolve under
6
+ * `/<label>/`. Root-absolute strings (the boot graph's `/plugins/...` URLs,
7
+ * injected script sources, `href="/"`-style anchors) do NOT follow `<base>`,
8
+ * so they are re-rooted explicitly — one generic rule, no namespace list.
9
+ */
10
+ /** Root of the mount including the trailing slash (`/omp` → `/omp/`). */
11
+ function mountPrefix(labelPath) {
12
+ return labelPath.endsWith('/') ? labelPath : `${labelPath}/`;
13
+ }
14
+ /**
15
+ * Rewrite a world index for serving under `<labelPath>/`.
16
+ *
17
+ * - pins `<base>` at the mount (inserting one after `<head>` when absent);
18
+ * - prefixes every double-quoted root-absolute string that is not already
19
+ * mounted and not protocol-relative (`//host/...`);
20
+ * - idempotent (a second pass changes nothing).
21
+ *
22
+ * @param html - the world's index.html (already rendered with its own rows).
23
+ * @param labelPath - mount root, e.g. `/omp`.
24
+ * @returns the mount-ready HTML.
25
+ */
26
+ export function rewriteIndexHtml(html, labelPath) {
27
+ if (typeof labelPath !== 'string' || !labelPath.startsWith('/') || labelPath === '/' || labelPath.endsWith('/')) {
28
+ throw new Error(`index-pass: labelPath must start with "/" and carry no trailing slash, got ${JSON.stringify(labelPath)}`);
29
+ }
30
+ const prefix = mountPrefix(labelPath);
31
+ let out = html;
32
+ if (/<base\b[^>]*>/i.test(out)) {
33
+ out = out.replace(/<base\b[^>]*>/i, `<base href="${prefix}">`);
34
+ }
35
+ else if (/<head(?:\s[^>]*)?>/i.test(out)) {
36
+ out = out.replace(/<head(?:\s[^>]*)?>/i, open => `${open}<base href="${prefix}">`);
37
+ }
38
+ out = out.replace(/"(\/[^"]*)"/g, (whole, path) => {
39
+ if (path.startsWith('//'))
40
+ return whole;
41
+ // Load-bearing contract: client-shim.ts derives its storage NS from the surviving "<label>/" literal (regression test: the storage NS survives the mount HTML pass). Changing this exclusion breaks that coupling.
42
+ if (path.startsWith(prefix))
43
+ return whole;
44
+ return `"${prefix}${path.slice(1)}"`;
45
+ });
46
+ return out;
47
+ }
@@ -16,7 +16,7 @@
16
16
  */
17
17
  export { handleAgentRuntime, registerSelectorRpc, AGENT_RUNTIME_RPC_PATH, registryTargets, type RuntimeTargetSource } from './rpc.js';
18
18
  export { registerForeignTarget, unregisterForeignTarget, getTarget, listRuntimeKeys, type ForeignTarget, type GatewayFace } from './targets.js';
19
- export { registerAgent, unregisterAgent, setReady, listAgents, onRosterChanged, type RosterEntry } from './roster.js';
19
+ export { registerAgent, unregisterAgent, setReady, setFailed, listAgents, onRosterChanged, type RosterEntry } from './roster.js';
20
20
  export { spawnWorld, type SpawnWorldOptions } from './spawn-world.js';
21
21
  export { provisionWorldProfile, resolveInstallAnchor, type ProvisionWorldOptions } from './world-provision.js';
22
22
  export { WorldWebServer, stripLabelFromUrl, type WorldWebRoute, type WorldWebUpgradeRoute, type RealWebServerFace } from './world-web-server.js';
@@ -24,12 +24,12 @@ export { parseClientRequest, serverResponse, serverError, serverResult, wireErro
24
24
  export { WorldMuxServer, parseClientMessage, defaultFailure, type MuxOpen, type MuxFailure, type WorldMuxOptions } from './world-mux.js';
25
25
  export { forwardApiCall, forwardWorldFetch, worldMuxOf, writeJson, type CarrierGatewayFace, type ForwardOptions } from './carrier.js';
26
26
  export { registerHubRoute, HUB_MOUNT_PATH, HUB_MUX_PATH, type HubRouteOptions } from './hub-route.js';
27
- export { renderFusionTransport, deriveFusionFlag, FUSION_FLAG_ENV, FUSION_FLAG_GLOBAL, type FusionConfig } from './fusion-transport.js';
27
+ export { renderFusionTransport, FUSION_FLAG_GLOBAL, type FusionConfig } from './fusion-transport.js';
28
28
  export { createSwitchController, parseLandingTarget, namespacedKey, storageNamespace, seatClickDecision, NATIVE_AGENT, SESSIONS_CURRENT_KEY, SWITCH_FALLBACK_MS, type SwitchHost, type SwitchController, type SwitchOptions, type SeatClickInput, type SeatClickDecision } from './switch-core.js';
29
29
  export { agentLinks, agentRosterRow, AGENT_ROSTER_GLOBAL, type AgentLink } from './agent-roster.js';
30
30
  export { createOwnershipIndex, type OwnershipIndex, type OwnershipRow } from './ownership.js';
31
31
  export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf, type HostMount } from './world-host.js';
32
- export { worldMountPatches, superdTrustedHosts, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, type WorldPatchLayer, } from './world-mount.js';
32
+ 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';
33
33
  export { buildTrustScript, deriveDefaultPageAuthorities, type BootScriptRow, type TrustConfig, } from './web-trust.js';
34
34
  export { withSystemHead, type HeadAwareSessionFace, type HeadPlaceholder, type WithSystemHead } from './system-head.js';
35
35
  export { default as worldApply, name as worldPluginName, type Config as WorldEntryConfig } from './world-entry.js';
@@ -16,7 +16,7 @@
16
16
  */
17
17
  export { handleAgentRuntime, registerSelectorRpc, AGENT_RUNTIME_RPC_PATH, registryTargets } from './rpc.js';
18
18
  export { registerForeignTarget, unregisterForeignTarget, getTarget, listRuntimeKeys } from './targets.js';
19
- export { registerAgent, unregisterAgent, setReady, listAgents, onRosterChanged } from './roster.js';
19
+ export { registerAgent, unregisterAgent, setReady, setFailed, listAgents, onRosterChanged } from './roster.js';
20
20
  export { spawnWorld } from './spawn-world.js';
21
21
  export { provisionWorldProfile, resolveInstallAnchor } from './world-provision.js';
22
22
  export { WorldWebServer, stripLabelFromUrl } from './world-web-server.js';
@@ -24,12 +24,12 @@ export { parseClientRequest, serverResponse, serverError, serverResult, wireErro
24
24
  export { WorldMuxServer, parseClientMessage, defaultFailure } from './world-mux.js';
25
25
  export { forwardApiCall, forwardWorldFetch, worldMuxOf, writeJson } from './carrier.js';
26
26
  export { registerHubRoute, HUB_MOUNT_PATH, HUB_MUX_PATH } from './hub-route.js';
27
- export { renderFusionTransport, deriveFusionFlag, FUSION_FLAG_ENV, FUSION_FLAG_GLOBAL } from './fusion-transport.js';
27
+ export { renderFusionTransport, FUSION_FLAG_GLOBAL } from './fusion-transport.js';
28
28
  export { createSwitchController, parseLandingTarget, namespacedKey, storageNamespace, seatClickDecision, NATIVE_AGENT, SESSIONS_CURRENT_KEY, SWITCH_FALLBACK_MS } from './switch-core.js';
29
29
  export { agentLinks, agentRosterRow, AGENT_ROSTER_GLOBAL } from './agent-roster.js';
30
30
  export { createOwnershipIndex } from './ownership.js';
31
31
  export { registerHostMount, takeHostMount, listHostMounts, noteWorldServer, worldServerOf } from './world-host.js';
32
- export { worldMountPatches, superdTrustedHosts, WORLD_MOUNT_PLUGIN, WORLD_MOUNT_ROW_ID, WORLD_HUB_PLUGIN, WORLD_HUB_ROW_ID, } from './world-mount.js';
32
+ 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';
33
33
  export { buildTrustScript, deriveDefaultPageAuthorities, } from './web-trust.js';
34
34
  export { withSystemHead } from './system-head.js';
35
35
  export { default as worldApply, name as worldPluginName } from './world-entry.js';
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Label table (AW-B DL2/DL4): one world ↔ one mount path under the ctx0 origin.
3
+ *
4
+ * The addressed world is the request's path (`/<label>/...`), so the label is
5
+ * the ONLY routing key. Reserving it must be atomic against the real
6
+ * webServer's duplicate-registration throw — that throw is the only collision
7
+ * signal the framework offers (the route tables are private and there is no
8
+ * query API), so it is used here as the probe.
9
+ *
10
+ * A collision is a composition-level conflict and must be loud; an optional
11
+ * alternate root lets a deployment re-home instead of failing to boot.
12
+ */
13
+ /** One reserved mount path. */
14
+ export interface LabelClaim {
15
+ /** Runtime key (`omp`, `codex`, …). */
16
+ readonly key: string;
17
+ /** Reserved mount root, e.g. `/omp` or `/_agents/omp` (no trailing slash). */
18
+ readonly path: string;
19
+ /** Release the claim (idempotent). */
20
+ release(): void;
21
+ }
22
+ /** How to reserve a path: the injected real-webServer registration. */
23
+ export interface ClaimOptions {
24
+ /** Attempt a prefix registration; throws when `(kind, path)` is already taken. */
25
+ register(path: string): () => void;
26
+ /** Optional root prefix (default: the origin root). */
27
+ root?: string;
28
+ /** Candidate roots to try, in order, when the clean path is taken. */
29
+ alternateRoots?: readonly string[];
30
+ }
31
+ /**
32
+ * Reserve the mount path for `key`.
33
+ * @param key - runtime key (one path segment).
34
+ * @param opts - registration probe plus optional roots.
35
+ * @returns the live claim.
36
+ */
37
+ export declare function claimLabel(key: string, opts: ClaimOptions): LabelClaim;
38
+ /** The reserved mount root for a key, or undefined when unclaimed. */
39
+ export declare function labelOf(key: string): string | undefined;
40
+ /** Release a key's claim; unknown keys are a no-op. */
41
+ export declare function releaseLabel(key: string): void;
42
+ /** Claimed keys, registration order. */
43
+ export declare function listLabels(): string[];
44
+ /** Test/introspection helper: is this object a claim? */
45
+ export declare function isLabelClaim(value: unknown): value is LabelClaim;