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
@@ -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 BOTH trees: ctx0 through the `super-dsh/web-trust` row (the
33
+ * composite bundle's insert list) and every world through the
34
+ * `@pgmi-builds/superd-hub/web-trust` row (world mount patches) — world pages
35
+ * emit `webserver/index-inject` on their own root, so a ctx0-only listener
36
+ * would never reach them.
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 BOTH trees: ctx0 through the `super-dsh/web-trust` row (the
33
+ * composite bundle's insert list) and every world through the
34
+ * `@pgmi-builds/superd-hub/web-trust` row (world mount patches) — world pages
35
+ * emit `webserver/index-inject` on their own root, so a ctx0-only listener
36
+ * would never reach them.
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 };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * World-side mount plugin (AW-B DL1/DL3) — `@pgmi-builds/agent-hub/world`.
2
+ * World-side mount plugin (AW-B DL1/DL3) — `@pgmi-builds/superd-hub/world`.
3
3
  *
4
4
  * Inserted into the WORLD root's composition (its own process-side root, no
5
5
  * listener). It provides the virtual `webServer` for that root: every route the
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import type { Context } from '@deepseek-ai/cordis';
21
21
  /** Stable Cordis plugin name. */
22
- export declare const name = "aw.world.mount";
22
+ export declare const name = "superd-world-mount";
23
23
  /** Row config: which world this root is. */
