super-dsh 0.1.3 → 0.1.5

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 (142) hide show
  1. package/README.md +3 -3
  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 +31 -51
  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 +31 -51
  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 +31 -52
  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 +37 -50
  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 +31 -51
  62. package/agent-pi/package.json +14 -11
  63. package/client/index.js +1994 -150
  64. package/cordis.patch.yml +38 -15
  65. package/package.json +32 -30
  66. package/{agent-hub → superd-hub}/cordis.patch.yml +3 -3
  67. package/{agent-hub → superd-hub}/dist/agent-roster.d.ts +3 -1
  68. package/{agent-hub → superd-hub}/dist/agent-roster.js +4 -2
  69. package/superd-hub/dist/carrier.d.ts +62 -0
  70. package/superd-hub/dist/carrier.js +213 -0
  71. package/superd-hub/dist/fusion-transport.d.ts +148 -0
  72. package/superd-hub/dist/fusion-transport.js +397 -0
  73. package/{agent-hub → superd-hub}/dist/gateway.d.ts +9 -4
  74. package/superd-hub/dist/gateway.js +44 -0
  75. package/superd-hub/dist/hub-route.d.ts +74 -0
  76. package/superd-hub/dist/hub-route.js +188 -0
  77. package/{agent-hub → superd-hub}/dist/index.d.ts +14 -6
  78. package/{agent-hub → superd-hub}/dist/index.js +14 -6
  79. package/{agent-hub → superd-hub}/dist/roster.d.ts +2 -2
  80. package/{agent-hub → superd-hub}/dist/rpc.js +1 -1
  81. package/{agent-hub → superd-hub}/dist/spawn-world.d.ts +27 -6
  82. package/superd-hub/dist/spawn-world.js +279 -0
  83. package/superd-hub/dist/switch-core.d.ts +630 -0
  84. package/superd-hub/dist/switch-core.js +734 -0
  85. package/superd-hub/dist/system-head.d.ts +69 -0
  86. package/superd-hub/dist/system-head.js +194 -0
  87. package/superd-hub/dist/web-trust.d.ts +112 -0
  88. package/superd-hub/dist/web-trust.js +126 -0
  89. package/superd-hub/dist/world-entry.d.ts +38 -0
  90. package/superd-hub/dist/world-entry.js +47 -0
  91. package/{agent-hub → superd-hub}/dist/world-host.d.ts +15 -20
  92. package/superd-hub/dist/world-host.js +65 -0
  93. package/{agent-hub → superd-hub}/dist/world-join.d.ts +2 -1
  94. package/{agent-hub → superd-hub}/dist/world-join.js +30 -50
  95. package/superd-hub/dist/world-mount.d.ts +82 -0
  96. package/superd-hub/dist/world-mount.js +105 -0
  97. package/{agent-hub → superd-hub}/dist/world-mux.d.ts +21 -6
  98. package/superd-hub/dist/world-mux.js +392 -0
  99. package/{agent-hub → superd-hub}/dist/world-provision.d.ts +20 -4
  100. package/superd-hub/dist/world-provision.js +203 -0
  101. package/superd-hub/dist/world-web-server.d.ts +113 -0
  102. package/superd-hub/dist/world-web-server.js +99 -0
  103. package/superd-hub/lib/client/index.js +2086 -0
  104. package/{agent-hub → superd-hub}/package.json +12 -7
  105. package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
  106. package/agent-agy/dist/agent-preset-projection.js +0 -33
  107. package/agent-claude/dist/agent-preset-projection.js +0 -30
  108. package/agent-codex/dist/agent-preset-projection.js +0 -37
  109. package/agent-codex/dist/app-home.js +0 -65
  110. package/agent-codex/dist/spike.js +0 -46
  111. package/agent-hermes/dist/agent-preset-projection.js +0 -34
  112. package/agent-hub/dist/carrier.d.ts +0 -43
  113. package/agent-hub/dist/carrier.js +0 -241
  114. package/agent-hub/dist/client-shim.d.ts +0 -28
  115. package/agent-hub/dist/client-shim.js +0 -251
  116. package/agent-hub/dist/gateway.js +0 -18
  117. package/agent-hub/dist/index-pass.d.ts +0 -22
  118. package/agent-hub/dist/index-pass.js +0 -47
  119. package/agent-hub/dist/labels.d.ts +0 -45
  120. package/agent-hub/dist/labels.js +0 -83
  121. package/agent-hub/dist/spawn-world.js +0 -72
  122. package/agent-hub/dist/world-entry.d.ts +0 -31
  123. package/agent-hub/dist/world-entry.js +0 -51
  124. package/agent-hub/dist/world-host.js +0 -44
  125. package/agent-hub/dist/world-mount.d.ts +0 -52
  126. package/agent-hub/dist/world-mount.js +0 -52
  127. package/agent-hub/dist/world-mux.js +0 -224
  128. package/agent-hub/dist/world-provision.js +0 -122
  129. package/agent-hub/dist/world-web-server.d.ts +0 -162
  130. package/agent-hub/dist/world-web-server.js +0 -248
  131. package/agent-hub/lib/client/index.js +0 -242
  132. package/agent-omp/dist/agent-preset-projection.js +0 -36
  133. package/agent-omp/dist/app-home.js +0 -86
  134. package/agent-pi/dist/agent-preset-projection.js +0 -29
  135. /package/{agent-hub → superd-hub}/dist/envelope.d.ts +0 -0
  136. /package/{agent-hub → superd-hub}/dist/envelope.js +0 -0
  137. /package/{agent-hub → superd-hub}/dist/ownership.d.ts +0 -0
  138. /package/{agent-hub → superd-hub}/dist/ownership.js +0 -0
  139. /package/{agent-hub → superd-hub}/dist/roster.js +0 -0
  140. /package/{agent-hub → superd-hub}/dist/rpc.d.ts +0 -0
  141. /package/{agent-hub → superd-hub}/dist/targets.d.ts +0 -0
  142. /package/{agent-hub → superd-hub}/dist/targets.js +0 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Protected system-surface head co-writer for bridge sessions (2026-09-24).
