super-dsh 0.1.6 → 0.1.10-e

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 (260) hide show
  1. package/README.md +25 -3
  2. package/agent-agy/cordis.patch.yml +7 -4
  3. package/agent-agy/dist/agent.d.ts +12 -11
  4. package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
  5. package/agent-agy/dist/agy-auth-gateway.js +233 -0
  6. package/agent-agy/dist/agy-auth.d.ts +153 -0
  7. package/agent-agy/dist/agy-auth.js +179 -0
  8. package/agent-agy/dist/agy-cli-client.d.ts +39 -9
  9. package/agent-agy/dist/agy-cli-client.js +82 -18
  10. package/agent-agy/dist/agy-store.d.ts +8 -0
  11. package/agent-agy/dist/index.d.ts +46 -16
  12. package/agent-agy/dist/index.js +82 -56
  13. package/agent-agy/dist/models.d.ts +70 -0
  14. package/agent-agy/dist/models.js +102 -3
  15. package/agent-agy/package.json +7 -5
  16. package/agent-agy-sdk/cordis.patch.yml +58 -0
  17. package/agent-agy-sdk/dist/adapter.d.ts +15 -0
  18. package/agent-agy-sdk/dist/adapter.js +42 -0
  19. package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
  20. package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
  21. package/agent-agy-sdk/dist/agent.d.ts +138 -0
  22. package/agent-agy-sdk/dist/agent.js +494 -0
  23. package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
  24. package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
  25. package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
  26. package/agent-agy-sdk/dist/agy-sessions.js +107 -0
  27. package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
  28. package/agent-agy-sdk/dist/agy-store.js +83 -0
  29. package/agent-agy-sdk/dist/inbox.d.ts +26 -0
  30. package/agent-agy-sdk/dist/inbox.js +53 -0
  31. package/agent-agy-sdk/dist/index.d.ts +93 -0
  32. package/agent-agy-sdk/dist/index.js +476 -0
  33. package/agent-agy-sdk/dist/models.d.ts +41 -0
  34. package/agent-agy-sdk/dist/models.js +74 -0
  35. package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
  36. package/agent-agy-sdk/dist/sdk-venv.js +173 -0
  37. package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
  38. package/agent-agy-sdk/dist/world-plugin.js +82 -0
  39. package/agent-agy-sdk/package.json +64 -0
  40. package/agent-claude/dist/adapter.js +106 -31
  41. package/agent-claude/dist/agent.js +154 -17
  42. package/agent-claude/dist/claude-client.js +84 -43
  43. package/agent-claude/dist/claude-launch.js +101 -0
  44. package/agent-claude/dist/claude-native-config.js +169 -0
  45. package/agent-claude/dist/claude-provider-gateway.js +340 -0
  46. package/agent-claude/dist/claude-providers.js +391 -0
  47. package/agent-claude/dist/claude-route.js +152 -0
  48. package/agent-claude/dist/index.js +214 -37
  49. package/agent-claude/dist/models.js +14 -0
  50. package/agent-claude/dist/sdk-runtime.js +320 -0
  51. package/agent-claude/package.json +6 -4
  52. package/agent-codex/README.md +141 -36
  53. package/agent-codex/cordis.patch.yml +14 -10
  54. package/agent-codex/dist/adapter.js +64 -28
  55. package/agent-codex/dist/agent.js +255 -81
  56. package/agent-codex/dist/app-server-approvals.js +165 -0
  57. package/agent-codex/dist/app-server-client.js +723 -0
  58. package/agent-codex/dist/app-server-events.js +432 -0
  59. package/agent-codex/dist/app-server-protocol.js +95 -0
  60. package/agent-codex/dist/app-server-wire.js +299 -0
  61. package/agent-codex/dist/codex-launch.js +78 -0
  62. package/agent-codex/dist/codex-native-config.js +392 -0
  63. package/agent-codex/dist/codex-provider-gateway.js +402 -0
  64. package/agent-codex/dist/codex-providers.js +376 -0
  65. package/agent-codex/dist/codex-route.js +86 -0
  66. package/agent-codex/dist/index.js +256 -115
  67. package/agent-codex/dist/permission.js +75 -1
  68. package/agent-codex/package.json +1 -2
  69. package/agent-codex-sdk/README.md +79 -0
  70. package/agent-codex-sdk/cordis.patch.yml +81 -0
  71. package/agent-codex-sdk/dist/adapter.js +91 -0
  72. package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
  73. package/agent-codex-sdk/dist/agent.js +1085 -0
  74. package/agent-codex-sdk/dist/codex-native-config.js +392 -0
  75. package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
  76. package/agent-codex-sdk/dist/codex-providers.js +376 -0
  77. package/agent-codex-sdk/dist/codex-route.js +86 -0
  78. package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
  79. package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
  80. package/agent-codex-sdk/dist/codex-store.js +180 -0
  81. package/agent-codex-sdk/dist/inbox.js +53 -0
  82. package/agent-codex-sdk/dist/index.js +739 -0
  83. package/agent-codex-sdk/dist/permission.js +71 -0
  84. package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
  85. package/agent-codex-sdk/dist/session-map.js +167 -0
  86. package/agent-codex-sdk/dist/world-plugin.js +93 -0
  87. package/agent-codex-sdk/package.json +79 -0
  88. package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
  89. package/agent-omp/README.md +72 -55
  90. package/agent-omp/dist/agent.js +36 -11
  91. package/agent-omp/dist/index.js +92 -52
  92. package/agent-omp/dist/lazy-rpc.js +11 -4
  93. package/agent-omp/dist/omp-cli.js +114 -97
  94. package/agent-omp/dist/omp-store.js +12 -4
  95. package/agent-omp/dist/permission.js +70 -5
  96. package/agent-omp/dist/rpc-client.js +409 -0
  97. package/agent-omp/dist/world-plugin.js +36 -3
  98. package/agent-omp/package.json +2 -6
  99. package/agent-omp-sdk/README.md +103 -0
  100. package/agent-omp-sdk/cordis.patch.yml +108 -0
  101. package/agent-omp-sdk/dist/adapter.d.ts +21 -0
  102. package/agent-omp-sdk/dist/adapter.js +36 -0
  103. package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
  104. package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
  105. package/agent-omp-sdk/dist/agent.d.ts +64 -0
  106. package/agent-omp-sdk/dist/agent.js +1178 -0
  107. package/agent-omp-sdk/dist/discovery.d.ts +57 -0
  108. package/agent-omp-sdk/dist/discovery.js +116 -0
  109. package/agent-omp-sdk/dist/inbox.d.ts +28 -0
  110. package/agent-omp-sdk/dist/inbox.js +53 -0
  111. package/agent-omp-sdk/dist/index.d.ts +60 -0
  112. package/agent-omp-sdk/dist/index.js +632 -0
  113. package/agent-omp-sdk/dist/knobs.d.ts +34 -0
  114. package/agent-omp-sdk/dist/knobs.js +50 -0
  115. package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
  116. package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
  117. package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
  118. package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
  119. package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
  120. package/agent-omp-sdk/dist/mobile-boot.js +38 -0
  121. package/agent-omp-sdk/dist/models.d.ts +39 -0
  122. package/agent-omp-sdk/dist/models.js +299 -0
  123. package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
  124. package/agent-omp-sdk/dist/omp-cli.js +164 -0
  125. package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
  126. package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
  127. package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
  128. package/agent-omp-sdk/dist/omp-executable.js +30 -0
  129. package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
  130. package/agent-omp-sdk/dist/omp-store.js +526 -0
  131. package/agent-omp-sdk/dist/pairing.d.ts +7 -0
  132. package/agent-omp-sdk/dist/pairing.js +45 -0
  133. package/agent-omp-sdk/dist/permission.d.ts +138 -0
  134. package/agent-omp-sdk/dist/permission.js +161 -0
  135. package/agent-omp-sdk/dist/protocol.d.ts +189 -0
  136. package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
  137. package/agent-omp-sdk/dist/rpc-types.js +1 -0
  138. package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
  139. package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
  140. package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
  141. package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
  142. package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
  143. package/agent-omp-sdk/dist/sidecar-client.js +197 -0
  144. package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
  145. package/agent-omp-sdk/dist/store/adapter.js +47 -0
  146. package/agent-omp-sdk/dist/store/db.d.ts +70 -0
  147. package/agent-omp-sdk/dist/store/db.js +210 -0
  148. package/agent-omp-sdk/dist/store/index.d.ts +23 -0
  149. package/agent-omp-sdk/dist/store/index.js +59 -0
  150. package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
  151. package/agent-omp-sdk/dist/store/reconcile.js +51 -0
  152. package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
  153. package/agent-omp-sdk/dist/store/schema.js +40 -0
  154. package/agent-omp-sdk/dist/transport.d.ts +49 -0
  155. package/agent-omp-sdk/dist/transport.js +35 -0
  156. package/agent-omp-sdk/dist/world-home.d.ts +4 -0
  157. package/agent-omp-sdk/dist/world-home.js +23 -0
  158. package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
  159. package/agent-omp-sdk/dist/world-plugin.js +137 -0
  160. package/agent-omp-sdk/package.json +87 -0
  161. package/client/index.js +1883 -287
  162. package/cordis.patch.yml +80 -0
  163. package/package.json +23 -9
  164. package/superd-hub/dist/a2a-delivery.d.ts +189 -0
  165. package/superd-hub/dist/a2a-delivery.js +240 -0
  166. package/superd-hub/dist/a2a-host.d.ts +76 -0
  167. package/superd-hub/dist/a2a-host.js +145 -0
  168. package/superd-hub/dist/a2a-ingest.d.ts +31 -0
  169. package/superd-hub/dist/a2a-ingest.js +636 -0
  170. package/superd-hub/dist/a2a-log.d.ts +17 -0
  171. package/superd-hub/dist/a2a-log.js +42 -0
  172. package/superd-hub/dist/a2a-row.d.ts +65 -0
  173. package/superd-hub/dist/a2a-row.js +162 -0
  174. package/superd-hub/dist/a2a-service.d.ts +357 -0
  175. package/superd-hub/dist/a2a-service.js +980 -0
  176. package/superd-hub/dist/a2a-wire.d.ts +220 -0
  177. package/superd-hub/dist/a2a-wire.js +305 -0
  178. package/superd-hub/dist/agent-ids.d.ts +10 -0
  179. package/superd-hub/dist/agent-ids.js +68 -0
  180. package/superd-hub/dist/agent-roster.d.ts +29 -18
  181. package/superd-hub/dist/agent-roster.js +54 -15
  182. package/superd-hub/dist/agent-status.d.ts +119 -0
  183. package/superd-hub/dist/agent-status.js +386 -0
  184. package/superd-hub/dist/client/agent-settings.d.ts +210 -0
  185. package/superd-hub/dist/client/agent-settings.js +300 -0
  186. package/superd-hub/dist/client/mesh-api.d.ts +134 -0
  187. package/superd-hub/dist/client/mesh-api.js +219 -0
  188. package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
  189. package/superd-hub/dist/client/mesh-groups.js +66 -0
  190. package/superd-hub/dist/client/roster.d.ts +48 -0
  191. package/superd-hub/dist/client/roster.js +102 -0
  192. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  193. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  194. package/superd-hub/dist/client/switch.d.ts +165 -0
  195. package/superd-hub/dist/client/switch.js +295 -0
  196. package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
  197. package/superd-hub/dist/exec-pty-channel.js +162 -0
  198. package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
  199. package/superd-hub/dist/exec-pty-framing.js +81 -0
  200. package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
  201. package/superd-hub/dist/exec-pty-rpc.js +128 -0
  202. package/superd-hub/dist/exec-pty-session.d.ts +76 -0
  203. package/superd-hub/dist/exec-pty-session.js +234 -0
  204. package/superd-hub/dist/exec.d.ts +56 -0
  205. package/superd-hub/dist/exec.js +225 -0
  206. package/superd-hub/dist/gateway.d.ts +39 -2
  207. package/superd-hub/dist/gateway.js +62 -3
  208. package/superd-hub/dist/hub-tool.d.ts +174 -0
  209. package/superd-hub/dist/hub-tool.js +464 -0
  210. package/superd-hub/dist/index.d.ts +15 -1
  211. package/superd-hub/dist/index.js +16 -0
  212. package/superd-hub/dist/machines.d.ts +45 -0
  213. package/superd-hub/dist/machines.js +125 -0
  214. package/superd-hub/dist/mesh-federate.d.ts +138 -0
  215. package/superd-hub/dist/mesh-federate.js +223 -0
  216. package/superd-hub/dist/mesh-gateway.d.ts +67 -0
  217. package/superd-hub/dist/mesh-gateway.js +408 -0
  218. package/superd-hub/dist/mesh-link.d.ts +274 -0
  219. package/superd-hub/dist/mesh-link.js +441 -0
  220. package/superd-hub/dist/mesh-rpc.d.ts +123 -0
  221. package/superd-hub/dist/mesh-rpc.js +250 -0
  222. package/superd-hub/dist/mux-client.d.ts +71 -0
  223. package/superd-hub/dist/mux-client.js +281 -0
  224. package/superd-hub/dist/pack-version.d.ts +2 -0
  225. package/superd-hub/dist/pack-version.js +30 -0
  226. package/superd-hub/dist/roster.d.ts +6 -0
  227. package/superd-hub/dist/rpc.d.ts +18 -2
  228. package/superd-hub/dist/rpc.js +34 -4
  229. package/superd-hub/dist/spawn-world.js +3 -1
  230. package/superd-hub/dist/switch-core.d.ts +62 -25
  231. package/superd-hub/dist/switch-core.js +53 -30
  232. package/superd-hub/dist/system-head.d.ts +22 -12
  233. package/superd-hub/dist/system-head.js +100 -84
  234. package/superd-hub/dist/wire-endpoints.d.ts +264 -0
  235. package/superd-hub/dist/wire-endpoints.js +180 -0
  236. package/superd-hub/lib/client/index.js +1883 -287
  237. package/superd-hub/package.json +11 -2
  238. package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
  239. package/agent-agy/dist/agent-preset-projection.js +0 -33
  240. package/agent-claude/dist/agent-preset-projection.js +0 -30
  241. package/agent-codex/dist/agent-preset-projection.js +0 -37
  242. package/agent-codex/dist/app-home.js +0 -65
  243. package/agent-codex/dist/models.js +0 -152
  244. package/agent-codex/dist/spike.js +0 -46
  245. package/agent-hermes/dist/agent-preset-projection.js +0 -34
  246. package/agent-omp/dist/agent-preset-projection.js +0 -36
  247. package/agent-omp/dist/app-home.js +0 -86
  248. package/agent-omp/dist/sidecar-client.js +0 -195
  249. package/agent-pi/dist/agent-preset-projection.js +0 -29
  250. package/superd-hub/dist/client-shim.d.ts +0 -28
  251. package/superd-hub/dist/client-shim.js +0 -251
  252. package/superd-hub/dist/index-pass.d.ts +0 -22
  253. package/superd-hub/dist/index-pass.js +0 -47
  254. package/superd-hub/dist/labels.d.ts +0 -45
  255. package/superd-hub/dist/labels.js +0 -83
  256. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  257. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  258. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  259. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  260. /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
