super-dsh 0.1.0 → 0.1.2

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 (178) hide show
  1. package/agent-agy/bridge/agy_bridge.py +250 -0
  2. package/agent-agy/bridge/gemini_openai_server.mjs +98 -0
  3. package/agent-agy/cordis.patch.yml +49 -0
  4. package/agent-agy/dist/adapter.d.ts +15 -0
  5. package/agent-agy/dist/adapter.js +42 -0
  6. package/agent-agy/dist/agent-preset-agy.d.ts +47 -0
  7. package/agent-agy/dist/agent-preset-agy.js +169 -0
  8. package/agent-agy/dist/agent-preset-projection.d.ts +31 -0
  9. package/agent-agy/dist/agent-preset-projection.js +33 -0
  10. package/agent-agy/dist/agent.d.ts +137 -0
  11. package/agent-agy/dist/agent.js +493 -0
  12. package/agent-agy/dist/agy-cli-client.d.ts +137 -0
  13. package/agent-agy/dist/agy-cli-client.js +301 -0
  14. package/agent-agy/dist/agy-client.d.ts +160 -0
  15. package/agent-agy/dist/agy-client.js +252 -0
  16. package/agent-agy/dist/agy-sessions.d.ts +24 -0
  17. package/agent-agy/dist/agy-sessions.js +116 -0
  18. package/agent-agy/dist/agy-store.d.ts +39 -0
  19. package/agent-agy/dist/agy-store.js +83 -0
  20. package/agent-agy/dist/inbox.d.ts +26 -0
  21. package/agent-agy/dist/inbox.js +53 -0
  22. package/agent-agy/dist/index.d.ts +80 -0
  23. package/agent-agy/dist/index.js +505 -0
  24. package/agent-agy/dist/models.d.ts +41 -0
  25. package/agent-agy/dist/models.js +74 -0
  26. package/agent-agy/dist/world-plugin.d.ts +9 -0
  27. package/agent-agy/dist/world-plugin.js +97 -0
  28. package/agent-agy/package.json +64 -0
  29. package/agent-claude/cordis.patch.yml +42 -0
  30. package/agent-claude/dist/adapter.js +62 -0
  31. package/agent-claude/dist/agent-preset-claude.js +169 -0
  32. package/agent-claude/dist/agent-preset-projection.js +30 -0
  33. package/agent-claude/dist/agent.js +1271 -0
  34. package/agent-claude/dist/claude-client.js +357 -0
  35. package/agent-claude/dist/claude-events.js +223 -0
  36. package/agent-claude/dist/claude-home.js +29 -0
  37. package/agent-claude/dist/commands.js +182 -0
  38. package/agent-claude/dist/content.js +134 -0
  39. package/agent-claude/dist/inbox.js +53 -0
  40. package/agent-claude/dist/index.js +555 -0
  41. package/agent-claude/dist/input-queue.js +64 -0
  42. package/agent-claude/dist/interaction.js +271 -0
  43. package/agent-claude/dist/models.js +158 -0
  44. package/agent-claude/dist/permission.js +120 -0
  45. package/agent-claude/dist/session-id.js +20 -0
  46. package/agent-claude/dist/session-map.js +165 -0
  47. package/agent-claude/dist/world-plugin.js +14 -0
  48. package/agent-claude/package.json +74 -0
  49. package/agent-codex/README.md +74 -0
  50. package/agent-codex/cordis.patch.yml +77 -0
  51. package/agent-codex/dist/adapter.js +55 -0
  52. package/agent-codex/dist/agent-preset-codex.js +171 -0
  53. package/agent-codex/dist/agent-preset-projection.js +37 -0
  54. package/agent-codex/dist/agent.js +1056 -0
  55. package/agent-codex/dist/app-home.js +65 -0
  56. package/agent-codex/dist/codex-client.js +469 -0
  57. package/agent-codex/dist/codex-events.js +297 -0
  58. package/agent-codex/dist/codex-store.js +190 -0
  59. package/agent-codex/dist/inbox.js +53 -0
  60. package/agent-codex/dist/index.js +605 -0
  61. package/agent-codex/dist/models.js +152 -0
  62. package/agent-codex/dist/permission.js +71 -0
  63. package/agent-codex/dist/session-map.js +170 -0
  64. package/agent-codex/dist/spike.js +46 -0
  65. package/agent-codex/dist/world-plugin.js +96 -0
  66. package/agent-codex/package.json +78 -0
  67. package/agent-hermes/README.md +144 -0
  68. package/agent-hermes/cordis.patch.yml +83 -0
  69. package/agent-hermes/dist/adapter.js +83 -0
  70. package/agent-hermes/dist/agent-preset-hermes.js +171 -0
  71. package/agent-hermes/dist/agent-preset-projection.js +34 -0
  72. package/agent-hermes/dist/agent.js +1068 -0
  73. package/agent-hermes/dist/hermes-client.js +953 -0
  74. package/agent-hermes/dist/hermes-events.js +289 -0
  75. package/agent-hermes/dist/hermes-store.js +158 -0
  76. package/agent-hermes/dist/inbox.js +53 -0
  77. package/agent-hermes/dist/index.js +541 -0
  78. package/agent-hermes/dist/models.js +324 -0
  79. package/agent-hermes/dist/permission.js +60 -0
  80. package/agent-hermes/dist/world-plugin.js +101 -0
  81. package/agent-hermes/package.json +76 -0
  82. package/agent-hub/cordis.patch.yml +12 -0
  83. package/agent-hub/dist/agent-roster.d.ts +34 -0
  84. package/agent-hub/dist/agent-roster.js +27 -0
  85. package/agent-hub/dist/carrier.d.ts +43 -0
  86. package/agent-hub/dist/carrier.js +241 -0
  87. package/agent-hub/dist/client-shim.d.ts +28 -0
  88. package/agent-hub/dist/client-shim.js +251 -0
  89. package/agent-hub/dist/envelope.d.ts +54 -0
  90. package/agent-hub/dist/envelope.js +92 -0
  91. package/agent-hub/dist/gateway.d.ts +28 -0
  92. package/agent-hub/dist/gateway.js +18 -0
  93. package/agent-hub/dist/index-pass.d.ts +22 -0
  94. package/agent-hub/dist/index-pass.js +47 -0
  95. package/agent-hub/dist/index.d.ts +29 -0
  96. package/agent-hub/dist/index.js +29 -0
  97. package/agent-hub/dist/labels.d.ts +45 -0
  98. package/agent-hub/dist/labels.js +83 -0
  99. package/agent-hub/dist/ownership.d.ts +37 -0
  100. package/agent-hub/dist/ownership.js +54 -0
  101. package/agent-hub/dist/roster.d.ts +28 -0
  102. package/agent-hub/dist/roster.js +38 -0
  103. package/agent-hub/dist/rpc.d.ts +38 -0
  104. package/agent-hub/dist/rpc.js +52 -0
  105. package/agent-hub/dist/spawn-world.d.ts +46 -0
  106. package/agent-hub/dist/spawn-world.js +72 -0
  107. package/agent-hub/dist/targets.d.ts +33 -0
  108. package/agent-hub/dist/targets.js +30 -0
  109. package/agent-hub/dist/world-entry.d.ts +31 -0
  110. package/agent-hub/dist/world-entry.js +51 -0
  111. package/agent-hub/dist/world-host.d.ts +69 -0
  112. package/agent-hub/dist/world-host.js +44 -0
  113. package/agent-hub/dist/world-join.d.ts +16 -0
  114. package/agent-hub/dist/world-join.js +122 -0
  115. package/agent-hub/dist/world-mount.d.ts +52 -0
  116. package/agent-hub/dist/world-mount.js +52 -0
  117. package/agent-hub/dist/world-mux.d.ts +72 -0
  118. package/agent-hub/dist/world-mux.js +224 -0
  119. package/agent-hub/dist/world-provision.d.ts +20 -0
  120. package/agent-hub/dist/world-provision.js +122 -0
  121. package/agent-hub/dist/world-web-server.d.ts +162 -0
  122. package/agent-hub/dist/world-web-server.js +248 -0
  123. package/agent-hub/lib/client/index.js +242 -0
  124. package/agent-hub/package.json +65 -0
  125. package/agent-omp/README.md +77 -0
  126. package/agent-omp/cordis.patch.yml +92 -0
  127. package/agent-omp/dist/adapter.js +36 -0
  128. package/agent-omp/dist/agent-preset-omp.js +171 -0
  129. package/agent-omp/dist/agent-preset-projection.js +36 -0
  130. package/agent-omp/dist/agent.js +1145 -0
  131. package/agent-omp/dist/app-home.js +86 -0
  132. package/agent-omp/dist/discovery.js +116 -0
  133. package/agent-omp/dist/inbox.js +53 -0
  134. package/agent-omp/dist/index.js +574 -0
  135. package/agent-omp/dist/knobs.js +50 -0
  136. package/agent-omp/dist/lazy-rpc.js +161 -0
  137. package/agent-omp/dist/mobile/gesture.js +148 -0
  138. package/agent-omp/dist/mobile/zoom-guard.js +294 -0
  139. package/agent-omp/dist/mobile-boot.js +38 -0
  140. package/agent-omp/dist/models.js +299 -0
  141. package/agent-omp/dist/omp-cli.js +147 -0
  142. package/agent-omp/dist/omp-disk-discovery.js +178 -0
  143. package/agent-omp/dist/omp-store.js +518 -0
  144. package/agent-omp/dist/pairing.js +45 -0
  145. package/agent-omp/dist/permission.js +96 -0
  146. package/agent-omp/dist/protocol.js +13 -0
  147. package/agent-omp/dist/rpc-types.js +1 -0
  148. package/agent-omp/dist/sdk-client.js +378 -0
  149. package/agent-omp/dist/sidecar-client.js +195 -0
  150. package/agent-omp/dist/store/adapter.js +47 -0
  151. package/agent-omp/dist/store/db.js +210 -0
  152. package/agent-omp/dist/store/index.js +59 -0
  153. package/agent-omp/dist/store/reconcile.js +51 -0
  154. package/agent-omp/dist/store/schema.js +40 -0
  155. package/agent-omp/dist/world-plugin.js +96 -0
  156. package/agent-omp/package.json +97 -0
  157. package/agent-omp/sidecar/main.ts +481 -0
  158. package/agent-pi/README.md +67 -0
  159. package/agent-pi/cordis.patch.yml +80 -0
  160. package/agent-pi/dist/adapter.js +53 -0
  161. package/agent-pi/dist/agent-preset-pi.js +169 -0
  162. package/agent-pi/dist/agent-preset-projection.js +29 -0
  163. package/agent-pi/dist/agent.js +949 -0
  164. package/agent-pi/dist/inbox.js +53 -0
  165. package/agent-pi/dist/index.js +595 -0
  166. package/agent-pi/dist/knobs.js +24 -0
  167. package/agent-pi/dist/models.js +200 -0
  168. package/agent-pi/dist/permission.js +66 -0
  169. package/agent-pi/dist/pi-client.js +224 -0
  170. package/agent-pi/dist/pi-events.js +155 -0
  171. package/agent-pi/dist/pi-home.js +55 -0
  172. package/agent-pi/dist/safe-json.js +25 -0
  173. package/agent-pi/dist/session-map.js +119 -0
  174. package/agent-pi/dist/world-plugin.js +97 -0
  175. package/agent-pi/package.json +77 -0
  176. package/cordis.patch.yml +22 -19
  177. package/package.json +88 -25
  178. package/client.js +0 -4
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Per-world virtual webServer (AW-B DL3).
3
+ *
4
+ * A world's plugins must be able to declare ANY path they like (`/api`,
5
+ * `/plugins`, `/whatever-name-i-like`). Enumerating those namespaces in the
6
+ * carrier is wrong by construction — a third-party plugin may declare a new
7
+ * one at any time. So this class translates at REGISTRATION time:
8
+ *
9
+ * world registers { kind, path } → real registers { kind, `${labelPath}${path}` }
10
+ * real receives /<label>/<path>/... → plugin handler sees /<path>/...
11
+ *
12
+ * Consequences:
13
+ * - any declared prefix lands under the label automatically; no list;
14
+ * - two worlds may both own `/api` (they become `/omp/api`, `/codex/api`);
15
+ * - ctx0 only ever sees the label namespaces, so a world cannot collide with
16
+ * ctx0's own routes.
17
+ *
18
+ * Index rows stay world-local: this instance owns its taps and emits nothing
19
+ * on the shared tree (worlds run as their own cordis ROOTS, whose event buses
20
+ * are isolated — probe 2026-09-16: emit on root B never reaches root A).
21
+ */
22
+ import { renderIndexInjections } from '@deepseek-ai/dsh-host-webserver';
23
+ import { rewriteIndexHtml } from './index-pass.js';
24
+ import { renderClientShim } from './client-shim.js';
25
+ import { agentRosterRow } from './agent-roster.js';
26
+ function translatePath(labelPath, path) {
27
+ if (typeof path !== 'string' || !path.startsWith('/')) {
28
+ throw new Error(`world-web-server: route path must start with "/", got ${JSON.stringify(path)}`);
29
+ }
30
+ return path === '/' ? labelPath : `${labelPath}${path}`;
31
+ }
32
+ /** Remove the mount label from `req.url`, preserving the trailing query. */
33
+ export function stripLabelFromUrl(url, labelPath) {
34
+ const raw = typeof url === 'string' && url !== '' ? url : '/';
35
+ const queryAt = raw.indexOf('?');
36
+ const path = queryAt === -1 ? raw : raw.slice(0, queryAt);
37
+ const rest = queryAt === -1 ? '' : raw.slice(queryAt);
38
+ if (path === labelPath)
39
+ return `/${rest}`;
40
+ if (path.startsWith(`${labelPath}/`))
41
+ return `${path.slice(labelPath.length)}${rest}`;
42
+ return raw;
43
+ }
44
+ /** One world's web surface, mounted under `<labelPath>`. */
45
+ export class WorldWebServer {
46
+ labelPath;
47
+ real;
48
+ options;
49
+ disposers = [];
50
+ taps = [];
51
+ /**
52
+ * @param labelPath - mount root, e.g. `/omp` (no trailing slash).
53
+ * @param real - the ctx0 webServer this world's routes forward into.
54
+ * @param options - world-local index row source.
55
+ */
56
+ constructor(labelPath, real, options = {}) {
57
+ this.labelPath = labelPath;
58
+ this.real = real;
59
+ this.options = options;
60
+ if (typeof labelPath !== 'string' || !labelPath.startsWith('/') || labelPath === '/' || labelPath.endsWith('/')) {
61
+ throw new Error(`world-web-server: labelPath must start with "/" and carry no trailing slash, got ${JSON.stringify(labelPath)}`);
62
+ }
63
+ }
64
+ /** The ctx0 listener port (read-through; the world owns no socket). */
65
+ get port() {
66
+ return this.real.port ?? 0;
67
+ }
68
+ /** The ctx0 bind host (read-through). */
69
+ get host() {
70
+ return this.real.host ?? '127.0.0.1';
71
+ }
72
+ /**
73
+ * The RPC channel belongs to the HUB, not to the world: the hub's carrier
74
+ * answers `/<label>/api/*` and `/<label>/api/remote.mux` with ctx0's auth
75
+ * domain, so the world's own connection/gateway registrations are swallowed
76
+ * here (returning a no-op disposer) instead of colliding on the real table.
77
+ * Non-envelope GET/HEAD traffic on `/<label>/api` still reaches the world's
78
+ * exact Fetch routes: `world-entry` publishes the world connection's shared
79
+ * `/api` fetch handler through `world-host`, and the carrier forwards into
80
+ * it AFTER ctx0's fence — the world's own browser-auth gate is never in the
81
+ * path (a different tree, a different secret).
82
+ */
83
+ static hubOwned(path) {
84
+ return path === '/api' || path.startsWith('/api/');
85
+ }
86
+ /**
87
+ * Mount-side authentication for one forwarded world route.
88
+ *
89
+ * The world's own browser-auth gate never sees a browser: it is reached
90
+ * in-ctx through this mount, and its cookie secret belongs to a different
91
+ * tree (every runtime is nested with its own home). So the decision is made
92
+ * here, with ctx0's connection: the same cookie that authenticated `<label>`
93
+ * in ctx0 authorizes its pages and assets.
94
+ */
95
+ gate(handler) {
96
+ return (req, ...rest) => {
97
+ const res = rest[0];
98
+ // false => the authorizer already answered (401, or token→cookie 303).
99
+ if (res !== undefined && this.options.authorize !== undefined
100
+ && !this.options.authorize(req, res))
101
+ return;
102
+ return handler(req, ...rest);
103
+ };
104
+ }
105
+ /** Register a world route; the real table stores its translated path. */
106
+ register(route) {
107
+ if (WorldWebServer.hubOwned(route.path))
108
+ return () => { };
109
+ return this.track(this.real.register({
110
+ kind: route.kind,
111
+ path: translatePath(this.labelPath, route.path),
112
+ handler: this.strip(this.gate(route.handler)),
113
+ }));
114
+ }
115
+ /** Register a world upgrade route (verbatim exact path; hub-owned RPC paths are swallowed). */
116
+ registerUpgrade(route) {
117
+ if (WorldWebServer.hubOwned(route.path))
118
+ return () => { };
119
+ return this.track(this.real.registerUpgrade({
120
+ path: translatePath(this.labelPath, route.path),
121
+ handler: this.strip(route.handler),
122
+ }));
123
+ }
124
+ /**
125
+ * Claim the world's SPA fallback: the ctx0 fallback seat is already owned by
126
+ * native frontend-static, so the world's fallback lives as a PREFIX route on
127
+ * the mount path (`/<label>`), which also keeps it inside the label namespace.
128
+ */
129
+ registerFallback(handler) {
130
+ // The world's index arrives HERE (its own frontend-static claims the
131
+ // fallback seat), so this is the route the mount must authenticate.
132
+ const gated = this.gate((req, ...rest) => handler(req, rest[0]));
133
+ return this.track(this.real.register({
134
+ kind: 'prefix',
135
+ path: this.labelPath,
136
+ handler: this.htmlRewrite(this.strip(gated)),
137
+ }));
138
+ }
139
+ /**
140
+ * Fix up HTML on its way out. Two passes belong here, in this order:
141
+ *
142
+ * 1. the world's own frontend-static injects `<base href="/">` AFTER
143
+ * `renderIndex`, and the FIRST `<base>` wins in a document, so the mount
144
+ * base is re-pinned on the bytes actually sent;
145
+ * 2. the agent roster is injected LAST — its values are the mount paths of
146
+ * OTHER worlds (`/`, `/omp/`, `/codex/`), which the mount pass above
147
+ * would otherwise re-root under this world's label.
148
+ */
149
+ htmlRewrite(handler) {
150
+ const labelPath = this.labelPath;
151
+ return (req, ...rest) => {
152
+ const res = rest[0];
153
+ const originalEnd = res?.end?.bind(res);
154
+ if (res !== undefined && originalEnd !== undefined) {
155
+ res.end = ((chunk, ...tail) => {
156
+ if (typeof chunk === 'string' || Buffer.isBuffer(chunk)) {
157
+ const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
158
+ if (/<head[\s>]/i.test(text) && /<base\b/i.test(text)) {
159
+ const mounted = rewriteIndexHtml(text, labelPath);
160
+ // A world that composes the hub row emits its own roster row during
161
+ // renderIndex — i.e. BEFORE this mount pass, so its paths were
162
+ // already re-rooted. Drop every earlier copy and inject the one
163
+ // this pass has just built from unrewritten values.
164
+ const stale = /<script>globalThis\["__DSH_AGENT_ROSTER__"\][^<]*<\/script>/g;
165
+ const cleaned = mounted.replace(stale, '');
166
+ return originalEnd(renderIndexInjections(cleaned, [agentRosterRow()]), ...tail);
167
+ }
168
+ }
169
+ return originalEnd(chunk, ...tail);
170
+ });
171
+ }
172
+ return handler(req, ...rest);
173
+ };
174
+ }
175
+ /** World-local raw index transform (never forwarded to ctx0's taps). */
176
+ tapIndex(transform) {
177
+ this.taps.push(transform);
178
+ return () => {
179
+ const at = this.taps.indexOf(transform);
180
+ if (at !== -1)
181
+ this.taps.splice(at, 1);
182
+ };
183
+ }
184
+ /** Apply this world's taps in registration order. */
185
+ applyIndexTaps(html) {
186
+ let out = html;
187
+ for (const transform of this.taps)
188
+ out = transform(out);
189
+ return out;
190
+ }
191
+ /** Collect this world's structured rows (supplied by the world root). */
192
+ collectIndexInjections() {
193
+ const table = [];
194
+ this.options.onIndexInject?.(table);
195
+ return table;
196
+ }
197
+ /**
198
+ * Render the world's index: the client mount shim first (it must run before
199
+ * the app entry module), then the world's rows, then its taps, then the mount
200
+ * pass (base pinned at `/<label>/`, root-absolute strings re-rooted).
201
+ *
202
+ * The agent roster is injected LAST, after the mount pass: its values are
203
+ * mount paths of OTHER worlds (`/`, `/omp/`, `/codex/`), and the pass would
204
+ * happily re-root them under this world's label.
205
+ */
206
+ renderIndex(html) {
207
+ const rows = [
208
+ { kind: 'script', placement: 'head', text: renderClientShim(this.labelPath) },
209
+ // No hub-invented widget here: the world composes the hub's own client
210
+ // plugin (see worldMountPatches), so its selector IS the native one.
211
+ ...this.collectIndexInjections(),
212
+ ];
213
+ const rendered = this.applyIndexTaps(renderIndexInjections(html, rows));
214
+ // The roster rides the outbound pass (htmlRewrite), not this one: the mount
215
+ // rewrite above would re-root other worlds' paths under this label.
216
+ return rewriteIndexHtml(rendered, this.labelPath);
217
+ }
218
+ /** Unregister every translated route (world unloaded). */
219
+ dispose() {
220
+ for (const dispose of this.disposers.splice(0).reverse()) {
221
+ try {
222
+ dispose();
223
+ }
224
+ catch {
225
+ // release must never throw during teardown
226
+ }
227
+ }
228
+ this.taps.length = 0;
229
+ }
230
+ /** Wrap a plugin handler so it sees the world-relative URL. */
231
+ strip(handler) {
232
+ const labelPath = this.labelPath;
233
+ return (req, ...rest) => {
234
+ req.url = stripLabelFromUrl(req.url, labelPath);
235
+ return handler(req, ...rest);
236
+ };
237
+ }
238
+ track(dispose) {
239
+ const wrapped = () => {
240
+ const at = this.disposers.indexOf(wrapped);
241
+ if (at !== -1)
242
+ this.disposers.splice(at, 1);
243
+ dispose();
244
+ };
245
+ this.disposers.push(wrapped);
246
+ return wrapped;
247
+ }
248
+ }
@@ -0,0 +1,242 @@
1
+ window.__ModuleLoader__.load({ id: "@pgmi-builds/agent-hub", factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ //#region rolldown:runtime
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
21
+ value: mod,
22
+ enumerable: true
23
+ }) : target, mod));
24
+
25
+ //#endregion
26
+ let react = require("react");
27
+ react = __toESM(react);
28
+ let react_jsx_runtime = require("react/jsx-runtime");
29
+ react_jsx_runtime = __toESM(react_jsx_runtime);
30
+
31
+ //#region src/client/RuntimeSeat.tsx
32
+ const NATIVE = {
33
+ key: "native",
34
+ label: "DSH",
35
+ path: "/"
36
+ };
37
+ /** Fallback labels for the read-only face (which only reports keys). */
38
+ const RUNTIME_LABELS = {
39
+ native: "DSH",
40
+ omp: "OMP",
41
+ codex: "Codex"
42
+ };
43
+ function fromGlobal() {
44
+ const agents = globalThis.__DSH_AGENT_ROSTER__?.agents;
45
+ if (!Array.isArray(agents)) return void 0;
46
+ const links = [];
47
+ for (const entry of agents) {
48
+ if (typeof entry !== "object" || entry === null) continue;
49
+ const { key, label, path } = entry;
50
+ if (typeof key !== "string" || typeof path !== "string") continue;
51
+ links.push({
52
+ key,
53
+ label: typeof label === "string" ? label : RUNTIME_LABELS[key] ?? key,
54
+ path
55
+ });
56
+ }
57
+ return links.length > 0 ? links : void 0;
58
+ }
59
+ /** Normalize the current mount root: `/omp` and `/omp/` are the same page. */
60
+ function currentPath() {
61
+ const pathname = typeof location === "undefined" ? "/" : location.pathname;
62
+ if (pathname === "" || pathname === "/") return "/";
63
+ return pathname.endsWith("/") ? pathname : `${pathname}/`;
64
+ }
65
+ function RuntimeSeat({ wide }) {
66
+ const [agents, setAgents] = react.useState(() => fromGlobal() ?? [NATIVE]);
67
+ const [open, setOpen] = react.useState(false);
68
+ const rootRef = react.useRef(null);
69
+ react.useEffect(() => {
70
+ let cancelled = false;
71
+ fetch("/api/agent-runtime", { credentials: "same-origin" }).then((r) => r.ok ? r.json() : Promise.reject(/* @__PURE__ */ new Error(`HTTP ${r.status}`))).then((j) => {
72
+ if (cancelled) return;
73
+ if (Array.isArray(j?.agents)) {
74
+ const listed = [];
75
+ for (const entry of j.agents) {
76
+ if (typeof entry !== "object" || entry === null) continue;
77
+ const { key, label, path } = entry;
78
+ if (typeof key !== "string" || typeof path !== "string") continue;
79
+ listed.push({
80
+ key,
81
+ label: typeof label === "string" ? label : RUNTIME_LABELS[key] ?? key,
82
+ path
83
+ });
84
+ }
85
+ if (listed.length > 0) {
86
+ setAgents(listed);
87
+ return;
88
+ }
89
+ }
90
+ if (!Array.isArray(j?.available)) return;
91
+ const links = [];
92
+ for (const key of j.available) {
93
+ if (typeof key !== "string") continue;
94
+ links.push(key === "native" ? NATIVE : {
95
+ key,
96
+ label: RUNTIME_LABELS[key] ?? key,
97
+ path: `/${key}/`
98
+ });
99
+ }
100
+ if (links.length > 0) setAgents(links);
101
+ }).catch(() => {});
102
+ return () => {
103
+ cancelled = true;
104
+ };
105
+ }, []);
106
+ react.useEffect(() => {
107
+ if (!open) return;
108
+ const onDown = (event) => {
109
+ if (rootRef.current !== null && !rootRef.current.contains(event.target)) setOpen(false);
110
+ };
111
+ document.addEventListener("mousedown", onDown);
112
+ return () => document.removeEventListener("mousedown", onDown);
113
+ }, [open]);
114
+ const here = currentPath();
115
+ const active = agents.find((agent) => agent.path === here) ?? NATIVE;
116
+ const styleBase = {
117
+ display: "flex",
118
+ alignItems: "center",
119
+ gap: 8,
120
+ width: "100%",
121
+ padding: wide ? "7px 10px" : "7px 0",
122
+ justifyContent: wide ? "flex-start" : "center",
123
+ border: "none",
124
+ borderRadius: 8,
125
+ background: "transparent",
126
+ color: "inherit",
127
+ font: "inherit",
128
+ fontSize: 13,
129
+ cursor: "pointer",
130
+ position: "relative"
131
+ };
132
+ const styleBadge = {
133
+ fontSize: 10,
134
+ lineHeight: 1.4,
135
+ padding: "1px 6px",
136
+ borderRadius: 999,
137
+ background: "color-mix(in srgb, currentColor 12%, transparent)",
138
+ whiteSpace: "nowrap"
139
+ };
140
+ const styleMenu = {
141
+ position: "absolute",
142
+ bottom: "calc(100% + 6px)",
143
+ left: 6,
144
+ right: 6,
145
+ zIndex: 40,
146
+ background: "var(--dsh-surface, #26262b)",
147
+ border: "1px solid color-mix(in srgb, currentColor 18%, transparent)",
148
+ borderRadius: 10,
149
+ boxShadow: "0 8px 24px rgba(0,0,0,.35)",
150
+ overflow: "hidden"
151
+ };
152
+ const styleItem = (isActive) => ({
153
+ display: "block",
154
+ width: "100%",
155
+ padding: "8px 12px",
156
+ border: "none",
157
+ background: isActive ? "color-mix(in srgb, currentColor 10%, transparent)" : "transparent",
158
+ color: "inherit",
159
+ font: "inherit",
160
+ fontSize: 13,
161
+ textAlign: "left",
162
+ cursor: "pointer"
163
+ });
164
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
165
+ ref: rootRef,
166
+ style: {
167
+ position: "relative",
168
+ width: "100%"
169
+ },
170
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
171
+ type: "button",
172
+ style: styleBase,
173
+ title: "Agent runtime — picking one opens that runtime's mount (/omp/, /codex/, …)",
174
+ onClick: () => {
175
+ setOpen((v) => !v);
176
+ },
177
+ children: wide ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
179
+ "aria-hidden": true,
180
+ children: "⌘"
181
+ }),
182
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
183
+ style: {
184
+ flex: 1,
185
+ textAlign: "left",
186
+ whiteSpace: "nowrap",
187
+ overflow: "hidden",
188
+ textOverflow: "ellipsis"
189
+ },
190
+ children: ["Agent · ", active.label]
191
+ }),
192
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
193
+ style: styleBadge,
194
+ children: "open"
195
+ })
196
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
197
+ "aria-hidden": true,
198
+ children: "⌘"
199
+ })
200
+ }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
201
+ style: styleMenu,
202
+ children: agents.map((agent) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
203
+ href: agent.path,
204
+ style: {
205
+ ...styleItem(agent.path === here),
206
+ textDecoration: "none"
207
+ },
208
+ "aria-current": agent.path === here ? "page" : void 0,
209
+ onClick: () => {
210
+ setOpen(false);
211
+ },
212
+ children: agent.path === here ? `${agent.label} ✓` : agent.label
213
+ }, agent.key))
214
+ })]
215
+ });
216
+ }
217
+
218
+ //#endregion
219
+ //#region src/client/index.ts
220
+ /** Required services (cordis fiber inject): the slot registry. */
221
+ const inject = ["slots"];
222
+ /**
223
+ * Mount the selector. Degrades silently when the slot service is absent (a
224
+ * composition without the sidebar renders nothing — a footer action is an
225
+ * optional occupant by contract).
226
+ *
227
+ * @param ctx - client root context.
228
+ */
229
+ function apply(ctx) {
230
+ ctx.inject(["slots"], (raw) => {
231
+ const scope = raw;
232
+ scope.effect(() => scope.slots.inject("sidebar.footer.action", () => scope.slots.register({
233
+ name: "sidebar.footer.action",
234
+ id: "agent-runtime"
235
+ }, RuntimeSeat)), "agent-hub: runtime footer action");
236
+ });
237
+ }
238
+
239
+ //#endregion
240
+ exports.apply = apply;
241
+ exports.inject = inject;
242
+ return module.exports; } });
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@pgmi-builds/agent-hub",
3
+ "version": "0.1.0",
4
+ "description": "Agent Worlds hub host half: world inventory control plane, roster, world spawner and the single-origin per-world mount carrier (no server-side selection; the addressed world is the request's mount path)",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./dist/index.js",
8
+ "./client": "./lib/client/index.js",
9
+ "./world": "./dist/world-entry.js",
10
+ "./join": "./dist/world-join.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "lib",
15
+ "cordis.patch.yml"
16
+ ],
17
+ "dsh": {
18
+ "bundle": {
19
+ "patch": "./cordis.patch.yml"
20
+ },
21
+ "client": {
22
+ "inject": [
23
+ "@deepseek-ai/dsh-client-ui-slots"
24
+ ],
25
+ "platform": "web"
26
+ }
27
+ },
28
+ "scripts": {
29
+ "build": "tsc -p .",
30
+ "build-client": "node scripts/build-client.mjs",
31
+ "test": "node --test --test-force-exit test/*.test.mjs"
32
+ },
33
+ "dependencies": {
34
+ "ws": "^8.21.0"
35
+ },
36
+ "peerDependencies": {
37
+ "@deepseek-ai/cordis": "*",
38
+ "@deepseek-ai/dsh-api-gateway": "*",
39
+ "@deepseek-ai/dsh-host-webserver": "*",
40
+ "@deepseek-ai/dsh-typert-protocol": "*"
41
+ },
42
+ "peerDependenciesMeta": {
43
+ "@deepseek-ai/cordis": {
44
+ "optional": true
45
+ },
46
+ "@deepseek-ai/dsh-api-gateway": {
47
+ "optional": true
48
+ },
49
+ "@deepseek-ai/dsh-host-webserver": {
50
+ "optional": true
51
+ },
52
+ "@deepseek-ai/dsh-typert-protocol": {
53
+ "optional": true
54
+ }
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^26.5.1",
58
+ "tsdown": "^0.15.12",
59
+ "typescript": "5.9.3"
60
+ },
61
+ "publishConfig": {
62
+ "access": "public"
63
+ },
64
+ "license": "MIT"
65
+ }
@@ -0,0 +1,77 @@
1
+ # omp-web(新线,0.0.1-alpha.0)
2
+
3
+ OMP Web 的 SDK 线:**衔接层(DSH 契约/术语/类形状)沿用 `apps/omp-web` 现行源码,内核从 `omp --mode rpc` 子进程换成 `@oh-my-pi/pi-coding-agent` SDK**。数据面目标 = `docs/plans/sdk-data-surface-assessment.md` 的 A1–A8 / S1–S10。
4
+
5
+ ## 集成形态(承 sdk-direction §3 裁决 (a))
6
+
7
+ ```
8
+ dsh 宿主 (Node 22, 本包 dist/) Bun sidecar (本包 sidecar/)
9
+ omp-web 衔接层(agent/adapter/ ←→ createAgentSession / ModelRegistry /
10
+ replay/store/supervisor 移植) Settings / SessionManager / discover*
11
+ ```
12
+
13
+ - dsh 进程树保持纯 Node;SDK 只活在 sidecar 进程(bun run)。
14
+ - 协议不再是 omp 的 rpc 面,而是**自有 sidecar 协议**:在数据流等价(prompt/steer/followUp/abort/事件流)之上,新增 app 层命令(settings 读写、auth/provider 状态、模型注册表、session list/open/fork、system prompt、custom message / messages 手术、compact)——即把 A/S 清单映射成方法表。
15
+
16
+ ## Spike 实测记录(2026-09-09,SDK 18.1.14 + bun 1.4.2)
17
+
18
+ - 安装:`npm i @oh-my-pi/pi-coding-agent`(main 直指 `./src/index.ts`,无需构建步骤)+ devDep `bun`(node_modules 内二进制,不装全局)。
19
+ - 根 exports 619 个。桥关心的全部在位:`createAgentSession` `AgentSession` `SessionManager(.create/.open/.continueRecent/.inMemory/.list/.listAll/.forkFrom)` `Settings(.init/.isolated/.loadReadOnly)` `ModelRegistry` `AuthStorage` `discoverAuthStorage` `AgentRegistry` `createSessionManager` `listSessions` `listAllSessions` `loadSessionMessagesReadOnly` `discoverSkills/SlashCommands/Extensions/ContextFiles/PromptTemplates` `buildSystemPrompt` `createCustomMessage` `wrapSteeringForModel` `expandPromptTemplate`。
20
+ - **pi 文档 vs omp 实现**:`createAgentSessionRuntime`/`ModelRuntime`/`DefaultResourceLoader`/`SettingsManager`/`defineTool`/`createEventBus` 不在 omp 根 exports——对应能力由 `AgentSession` 实例面与 `Settings`/`discover*` 家族承担。评估文档中标 [pi] 的条目按此折算。
21
+ - **`AgentSession` 实例面远超 pi 文档**:`state{systemPrompt,model,thinkingLevel,tools,messages,isStreaming,streamMessage,pendingToolCalls,error}`、`messages`、`systemPrompt`、`prompt`/`promptCustomMessage`/`sendCustomMessage`/`sendUserMessage`、`steer`/`followUp`、`setModel`/`cycleModel`/`applyRoleModel`、`compact`/`abortCompaction`、`switchSession`/`branch`/`navigateTree`/`fork`/`handoff`、`newSession`/`moveSession`、`abort`、`beginDispose`/`dispose`。S1/S2/S3/S6/S7/S8/S10 均有一等 API;S5(messages 手术)通道结构性在位(`state` 暴露 messages/tools 键),赋值语义待 TS 类型面确认。
22
+ - **真机冒烟**:`OMP_HOME=~/.omp` 下 `discoverAuthStorage`+`ModelRegistry.refresh()` 得 50 模型 / 5 provider(deepseek,xai,bailian,kimi-plan,zai-plan)——与 `omp models --json` 完全一致;`SessionManager.inMemory()` 建会话成功、dispose 干净、零 LLM 调用。
23
+ - **沙箱注意**:SDK 以读写模式打开 `~/.omp/agent/agent.db`;agent 沙箱内跑 sidecar/spike 需要对该库的写权限(本轮以 danger-full-access 实测通过)。
24
+
25
+ ## 下一步(M2 → M3 收敛)
26
+
27
+ 1. sidecar 骨架:bun 入口 + 方法表协议(JSON lines over stdio)+ 生命周期(spawn/健康检查/退出)。
28
+ 2. 移植衔接层:从 `apps/omp-web/src` 拷 `agent.ts`/`adapter.ts`/`replay.ts`/`session-persistence-omp.ts`/`store/`,把 `rpc.ts` 调用点逐个改打 sidecar 协议;`omp-cli.ts` 的 models/config 面改走 SDK(A1/A3)。
29
+ 3. S5 spike:`state.messages` 注入 toolResult + 真 turn 验证 journal 落盘。
30
+ 4. 会话所有权对齐:SDK 写 OMP session store 后与 bridge-store/supervisor/TUI 的三方避让(dev_0.0.3 §9 过一遍)。
31
+
32
+ ## As-built:sidecar 骨架(2026-09-09,已完成 ✅)
33
+
34
+ - `sidecar/main.ts`(bun 入口):ready 握手 + JSON-lines 请求/响应/事件三帧协议;app 级共享单例 `authStorage`/`modelRegistry`;session 池(create/dispose/事件转发)。
35
+ - `src/protocol.ts`:协议与方法表类型(sys.ping / models.list / settings.get / session.create|info|prompt|steer|followUp|abort|dispose / sessions.list)。
36
+ - `src/sidecar-client.ts`:Node 侧 spawn/关联/事件分发/优雅关停;`src/index.ts` 暂导出 client。
37
+ - 冒烟 `test/sidecar-smoke.test.mjs`:ready 0.9s,全链路(ping→50 模型→session create/info/dispose→sessions.list)2.4s,`npm test` 1.8s 绿。
38
+ - 坑已修:`ModelRegistry.refreshInBackground()` 此版本返回 void(非 Promise),可选链后接 `.catch` 会炸;ready-timeout 定时器必须 clear 否则 Node 进程挂 60s。
39
+
40
+ ## As-built:衔接层移植(2026-09-09,已完成 ✅)
41
+
42
+ - `src/rpc-types.ts`:从 omp-web `rpc.ts` 提取的 OMP wire 类型(verbatim),两条线共用同一载荷形状。
43
+ - `src/sdk-client.ts`:**`OmpSdkClient`——与 `OmpRpcClient` 同公共面**(on/onFailure/sendRaw/send/getState/getMessages/getSessionStats/getSubagents/prompt/followUp/steer/setModel/abort/newSession/close),底层复用单个共享 sidecar(模块级单例 + 引用计数,refcount 归零自动关停)。spawn args 解析 `--approval-mode`(yolo→autoApprove)与 `--resume`;未知 flag fail-loud。
44
+ - 衔接层整体移植(与 omp-web 逐字节同源,仅换接缝):`agent.ts` `adapter.ts` `index.ts` `models.ts` `knobs.ts` `pairing.ts` `permission.ts` `replay.ts` `session-persistence-omp.ts` `supervisor.ts` `omp-store.ts` `agent-preset-omp.ts` `omp-cli.ts` `store/`。
45
+ - sidecar 升级:session handle 化(`newSession` 换 OMP session id 但 handle 稳定);补齐 `session.state/messages/stats/subagents/setModel/new`;`getSessionStats` 实测返回 contextUsage(contextWindow 1000000)。
46
+ - 测试:omp-web 单测四件(content-detection/omp-store/replay/store)+ sidecar 冒烟 + sdk-client 集成 = **20/20 绿,3.6s**;tsc 0 错。
47
+ - 本地 dev fixture:`node_modules/@deepseek-ai/*` symlink 至真实 dsh 安装(照 omp-web 惯例,node_modules 不入库)。
48
+
49
+ ### 已知差异(SDK 线 vs RPC 线,待办)
50
+
51
+ - `extension_ui_response`(审批卡应答)在 SDK 线暂为 no-op:SDK 侧审批走 `autoApprove`/approval 事件,卡片化审批的等价通道待接。
52
+ - `getSubagents` 暂返回 `[]`(SDK 的 subagent registry 暴露面待查)。
53
+ - 未跑 4999 全链路(dsh 宿主内 provider 挂载)——下一步。
54
+
55
+ ## As-built:4999 全链路冒烟(2026-09-09,PASS ✅)
56
+
57
+ - test profile `omp-web-test` 切换 dep 至 `link:…/apps/omp-web`(bundle 位同步换 `@pgmi-builds/omp-web`,patch 语义同 omp-web:mount omp-provider + 禁 agent-loop/llm 路由/agent-presets)。
58
+ - systemd-run `omp-web-4999-test`(DSH_HOME=~/.omp/omp-web、OMP_HOME=~/.omp、port 4999)拉起全局 dsh 0.1.3-alpha.2,unit active。
59
+ - 验证链:auth 200 → `session/create` ok → **dsh 进程树内 1 个共享 bun sidecar** → `session/prompt`(mode:queue + requestId,typert request 形状)accepted → `session/page`(`records[].event`,`throughSeq ≤ cursor`)回读:turn/start、user/message、2×step(reasoning + `think` 工具调用 + tool/result)、assistant/message(text "sdk-line-alive",xai/grok-4.6,含 stream chunks)、turn/end。
60
+ - wire 备忘:`session/prompt` 必填 `requestId`/`mode`/`content`;`session/page` 用 `args.request`(非 `_request`),`address:{kind:"session",sessionId}` + `throughSeq` 超 cursor 直接报错并回吐当前 cursor。
61
+
62
+ ## As-built:四阶段增强(2026-09-09,全部 doer(v4-pro)+verify 双签 PASS ✅)
63
+
64
+ - **P1 `7e44412`** preset 槽位:桥自供 `agentPresets` typert remote(服务 key==wire namespace==ctx.remote.agentPresets),roster 恰一条 `omp`/`OMP`/isDefault;copy/delete 报 `agent-preset/read-only`,未知 id 报 `not-found`。关键:注册必须挂顶层 fiber(嵌套 ctx.plugin 会 404)。4999 wire 实测单条 OMP 行。
65
+ - **P2 `fee6406`** system prompt → UI:`request/header.data.header.system`(EpochHeader.system)即 UI System-prompt 行的数据源。sidecar 加 `session.systemPrompt`(live,refreshBaseSystemPrompt 后 38807 字符)与 `session.systemPrompt.forFile`(冷会话 throwaway 渲染,dispose 前 releaseRetainedEntries 抑制 session_exit 落盘,实测只读)。replay/cold-shadow/open 注入;live turn 不合成 header(无 per-turn 成本)。已知 gap:live 会话要到 replay 路径才显示 System prompt 行。
66
+ - **P3 `8e9f21d`** settings/models 切 sidecar:`settings.modelRoles.get/set`(set 带 flush→loadReadOnly→deepEquals 读回校验,shadow-key 只在真实落盘后推进;echo roundtrip 实测 config.yml SHA 不变)+ 富化 `models.list`(12 键与 normalizeModel 逐一对照)。omp-cli 四导出改缓存+异步暖缓存语义,Sync 变体冷启动 undefined→on-disk 回退。4999 实测 modelCatalog 5 provider 全量。
67
+ - **P3.1 `e4db900`** EPIPE 加固(verify 发现的历史崩溃):child stdin 永久 no-op error sink + `#writeSafe` per-write 降级——sidecar 死亡从崩宿主变为拒绝当次调用。
68
+ - **P4 `97cbc7b`** JSONL 解析切 SDK:`sessions.listAll` 一次全库索引(147 会话 26ms;id/cwd/size 0 diff,20 处 title 差异经实证为 SDK 当前 title slot 更正确)+ `sessions.messagesReadOnly` 按需 transcript(compaction 折叠语义);自研解析保留为 fail-soft fallback;顺带修了 `sessions.list {all:true}` 恒 0 的旧 bug。supervisor 热路径保留 naive 防游标错位。
69
+ - 总验收:29/29 测试、4999 wire 三连(roster 单条 OMP / session list 147 / modelCatalog)、实例留 4999 供手动验收。
70
+ - 遗留(低):forFile 对个别大文件 "Agent Main was replaced"(~1/tick,fail-soft);SDK 元数据快照滞后一个 tick;`sessions.messagesReadOnly` 大文件双读。
71
+
72
+ ## As-built:P1.1 修复 + P5 mobile 包(2026-09-09)
73
+
74
+ - **P1.1 `434f046`**("no omp mode" 根因修复):P1 的 roster 服务 200 但 chip 仍不显示——UI 的 preset chip 门控在 `session.projectionValues.agentPreset` 上,该投影原本由(被我们禁用的)dsh-agent-presets 宿主半驱动。桥现在自己注册同一 fold(`ctx.inject(['sessionProjections'])` + `agent-preset/selected` 事件推进),headless 截图确认 hero 行渲染 `OMP ˅` chip。配套 vendored `dsh-session-projection` 类型(父 repo)。
75
+ - **P5 `29b043b`**(dashr mobile 包移植,本插件首个 client 半):`src/mobile/`(gesture/zoom-guard/client)+ `src/client/index.ts` 入口 + `scripts/build-client.ts`(tsdown 闭包工厂 → `lib/client/index.js`,id=包名)+ host 半 `src/mobile-boot.ts`(`window.__OMP_WEB_MOBILE__` + iOS zoom-guard 经 webserver/index-inject)。`dsh.client` manifest inject 只挂 ui-layout。测试 41/41。
76
+ - **P5 视觉验证**(5199 实测,4999 当时被 superd 的 ui-preact 实例占用):桌面回归 OK(OMP chip 在);390x844 断点折叠(rail→0px、无横向滚动);全局注入 OK;边缘滑动手势开关侧栏实测通过;截图证据 `.scratch/p5-verify/`。
77
+ - 遗留:移动端侧栏为 push-drawer 语义(打开时内容窗格被压窄,与 dashr 原行为一致);`npm install` 会剪掉 `node_modules/@deepseek-ai` symlink(需重挂);4999 端口与 superd 测试线冲突时用 `--patch` overlay 换端口。