3
+ *
4
+ * dsh session format v4 (0.1.7 reader) constrains every `system/message`
5
+ * twice: it must sit INSIDE an open, matching turn+step (`requireStep`), and
6
+ * it must either BE the surface's first node (the protected head) or replace
7
+ * one ("system/message requires a protected first surface head"). Bridge
8
+ * adapters learn the runtime's real prompt only after the first user message
9
+ * is already on the surface, and most write the user message at turn level
10
+ * (before any step) — so every bridge-written log violated one rule or the
11
+ * other and became unresumable across restarts.
12
+ *
13
+ * The native loop never hits this because it opens the step before writing
14
+ * anything: `turn/start → step/start → system/message → user/message …`,
15
+ * and later prompt changes REPLACE the protected head (the operation
16
+ * compaction also uses). This module gives bridge adapters that posture with
17
+ * a one-line wrap:
18
+ *
19
+ * this.session = withSystemHead(session, () => createSystemMessage(""))
20
+ *
21
+ * The wrap is a transparent Proxy that tracks the open turn/step (exactly as
22
+ * the v4 reader does) and intervenes minimally:
23
+ *
24
+ * - the FIRST `user/message` append on a still-headless, still-empty
25
+ * surface first opens the turn's next step (when a turn is open but no
26
+ * step is) and appends a placeholder head; the adapters' real prompt
27
+ * later REPLACES it (replaced heads are shadowed out of derived model
28
+ * history, so the empty placeholder never reaches the model or the UI);
29
+ * - the adapter's own `step/start` for a step THIS wrap opened is dropped
30
+ * (the log already carries it — appending again would duplicate), and a
31
+ * wrap-opened step still open at `turn/end` is closed just before it;
32
+ * - `stampSystemPrompt(turn, step, message)` appends-or-replaces the
33
+ * protected head with the runtime's real prompt. A stamp arriving with
34
+ * no open step (an adapter bootstrapping before the first turn) is
35
+ * queued and flushed inside the next step the adapter opens;
36
+ * - everything else delegates unchanged.
37
+ *
38
+ * A surface that already starts with a user message and has no head (legacy
39
+ * logs) stays headless — valid — and is never stamped: appending a head
40
+ * there is exactly the corruption this module exists to prevent.
41
+ */
42
+ /**
43
+ * The session slice the co-writer reads. Deliberately loose so the real
44
+ * (generic-typed) `Session.append` satisfies it; the system-head calls go
45
+ * through {@link SystemHeadAppend}, the shape this module itself issues.
46
+ */
47
+ export interface HeadAwareSessionFace {
48
+ append(...args: never[]): unknown;
49
+ snapshotEvents(): ReadonlyArray<{
50
+ type: string;
51
+ seq: number;
52
+ surfaceOp?: unknown;
53
+ }>;
54
+ }
55
+ /** Builds the placeholder head message (adapters pass their createSystemMessage). */
56
+ export type HeadPlaceholder = () => unknown;
57
+ /** One co-written session: transparent delegation + head bookkeeping. */
58
+ export type WithSystemHead<S> = S & {
59
+ /** Append-or-replace the protected head with the runtime's system prompt. */
60
+ stampSystemPrompt(turn: number, step: number, message: unknown): void;
61
+ };
62
+ /**
63
+ * Wrap one bridge session with protected-head co-writing.
64
+ * @param session - the adapter's real Session.
65
+ * @param placeholder - builds the placeholder head message when the first
66
+ * user message arrives on an empty surface.
67
+ * @returns the session (transparently delegated) with `stampSystemPrompt`.
68
+ */
69
+ export declare function withSystemHead<S extends HeadAwareSessionFace>(session: S, placeholder: HeadPlaceholder): WithSystemHead<S>;
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Protected system-surface head co-writer for bridge sessions (2026-09-24).
3
+ *
4
+ * dsh session format v4 (0.1.7 reader) constrains every `system/message`
5
+ * twice: it must sit INSIDE an open, matching turn+step (`requireStep`), and
6
+ * it must either BE the surface's first node (the protected head) or replace
7
+ * one ("system/message requires a protected first surface head"). Bridge
8
+ * adapters learn the runtime's real prompt only after the first user message
9
+ * is already on the surface, and most write the user message at turn level
10
+ * (before any step) — so every bridge-written log violated one rule or the
11
+ * other and became unresumable across restarts.
12
+ *
13
+ * The native loop never hits this because it opens the step before writing
14
+ * anything: `turn/start → step/start → system/message → user/message …`,
15
+ * and later prompt changes REPLACE the protected head (the operation
16
+ * compaction also uses). This module gives bridge adapters that posture with
17
+ * a one-line wrap:
18
+ *
19
+ * this.session = withSystemHead(session, () => createSystemMessage(""))
20
+ *
21
+ * The wrap is a transparent Proxy that tracks the open turn/step (exactly as
22
+ * the v4 reader does) and intervenes minimally:
23
+ *
24
+ * - the FIRST `user/message` append on a still-headless, still-empty
25
+ * surface first opens the turn's next step (when a turn is open but no
26
+ * step is) and appends a placeholder head; the adapters' real prompt
27
+ * later REPLACES it (replaced heads are shadowed out of derived model
28
+ * history, so the empty placeholder never reaches the model or the UI);
29
+ * - the adapter's own `step/start` for a step THIS wrap opened is dropped
30
+ * (the log already carries it — appending again would duplicate), and a
31
+ * wrap-opened step still open at `turn/end` is closed just before it;
32
+ * - `stampSystemPrompt(turn, step, message)` appends-or-replaces the
33
+ * protected head with the runtime's real prompt. A stamp arriving with
34
+ * no open step (an adapter bootstrapping before the first turn) is
35
+ * queued and flushed inside the next step the adapter opens;
36
+ * - everything else delegates unchanged.
37
+ *
38
+ * A surface that already starts with a user message and has no head (legacy
39
+ * logs) stays headless — valid — and is never stamped: appending a head
40
+ * there is exactly the corruption this module exists to prevent.
41
+ */
42
+ /** Events that occupy a slot on the ordered message surface. */
43
+ const SURFACE_TYPES = new Set(['system/message', 'user/message', 'developer/message', 'assistant/message', 'tool/result']);
44
+ /** Seq of the live protected head: the LAST `system/message` on the surface (replacements shadow their source). */
45
+ function liveHeadSeq(events) {
46
+ let head;
47
+ for (const event of events) {
48
+ if (event.type === 'system/message' && event.surfaceOp !== undefined)
49
+ head = event.seq;
50
+ }
51
+ return head;
52
+ }
53
+ /** Whether any surface node exists yet. */
54
+ function surfaceIsEmpty(events) {
55
+ return !events.some(event => SURFACE_TYPES.has(event.type) && event.surfaceOp !== undefined);
56
+ }
57
+ /**
58
+ * Wrap one bridge session with protected-head co-writing.
59
+ * @param session - the adapter's real Session.
60
+ * @param placeholder - builds the placeholder head message when the first
61
+ * user message arrives on an empty surface.
62
+ * @returns the session (transparently delegated) with `stampSystemPrompt`.
63
+ */
64
+ export function withSystemHead(session, placeholder) {
65
+ // The one narrow this module performs: the loose face above is what makes
66
+ // the generic real Session assignable; every call below issues exactly the
67
+ // SystemHeadAppend shape.
68
+ const appendHead = session.append.bind(session);
69
+ const rawAppend = session.append.bind(session);
70
+ // Reader-mirrored turn/step state.
71
+ let turn = null;
72
+ let step = null;
73
+ let nextStep = 1;
74
+ let wrapOpenedStep = false;
75
+ let wrapClosedStep;
76
+ let headSeq;
77
+ let queuedStamp;
78
+ const openWrapStep = () => {
79
+ if (turn === null || step !== null)
80
+ return step !== null;
81
+ rawAppend('step/start', { turn, step: nextStep });
82
+ step = nextStep;
83
+ wrapOpenedStep = true;
84
+ return true;
85
+ };
86
+ const ensureHead = () => {
87
+ if (headSeq !== undefined)
88
+ return;
89
+ headSeq = liveHeadSeq(session.snapshotEvents());
90
+ if (headSeq !== undefined)
91
+ return;
92
+ if (!surfaceIsEmpty(session.snapshotEvents()))
93
+ return; // headless-but-valid legacy surface: never append here
94
+ if (!openWrapStep())
95
+ return; // no open turn: a head is impossible; stay headless
96
+ headSeq = appendHead('system/message', { turn: turn, step: step, message: placeholder() }, { surfaceOp: 'append' }).seq;
97
+ };
98
+ const stamp = (turnArg, stepArg, message) => {
99
+ if (step === null) {
100
+ // No open step (adapter bootstrapping before the first turn): queue and
101
+ // flush inside the step the adapter opens next.
102
+ queuedStamp = { turn: turnArg, step: stepArg, message };
103
+ return;
104
+ }
105
+ if (headSeq === undefined) {
106
+ headSeq = liveHeadSeq(session.snapshotEvents());
107
+ if (headSeq === undefined && surfaceIsEmpty(session.snapshotEvents())) {
108
+ headSeq = appendHead('system/message', { turn: turnArg, step: stepArg, message }, { surfaceOp: 'append' }).seq;
109
+ return;
110
+ }
111
+ }
112
+ if (headSeq === undefined) {
113
+ queuedStamp = { turn: turnArg, step: stepArg, message };
114
+ return; // headless legacy surface: stamping would corrupt it
115
+ }
116
+ headSeq = appendHead('system/message', { turn: turn ?? turnArg, step: step ?? stepArg, message }, { surfaceOp: { op: 'replace', startSeq: headSeq, endSeq: headSeq }, sourceEventSeqs: [headSeq] }).seq;
117
+ };
118
+ return new Proxy(session, {
119
+ get(target, property) {
120
+ if (property === 'stampSystemPrompt') {
121
+ return (turnArg, stepArg, message) => { stamp(turnArg, stepArg, message); };
122
+ }
123
+ if (property === 'append') {
124
+ return (type, data, opts) => {
125
+ if (type === 'turn/start') {
126
+ turn = data.turn;
127
+ nextStep = 1;
128
+ wrapOpenedStep = false;
129
+ wrapClosedStep = undefined;
130
+ }
131
+ else if (type === 'turn/end') {
132
+ // A step THIS wrap opened must not survive the turn boundary.
133
+ if (step !== null && wrapOpenedStep) {
134
+ rawAppend('step/end', { turn, step });
135
+ wrapClosedStep = step;
136
+ step = null;
137
+ nextStep += 1;
138
+ }
139
+ turn = null;
140
+ }
141
+ else if (type === 'step/start') {
142
+ const dataStep = data;
143
+ if (step !== null && wrapOpenedStep && dataStep.turn === turn && dataStep.step === step) {
144
+ // The adapter opened the step the wrap already wrote: drop the
145
+ // duplicate (the adapter's own counters stay consistent).
146
+ return { type, seq: session.snapshotEvents().length, time: Date.now(), data };
147
+ }
148
+ if (step === null && dataStep.step !== undefined)
149
+ step = dataStep.step;
150
+ wrapOpenedStep = false;
151
+ const applied = rawAppend(type, data);
152
+ if (queuedStamp !== undefined && step !== null) {
153
+ const pending = queuedStamp;
154
+ queuedStamp = undefined;
155
+ stamp(pending.turn, pending.step, pending.message);
156
+ }
157
+ return applied;
158
+ }
159
+ else if (type === 'step/end') {
160
+ const dataStep = data;
161
+ if (step === null && wrapClosedStep !== undefined && dataStep.step === wrapClosedStep) {
162
+ wrapClosedStep = undefined;
163
+ return { type, seq: session.snapshotEvents().length, time: Date.now(), data }; // the wrap already closed it
164
+ }
165
+ step = null;
166
+ if (dataStep.step !== undefined)
167
+ nextStep = dataStep.step + 1;
168
+ }
169
+ else if (type === 'user/message') {
170
+ ensureHead();
171
+ }
172
+ else if (type === 'system/message') {
173
+ // A direct adapter append (notices, bootstraps): give it the step
174
+ // context the reader demands — open the turn's next step when
175
+ // none is open and NORMALIZE the coordinates to that context
176
+ // (requireStep compares them for equality; a mismatched append
177
+ // is doomed otherwise). Surface ordering beyond the head rules
178
+ // stays the adapter's own responsibility.
179
+ if (step === null && turn !== null)
180
+ openWrapStep();
181
+ if (step !== null && turn !== null) {
182
+ const direct = data;
183
+ if (direct.turn !== turn || direct.step !== step)
184
+ data = { ...data, turn, step };
185
+ }
186
+ }
187
+ return Reflect.apply(target.append, target, [type, data, opts]);
188
+ };
189
+ }
190
+ const value = Reflect.get(target, property, target);
191
+ return typeof value === 'function' ? value.bind(target) : value;
192
+ },
193
+ });
194
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Web page trust component — the browser-side twin of the connection row's
3
+ * server-side `trustedHosts` fence (ported from better-dsh `web-trust.ts`,
4
+ * authorities leg only).
5
+ *
6
+ * Why: when the Web UI is served on an operator-declared non-loopback
7
+ * authority (Caddy domain, LAN IP), the server side accepts the Host through
8
+ * `trustedHosts`, but the BROWSER-side connection client computes
9
+ * `ctx.connection.isLoopback` from `location.hostname` — non-loopback → the
10
+ * page degrades into the describe-mirror shape (Host settings persistence
11
+ * reports terminal `memory` unavailability). This component injects one
12
+ * inline `<head>` script through the webserver's public
13
+ * `webserver/index-inject` event (every index render re-emits; row data is
14
+ * read fresh at emit time, so a config reload needs no listener re-wiring):
15
+ * when the page's hostname is one the operator declared their own, it sets
16
+ * `window.__DSH_TRANSPORT__ = { ownsHost: true }` BEFORE any application
17
+ * bundle materializes — the connection client's only whole-repo consumer —
18
+ * restoring the upstream-documented shape for trusted authorities.
19
+ *
20
+ * The object deliberately carries NO fetch/openStream: the connection
21
+ * client's optional chaining then builds the default HTTP/WS transport, so
22
+ * the flag's only effect is the loopback verdict. An existing
23
+ * `__DSH_TRANSPORT__` (a worker shell that owns a real transport) is never
24
+ * overwritten.
25
+ *
26
+ * On/off (two independent levels, both standard):
27
+ * - row level: a profile layer with `- id: superd-web-trust / disabled: true`
28
+ * removes the component entirely;
29
+ * - config level: `trustedPageAuthorities: []` keeps the row mounted but
30
+ * injects nothing (fully inert).
31
+ *
32
+ * Composed on ctx0 only (since path-separation T-D): the `super-dsh/web-trust`
33
+ * row in the composite bundle's insert list. The per-world row retired with
34
+ * the world pages themselves (T-D disabled the world webui rows — no world
35
+ * page renders, so no world-local injection exists); `/hub` is the sole
36
+ * foreign entry and it serves ctx0's index, which this ctx0 row covers.
37
+ *
38
+ * @module @pgmi-builds/superd-hub/web-trust
39
+ */
40
+ import type { Context } from '@deepseek-ai/cordis';
41
+ import z from '@deepseek-ai/schemastery';
42
+ /**
43
+ * The schema-level default for `trustedPageAuthorities` (better-dsh v0.2.2a
44
+ * shape): derive from the trusted-hosts environment — the SAME single source
45
+ * the server-side fence legs read (`SUPERD_TRUSTED_HOSTS` on this line, upstream
46
+ * spelling `DSH_TRUSTED_HOSTS` honored first) — so ONE declaration (e.g. one
47
+ * `Environment=` line in the service unit) drives the server fence and the
48
+ * page leg with zero per-instance plugin config. Bare hostnames only:
49
+ * `location.hostname` carries no port, and {@link assertBareHostname} would
50
+ * reject a portful entry at boot, so portful/schematic entries are dropped
51
+ * here instead of thrown. Declared in the Config SCHEMA rather than the
52
+ * bundle patch row because a profile/home layer row with the same id
53
+ * whole-row-overrides the row's config object, while schema defaults fill
54
+ * per-key at plugin load and survive every overlay layer. An explicit
55
+ * configured value always wins.
56
+ *
57
+ * @param env - the raw trusted-hosts value (whitespace/comma separated).
58
+ * @returns the bare-hostname authorities to trust by default.
59
+ */
60
+ export declare function deriveDefaultPageAuthorities(env: string | undefined): string[];
61
+ /** The `webserver/index-inject` row shape this component pushes. */
62
+ export interface BootScriptRow {
63
+ kind: 'script';
64
+ placement: 'head';
65
+ text: string;
66
+ }
67
+ /**
68
+ * Build the authorities-leg script. Pure: same config in, same script out —
69
+ * the unit tests pin the shape (authority match, no-overwrite guard,
70
+ * inert-when-empty, hostile-string escaping).
71
+ *
72
+ * @param trustedPageAuthorities - the operator-declared hostnames.
73
+ * @returns the script text, or `undefined` when the list is empty (inject
74
+ * nothing — the component is fully inert).
75
+ */
76
+ export declare function buildTrustScript(trustedPageAuthorities: readonly string[] | undefined): string | undefined;
77
+ /** Row config schema. */
78
+ export interface TrustConfig {
79
+ /** Hostnames this operator declares their own devices' pages run on. */
80
+ trustedPageAuthorities?: readonly string[];
81
+ }
82
+ /** Row config schema (schema-level default derives from the trusted-hosts env). */
83
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
84
+ trustedPageAuthorities: z<string[], string[], "defined">;
85
+ }>>, Schemastery.ObjectT<NoInfer<{
86
+ trustedPageAuthorities: z<string[], string[], "defined">;
87
+ }>>, "plain">;
88
+ /** Cordis plugin name. */
89
+ export declare const name = "superd-web-trust";
90
+ /**
91
+ * No static injects: `webServer` is composed conditionally inside
92
+ * {@link apply}, so the row loads (dormant) in compositions without one.
93
+ */
94
+ export declare const inject: string[];
95
+ /**
96
+ * Mount the authorities-leg injection: one `webserver/index-inject` listener
97
+ * pushing the rendered row.
98
+ * @param ctx - host plugin context.
99
+ * @param config - the row config.
100
+ */
101
+ export declare function apply(ctx: Context, config: TrustConfig | undefined): void;
102
+ declare const _default: {
103
+ name: string;
104
+ inject: string[];
105
+ Config: z<Schemastery.ObjectS<NoInfer<{
106
+ trustedPageAuthorities: z<string[], string[], "defined">;
107
+ }>>, Schemastery.ObjectT<NoInfer<{
108
+ trustedPageAuthorities: z<string[], string[], "defined">;
109
+ }>>, "plain">;
110
+ apply: typeof apply;
111
+ };
112
+ export default _default;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Web page trust component — the browser-side twin of the connection row's
3
+ * server-side `trustedHosts` fence (ported from better-dsh `web-trust.ts`,
4
+ * authorities leg only).
5
+ *
6
+ * Why: when the Web UI is served on an operator-declared non-loopback
7
+ * authority (Caddy domain, LAN IP), the server side accepts the Host through
8
+ * `trustedHosts`, but the BROWSER-side connection client computes
9
+ * `ctx.connection.isLoopback` from `location.hostname` — non-loopback → the
10
+ * page degrades into the describe-mirror shape (Host settings persistence
11
+ * reports terminal `memory` unavailability). This component injects one
12
+ * inline `<head>` script through the webserver's public
13
+ * `webserver/index-inject` event (every index render re-emits; row data is
14
+ * read fresh at emit time, so a config reload needs no listener re-wiring):
15
+ * when the page's hostname is one the operator declared their own, it sets
16
+ * `window.__DSH_TRANSPORT__ = { ownsHost: true }` BEFORE any application
17
+ * bundle materializes — the connection client's only whole-repo consumer —
18
+ * restoring the upstream-documented shape for trusted authorities.
19
+ *
20
+ * The object deliberately carries NO fetch/openStream: the connection
21
+ * client's optional chaining then builds the default HTTP/WS transport, so
22
+ * the flag's only effect is the loopback verdict. An existing
23
+ * `__DSH_TRANSPORT__` (a worker shell that owns a real transport) is never
24
+ * overwritten.
25
+ *
26
+ * On/off (two independent levels, both standard):
27
+ * - row level: a profile layer with `- id: superd-web-trust / disabled: true`
28
+ * removes the component entirely;
29
+ * - config level: `trustedPageAuthorities: []` keeps the row mounted but
30
+ * injects nothing (fully inert).
31
+ *
32
+ * Composed on ctx0 only (since path-separation T-D): the `super-dsh/web-trust`
33
+ * row in the composite bundle's insert list. The per-world row retired with
34
+ * the world pages themselves (T-D disabled the world webui rows — no world
35
+ * page renders, so no world-local injection exists); `/hub` is the sole
36
+ * foreign entry and it serves ctx0's index, which this ctx0 row covers.
37
+ *
38
+ * @module @pgmi-builds/superd-hub/web-trust
39
+ */
40
+ import z from '@deepseek-ai/schemastery';
41
+ /**
42
+ * The schema-level default for `trustedPageAuthorities` (better-dsh v0.2.2a
43
+ * shape): derive from the trusted-hosts environment — the SAME single source
44
+ * the server-side fence legs read (`SUPERD_TRUSTED_HOSTS` on this line, upstream
45
+ * spelling `DSH_TRUSTED_HOSTS` honored first) — so ONE declaration (e.g. one
46
+ * `Environment=` line in the service unit) drives the server fence and the
47
+ * page leg with zero per-instance plugin config. Bare hostnames only:
48
+ * `location.hostname` carries no port, and {@link assertBareHostname} would
49
+ * reject a portful entry at boot, so portful/schematic entries are dropped
50
+ * here instead of thrown. Declared in the Config SCHEMA rather than the
51
+ * bundle patch row because a profile/home layer row with the same id
52
+ * whole-row-overrides the row's config object, while schema defaults fill
53
+ * per-key at plugin load and survive every overlay layer. An explicit
54
+ * configured value always wins.
55
+ *
56
+ * @param env - the raw trusted-hosts value (whitespace/comma separated).
57
+ * @returns the bare-hostname authorities to trust by default.
58
+ */
59
+ export function deriveDefaultPageAuthorities(env) {
60
+ return (env ?? '').split(/[\s,]+/).filter(h => h.length > 0 && !/[:/@?#]/.test(h));
61
+ }
62
+ /** Assert one configured authority is a bare hostname (no port/path/scheme). */
63
+ function assertBareHostname(entry) {
64
+ if (entry.length === 0 || /[:/@?#]/.test(entry)) {
65
+ throw new Error(`superd-web-trust: trustedPageAuthorities entry ${JSON.stringify(entry)} is not a bare hostname `
66
+ + '(location.hostname carries no port; an entry with port/path would never match)');
67
+ }
68
+ }
69
+ /** The IIFE framing (a throwing leg stays invisible on the page). */
70
+ function frame(body) {
71
+ return ['(function(){try{', ...body, '}catch(e){}})();'].join('');
72
+ }
73
+ /**
74
+ * Build the authorities-leg script. Pure: same config in, same script out —
75
+ * the unit tests pin the shape (authority match, no-overwrite guard,
76
+ * inert-when-empty, hostile-string escaping).
77
+ *
78
+ * @param trustedPageAuthorities - the operator-declared hostnames.
79
+ * @returns the script text, or `undefined` when the list is empty (inject
80
+ * nothing — the component is fully inert).
81
+ */
82
+ export function buildTrustScript(trustedPageAuthorities) {
83
+ const authorities = [...(trustedPageAuthorities ?? [])];
84
+ if (authorities.length === 0)
85
+ return undefined;
86
+ for (const entry of authorities)
87
+ assertBareHostname(entry);
88
+ return frame([
89
+ 'if(!window.__DSH_TRANSPORT__){',
90
+ `var h=location.hostname,a=${JSON.stringify(authorities)};`,
91
+ 'for(var i=0;i<a.length;i++)if(a[i]===h){window.__DSH_TRANSPORT__={ownsHost:true};break}',
92
+ '}',
93
+ ]);
94
+ }
95
+ /** The trusted-hosts single source: upstream spelling first, this line's second. */
96
+ function trustedHostsEnv() {
97
+ return process.env.DSH_TRUSTED_HOSTS ?? process.env.SUPERD_TRUSTED_HOSTS;
98
+ }
99
+ /** Row config schema (schema-level default derives from the trusted-hosts env). */
100
+ export const Config = z.object({
101
+ trustedPageAuthorities: z.array(String).default(deriveDefaultPageAuthorities(trustedHostsEnv())),
102
+ });
103
+ /** Cordis plugin name. */
104
+ export const name = 'superd-web-trust';
105
+ /**
106
+ * No static injects: `webServer` is composed conditionally inside
107
+ * {@link apply}, so the row loads (dormant) in compositions without one.
108
+ */
109
+ export const inject = [];
110
+ /**
111
+ * Mount the authorities-leg injection: one `webserver/index-inject` listener
112
+ * pushing the rendered row.
113
+ * @param ctx - host plugin context.
114
+ * @param config - the row config.
115
+ */
116
+ export function apply(ctx, config) {
117
+ const authorities = config?.trustedPageAuthorities;
118
+ ctx.inject(['webServer'], () => {
119
+ ctx.on('webserver/index-inject', (table) => {
120
+ const text = buildTrustScript(authorities);
121
+ if (text !== undefined)
122
+ table.push({ kind: 'script', placement: 'head', text });
123
+ });
124
+ });
125
+ }
126
+ export default { name, inject, Config, apply };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * World-side mount plugin (AW-B DL1/DL3) — `@pgmi-builds/superd-hub/world`.
3
+ *
4
+ * Inserted into the WORLD root's composition (its own process-side root, no
5
+ * listener). Since path-separation T-D (2026-09-26, "disable their webui
6
+ * build, just like disable their webserver") it provides the world's whole
7
+ * remaining web-adjacent surface, shrunk to data-plane facts:
8
+ *
9
+ * - the VIRTUAL `webServer` — a silent, registration-only shell
10
+ * (`world-web-server.ts`): every route a world plugin declares is
11
+ * accepted and swallowed, so the tree boots exactly as before while
12
+ * nothing is ever served (`/hub` is the sole entry, T-C);
13
+ * - the neutral `webRuntime` values — the ONE leg the disabled `web-runtime`
14
+ * row used to provide that the DATA plane still reads (the `connection`
15
+ * row injects it): loopback bind ⇒ no LAN literals, plus the deploy-level
16
+ * `--trusted-host` extras spawnWorld seeds the tree with (carried here by
17
+ * `worldMountPatches` in the row config).
18
+ *
19
+ * The RPC channel is NOT the world's: the hub's `/hub` family
20
+ * (`hub-route.ts`) answers `?agent=<key>` traffic in ctx0's auth domain,
21
+ * dispatching to this world's gateway instance. Non-envelope GET/HEAD traffic
22
+ * is forwarded into the world's registered exact Fetch routes (session-log
23
+ * export and friends) through the shared `/api` fetch handler this plugin
24
+ * publishes into the cross-root registry (`world-host.ts`) — still behind
25
+ * ctx0's fence, never the world's own.
26
+ */
27
+ import type { Context } from '@deepseek-ai/cordis';
28
+ /** Stable Cordis plugin name. */
29
+ export declare const name = "superd-world-mount";
30
+ /** Row config: which world this root is. */
31
+ export interface Config {
32
+ key: string;
33
+ }
34
+ /** Apply the world mount. @param ctx - the WORLD root context. @param config - `{ key }`. */
35
+ export declare function apply(ctx: Context, config?: {
36
+ key?: unknown;
37
+ }): void;
38
+ export default apply;
@@ -0,0 +1,47 @@
1
+ import { noteWorldServer, publishWorldFetch, takeHostMount } from './world-host.js';
2
+ import { WorldWebServer } from './world-web-server.js';
3
+ import { superdTrustedHosts } from './world-mount.js';
4
+ /** Stable Cordis plugin name. */
5
+ export const name = 'superd-world-mount';
6
+ /** Apply the world mount. @param ctx - the WORLD root context. @param config - `{ key }`. */
7
+ export function apply(ctx, config) {
8
+ const key = typeof config?.key === 'string' ? config.key : undefined;
9
+ if (key === undefined) {
10
+ ctx.logger?.warn?.('superd-world-mount: missing config.key — world not mounted');
11
+ return;
12
+ }
13
+ const mount = takeHostMount(key);
14
+ if (mount === undefined) {
15
+ ctx.logger?.warn?.(`superd-world-mount: no host mount registered for key ${JSON.stringify(key)}`);
16
+ return;
17
+ }
18
+ const worldWebServer = new WorldWebServer(mount.labelPath);
19
+ noteWorldServer(key, worldWebServer);
20
+ ctx.reflect.provide('webServer', worldWebServer);
21
+ // The data-plane leg of the disabled `web-runtime` row: the `connection`
22
+ // row injects `webRuntime` (and reads `ctx.webRuntime.trustedHosts` in its
23
+ // config). Same neutral shape as before T-D: a loopback-only bind resolves
24
+ // no LAN literals; the trusted-host extras are the same deploy-level env
25
+ // list `spawnWorld` feeds this tree as `--trusted-host` arguments (one
26
+ // derivation: `superdTrustedHosts`).
27
+ ctx.reflect.provide('webRuntime', { lanAddresses: [], trustedHosts: superdTrustedHosts(process.env.SUPERD_TRUSTED_HOSTS) });
28
+ // The world's `/api` fetch handler (exact Fetch routes + the gateway's RPC
29
+ // interceptor) is published here so the hub's `/hub` family can forward
30
+ // non-envelope GET/HEAD traffic into the world's registered routes; auth
31
+ // stays in ctx0's domain because the family fences the request BEFORE this
32
+ // handler runs. (The world's own index-authorization neutralization is
33
+ // gone with T-D: no world page exists to authorize.)
34
+ let dropWorldFetch;
35
+ ctx.inject(['connection'], (webCtx) => {
36
+ const connection = webCtx.get('connection');
37
+ if (connection === undefined)
38
+ return;
39
+ if (typeof connection.createSharedFetchHandler === 'function') {
40
+ dropWorldFetch = publishWorldFetch(key, connection.createSharedFetchHandler('/api'));
41
+ }
42
+ });
43
+ ctx.effect(() => () => {
44
+ dropWorldFetch?.();
45
+ }, 'superd-world-mount: virtual webServer');
46
+ }
47
+ export default apply;
@@ -4,32 +4,26 @@
4
4
  * A world runs as its OWN cordis ROOT (events are root-scoped — probe
5
5
  * 2026-09-16), so ctx0 and the world cannot share service instances through
6
6
  * the context tree. They DO share this module (the profile links
7
- * `@pgmi-builds/agent-hub` at the same path), which is where the host hands the
8
- * world everything it needs to mount itself:
7
+ * `@pgmi-builds/superd-hub` at the same path), which is where the host hands
8
+ * the world what it needs to mount itself:
9
9
  *
10
- * ctx0 hub → registerHostMount({ key, labelPath, real }) (before spawn)
10
+ * ctx0 hub → registerHostMount({ key, labelPath }) (before spawn)
11
11
  * world → takeHostMount(key) → provides the virtual `webServer`
12
12
  *
13
+ * T-C (2026-09-26): the `real`/`authorize` legs were RETIRED with the
14
+ * per-label URL family. T-D (2026-09-26) swept the vestiges: the mount record
15
+ * is the world's ADDRESSABILITY entry only (`{ key, labelPath }`) — no ctx0
16
+ * webServer instance, no index gate; the adapter world-plugins' call sites
17
+ * were updated in the same round.
18
+ *
13
19
  * No selection state: the key is the world's own identity, fixed at spawn.
14
20
  */
15
- import type { RealWebServerFace } from './world-web-server.js';
16
- /** What a world needs to mount its surface onto ctx0's real webServer. */
21
+ /** What the host publishes for one world mount (the world's identity record). */
17
22
  export interface HostMount {
18
23
  /** Runtime key (`omp`, `codex`, …) — equals the mount label. */
19
24
  key: string;
20
25
  /** Mount root (`/omp`). */
21
26
  labelPath: string;
22
- /** ctx0's webServer instance (live object, passed across roots). */
23
- real: RealWebServerFace;
24
- /**
25
- * ctx0's index authorization (the SAME gate the native index goes through).
26
- * The mount is the only ingress and answers the world's pages/assets itself,
27
- * so auth is decided in ctx0's domain — by the cookie that authenticated
28
- * `<label>` — and never by the world's own browser-auth gate (a different
29
- * tree, a different secret). Returning false means the authorizer already
30
- * answered (401, or the token→cookie 303).
31
- */
32
- authorize?(req: unknown, res: unknown): boolean;
33
27
  }
34
28
  /** Publish the host side of one world mount (idempotent replace). */
35
29
  export declare function registerHostMount(mount: HostMount): () => void;
@@ -59,10 +53,11 @@ export interface WorldFetchHandler {
59
53
  fetch(request: Request): Promise<Response>;
60
54
  }
61
55
  /**
62
- * Publish one world's shared `/api` fetch handler so the carrier can forward
63
- * non-envelope traffic on `/<label>/api` into the world's registered routes
64
- * (session-log export and friends). Called from inside the world root
65
- * (`world-entry`); read from the host root (`carrier`). Idempotent replace.
56
+ * Publish one world's shared `/api` fetch handler so the hub's `/hub` family
57
+ * can forward non-envelope traffic (`?agent=<label>` GET/HEAD) into the
58
+ * world's registered routes (session-log export and friends). Called from
59
+ * inside the world root (`world-entry`); read from the host root
60
+ * (`carrier`). Idempotent replace.
66
61
  */
67
62
  export declare function publishWorldFetch(key: string, handler: WorldFetchHandler): () => void;
68
63
  /** The world's published shared `/api` fetch handler, if its root published one yet. */