@@ -3,20 +3,28 @@
3
3
  *
4
4
  * OMP has no runtime approval control: `--approval-mode` is a launch flag, no
5
5
  * RPC changes it mid-session. Dash's 3-way permission preset is therefore
6
- * resolved once, when the OMP child is spawned, and mapped explicitly:
6
+ * resolved ONCE, at the spawn of that session's `omp --mode rpc` child, and
7
+ * mapped explicitly:
7
8
  *
8
9
  * danger-full-access → `--approval-mode yolo` (OMP's own native default)
9
10
  * workspace-write → `--approval-mode write`
10
11
  * read-only → `--approval-mode always-ask`
11
12
  * no explicit preset → yolo (native default)
12
13
  *
14
+ * "At the spawn" is load-bearing, not incidental. The Dash session is prepared
15
+ * (and `pinInitialPermission`-stamped) only AFTER the transport wrapper exists,
16
+ * and the new-chat permission pick arrives as a later `permission/preset` — the
17
+ * child materializes on the first prompt, so the launch flag must be read from
18
+ * the session at THAT moment, never frozen at create. See
19
+ * {@link resolveSpawnPermission}.
20
+ *
13
21
  * `OMP_APPROVAL_MODE` (a valid mode) overrides the mapping — headless test
14
22
  * runs pin the flag without composing a preset default.
15
23
  *
16
- * The preset is a Dash-side session fact OMP never records. It now lives in
17
- * the centralized index (`sessions.permission_preset`), seeded once from the
18
- * legacy per-session webui.json by the migration; webui.json stays on disk
19
- * but is never read or written again (D3).
24
+ * The preset is a Dash-side session fact OMP never records. It lives in the
25
+ * dsh session log (`permission/preset`, the event `/permission` writes) and is
26
+ * mirrored into the centralized index (`sessions.permission_preset`) at first
27
+ * spawn; the index row is only a fallback for logs predating the event.
20
28
  */
21
29
  import { dirname, join } from "node:path";
22
30
  /** The approval modes OMP actually implements (unknown modes degrade to yolo). */
@@ -71,6 +79,63 @@ export function presetFromEvents(events) {
71
79
  }
72
80
  return undefined;
73
81
  }
