super-dsh 0.1.9 → 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 (178) 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 +1204 -93
  162. package/cordis.patch.yml +64 -0
  163. package/package.json +17 -8
  164. package/superd-hub/dist/client/agent-settings.d.ts +210 -0
  165. package/superd-hub/dist/client/agent-settings.js +300 -0
  166. package/superd-hub/dist/client/roster.js +6 -0
  167. package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
  168. package/superd-hub/dist/client/runtime-settings-api.js +137 -0
  169. package/superd-hub/dist/client/switch.d.ts +165 -0
  170. package/superd-hub/dist/client/switch.js +295 -0
  171. package/superd-hub/lib/client/index.js +1204 -93
  172. package/agent-codex/dist/models.js +0 -152
  173. package/agent-omp/dist/sidecar-client.js +0 -195
  174. /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
  175. /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
  176. /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
  177. /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
  178. /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
package/cordis.patch.yml CHANGED
@@ -71,6 +71,70 @@
71
71
  name: 'super-dsh/agents/hermes'
72
72
  - id: agent-agy
73
73
  name: 'super-dsh/agents/agy'
74
+ # Opt-IN SDK half of the Antigravity split (2026-10-02 user ruling). The
75
+ # CLI row above is the PRIMARY path: the user already runs Antigravity, so
76
+ # the CLI reuses that environment (their ~/.gemini, ADC and keyring) for
77
+ # free, while this row drives the Python `google-antigravity` SDK instead.
78
+ #
79
+ # DISABLED BY DEFAULT — turning the row on IS the switch
80
+ # (`plugin-presence IS the roster`): no env var, no config key. It runs as
81
+ # world key `agy-sdk` (roster label "Antigravity (SDK)", world home
82
+ # `agents/agy-sdk`), deliberately distinct from `agy` because the hub's
83
+ # registerAgent hard-throws on a duplicate key and this row ships beside
84
+ # the CLI row — the two can therefore both be enabled harmlessly.
85
+ #
86
+ # No Python is installed at `dsh plugin add` time and this package declares
87
+ # no SDK dependency: the durable venv is fetched into that world home on
88
+ # first real use (agent-agy-sdk/src/sdk-venv.ts).
89
+ - id: agent-agy-sdk
90
+ name: 'super-dsh/agents/agy-sdk'
91
+ disabled: true
92
+ # Opt-IN SDK transport for OMP (2026-10-03 user ruling). `agent-omp` is the
93
+ # PRIMARY path: it drives the operator's own `omp` binary, so the plugin
94
+ # ships no runtime at all. This row drives the SAME agent through the
95
+ # package's in-process SDK face instead — one bun sidecar hosting every
96
+ # session (shared authStorage/modelRegistry), which is the cheaper memory
97
+ # shape for many concurrent sessions, at the cost of fetching a ~900 MB
98
+ # runtime tree.
99
+ #
100
+ # DISABLED BY DEFAULT — turning the row on IS the switch
101
+ # (`plugin-presence IS the roster`): no env var, no config key. It runs as
102
+ # world key `omp-sdk` (roster label "OMP (SDK)", world home
103
+ # `agents/omp-sdk`), deliberately distinct from `omp` because the hub's
104
+ # registerAgent hard-throws on a duplicate key and this row ships beside
105
+ # the RPC row — the two can therefore both be enabled harmlessly.
106
+ #
107
+ # Nothing is installed at `dsh plugin add` time and this package declares no
108
+ # dependency: the durable bun runtime + `@oh-my-pi/pi-coding-agent` tree is
109
+ # fetched into that world home on first real use
110
+ # (agent-omp-sdk/src/sdk-runtime.ts).
111
+ - id: agent-omp-sdk
112
+ name: 'super-dsh/agents/omp-sdk'
113
+ disabled: true
114
+ # PRESERVED SDK line for Codex (2026-10-04 user ruling). `agent-codex` is the
115
+ # PRIMARY path — it drives a `codex app-server` child process, so the plugin
116
+ # bundles no SDK: the CLI the operator already has is the runtime. This row
117
+ # drives the SAME agent through the older in-process `@openai/codex-sdk` face
118
+ # instead, kept because the code was already written and it is a real option
119
+ # (no child process per session, and it works with no `codex` binary on PATH
120
+ # once its runtime is fetched).
121
+ #
122
+ # DISABLED BY DEFAULT — turning the row on IS the switch (`plugin-presence IS
123
+ # the roster`): no env var, no config key. It runs as world key `codex-sdk`
124
+ # (roster label "Codex (SDK)", world home `agents/codex-sdk`), deliberately
125
+ # distinct from `codex` because the hub's registerAgent hard-throws on a
126
+ # duplicate key and this row ships beside the app-server row — the two can
127
+ # therefore both be enabled harmlessly.
128
+ #
129
+ # Nothing is installed at `dsh plugin add` time and this package declares NO
130
+ # dependency — not a dependency, not an optionalDependency, not a peer: the
131
+ # SDK tree (`@openai/codex-sdk` + the codex binary package, pinned together)
132
+ # is fetched into that world home on the first real prompt and is durable
133
+ # there (agent-codex-sdk/src/sdk-runtime.ts; pre-warm with
134
+ # `npm run fetch-sdk-runtime`). Zero-spawn, zero-download window before it.
135
+ - id: agent-codex-sdk
136
+ name: 'super-dsh/agents/codex-sdk'
137
+ disabled: true
74
138
  # R2 (2026-09-16): the claude adapter ships STANDALONE only — its /world