24
24
  export interface Config {
25
25
  key: string;
@@ -1,17 +1,17 @@
1
1
  import { noteWorldServer, publishWorldFetch, takeHostMount } from './world-host.js';
2
2
  import { WorldWebServer } from './world-web-server.js';
3
3
  /** Stable Cordis plugin name. */
4
- export const name = 'aw.world.mount';
4
+ export const name = 'superd-world-mount';
5
5
  /** Apply the world mount. @param ctx - the WORLD root context. @param config - `{ key }`. */
6
6
  export function apply(ctx, config) {
7
7
  const key = typeof config?.key === 'string' ? config.key : undefined;
8
8
  if (key === undefined) {
9
- ctx.logger?.warn?.('aw.world.mount: missing config.key — world not mounted');
9
+ ctx.logger?.warn?.('superd-world-mount: missing config.key — world not mounted');
10
10
  return;
11
11
  }
12
12
  const mount = takeHostMount(key);
13
13
  if (mount === undefined) {
14
- ctx.logger?.warn?.(`aw.world.mount: no host mount registered for key ${JSON.stringify(key)}`);
14
+ ctx.logger?.warn?.(`superd-world-mount: no host mount registered for key ${JSON.stringify(key)}`);
15
15
  return;
16
16
  }
17
17
  const worldWebServer = new WorldWebServer(mount.labelPath, mount.real, {
@@ -46,6 +46,6 @@ export function apply(ctx, config) {
46
46
  ctx.effect(() => () => {
47
47
  worldWebServer.dispose();
48
48
  dropWorldFetch?.();
49
- }, 'aw.world.mount: virtual webServer');
49
+ }, 'superd-world-mount: virtual webServer');
50
50
  }
51
51
  export default apply;
@@ -4,7 +4,7 @@
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
7
+ * `@pgmi-builds/superd-hub` at the same path), which is where the host hands the
8
8
  * world everything it needs to mount itself:
9
9
  *
10
10
  * ctx0 hub → registerHostMount({ key, labelPath, real }) (before spawn)
@@ -1,5 +1,5 @@
1
1
  /** Stable Cordis plugin name. */
2
- export declare const name = "aw.world.join";
2
+ export declare const name = "superd-join";
3
3
  /** Row config: which world to join, under which roster label. */
4
4
  export interface Config {
5
5
  key: string;
@@ -10,6 +10,7 @@ interface HostContext {
10
10
  provide(name: string, value: unknown): void;
11
11
  inject(deps: string[], callback: (ctx: unknown) => void): unknown;
12
12
  effect?(callback: () => () => void, label?: string): unknown;
13
+ get(name: string): unknown;
13
14
  }
14
15
  /** Apply the join. @param ctx - the CTX0 root context. @param config - `{ key, label? }`. */
15
16
  export declare function apply(ctx: HostContext, config?: Partial<Config>): void;
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Hub-side world joiner (AW-B DL1–DL5) — `@pgmi-builds/agent-hub/join`.
2
+ * Hub-side world joiner (AW-B DL1–DL5) — `@pgmi-builds/superd-hub/join`.
3
3
  *
4
4
  * Ruling R2 (2026-09-16): an adapter may ship STANDALONE only — joining the
5
- * agent-worlds hub is the HUB side's job. This plugin is that job, generic:
5
+ * super-dsh hub is the HUB side's job. This plugin is that job, generic:
6
6
  * the ctx0 composition inserts one row with `{ key }` and the hub does the
7
7
  * spawn/roster/mount wiring the adapter's own world plugin would otherwise
8
8
  * carry (the omp/codex adapters ship active per-adapter plugins; the claude
@@ -31,19 +31,17 @@ import { join } from 'node:path';
31
31
  import { registerAgent, setReady } from './roster.js';
32
32
  import { registerForeignTarget } from './targets.js';
33
33
  import { spawnWorld } from './spawn-world.js';
34
- import { provisionWorldProfile } from './world-provision.js';
34
+ import { provisionWorldProfile, resolveInstallAnchor } from './world-provision.js';
35
35
  import { registerHostMount } from './world-host.js';
36
36
  import { mountWorld } from './carrier.js';
37
37
  import { worldMountPatches } from './world-mount.js';
38
38
  /** Stable Cordis plugin name. */
39
- export const name = 'aw.world.join';
40
- const ANCHOR = process.env.SUPERD_DSH_ANCHOR
41
- ?? '/home/u1/.local/lib/node_modules/@deepseek-ai/dsh/package.json';
39
+ export const name = 'superd-join';
42
40
  /** Apply the join. @param ctx - the CTX0 root context. @param config - `{ key, label? }`. */
43
41
  export function apply(ctx, config) {
44
42
  const key = typeof config?.key === 'string' ? config.key : undefined;
45
43
  if (key === undefined || key === '' || key.includes('/')) {
46
- ctx.provide('aw.world.join.error', `invalid config.key ${JSON.stringify(config?.key)}`);
44
+ ctx.provide('superd.world.join.error', `invalid config.key ${JSON.stringify(config?.key)}`);
47
45
  return;
48
46
  }
49
47
  const label = typeof config?.label === 'string' && config.label !== '' ? config.label : key;
@@ -58,12 +56,10 @@ export function apply(ctx, config) {
58
56
  const worldHome = join(root, 'agents', key);
59
57
  mkdirSync(join(worldHome, 'profiles'), { recursive: true });
60
58
  // Publish story: a foreign install has no line bootstrap, so the nested
61
- // profile + @pgmi-builds links are provisioned HERE (idempotent; dev lines
62
- // keep parity — smoke.mjs wrote the same layout, and AW_BARE_BASE still
63
- // overrides the resolved base).
64
- const bareBase = provisionWorldProfile({
59
+ // profile + @pgmi-builds links are provisioned HERE (idempotent).
60
+ provisionWorldProfile({
65
61
  key,
66
- adapterPkg: '@pgmi-builds/agent-adapter-claude',
62
+ adapterPkg: '@pgmi-builds/agent-claude',
67
63
  worldHome,
68
64
  });
69
65
  const world = (async () => {
@@ -84,16 +80,15 @@ export function apply(ctx, config) {
84
80
  authorize: (req, res) => connection.authorizeIndex(req, res),
85
81
  });
86
82
  const ctxW = await spawnWorld({
87
- appName: `aw-${key}`,
83
+ appName: `superd-${key}`,
88
84
  profileName: 'web',
89
- installAnchor: ANCHOR,
85
+ installAnchor: resolveInstallAnchor(ctx), // lazy: never at module scope — import must stay side-effect-free
90
86
  home: worldHome,
91
87
  dataHome: worldHome,
92
- // Adapter-local bundle names resolve from the profile's own
93
- // node_modules (heal owns @deepseek-ai there; @pgmi-builds links are
94
- // provisioned by provisionWorldProfile above, or by the line bootstrap
95
- // when the adapter package was not resolvable — env wins over both).
96
- bareModuleBaseUrl: process.env.AW_BARE_BASE ?? bareBase ?? undefined,
88
+ // The host's live table carries this world: spawnWorld republishes a
89
+ // widened successor before boot imports anything (spec §2.1), and
90
+ // @pgmi-builds rows resolve from the profile-local links written above.
91
+ host: ctx,
97
92
  extraPatches: worldMountPatches(key),
98
93
  });
99
94
  const gateway = ctxW.get('typertGateway');
@@ -109,7 +104,7 @@ export function apply(ctx, config) {
109
104
  ctx.effect?.(() => () => {
110
105
  mounted.dispose();
111
106
  dropHostMount();
112
- }, `aw.world.join: ${key} mount`);
107
+ }, `superd-join: ${key} mount`);
113
108
  return ctxW;
114
109
  })().catch((cause) => {
115
110
  // A failed world degrades to a not-ready roster row; a rejected provided
@@ -117,6 +112,6 @@ export function apply(ctx, config) {
117
112
  console.error(`[super-dsh] ${key} world failed:`, cause);
118
113
  return null;
119
114
  });
120
- ctx.provide(`aw.world.${key}`, world);
115
+ ctx.provide(`superd.world.${key}`, world);
121
116
  }
122
117
  export default apply;