82
+ /**
83
+ * Resolve a spawn's `--approval-mode` (and the preset to record for it) from
84
+ * the session's OWN log, evaluated at the moment the child materializes.
85
+ *
86
+ * Order of authority: the last valid `permission/preset` (= the user's pick in
87
+ * the new chat, or the draft's `pinInitialPermission` stamp) → `fallbackPreset`
88
+ * (the composition default for a create, the bridge-index row for a resume of a
89
+ * log that predates the event) → OMP's native default (`yolo`). An
90
+ * `OMP_APPROVAL_MODE` env override (headless runs) still wins over all of them.
91
+ *
92
+ * @param events - the live session's events (never a stale create-time read).
93
+ * @param fallbackPreset - used only when the log carries no valid preset.
94
+ * @param envMode - `envApprovalMode()`; undefined when unset or invalid.
95
+ * @returns the launch flag value plus the preset that produced it.
96
+ */
97
+ export function resolveSpawnPermission(events, fallbackPreset, envMode) {
98
+ const preset = presetFromEvents(events) ?? (isPresetName(fallbackPreset) ? fallbackPreset : undefined);
99
+ return { approvalMode: envMode ?? ompApprovalMode(preset), preset };
100
+ }
101
+ /**
102
+ * Decide a `/permission` invocation for one OMP session.
103
+ *
104
+ * A permission pick can only become real at the child's launch, so the shadow
105
+ * accepts it exactly while no child exists (the new-chat draft, or a resumed
106
+ * session before its first prompt) and refuses it afterwards — appending a
107
+ * preset the running `omp` cannot honor would make the UI lie about the
108
+ * session's effective policy.
109
+ *
110
+ * @param input.rawInput - the preset name as typed (may be empty or padded).
111
+ * @param input.spawned - whether THIS session's `omp --mode rpc` child exists.
112
+ * @param input.names - the host's advertised preset names.
113
+ * @param input.current - the session's current preset (for the bare command).
114
+ * @returns the decision; the caller performs the write for `apply`.
115
+ */
116
+ export function ompPermissionCommand(input) {
117
+ const available = input.names.join(", ");
118
+ const name = input.rawInput.trim();
119
+ if (name === "") {
120
+ return {
121
+ action: "settled",
122
+ result: { kind: "success", text: `current preset ${input.current} (available: ${available})` },
123
+ };
124
+ }
125
+ if (!input.names.includes(name)) {
126
+ return { action: "settled", result: { kind: "error", text: `unknown preset "${name}" (available: ${available})` } };
127
+ }
128
+ if (input.spawned) {
129
+ return {
130
+ action: "settled",
131
+ result: {
132
+ kind: "error",
133
+ text: `the OMP session is already running with a fixed approval policy: pick "${name}" in a new chat, before the first prompt`,
134
+ },
135
+ };
136
+ }
137
+ return { action: "apply", preset: name, result: { kind: "success", text: `preset ${name}` } };
138
+ }
74
139
  /**
75
140
  * The three Dash events `pinInitialPermission` stamps for a fresh session,
76
141
  * in its append order (preset, sandbox, approval). Cold replay prepends
@@ -0,0 +1,409 @@
1
+ /**
2
+ * OmpRpcClient — NDJSON client over `omp --mode rpc` stdio (RPC transport line).
3
+ *
4
+ * Ported from `git show 6d2fbe1:apps/multi-agent/agent-omp/src/rpc.ts`.
5
+ *
6
+ * Protocol: `ready` is a plain event; the current binary announces
7
+ * `{type:"ready",protocolVersion:1,supportedProtocolVersions:[1,2],
8
+ * maxFrameBytes:1048576,maxReassembledFrameBytes:67108864}`.
9
+ * We deliberately DO NOT call `negotiate_protocol`: v1 is plain NDJSON, which
10
+ * is exactly what this reader implements. Negotiating 2 would switch the peer
11
+ * to >1MiB frame chunking and require a reassembler (registered gap, spec §4).
12
+ */
13
+ import { spawn } from "node:child_process";
14
+ import { existsSync } from "node:fs";
15
+ import { homedir } from "node:os";
16
+ import { join } from "node:path";
17
+ /** `$OMP_EXECUTABLE` wins; else PATH, else the common native-installer spots. */
18
+ export function resolveOmpExecutable() {
19
+ const configured = process.env.OMP_EXECUTABLE?.trim();
20
+ if (configured !== undefined && configured !== "")
21
+ return configured;
22
+ for (const dir of (process.env.PATH ?? "").split(":").filter(Boolean)) {
23
+ if (existsSync(join(dir, "omp")))
24
+ return "omp";
25
+ }
26
+ for (const candidate of [join(homedir(), ".local/bin/omp"), "/usr/local/bin/omp"]) {
27
+ if (existsSync(candidate))
28
+ return candidate;
29
+ }
30
+ return "omp";
31
+ }
32
+ /**
33
+ * Readiness deadline: bound the `ready` handshake so a broken spawn fails
34
+ * instead of hanging the caller forever. OMP can take a long time to emit
35
+ * `ready` while it performs MCP server connection and model discovery — a
36
+ * localhost model provider that drops (rather than rejects) the connection can
37
+ * hang until its own timeout, and a cold `npx` MCP server can take tens of
38
+ * seconds to spawn. The default is therefore generous; `0` disables the
39
+ * deadline. Override with `OMP_READY_TIMEOUT_MS`.
40
+ */
41
+ const READY_TIMEOUT_MS = parseTimeout(process.env.OMP_READY_TIMEOUT_MS, 120_000);
42
+ /**
43
+ * Per-command response deadline: bound every blocking request so an
44
+ * unresponsive OMP (e.g. a hung `get_messages` during resume) fails within a
45
+ * fixed window instead of stalling the caller indefinitely. `0` disables the
46
+ * deadline. Override with `OMP_COMMAND_TIMEOUT_MS`.
47
+ */
48
+ const COMMAND_TIMEOUT_MS = parseTimeout(process.env.OMP_COMMAND_TIMEOUT_MS, 30_000);
49
+ /** Parse a non-negative millisecond timeout; empty/missing → `fallback`, invalid → `fallback`. */
50
+ function parseTimeout(raw, fallback) {
51
+ if (raw === undefined || raw.trim() === "")
52
+ return fallback;
53
+ const value = Number(raw);
54
+ return Number.isFinite(value) && value >= 0 ? value : fallback;
55
+ }
56
+ /**
57
+ * Grace period between SIGTERM and the SIGKILL escalation that guarantees a
58
+ * stubborn `omp` (and its tool subprocesses) never survives teardown.
59
+ */
60
+ const KILL_GRACE_MS = 2_000;
61
+ export class OmpRpcClient {
62
+ #proc;
63
+ #listeners = new Set();
64
+ #titleListeners = new Set();
65
+ #failureListeners = new Set();
66
+ #pending = new Map();
67
+ #ready;
68
+ #readyResolve;
69
+ #readyReject;
70
+ #nextId = 0;
71
+ #buffer = "";
72
+ #closed = false;
73
+ #killed = false;
74
+ #killTimer = null;
75
+ #failure = null;
76
+ /** Spawn `omp --mode rpc` and resolve once the `ready` handshake arrives. */
77
+ static async spawn(args = [], cwd) {
78
+ const client = new OmpRpcClient(args, cwd);
79
+ await client.#ready;
80
+ return client;
81
+ }
82
+ constructor(args, cwd) {
83
+ this.#proc = spawn(resolveOmpExecutable(), ["--mode", "rpc", ...args], {
84
+ stdio: ["pipe", "pipe", "pipe"],
85
+ // Own process group so teardown can signal omp AND its tool subprocesses.
86
+ detached: true,
87
+ ...(cwd === undefined ? {} : { cwd }),
88
+ });
89
+ this.#ready = new Promise((resolve, reject) => {
90
+ const timer = setTimeout(() => {
91
+ this.#fail(new Error("omp --mode rpc: timed out waiting for the `ready` event"));
92
+ }, READY_TIMEOUT_MS);
93
+ this.#readyResolve = () => {
94
+ clearTimeout(timer);
95
+ resolve();
96
+ };
97
+ this.#readyReject = (error) => {
98
+ clearTimeout(timer);
99
+ reject(error);
100
+ };
101
+ });
102
+ this.#proc.stdout.setEncoding("utf8");
103
+ this.#proc.stdout.on("data", (chunk) => this.#onData(chunk));
104
+ this.#proc.stderr.setEncoding("utf8");
105
+ this.#proc.stderr.on("data", (chunk) => {
106
+ // OMP writes diagnostics to stderr; surface them for debugging.
107
+ process.stderr.write(chunk);
108
+ });
109
+ this.#proc.on("error", (error) => this.#fail(error));
110
+ this.#proc.on("exit", (code) => {
111
+ if (this.#killTimer !== null) {
112
+ clearTimeout(this.#killTimer);
113
+ this.#killTimer = null;
114
+ }
115
+ if (!this.#closed)
116
+ this.#fail(new Error(`omp --mode rpc exited unexpectedly (code ${code})`));
117
+ });
118
+ }
119
+ /** Register an event listener; returns the unsubscriber. */
120
+ on(listener) {
121
+ this.#listeners.add(listener);
122
+ return () => {
123
+ this.#listeners.delete(listener);
124
+ };
125
+ }
126
+ /** Register a failure listener invoked when the child exits or errors. Returns the unsubscriber. */
127
+ onFailure(listener) {
128
+ this.#failureListeners.add(listener);
129
+ return () => {
130
+ this.#failureListeners.delete(listener);
131
+ };
132
+ }
133
+ /**
134
+ * Write one fire-and-forget command that must NOT carry a correlation id.
135
+ * `extension_ui_response` keys its `id` to the originating UI request, so the
136
+ * correlation-adding `send()` would clobber it.
137
+ */
138
+ sendRaw(command) {
139
+ if (this.#failure !== null || this.#closed)
140
+ return;
141
+ try {
142
+ this.#proc.stdin.write(JSON.stringify(command) + "\n");
143
+ }
144
+ catch (error) {
145
+ process.stderr.write(`omp rpc write error: ${String(error)}\n`);
146
+ }
147
+ }
148
+ /**
149
+ * Send one command and resolve with its `response`. Fire-and-forget callers
150
+ * may ignore the promise. A per-command deadline (see COMMAND_TIMEOUT_MS)
151
+ * rejects the request if OMP never answers, so blocking callers
152
+ * (`getMessages`) can't stall indefinitely; pass `timeoutMs` to override,
153
+ * or `0` to disable the deadline.
154
+ */
155
+ send(command, timeoutMs = COMMAND_TIMEOUT_MS) {
156
+ if (this.#failure !== null)
157
+ return Promise.reject(this.#failure);
158
+ if (this.#closed)
159
+ return Promise.reject(new Error("omp rpc client is closed"));
160
+ const id = `omp-${++this.#nextId}`;
161
+ return new Promise((resolve, reject) => {
162
+ const pending = {
163
+ resolve,
164
+ reject,
165
+ clear: () => { },
166
+ };
167
+ if (timeoutMs > 0) {
168
+ const timer = setTimeout(() => {
169
+ if (this.#pending.delete(id)) {
170
+ reject(new Error(`omp rpc command timed out after ${timeoutMs}ms`));
171
+ }
172
+ }, timeoutMs);
173
+ pending.clear = () => clearTimeout(timer);
174
+ }
175
+ this.#pending.set(id, pending);
176
+ try {
177
+ this.#proc.stdin.write(JSON.stringify({ ...command, id }) + "\n");
178
+ }
179
+ catch (error) {
180
+ this.#pending.delete(id);
181
+ pending.clear();
182
+ reject(error);
183
+ }
184
+ });
185
+ }
186
+ /** Query `get_state`; returns the state payload. */
187
+ async getState() {
188
+ const response = await this.send({ type: "get_state" });
189
+ if (!response.success)
190
+ throw new Error(`omp get_state failed: ${String(response.error ?? "unknown error")}`);
191
+ return (response.data ?? { isStreaming: false });
192
+ }
193
+ /** Query `get_messages`; returns the full conversation. */
194
+ async getMessages() {
195
+ const response = await this.send({ type: "get_messages" });
196
+ if (!response.success)
197
+ throw new Error(`omp get_messages failed: ${String(response.error ?? "unknown error")}`);
198
+ const data = response.data;
199
+ return data?.messages ?? [];
200
+ }
201
+ /** Query `get_session_stats`; live token/context/cost accounting (live sessions only). */
202
+ async getSessionStats() {
203
+ const response = await this.send({ type: "get_session_stats" });
204
+ if (!response.success)
205
+ throw new Error(`omp get_session_stats failed: ${String(response.error ?? "unknown error")}`);
206
+ return (response.data ?? {});
207
+ }
208
+ /** Query `get_subagents`; live subagent registry (fail-soft: [] when unavailable). */
209
+ async getSubagents() {
210
+ try {
211
+ const response = await this.send({ type: "get_subagents" });
212
+ if (!response.success)
213
+ return [];
214
+ const data = response.data;
215
+ return data?.subagents ?? [];
216
+ }
217
+ catch {
218
+ return [];
219
+ }
220
+ }
221
+ /**
222
+ * Start a turn with a user prompt. On an idle session `prompt` wakes the
223
+ * agent and starts a fresh turn. Never send this while OMP is streaming:
224
+ * without `streamingBehavior` OMP rejects it, and `prompt` with
225
+ * `streamingBehavior: "followUp"` queues with `resumeIfIdle: false` (delivered
226
+ * as a user message on idle, never auto-generating a turn).
227
+ */
228
+ prompt(message) {
229
+ return this.send({ type: "prompt", message });
230
+ }
231
+ /**
232
+ * Queue a follow-up on a busy session. OMP delivers it as the next turn's
233
+ * user message once the current turn ends AND auto-generates that turn —
234
+ * the dedicated `follow_up` command's connection layer hardcodes
235
+ * `resumeIfIdle: true` (unlike `prompt` + streamingBehavior). Verified
236
+ * empirically: docs/probe-omp-followup.mjs.
237
+ */
238
+ followUp(message) {
239
+ return this.send({ type: "follow_up", message });
240
+ }
241
+ /** Steer an in-flight turn with a user message. */
242
+ steer(message) {
243
+ return this.send({ type: "steer", message });
244
+ }
245
+ /** Select the active model by provider + model id (e.g. `deepseek`/`deepseek-v4-pro`). */
246
+ setModel(provider, modelId) {
247
+ return this.send({ type: "set_model", provider, modelId });
248
+ }
249
+ /** Abort the current streaming run. */
250
+ abort() {
251
+ return this.send({ type: "abort" });
252
+ }
253
+ /** Discard the current conversation and start a fresh OMP session. */
254
+ newSession() {
255
+ return this.send({ type: "new_session" });
256
+ }
257
+ /** Title frames (OMP `session_info_update.title`, plus `setTitle` when the
258
+ * child runs with `PI_RPC_EMIT_TITLE=1`). Returns the unsubscriber. */
259
+ onTitle(listener) {
260
+ this.#titleListeners.add(listener);
261
+ return () => { this.#titleListeners.delete(listener); };
262
+ }
263
+ /** System prompt as OMP renders it (v18 `get_state.systemPrompt`). */
264
+ async getSystemPrompt() {
265
+ try {
266
+ const state = await this.getState();
267
+ return state.systemPrompt;
268
+ }
269
+ catch {
270
+ return undefined;
271
+ }
272
+ }
273
+ /** Context-usage snapshot for pre/post compact metering (fail-soft). */
274
+ async contextUsage() {
275
+ try {
276
+ const state = await this.getState();
277
+ return state.contextUsage;
278
+ }
279
+ catch {
280
+ return undefined;
281
+ }
282
+ }
283
+ /** Compact the live conversation (RPC `compact` command). */
284
+ compact(customInstructions) {
285
+ return this.send(customInstructions === undefined
286
+ ? { type: "compact" }
287
+ : { type: "compact", customInstructions });
288
+ }
289
+ close() {
290
+ if (this.#closed)
291
+ return;
292
+ this.#closed = true;
293
+ if (process.env.OMP_TRACE === "1") {
294
+ process.stderr.write(`[omp-rpc ${Date.now() % 1_000_000}] close() stack=${new Error().stack?.split("\n").slice(1, 6).join(" <- ")}\n`);
295
+ }
296
+ this.#kill();
297
+ this.#fail(new Error("omp rpc client closed"));
298
+ }
299
+ /** SIGTERM the process group, escalating to SIGKILL if it lingers. */
300
+ #kill() {
301
+ if (this.#killed)
302
+ return;
303
+ this.#killed = true;
304
+ const pid = this.#proc.pid;
305
+ if (pid === undefined)
306
+ return;
307
+ try {
308
+ process.kill(-pid, "SIGTERM");
309
+ }
310
+ catch {
311
+ return; // group already gone
312
+ }
313
+ this.#killTimer = setTimeout(() => {
314
+ this.#killTimer = null;
315
+ try {
316
+ process.kill(-pid, "SIGKILL");
317
+ }
318
+ catch {
319
+ // already dead
320
+ }
321
+ }, KILL_GRACE_MS);
322
+ }
323
+ #onData(chunk) {
324
+ this.#buffer += chunk;
325
+ let index;
326
+ while ((index = this.#buffer.indexOf("\n")) >= 0) {
327
+ let line = this.#buffer.slice(0, index);
328
+ this.#buffer = this.#buffer.slice(index + 1);
329
+ if (line.endsWith("\r"))
330
+ line = line.slice(0, -1);
331
+ if (line.trim() === "")
332
+ continue;
333
+ this.#onLine(line);
334
+ }
335
+ }
336
+ #onLine(line) {
337
+ let record;
338
+ try {
339
+ record = JSON.parse(line);
340
+ }
341
+ catch {
342
+ return; // ignore malformed lines
343
+ }
344
+ if (record.type === "ready") {
345
+ this.#readyResolve();
346
+ return;
347
+ }
348
+ if (record.type === "session_info_update" && typeof record.title === "string" && record.title !== "") {
349
+ for (const listener of [...this.#titleListeners]) {
350
+ try {
351
+ listener(record.title);
352
+ }
353
+ catch { /* contained */ }
354
+ }
355
+ }
356
+ if (record.type === "setTitle" && typeof record.title === "string" && record.title !== "") {
357
+ for (const listener of [...this.#titleListeners]) {
358
+ try {
359
+ listener(record.title);
360
+ }
361
+ catch { /* contained */ }
362
+ }
363
+ }
364
+ if (record.type === "response") {
365
+ const response = record;
366
+ if (response.id !== undefined) {
367
+ const pending = this.#pending.get(response.id);
368
+ if (pending !== undefined) {
369
+ this.#pending.delete(response.id);
370
+ pending.clear();
371
+ pending.resolve(response);
372
+ }
373
+ }
374
+ return;
375
+ }
376
+ for (const listener of [...this.#listeners]) {
377
+ try {
378
+ listener(record);
379
+ }
380
+ catch (error) {
381
+ // Listener failures are contained — one bad observer must not break the stream.
382
+ process.stderr.write(`omp rpc listener error: ${String(error)}\n`);
383
+ }
384
+ }
385
+ }
386
+ #fail(error) {
387
+ if (this.#failure !== null)
388
+ return;
389
+ this.#failure = error;
390
+ // Reject an in-flight handshake immediately (fix: not just via the 30s timeout).
391
+ this.#readyReject(error);
392
+ for (const pending of this.#pending.values()) {
393
+ pending.clear();
394
+ pending.reject(error);
395
+ }
396
+ this.#pending.clear();
397
+ // Any failure path (ready-timeout, pre-ready exit, spawn error) must also
398
+ // terminate the child so no orphan keeps the session file held.
399
+ this.#kill();
400
+ for (const listener of [...this.#failureListeners]) {
401
+ try {
402
+ listener(error);
403
+ }
404
+ catch (listenerError) {
405
+ process.stderr.write(`omp rpc failure listener error: ${String(listenerError)}\n`);
406
+ }
407
+ }
408
+ }
409
+ }
@@ -16,15 +16,42 @@
16
16
  * per-label `/<label>/api` + `/<label>/api/remote.mux` mounts retired
17
17
  * in T-C, `mountWorld` deleted in T-D).
18
18
  */
19
- import { mkdirSync } from 'node:fs';
19
+ import { existsSync, mkdirSync } from 'node:fs';
20
20
  import { resolveDshHome } from '@deepseek-ai/dsh-home-paths';
21
21
  import { join } from 'node:path';
22
- import { spawnWorld, registerAgent, setReady, registerForeignTarget, registerHostMount, worldMountPatches, provisionWorldProfile, resolveInstallAnchor, } from '../../superd-hub/dist/index.js';
22
+ import { spawnWorld, registerAgent, unregisterAgent, setReady, registerForeignTarget, registerHostMount, worldMountPatches, provisionWorldProfile, resolveInstallAnchor, } from '../../superd-hub/dist/index.js';
23
+ import { resolveOmpExecutable } from './rpc-client.js';
23
24
  export const name = 'agent-omp';
24
25
  /** Runtime key — also the mount label. */
25
26
  const KEY = 'omp';
27
+ /**
28
+ * Startup diagnostic (spec §3.5): the OMP world runs the operator's own `omp`
29
+ * binary over `omp --mode rpc` — the adapter bundles no OMP copy, so a missing
30
+ * binary fails at the FIRST PROMPT, not at boot. Probe the resolution order
31
+ * once and say so explicitly instead of mounting silently; readiness semantics
32
+ * are unchanged (they follow the world mount, not this probe).
33
+ */
34
+ function diagnoseOmpBinary() {
35
+ const executable = resolveOmpExecutable();
36
+ // A bare name is what the resolver yields for BOTH a PATH hit and "nothing
37
+ // installed" — walk PATH to tell those apart. A slashed candidate is already
38
+ // a concrete path and is checked directly.
39
+ const present = executable.includes('/')
40
+ ? existsSync(executable)
41
+ : (process.env.PATH ?? '').split(':').filter(Boolean).some((dir) => existsSync(join(dir, executable)));
42
+ if (!present) {
43
+ console.error('[super-dsh] omp binary not found (checked $OMP_EXECUTABLE, PATH, ~/.local/bin/omp) — the OMP world will fail at first prompt');
44
+ }
45
+ }
26
46
  export function apply(ctx) {
27
- registerAgent({ key: KEY, label: 'OMP', ready: false });
47
+ diagnoseOmpBinary();
48
+ // Fiber-scoped roster entry: disabling the row disposes this fiber and the
49
+ // entry must go with it, otherwise a re-enable dies on `roster: duplicate
50
+ // agent key omp` (same defect the omp-sdk acceptance surfaced live).
51
+ ctx.effect?.(() => {
52
+ registerAgent({ key: KEY, label: 'OMP', ready: false });
53
+ return () => unregisterAgent(KEY);
54
+ }, `agent-${KEY}: roster`);
28
55
  // Harness resolver (env → ~/.dsh): a consumer install has no DSH_HOME and
29
56
  // no cwd contract — deriving the world root from cwd spawns worlds into a
30
57
  // garbage tree or throws inside apply() and kills the whole host boot.
@@ -64,6 +91,12 @@ export function apply(ctx) {
64
91
  ctx.effect?.(() => () => {
65
92
  dropHostMount();
66
93
  }, `agent-${KEY}: mount`);
94
+ // Unloading the row must unload the world: disposing the world's fiber
95
+ // disposes its provider, which closes every held transport (and therefore
96
+ // the `omp --mode rpc` children).
97
+ ctx.effect?.(() => () => {
98
+ void ctxW.fiber?.dispose();
99
+ }, `agent-${KEY}: world teardown`);
67
100
  return ctxW;
68
101
  })().catch((cause) => {
69
102
  // A failed world degrades to a not-ready roster row; a rejected provided
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pgmi-builds/agent-omp",
3
3
  "version": "0.1.0",
4
- "description": "OMP Web: OMP provider for DeepSeek Harness (dsh) — an AgentFactory that embeds OMP via the @oh-my-pi/pi-coding-agent SDK (Bun sidecar) and bridges OMP's session surface into Dash Agent/Session contracts.",
4
+ "description": "OMP Web: OMP provider for DeepSeek Harness (dsh) — an AgentFactory that drives the operator's own omp binary as an `omp --mode rpc` child (NDJSON protocol v1 over stdio) and bridges OMP's session surface into Dash Agent/Session contracts.",
5
5
  "type": "module",
6
6
  "exports": {
7
7
  ".": "./dist/index.js",
@@ -11,7 +11,6 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "lib",
14
- "sidecar",
15
14
  "cordis.patch.yml",
16
15
  "README.md"
17
16
  ],
@@ -32,10 +31,7 @@
32
31
  "build-client": "npm run typecheck:client && tsx scripts/build-client.ts",
33
32
  "test": "node --test test/*.test.mjs"
34
33
  },
35
- "dependencies": {
36
- "@oh-my-pi/pi-coding-agent": "^18.1.14",
37
- "bun": "^1.4.2"
38
- },
34
+ "dependencies": {},
39
35
  "peerDependencies": {
40
36
  "@deepseek-ai/cordis": "^4.0.1",
41
37
  "@deepseek-ai/dsh-agent": ">=0.1.5-0 <0.3.0-0",