75
139
  # entry is inert. The HUB side joins it: one generic joiner row with the
76
140
  # key; spawn/roster/mount wiring lives in super-dsh/join.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "super-dsh",
3
- "version": "0.1.9",
4
- "description": "Super D for the DeepSeek Harness (DSH) — one dsh plugin that mounts the superd hub (runtime selector, roster, per-runtime agents) and joins local foreign-agent runtimes (OMP, Codex, Claude, Pi, Hermes, Antigravity) into the DSH Web UI. Each runtime keeps its native home and session store; the hub only bridges.",
3
+ "version": "0.1.10-e",
4
+ "description": "Super D for the DeepSeek Harness (DSH) \u2014 one dsh plugin that mounts the superd hub (runtime selector, roster, per-runtime agents) and joins local foreign-agent runtimes (OMP, Codex, Claude, Pi, Hermes, Antigravity) into the DSH Web UI. Each runtime keeps its native home and session store; the hub only bridges.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -18,6 +18,9 @@
18
18
  "./agents/pi": "./agent-pi/dist/world-plugin.js",
19
19
  "./agents/hermes": "./agent-hermes/dist/world-plugin.js",
20
20
  "./agents/agy": "./agent-agy/dist/world-plugin.js",
21
+ "./agents/agy-sdk": "./agent-agy-sdk/dist/world-plugin.js",
22
+ "./agents/omp-sdk": "./agent-omp-sdk/dist/world-plugin.js",
23
+ "./agents/codex-sdk": "./agent-codex-sdk/dist/world-plugin.js",
21
24
  "./client": "./client/index.js",
22
25
  "./cordis.patch.yml": "./cordis.patch.yml",
23
26
  "./package.json": "./package.json",
@@ -36,10 +39,17 @@
36
39
  "agent-omp/dist",
37
40
  "agent-omp/package.json",
38
41
  "agent-omp/cordis.patch.yml",
39
- "agent-omp/sidecar",
42
+ "agent-omp-sdk/dist",
43
+ "agent-omp-sdk/sidecar",
44
+ "agent-omp-sdk/package.json",
45
+ "agent-omp-sdk/cordis.patch.yml",
40
46
  "agent-codex/dist",
41
47
  "agent-codex/package.json",
42
48
  "agent-codex/cordis.patch.yml",
49
+ "agent-codex-sdk/dist",
50
+ "agent-codex-sdk/scripts",
51
+ "agent-codex-sdk/package.json",
52
+ "agent-codex-sdk/cordis.patch.yml",
43
53
  "agent-claude/dist",
44
54
  "agent-claude/package.json",
45
55
  "agent-claude/cordis.patch.yml",
@@ -52,7 +62,10 @@
52
62
  "agent-agy/dist",
53
63
  "agent-agy/package.json",
54
64
  "agent-agy/cordis.patch.yml",
55
- "agent-agy/bridge"
65
+ "agent-agy-sdk/dist",
66
+ "agent-agy-sdk/package.json",
67
+ "agent-agy-sdk/cordis.patch.yml",
68
+ "agent-agy-sdk/bridge"
56
69
  ],
57
70
  "dsh": {
58
71
  "bundle": {
@@ -66,12 +79,8 @@
66
79
  }
67
80
  },
68
81
  "dependencies": {
69
- "@anthropic-ai/claude-agent-sdk": "0.3.263",
70
82
  "@earendil-works/pi-coding-agent": "0.84.2",
71
- "@oh-my-pi/pi-coding-agent": "^18.1.14",
72
83
  "@openai/codex": "0.154.0",
73
- "@openai/codex-sdk": "0.154.0",
74
- "bun": "^1.4.2",
75
84
  "ws": "^8.21.0",
76
85
  "zod": "4.4.3",
77
86
  "@modelcontextprotocol/sdk": "^1.29.0"
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Runtime settings panels for the Hub's Agent Runtimes rows (P3, 2026-10-02
3
+ * user ruling).
4
+ *
5
+ * Interaction contract the UI follows (ruling, verbatim intent):
6
+ * - clicking the ROW BODY expands that runtime's OWN settings inline
7
+ * (accordion: at most one row open at a time);
8
+ * - the row's existing right-hand `switch` affordance is the ONLY switch —
9
+ * the panel never carries a second one.
10
+ *
11
+ * Config belongs to the adapter (agent-adapter-dev-rules §12), so this module
12
+ * owns only the presentation-side logic: which runtimes have a panel, the
13
+ * accordion transition, and a defensive parse of whatever the adapter's
14
+ * `*Auth/get` RPC returns. The adapter decides which auth methods exist, which
15
+ * are available on this machine, and which one is in force.
16
+ *
17
+ * Node-safe by construction (no DOM, no React, no fetch) — the host tsconfig
18
+ * dual-emits this file to `dist/client/agent-settings.js` so
19
+ * test/client-agent-settings.test.mjs can pin the parse and accordion rules
20
+ * under `node --test` (the roster.ts / mesh-api.ts precedent).
21
+ *
22
+ * @module superd-hub/client/agent-settings
23
+ */
24
+ /** One selectable auth method as the adapter describes it. */
25
+ export interface AuthMethodOption {
26
+ /** Stable id the RPC round-trips (`apiKey` | `oauth` | `adc`). */
27
+ readonly key: string;
28
+ /** Human label rendered on the option. */
29
+ readonly label: string;
30
+ /** Detected on THIS machine. Drives the availability annotation. */
31
+ readonly available: boolean;
32
+ /**
33
+ * Rendered but not selectable this round (the OAuth placeholder): the UI
34
+ * shows the shape and says why it is inert, instead of hiding it.
35
+ */
36
+ readonly placeholder: boolean;
37
+ /** One short line explaining the option (or why it is inert). */
38
+ readonly hint: string;
39
+ }
40
+ /** The adapter's answer to `<runtime>Auth/get`. */
41
+ export interface RuntimeAuthView {
42
+ readonly methods: readonly AuthMethodOption[];
43
+ /** The user's explicit persisted choice; `null` when they have not made one. */
44
+ readonly selected: string | null;
45
+ /** What a new session would ACTUALLY use right now (ladder fallback applied). */
46
+ readonly effective: string | null;
47
+ /** Native file the adapter maintains for this runtime, when it owns one. */
48
+ readonly settingsPath?: string;
49
+ /** Free-form note from the adapter (e.g. why a mode is unavailable). */
50
+ readonly note?: string;
51
+ }
52
+ /**
53
+ * A runtime's settings panel: which adapter Remote answers for it.
54
+ *
55
+ * The namespace is the adapter's own service key — the hub forwards
56
+ * `envelope.method` verbatim, so the client needs only the name. A runtime absent
57
+ * from this table has no panel yet: its row still expands, to an explicit empty
58
+ * state rather than a dead control (the interaction stays uniform, and a runtime
59
+ * with nothing to configure SAYS so).
60
+ */
61
+ export interface SettingsPanelSpec {
62
+ /** The adapter's Remote namespace (`agyAuth`). */
63
+ readonly namespace: string;
64
+ /**
65
+ * Which body the expanded row mounts. A runtime's config is not always a
66
+ * choice among a fixed set: Antigravity picks one sign-in form from a ladder,
67
+ * while Codex carries a LIST of providers the user adds, edits and removes
68
+ * (2026-10-03 ruling; the former active/select toggle is gone — which provider
69
+ * a Codex session runs on is DSH's own per-session model selection, not a
70
+ * hub-side switch). One namespace, two shapes — the kind discriminates.
71
+ */
72
+ readonly kind: 'auth' | 'providers';
73
+ }
74
+ /** The panel spec for a runtime, or `undefined` when it has none yet. */
75
+ export declare function settingsPanelFor(agentKey: string): SettingsPanelSpec | undefined;
76
+ /** Whether this runtime's row body expands into a real settings panel. */
77
+ export declare function hasSettingsPanel(agentKey: string): boolean;
78
+ /**
79
+ * Accordion transition: at most one row open. Clicking the open row closes it;
80
+ * clicking another moves the panel there in one click.
81
+ */
82
+ export declare function toggleExpanded(current: string | null, key: string): string | null;
83
+ /**
84
+ * Defensive parse of the RPC answer. Returns `undefined` for anything that is
85
+ * not a usable view (no methods, wrong shape, adapter absent) so the panel can
86
+ * render an explicit failure instead of an empty list that looks like success.
87
+ */
88
+ export declare function parseAuthView(raw: unknown): RuntimeAuthView | undefined;
89
+ /**
90
+ * The option the panel marks as in force: the effective one when the adapter
91
+ * named it, else the explicit selection. `undefined` when neither is known —
92
+ * the panel then marks nothing rather than guessing.
93
+ */
94
+ export declare function activeMethod(view: RuntimeAuthView): AuthMethodOption | undefined;
95
+ /** Whether an option may be picked (placeholders are rendered, never selected). */
96
+ export declare function selectable(option: AuthMethodOption): boolean;
97
+ /** One model behind a provider, as the panel lists it. */
98
+ export interface ProviderModelOption {
99
+ readonly id: string;
100
+ /** Human label; the adapter sends one, else the id stands in. */
101
+ readonly label: string;
102
+ readonly contextWindow?: number;
103
+ }
104
+ /**
105
+ * The auth shapes the provider faces use between them: `apiKey` is shared,
106
+ * codex adds `env` (the key lives in a named variable), claude adds
107
+ * `authToken` (a Bearer token). Each face's adapter rejects the other's
108
+ * addition on save, which is why the form offers kinds per view
109
+ * (`authKindOptions`) instead of one fixed list.
110
+ */
111
+ export type ProviderAuthKind = 'apiKey' | 'env' | 'authToken';
112
+ /** One configured provider, listed but never carrying its key. */
113
+ export interface ProviderOption {
114
+ readonly id: string;
115
+ readonly label: string;
116
+ readonly baseUrl: string;
117
+ readonly protocol: string;
118
+ readonly models: readonly ProviderModelOption[];
119
+ /** Which key shape the entry stores — see `ProviderAuthKind`. */
120
+ readonly authKind: ProviderAuthKind;
121
+ /** The variable an env-shaped provider expects. */
122
+ readonly envName?: string;
123
+ /** Whether a key is available — the ONLY thing the wire says about it. */
124
+ readonly hasKey: boolean;
125
+ }
126
+ /** One wire protocol the adapter offers, and whether it may be picked. */
127
+ export interface ProviderProtocolOption {
128
+ readonly value: string;
129
+ readonly label: string;
130
+ readonly available: boolean;
131
+ readonly hint: string;
132
+ }
133
+ /**
134
+ * The files this surface reads and writes, shown so where an edit lands is
135
+ * never a surprise. Only `store` is common: each face names its own native
136
+ * files (codex: config/auth/catalogs; claude: settings), and a face that
137
+ * lacks a slot simply omits it — the footer renders what is there.
138
+ */
139
+ export interface ProviderPaths {
140
+ readonly store: string;
141
+ readonly config?: string;
142
+ readonly auth?: string;
143
+ readonly catalogs?: string;
144
+ readonly settings?: string;
145
+ }
146
+ /** The adapter's answer to `codexProviders/get` / `claudeProviders/get` (and to every mutation). */
147
+ export interface RuntimeProvidersView {
148
+ readonly providers: readonly ProviderOption[];
149
+ /**
150
+ * What the user's OWN native config currently names (codex: the provider
151
+ * `~/.codex/config.toml` selects; claude: the endpoint the `settings.json`
152
+ * env pins). Informational only — there is no action attached to it, and
153
+ * DSH's per-session model selection (not this list) decides what a session
154
+ * runs.
155
+ */
156
+ readonly native: {
157
+ readonly providerId?: string;
158
+ readonly baseUrl?: string;
159
+ readonly model?: string;
160
+ } | null;
161
+ /**
162
+ * The wire protocols the adapter offers, in the order it offers them. Empty
163
+ * for a face with no protocol choice (claude) — the panel then omits the
164
+ * dropdown instead of inventing options the runtime cannot run.
165
+ */
166
+ readonly protocols: readonly ProviderProtocolOption[];
167
+ readonly paths: ProviderPaths;
168
+ /** Epoch ms of the last reconcile against the native config, when one has happened. */
169
+ readonly syncedAt?: number;
170
+ readonly note?: string;
171
+ }
172
+ /**
173
+ * Defensive parse of the adapter's providers answer.
174
+ *
175
+ * `undefined` for anything that is not a usable view — but an empty `providers`
176
+ * array is valid (a fresh install), and so is a provider whose `models` array
177
+ * is empty. `protocols` is the codex face's field: an ABSENT field (the claude
178
+ * face) reads as "no protocol choice" and never fails the parse, while a
179
+ * present-but-unusable list is a malformed codex answer and rejects as before.
180
+ */
181
+ export declare function parseProvidersView(raw: unknown): RuntimeProvidersView | undefined;
182
+ /** One selectable auth shape for the add/edit form (`authKindOptions`). */
183
+ export interface ProviderAuthKindOption {
184
+ readonly value: ProviderAuthKind;
185
+ readonly label: string;
186
+ readonly hint: string;
187
+ }
188
+ /**
189
+ * The auth shapes the add/edit form may pick for a provider, in display order.
190
+ *
191
+ * The view carries no explicit vocabulary field, so the panel reads it off the
192
+ * one field that distinguishes the faces today: `protocols`. The codex face
193
+ * advertises wire protocols and offers key-or-env-var; the claude face carries
194
+ * neither and offers its two header shapes instead. The list is per-view,
195
+ * never a fixed union — each adapter rejects the other's kinds on save, so a
196
+ * union dropdown would miscode silently. A third face extends the table.
197
+ */
198
+ export declare function authKindOptions(view: RuntimeProvidersView): readonly ProviderAuthKindOption[];
199
+ /**
200
+ * `provider / model` (codex) or `endpoint / model` (claude) — whichever halves
201
+ * the user's own native config names, joined for the one-line fact the panel
202
+ * states and never acts on.
203
+ */
204
+ export declare function nativeDescription(native: RuntimeProvidersView['native']): string;
205
+ /**
206
+ * The non-store paths joined for the panel's second footer line, in a fixed
207
+ * slot order. `undefined` when the face named none — the line is omitted
208
+ * entirely rather than rendered with blank slots.
209
+ */
210
+ export declare function nativePathsLine(paths: ProviderPaths): string | undefined;
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Runtime settings panels for the Hub's Agent Runtimes rows (P3, 2026-10-02
3
+ * user ruling).
4
+ *
5
+ * Interaction contract the UI follows (ruling, verbatim intent):
6
+ * - clicking the ROW BODY expands that runtime's OWN settings inline
7
+ * (accordion: at most one row open at a time);
8
+ * - the row's existing right-hand `switch` affordance is the ONLY switch —
9
+ * the panel never carries a second one.
10
+ *
11
+ * Config belongs to the adapter (agent-adapter-dev-rules §12), so this module
12
+ * owns only the presentation-side logic: which runtimes have a panel, the
13
+ * accordion transition, and a defensive parse of whatever the adapter's
14
+ * `*Auth/get` RPC returns. The adapter decides which auth methods exist, which
15
+ * are available on this machine, and which one is in force.
16
+ *
17
+ * Node-safe by construction (no DOM, no React, no fetch) — the host tsconfig
18
+ * dual-emits this file to `dist/client/agent-settings.js` so
19
+ * test/client-agent-settings.test.mjs can pin the parse and accordion rules
20
+ * under `node --test` (the roster.ts / mesh-api.ts precedent).
21
+ *
22
+ * @module superd-hub/client/agent-settings
23
+ */
24
+ const SETTINGS_PANELS = {
25
+ // The one runtime with a settings surface this round: the CLI adapter owns the
26
+ // auth ladder and is the configuration surface for the user's local Antigravity.
27
+ agy: { namespace: 'agyAuth', kind: 'auth' },
28
+ // Codex's own config.toml names exactly one `model_provider`, but that file
29
+ // is the USER's and no longer the hub's toggle: Super D hosts the list
30
+ // cc-switch otherwise holds (add / edit / remove) and the adapter owns both
31
+ // the list and whatever it does with the native file.
32
+ codex: { namespace: 'codexProviders', kind: 'providers' },
33
+ // Both Codex lines host the SAME provider list (2026-10-04): the SDK half is
34
+ // the same agent through a different transport, so it carries the same panel
35
+ // and the same Remote namespace. The two worlds keep separate stores under
36
+ // their own `agents/<key>/`, so neither sees the other's list.
37
+ 'codex-sdk': { namespace: 'codexProviders', kind: 'providers' },
38
+ // Claude serves the same providers view shape as Codex with no protocol
39
+ // dropdown: its adapter's view carries no `protocols` field (claude has no
40
+ // wire-protocol choice to make), so the body degrades to what the view does
41
+ // carry — two header auth kinds and its own native fact (2026-10-04).
42
+ claude: { namespace: 'claudeProviders', kind: 'providers' },
43
+ };
44
+ /** The panel spec for a runtime, or `undefined` when it has none yet. */
45
+ export function settingsPanelFor(agentKey) {
46
+ return SETTINGS_PANELS[agentKey];
47
+ }
48
+ /** Whether this runtime's row body expands into a real settings panel. */
49
+ export function hasSettingsPanel(agentKey) {
50
+ return settingsPanelFor(agentKey) !== undefined;
51
+ }
52
+ /**
53
+ * Accordion transition: at most one row open. Clicking the open row closes it;
54
+ * clicking another moves the panel there in one click.
55
+ */
56
+ export function toggleExpanded(current, key) {
57
+ return current === key ? null : key;
58
+ }
59
+ function str(value) {
60
+ return typeof value === 'string' && value !== '' ? value : undefined;
61
+ }
62
+ function methodOption(raw) {
63
+ if (typeof raw !== 'object' || raw === null)
64
+ return undefined;
65
+ const record = raw;
66
+ const key = str(record.key);
67
+ const label = str(record.label);
68
+ if (key === undefined || label === undefined)
69
+ return undefined;
70
+ return {
71
+ key,
72
+ label,
73
+ // Absent flags read as the conservative value: an older adapter that omits
74
+ // `available` must not render an option as usable when we cannot tell.
75
+ available: record.available === true,
76
+ placeholder: record.placeholder === true,
77
+ hint: str(record.hint) ?? '',
78
+ };
79
+ }
80
+ /**
81
+ * Defensive parse of the RPC answer. Returns `undefined` for anything that is
82
+ * not a usable view (no methods, wrong shape, adapter absent) so the panel can
83
+ * render an explicit failure instead of an empty list that looks like success.
84
+ */
85
+ export function parseAuthView(raw) {
86
+ if (typeof raw !== 'object' || raw === null)
87
+ return undefined;
88
+ const record = raw;
89
+ if (!Array.isArray(record.methods))
90
+ return undefined;
91
+ const methods = record.methods.map(methodOption).filter((m) => m !== undefined);
92
+ if (methods.length === 0)
93
+ return undefined;
94
+ const settingsPath = str(record.settingsPath);
95
+ const note = str(record.note);
96
+ return {
97
+ methods,
98
+ selected: str(record.selected) ?? null,
99
+ effective: str(record.effective) ?? null,
100
+ ...(settingsPath === undefined ? {} : { settingsPath }),
101
+ ...(note === undefined ? {} : { note }),
102
+ };
103
+ }
104
+ /**
105
+ * The option the panel marks as in force: the effective one when the adapter
106
+ * named it, else the explicit selection. `undefined` when neither is known —
107
+ * the panel then marks nothing rather than guessing.
108
+ */
109
+ export function activeMethod(view) {
110
+ const wanted = view.effective ?? view.selected;
111
+ if (wanted === null)
112
+ return undefined;
113
+ return view.methods.find((method) => method.key === wanted);
114
+ }
115
+ /** Whether an option may be picked (placeholders are rendered, never selected). */
116
+ export function selectable(option) {
117
+ return !option.placeholder;
118
+ }
119
+ function nonEmpty(value) {
120
+ return typeof value === 'string' && value !== '' ? value : undefined;
121
+ }
122
+ function modelOption(raw) {
123
+ if (typeof raw !== 'object' || raw === null)
124
+ return undefined;
125
+ const record = raw;
126
+ const id = nonEmpty(record.id);
127
+ if (id === undefined)
128
+ return undefined;
129
+ const contextWindow = record.contextWindow;
130
+ return {
131
+ id,
132
+ label: nonEmpty(record.displayName) ?? id,
133
+ ...(typeof contextWindow === 'number' && Number.isSafeInteger(contextWindow) && contextWindow > 0
134
+ ? { contextWindow }
135
+ : {}),
136
+ };
137
+ }
138
+ function providerAuthKind(raw) {
139
+ // An unstated or unknown kind reads as the shared conservative default,
140
+ // never as the other face's shape.
141
+ return raw === 'env' ? 'env' : raw === 'authToken' ? 'authToken' : 'apiKey';
142
+ }
143
+ function providerOption(raw) {
144
+ if (typeof raw !== 'object' || raw === null)
145
+ return undefined;
146
+ const record = raw;
147
+ const id = nonEmpty(record.id);
148
+ const label = nonEmpty(record.label);
149
+ const baseUrl = nonEmpty(record.baseUrl);
150
+ if (id === undefined || label === undefined || baseUrl === undefined)
151
+ return undefined;
152
+ const models = Array.isArray(record.models)
153
+ ? record.models.map(modelOption).filter((entry) => entry !== undefined)
154
+ : [];
155
+ const envName = nonEmpty(record.envName);
156
+ return {
157
+ id,
158
+ label,
159
+ baseUrl,
160
+ // An unknown protocol is never echoed into a control; the only value this
161
+ // harness can actually run is the safe default.
162
+ protocol: record.protocol === 'chat' ? 'chat' : 'responses',
163
+ models,
164
+ authKind: providerAuthKind(record.authKind),
165
+ ...(envName === undefined ? {} : { envName }),
166
+ hasKey: record.hasKey === true,
167
+ };
168
+ }
169
+ function protocolOption(raw) {
170
+ if (typeof raw !== 'object' || raw === null)
171
+ return undefined;
172
+ const record = raw;
173
+ const value = nonEmpty(record.value);
174
+ const label = nonEmpty(record.label);
175
+ if (value === undefined || label === undefined)
176
+ return undefined;
177
+ return { value, label, available: record.available === true, hint: nonEmpty(record.hint) ?? '' };
178
+ }
179
+ function providerPaths(raw) {
180
+ if (typeof raw !== 'object' || raw === null)
181
+ return undefined;
182
+ const record = raw;
183
+ const store = nonEmpty(record.store);
184
+ // The store is the only slot every face promises; the native files are the
185
+ // face's own, and an absent one stays absent rather than blank.
186
+ if (store === undefined)
187
+ return undefined;
188
+ const config = nonEmpty(record.config);
189
+ const auth = nonEmpty(record.auth);
190
+ const catalogs = nonEmpty(record.catalogs);
191
+ const settings = nonEmpty(record.settings);
192
+ return {
193
+ store,
194
+ ...(config === undefined ? {} : { config }),
195
+ ...(auth === undefined ? {} : { auth }),
196
+ ...(catalogs === undefined ? {} : { catalogs }),
197
+ ...(settings === undefined ? {} : { settings }),
198
+ };
199
+ }
200
+ /**
201
+ * Defensive parse of the adapter's providers answer.
202
+ *
203
+ * `undefined` for anything that is not a usable view — but an empty `providers`
204
+ * array is valid (a fresh install), and so is a provider whose `models` array
205
+ * is empty. `protocols` is the codex face's field: an ABSENT field (the claude
206
+ * face) reads as "no protocol choice" and never fails the parse, while a
207
+ * present-but-unusable list is a malformed codex answer and rejects as before.
208
+ */
209
+ export function parseProvidersView(raw) {
210
+ if (typeof raw !== 'object' || raw === null)
211
+ return undefined;
212
+ const record = raw;
213
+ if (!Array.isArray(record.providers))
214
+ return undefined;
215
+ // Absent `protocols` (the claude face) → nothing to offer, the panel omits
216
+ // the dropdown. Present (the codex face) → must parse to at least one
217
+ // option, exactly as before.
218
+ let protocols = [];
219
+ if (record.protocols !== undefined) {
220
+ if (!Array.isArray(record.protocols))
221
+ return undefined;
222
+ protocols = record.protocols.map(protocolOption).filter((entry) => entry !== undefined);
223
+ if (protocols.length === 0)
224
+ return undefined;
225
+ }
226
+ const paths = providerPaths(record.paths);
227
+ if (paths === undefined)
228
+ return undefined;
229
+ const providers = record.providers.map(providerOption).filter((entry) => entry !== undefined);
230
+ const syncedAt = record.syncedAt;
231
+ const note = nonEmpty(record.note);
232
+ return {
233
+ providers,
234
+ native: nativeConfig(record.native),
235
+ protocols,
236
+ paths,
237
+ ...(typeof syncedAt === 'number' && Number.isFinite(syncedAt) ? { syncedAt } : {}),
238
+ ...(note === undefined ? {} : { note }),
239
+ };
240
+ }
241
+ /**
242
+ * The user's own native config as the adapter last read it. `null` when the
243
+ * adapter said nothing; otherwise an object carrying whichever halves were
244
+ * named — codex names a provider id, claude an endpoint URL — and an empty
245
+ * one is still news (the panel says the file names nothing).
246
+ */
247
+ function nativeConfig(raw) {
248
+ if (typeof raw !== 'object' || raw === null)
249
+ return null;
250
+ const record = raw;
251
+ const providerId = nonEmpty(record.providerId);
252
+ const baseUrl = nonEmpty(record.baseUrl);
253
+ const model = nonEmpty(record.model);
254
+ return {
255
+ ...(providerId === undefined ? {} : { providerId }),
256
+ ...(baseUrl === undefined ? {} : { baseUrl }),
257
+ ...(model === undefined ? {} : { model }),
258
+ };
259
+ }
260
+ /** The codex face: a stored key, or the name of a variable holding it. */
261
+ const KEY_OR_ENV = [
262
+ { value: 'apiKey', label: 'API key', hint: 'Stored with the provider; injected at launch.' },
263
+ { value: 'env', label: 'Environment variable', hint: 'The child reads the key from a named variable.' },
264
+ ];
265
+ /** The claude face: both kinds are literal keys, differing only in the header. */
266
+ const TWO_HEADER_SHAPES = [
267
+ { value: 'apiKey', label: 'API key (x-api-key)', hint: 'Sent as the x-api-key header.' },
268
+ { value: 'authToken', label: 'Bearer token', hint: 'Sent as the Authorization header.' },
269
+ ];
270
+ /**
271
+ * The auth shapes the add/edit form may pick for a provider, in display order.
272
+ *
273
+ * The view carries no explicit vocabulary field, so the panel reads it off the
274
+ * one field that distinguishes the faces today: `protocols`. The codex face
275
+ * advertises wire protocols and offers key-or-env-var; the claude face carries
276
+ * neither and offers its two header shapes instead. The list is per-view,
277
+ * never a fixed union — each adapter rejects the other's kinds on save, so a
278
+ * union dropdown would miscode silently. A third face extends the table.
279
+ */
280
+ export function authKindOptions(view) {
281
+ return view.protocols.length > 0 ? KEY_OR_ENV : TWO_HEADER_SHAPES;
282
+ }
283
+ /**
284
+ * `provider / model` (codex) or `endpoint / model` (claude) — whichever halves
285
+ * the user's own native config names, joined for the one-line fact the panel
286
+ * states and never acts on.
287
+ */
288
+ export function nativeDescription(native) {
289
+ const parts = [native?.providerId ?? native?.baseUrl, native?.model].filter((part) => part !== undefined);
290
+ return parts.length === 0 ? 'no provider' : parts.join(' / ');
291
+ }
292
+ /**
293
+ * The non-store paths joined for the panel's second footer line, in a fixed
294
+ * slot order. `undefined` when the face named none — the line is omitted
295
+ * entirely rather than rendered with blank slots.
296
+ */
297
+ export function nativePathsLine(paths) {
298
+ const parts = [paths.config, paths.auth, paths.catalogs, paths.settings].filter((part) => part !== undefined);
299
+ return parts.length === 0 ? undefined : parts.join(' · ');
300
+ }
@@ -25,6 +25,12 @@ const FALLBACK_LABELS = {
25
25
  pi: 'Pi',
26
26
  hermes: 'Hermes',
27
27
  agy: 'Antigravity',
28
+ // The opt-in SDK halves carry their own keys so a host that reports only keys
29
+ // still renders a distinguishable label (the composite rows are `disabled: true`
30
+ // by default, so these appear only once an operator turns one on).
31
+ 'omp-sdk': 'OMP (SDK)',
32
+ 'agy-sdk': 'Antigravity (SDK)',
33
+ 'codex-sdk': 'Codex (SDK)',
28
34
  };
29
35
  /** Coerce a wire `agents` array into links; undefined when unusable/empty. */
30
36
  function coerceAgents(raw) {