aicodeman 1.24.4 → 1.24.6

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 (174) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +3 -4
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config/cli-registry/argv.d.ts +44 -0
  5. package/dist/config/cli-registry/argv.d.ts.map +1 -0
  6. package/dist/config/cli-registry/argv.js +157 -0
  7. package/dist/config/cli-registry/argv.js.map +1 -0
  8. package/dist/config/cli-registry/index.d.ts +17 -0
  9. package/dist/config/cli-registry/index.d.ts.map +1 -0
  10. package/dist/config/cli-registry/index.js +11 -0
  11. package/dist/config/cli-registry/index.js.map +1 -0
  12. package/dist/config/cli-registry/patterns.d.ts +42 -0
  13. package/dist/config/cli-registry/patterns.d.ts.map +1 -0
  14. package/dist/config/cli-registry/patterns.js +104 -0
  15. package/dist/config/cli-registry/patterns.js.map +1 -0
  16. package/dist/config/cli-registry/profiles.d.ts +74 -0
  17. package/dist/config/cli-registry/profiles.d.ts.map +1 -0
  18. package/dist/config/cli-registry/profiles.js +90 -0
  19. package/dist/config/cli-registry/profiles.js.map +1 -0
  20. package/dist/config/cli-registry/registry.d.ts +63 -0
  21. package/dist/config/cli-registry/registry.d.ts.map +1 -0
  22. package/dist/config/cli-registry/registry.js +216 -0
  23. package/dist/config/cli-registry/registry.js.map +1 -0
  24. package/dist/config/cli-registry/schema.d.ts +259 -0
  25. package/dist/config/cli-registry/schema.d.ts.map +1 -0
  26. package/dist/config/cli-registry/schema.js +398 -0
  27. package/dist/config/cli-registry/schema.js.map +1 -0
  28. package/dist/config/cli-registry/stock.d.ts +15 -0
  29. package/dist/config/cli-registry/stock.d.ts.map +1 -0
  30. package/dist/config/cli-registry/stock.js +996 -0
  31. package/dist/config/cli-registry/stock.js.map +1 -0
  32. package/dist/config/cli-registry/types.d.ts +565 -0
  33. package/dist/config/cli-registry/types.d.ts.map +1 -0
  34. package/dist/config/cli-registry/types.js +13 -0
  35. package/dist/config/cli-registry/types.js.map +1 -0
  36. package/dist/config/dependency-registry.d.ts +12 -1
  37. package/dist/config/dependency-registry.d.ts.map +1 -1
  38. package/dist/config/dependency-registry.js +155 -203
  39. package/dist/config/dependency-registry.js.map +1 -1
  40. package/dist/cron/cron-service.d.ts.map +1 -1
  41. package/dist/cron/cron-service.js +51 -17
  42. package/dist/cron/cron-service.js.map +1 -1
  43. package/dist/docker-hosts.d.ts +9 -1
  44. package/dist/docker-hosts.d.ts.map +1 -1
  45. package/dist/docker-hosts.js +25 -17
  46. package/dist/docker-hosts.js.map +1 -1
  47. package/dist/remote-hosts.d.ts +73 -0
  48. package/dist/remote-hosts.d.ts.map +1 -1
  49. package/dist/remote-hosts.js +139 -45
  50. package/dist/remote-hosts.js.map +1 -1
  51. package/dist/remote-reconnect.d.ts +10 -1
  52. package/dist/remote-reconnect.d.ts.map +1 -1
  53. package/dist/remote-reconnect.js +9 -0
  54. package/dist/remote-reconnect.js.map +1 -1
  55. package/dist/session-cli-registry-bridge.d.ts +66 -0
  56. package/dist/session-cli-registry-bridge.d.ts.map +1 -0
  57. package/dist/session-cli-registry-bridge.js +165 -0
  58. package/dist/session-cli-registry-bridge.js.map +1 -0
  59. package/dist/session.d.ts +13 -1
  60. package/dist/session.d.ts.map +1 -1
  61. package/dist/session.js +69 -76
  62. package/dist/session.js.map +1 -1
  63. package/dist/tmux-manager.d.ts +50 -35
  64. package/dist/tmux-manager.d.ts.map +1 -1
  65. package/dist/tmux-manager.js +258 -623
  66. package/dist/tmux-manager.js.map +1 -1
  67. package/dist/utils/antigravity-cli-resolver.d.ts.map +1 -1
  68. package/dist/utils/antigravity-cli-resolver.js +8 -10
  69. package/dist/utils/antigravity-cli-resolver.js.map +1 -1
  70. package/dist/utils/cli-executable-resolver.d.ts +9 -1
  71. package/dist/utils/cli-executable-resolver.d.ts.map +1 -1
  72. package/dist/utils/cli-executable-resolver.js +3 -2
  73. package/dist/utils/cli-executable-resolver.js.map +1 -1
  74. package/dist/utils/cli-launcher.d.ts +46 -0
  75. package/dist/utils/cli-launcher.d.ts.map +1 -0
  76. package/dist/utils/cli-launcher.js +101 -0
  77. package/dist/utils/cli-launcher.js.map +1 -0
  78. package/dist/utils/cli-resolver.d.ts +101 -0
  79. package/dist/utils/cli-resolver.d.ts.map +1 -0
  80. package/dist/utils/cli-resolver.js +220 -0
  81. package/dist/utils/cli-resolver.js.map +1 -0
  82. package/dist/utils/codex-cli-resolver.d.ts.map +1 -1
  83. package/dist/utils/codex-cli-resolver.js +8 -11
  84. package/dist/utils/codex-cli-resolver.js.map +1 -1
  85. package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
  86. package/dist/utils/deepseek-cli-resolver.js +8 -6
  87. package/dist/utils/deepseek-cli-resolver.js.map +1 -1
  88. package/dist/utils/gemini-cli-resolver.d.ts.map +1 -1
  89. package/dist/utils/gemini-cli-resolver.js +8 -10
  90. package/dist/utils/gemini-cli-resolver.js.map +1 -1
  91. package/dist/utils/grok-cli-resolver.js +8 -8
  92. package/dist/utils/grok-cli-resolver.js.map +1 -1
  93. package/dist/utils/omp-cli-resolver.d.ts.map +1 -1
  94. package/dist/utils/omp-cli-resolver.js +10 -15
  95. package/dist/utils/omp-cli-resolver.js.map +1 -1
  96. package/dist/utils/opencode-cli-resolver.d.ts.map +1 -1
  97. package/dist/utils/opencode-cli-resolver.js +8 -11
  98. package/dist/utils/opencode-cli-resolver.js.map +1 -1
  99. package/dist/utils/pi-cli-resolver.d.ts.map +1 -1
  100. package/dist/utils/pi-cli-resolver.js +8 -9
  101. package/dist/utils/pi-cli-resolver.js.map +1 -1
  102. package/dist/web/public/admin-ui.js.gz +0 -0
  103. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  104. package/dist/web/public/app.a5d2fc08.js.gz +0 -0
  105. package/dist/web/public/approvals-ui.js.gz +0 -0
  106. package/dist/web/public/constants.7bd482dc.js.gz +0 -0
  107. package/dist/web/public/cron-ui.js.gz +0 -0
  108. package/dist/web/public/entrance-animations.js.gz +0 -0
  109. package/dist/web/public/home-sessions.js.gz +0 -0
  110. package/dist/web/public/i18n.9ae15b51.js.gz +0 -0
  111. package/dist/web/public/image-input.ee16ad88.js.gz +0 -0
  112. package/dist/web/public/index.html +9 -2
  113. package/dist/web/public/index.html.br +0 -0
  114. package/dist/web/public/index.html.gz +0 -0
  115. package/dist/web/public/input-cjk.87f14251.js.gz +0 -0
  116. package/dist/web/public/keyboard-accessory.0454a3e9.js.gz +0 -0
  117. package/dist/web/public/mobile-handlers.0f83a608.js.gz +0 -0
  118. package/dist/web/public/mobile-overview.js.gz +0 -0
  119. package/dist/web/public/mobile.87674154.css.gz +0 -0
  120. package/dist/web/public/notification-manager.5d229063.js.gz +0 -0
  121. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  122. package/dist/web/public/panels-ui.e6e06034.js.gz +0 -0
  123. package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
  124. package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
  125. package/dist/web/public/readmymind-ui.js.gz +0 -0
  126. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  127. package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
  128. package/dist/web/public/session-lineage.js.gz +0 -0
  129. package/dist/web/public/session-ui.c558f981.js.gz +0 -0
  130. package/dist/web/public/{settings-ui.2a814a17.js → settings-ui.95b333c6.js} +1 -1
  131. package/dist/web/public/settings-ui.95b333c6.js.br +0 -0
  132. package/dist/web/public/settings-ui.95b333c6.js.gz +0 -0
  133. package/dist/web/public/styles.421b36d4.css.gz +0 -0
  134. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  135. package/dist/web/public/sw.js.gz +0 -0
  136. package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
  137. package/dist/web/public/terminal-ui.70445f81.js.gz +0 -0
  138. package/dist/web/public/ultracode-panel.js.gz +0 -0
  139. package/dist/web/public/ultracode-windows.js.gz +0 -0
  140. package/dist/web/public/upload.html.gz +0 -0
  141. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  142. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  143. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  144. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  145. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  146. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  147. package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
  148. package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
  149. package/dist/web/public/vendor/xterm.css.gz +0 -0
  150. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  151. package/dist/web/public/voice-input.4ec670b0.js.gz +0 -0
  152. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  153. package/dist/web/public/webview-tabs.js.gz +0 -0
  154. package/dist/web/routes/cron-routes.d.ts.map +1 -1
  155. package/dist/web/routes/cron-routes.js +3 -2
  156. package/dist/web/routes/cron-routes.js.map +1 -1
  157. package/dist/web/routes/session-routes.d.ts +8 -34
  158. package/dist/web/routes/session-routes.d.ts.map +1 -1
  159. package/dist/web/routes/session-routes.js +164 -251
  160. package/dist/web/routes/session-routes.js.map +1 -1
  161. package/dist/web/routes/system-routes.d.ts.map +1 -1
  162. package/dist/web/routes/system-routes.js +2 -1
  163. package/dist/web/routes/system-routes.js.map +1 -1
  164. package/dist/web/schemas.d.ts +12 -48
  165. package/dist/web/schemas.d.ts.map +1 -1
  166. package/dist/web/schemas.js +82 -37
  167. package/dist/web/schemas.js.map +1 -1
  168. package/dist/web/session-wait-registry.d.ts +1 -1
  169. package/dist/web/session-wait-registry.d.ts.map +1 -1
  170. package/dist/web/session-wait-registry.js +20 -12
  171. package/dist/web/session-wait-registry.js.map +1 -1
  172. package/package.json +1 -1
  173. package/dist/web/public/settings-ui.2a814a17.js.br +0 -0
  174. package/dist/web/public/settings-ui.2a814a17.js.gz +0 -0
@@ -0,0 +1,996 @@
1
+ /**
2
+ * @fileoverview The shipped stock catalog — one `CliEntry` per CLI Codeman supports out of
3
+ * the box, transcribed to be byte-identical (via the argv engine) to the hand-written
4
+ * builders in tmux-manager.ts that they replace.
5
+ *
6
+ * This is the ONE file allowed to know a CLI's id by name (`test/cli-registry-no-id-branching
7
+ * .test.ts` enforces that nowhere else does). Everything downstream — session.ts,
8
+ * tmux-manager.ts, the routes, the frontend — reads capability flags, never `entry.id ===`.
9
+ *
10
+ * @module config/cli-registry/stock
11
+ */
12
+ const HOME_DIRS = {
13
+ local: '~/.local/bin',
14
+ usrLocal: '/usr/local/bin',
15
+ bunBin: '~/.bun/bin',
16
+ npmGlobal: '~/.npm-global/bin',
17
+ homeBin: '~/bin',
18
+ };
19
+ const NO_GATES = {};
20
+ const NO_PRIVILEGED_PARAMS = [];
21
+ /**
22
+ * The common case: every CLI whose privileged switch is a command-line FLAG, reachable
23
+ * only through its own config object and therefore already covered by `privilegedParams`.
24
+ * DeepSeek is the sole exception — its switch is an env var. See CliCapabilities.
25
+ */
26
+ const NO_PRIVILEGED_ENV_KEYS = [];
27
+ /** Shared skeleton for the "agent CLI, no unusual behaviour" case (pi's own shape). */
28
+ function agentDefaults() {
29
+ return {
30
+ external: true,
31
+ requiresMux: true,
32
+ hooks: 'none',
33
+ transcript: 'none',
34
+ altScreen: 'strip-mux-only',
35
+ wheelForward: { mode: 'never' },
36
+ keyboardAccessory: 'agent',
37
+ privilegedCommandGate: false,
38
+ startMode: 'interactive',
39
+ stripInkBloat: true,
40
+ ralph: false,
41
+ respawn: false,
42
+ effort: false,
43
+ agentSkillInjection: false,
44
+ statusLineTelemetry: false,
45
+ model: { source: 'flag', param: 'model' },
46
+ privilegedParams: NO_PRIVILEGED_PARAMS,
47
+ privilegedEnvKeys: NO_PRIVILEGED_ENV_KEYS,
48
+ gates: NO_GATES,
49
+ };
50
+ }
51
+ const CLAUDE = {
52
+ id: 'claude',
53
+ label: 'Claude',
54
+ shortBadge: 'CC',
55
+ accent: '#d97757',
56
+ enabled: true,
57
+ stock: true,
58
+ order: 0,
59
+ kind: 'agent',
60
+ discovery: {
61
+ binaries: ['claude'],
62
+ searchDirs: [HOME_DIRS.local, '~/.claude/local', HOME_DIRS.usrLocal, HOME_DIRS.npmGlobal, HOME_DIRS.homeBin],
63
+ version: { arg: '--version', regex: '(\\d+\\.\\d+\\.\\d+)', retryOnTransientFailure: true },
64
+ install: {
65
+ command: {
66
+ linux: 'curl -fsSL https://claude.ai/install.sh | bash',
67
+ darwin: 'curl -fsSL https://claude.ai/install.sh | bash',
68
+ wsl: 'curl -fsSL https://claude.ai/install.sh | bash',
69
+ },
70
+ npmPackage: '@anthropic-ai/claude-code',
71
+ docsUrl: 'https://docs.claude.com/claude-code',
72
+ },
73
+ },
74
+ launch: {
75
+ chain: 'fallback',
76
+ params: {
77
+ claudeMode: {
78
+ type: 'enum',
79
+ values: ['dangerously-skip-permissions', 'auto', 'normal', 'allowedTools'],
80
+ default: 'dangerously-skip-permissions',
81
+ },
82
+ allowedTools: { type: 'token', pattern: 'tool-list' },
83
+ model: { type: 'token', pattern: 'model-claude' },
84
+ resumeId: { type: 'token', pattern: 'uuid' },
85
+ // buildEffortCliArgs carries `ultracode` as a settings JSON blob and every other
86
+ // level as a plain `--effort <level>` flag — two engine values because the two
87
+ // shapes are mutually exclusive and neither is user-typed text (both are produced
88
+ // from the EFFORT_LEVELS allowlist upstream, same as every other engine value).
89
+ effortLevel: { type: 'engine', source: 'effortLevel' },
90
+ effortJson: { type: 'engine', source: 'effortSettingsJson' },
91
+ sessionId: { type: 'engine', source: 'sessionId' },
92
+ sessionName: { type: 'engine', source: 'sessionName' },
93
+ },
94
+ variants: [
95
+ {
96
+ id: 'resume',
97
+ when: { param: 'resumeId', state: 'set' },
98
+ args: [
99
+ { lit: 'claude' },
100
+ { flag: '--dangerously-skip-permissions', when: { param: 'claudeMode', is: 'dangerously-skip-permissions' } },
101
+ { flag: '--permission-mode', value: 'auto', when: { param: 'claudeMode', is: 'auto' } },
102
+ {
103
+ flag: '--allowedTools',
104
+ valueFrom: 'allowedTools',
105
+ quote: 'double',
106
+ when: {
107
+ allOf: [
108
+ { param: 'claudeMode', is: 'allowedTools' },
109
+ { param: 'allowedTools', state: 'set' },
110
+ ],
111
+ },
112
+ },
113
+ { flag: '--resume', valueFrom: 'resumeId', quote: 'double' },
114
+ { flag: '--model', valueFrom: 'model', quote: 'double', when: { param: 'model', state: 'set' } },
115
+ { flag: '--effort', valueFrom: 'effortLevel', quote: 'single', when: { param: 'effortLevel', state: 'set' } },
116
+ { flag: '--settings', valueFrom: 'effortJson', quote: 'single', when: { param: 'effortJson', state: 'set' } },
117
+ { flag: '--name', valueFrom: 'sessionName', quote: 'double', when: { capabilityGate: 'nameFlag' } },
118
+ ],
119
+ },
120
+ {
121
+ id: 'new',
122
+ args: [
123
+ { lit: 'claude' },
124
+ { flag: '--dangerously-skip-permissions', when: { param: 'claudeMode', is: 'dangerously-skip-permissions' } },
125
+ { flag: '--permission-mode', value: 'auto', when: { param: 'claudeMode', is: 'auto' } },
126
+ {
127
+ flag: '--allowedTools',
128
+ valueFrom: 'allowedTools',
129
+ quote: 'double',
130
+ when: {
131
+ allOf: [
132
+ { param: 'claudeMode', is: 'allowedTools' },
133
+ { param: 'allowedTools', state: 'set' },
134
+ ],
135
+ },
136
+ },
137
+ { flag: '--session-id', valueFrom: 'sessionId', quote: 'double' },
138
+ { flag: '--model', valueFrom: 'model', quote: 'double', when: { param: 'model', state: 'set' } },
139
+ { flag: '--effort', valueFrom: 'effortLevel', quote: 'single', when: { param: 'effortLevel', state: 'set' } },
140
+ { flag: '--settings', valueFrom: 'effortJson', quote: 'single', when: { param: 'effortJson', state: 'set' } },
141
+ { flag: '--name', valueFrom: 'sessionName', quote: 'double', when: { capabilityGate: 'nameFlag' } },
142
+ ],
143
+ },
144
+ ],
145
+ // Claude has no `<Mode>Config` object of its own — the bridge synthesizes one from its
146
+ // discrete top-level spawn fields, under their EXISTING field name `resumeSessionId`.
147
+ legacyConfigAliases: { resumeId: 'resumeSessionId' },
148
+ },
149
+ env: {
150
+ exports: [],
151
+ unset: ['CLAUDECODE', 'COLORTERM'],
152
+ tmuxSetenvKeys: [],
153
+ dockerExecEnvNames: [],
154
+ allowedPrefixes: ['CLAUDE_CODE_'],
155
+ allowedKeys: ['CLAUDE_CONFIG_DIR'],
156
+ },
157
+ capabilities: {
158
+ external: false,
159
+ requiresMux: false,
160
+ // Claude installs Codeman's own hooks block into every workspace it runs in, so its
161
+ // stop/idle signals are unconditional — no per-session veto, unlike deepseek's bridge.
162
+ hooks: 'always',
163
+ transcript: 'claude-jsonl',
164
+ altScreen: 'strip-full',
165
+ echo: { policy: 'buffer', anchor: { kind: 'glyph', glyph: '❯', offset: 2 } },
166
+ wheelForward: { mode: 'version-gated', minVersion: '2.1.187' },
167
+ keyboardAccessory: 'agent',
168
+ privilegedCommandGate: false,
169
+ startMode: 'interactive',
170
+ stripInkBloat: true,
171
+ ralph: true,
172
+ respawn: true,
173
+ effort: true,
174
+ agentSkillInjection: true,
175
+ statusLineTelemetry: true,
176
+ model: { source: 'claude-settings-file' },
177
+ privilegedParams: [],
178
+ privilegedEnvKeys: [],
179
+ gates: { nameFlag: { minVersion: '2.1.224', failClosed: true } },
180
+ },
181
+ overlays: {
182
+ // Mirrors the local default so the remote/in-container agent runs non-interactively
183
+ // (no trust-folder/permission prompt that nothing on that side can answer). A per-host
184
+ // `commands.claude` override, or the docker multi-user clamp, stays the escape hatch.
185
+ remote: { command: 'claude --dangerously-skip-permissions' },
186
+ docker: { command: 'claude --dangerously-skip-permissions' },
187
+ // Claude's docker/remote credential handling has its own dedicated code path
188
+ // (claudeDockerPaneCommand, artifacts at docker-hosts.ts:537-575) — no generic credStore.
189
+ },
190
+ };
191
+ const SHELL = {
192
+ id: 'shell',
193
+ label: 'Shell',
194
+ shortBadge: 'SH',
195
+ accent: '#6b7280',
196
+ enabled: true,
197
+ stock: true,
198
+ order: 1,
199
+ kind: 'shell',
200
+ discovery: {
201
+ binaries: [],
202
+ searchDirs: [],
203
+ install: { command: {} },
204
+ },
205
+ launch: {
206
+ params: {},
207
+ variants: [{ id: 'shell', args: [] }], // tmux-manager resolves the real login shell in code
208
+ },
209
+ env: {
210
+ exports: [],
211
+ unset: ['COLORTERM'],
212
+ tmuxSetenvKeys: [],
213
+ dockerExecEnvNames: [],
214
+ allowedPrefixes: [],
215
+ allowedKeys: [],
216
+ },
217
+ capabilities: {
218
+ external: false,
219
+ requiresMux: false,
220
+ // ⚠️ `false` here while `external` is ALSO false is the pairing that matters: a shell
221
+ // has no hooks but is not an "external CLI", so a predicate derived from `external`
222
+ // once accepted `until=stop` on a shell session and hung for the full timeout.
223
+ hooks: 'none',
224
+ transcript: 'none',
225
+ altScreen: 'preserve',
226
+ echo: { policy: 'off', anchor: { kind: 'none' } },
227
+ wheelForward: { mode: 'never' },
228
+ keyboardAccessory: 'shell',
229
+ privilegedCommandGate: true,
230
+ startMode: 'shell',
231
+ stripInkBloat: false,
232
+ ralph: false,
233
+ respawn: false,
234
+ effort: false,
235
+ agentSkillInjection: false,
236
+ statusLineTelemetry: false,
237
+ model: { source: 'none' },
238
+ privilegedParams: [],
239
+ privilegedEnvKeys: [],
240
+ gates: {},
241
+ },
242
+ overlays: {
243
+ // No `remote` entry: defaultRemoteCommandForMode special-cases kind==='shell' directly
244
+ // (an interactive login shell, no `-c '<command>'` wrapping at all).
245
+ docker: { disabled: true },
246
+ },
247
+ };
248
+ const OPENCODE = {
249
+ id: 'opencode',
250
+ label: 'OpenCode',
251
+ shortBadge: 'OC',
252
+ accent: '#f59e0b',
253
+ enabled: true,
254
+ stock: true,
255
+ order: 10,
256
+ kind: 'agent',
257
+ discovery: {
258
+ binaries: ['opencode'],
259
+ searchDirs: [
260
+ '~/.opencode/bin',
261
+ HOME_DIRS.local,
262
+ HOME_DIRS.usrLocal,
263
+ '~/go/bin',
264
+ HOME_DIRS.bunBin,
265
+ HOME_DIRS.npmGlobal,
266
+ HOME_DIRS.homeBin,
267
+ ],
268
+ version: { arg: '--version', regex: '(\\d+\\.\\d+\\.\\d+)' },
269
+ install: {
270
+ command: {
271
+ linux: 'curl -fsSL https://opencode.ai/install | bash',
272
+ darwin: 'curl -fsSL https://opencode.ai/install | bash',
273
+ },
274
+ npmPackage: 'opencode-ai',
275
+ docsUrl: 'https://opencode.ai/docs',
276
+ },
277
+ },
278
+ launch: {
279
+ params: {
280
+ model: { type: 'token', pattern: 'model' },
281
+ resumeId: { type: 'token', pattern: 'id' },
282
+ forkSession: { type: 'bool' },
283
+ },
284
+ variants: [
285
+ {
286
+ id: 'default',
287
+ args: [
288
+ { lit: 'opencode' },
289
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
290
+ { flag: '--session', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
291
+ {
292
+ flag: '--fork',
293
+ when: {
294
+ allOf: [
295
+ { param: 'resumeId', state: 'set' },
296
+ { param: 'forkSession', is: true },
297
+ ],
298
+ },
299
+ },
300
+ ],
301
+ },
302
+ ],
303
+ legacyConfigAliases: { resumeId: 'continueSession' },
304
+ legacyConfigField: 'openCodeConfig',
305
+ },
306
+ env: {
307
+ exports: [],
308
+ unset: ['COLORTERM'],
309
+ tmuxSetenvKeys: ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_API_KEY'],
310
+ dockerExecEnvNames: [],
311
+ allowedPrefixes: ['OPENCODE_'],
312
+ allowedKeys: [],
313
+ configContentVar: 'OPENCODE_CONFIG_CONTENT',
314
+ },
315
+ capabilities: {
316
+ ...agentDefaults(),
317
+ altScreen: 'strip-mux-only',
318
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' }, predictProfile: undefined },
319
+ },
320
+ overlays: {
321
+ credStore: { rel: '.config/opencode', seedWhole: true },
322
+ },
323
+ };
324
+ const CODEX = {
325
+ id: 'codex',
326
+ label: 'Codex',
327
+ shortBadge: 'CX',
328
+ accent: '#6b7fd7',
329
+ enabled: true,
330
+ stock: true,
331
+ order: 20,
332
+ kind: 'agent',
333
+ discovery: {
334
+ binaries: ['codex'],
335
+ searchDirs: [
336
+ '~/.codex/bin',
337
+ HOME_DIRS.local,
338
+ HOME_DIRS.usrLocal,
339
+ HOME_DIRS.bunBin,
340
+ HOME_DIRS.npmGlobal,
341
+ HOME_DIRS.homeBin,
342
+ ],
343
+ version: { arg: '--version', regex: '(\\d+\\.\\d+\\.\\d+)' },
344
+ install: {
345
+ command: { linux: 'npm install -g @openai/codex', darwin: 'npm install -g @openai/codex' },
346
+ npmPackage: '@openai/codex',
347
+ docsUrl: 'https://developers.openai.com/codex/cli',
348
+ },
349
+ },
350
+ launch: {
351
+ params: {
352
+ bypassApprovals: { type: 'bool' },
353
+ animations: { type: 'bool' },
354
+ model: { type: 'token', pattern: 'model' },
355
+ resumeId: { type: 'token', pattern: 'id' },
356
+ },
357
+ variants: [
358
+ {
359
+ id: 'default',
360
+ args: [
361
+ { lit: 'codex' },
362
+ { flag: '--dangerously-bypass-approvals-and-sandbox', when: { param: 'bypassApprovals', is: true } },
363
+ { flag: '--config', value: 'tui.animations=true', when: { param: 'animations', is: true } },
364
+ { flag: '--config', value: 'tui.animations=false', when: { param: 'animations', is: false } },
365
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
366
+ { lit: 'resume', when: { param: 'resumeId', state: 'set' } },
367
+ { valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
368
+ ],
369
+ },
370
+ ],
371
+ legacyConfigAliases: { bypassApprovals: 'dangerouslyBypassApprovals', resumeId: 'resumeSessionId' },
372
+ legacyConfigField: 'codexConfig',
373
+ resumeAppend: { style: 'positional', token: 'resume' },
374
+ },
375
+ env: {
376
+ exports: [
377
+ { name: 'COLORTERM', value: 'truecolor' },
378
+ { name: 'CODEX_INTERNAL_ORIGINATOR_OVERRIDE', value: { engine: 'codemanPrefixedSessionId' } },
379
+ ],
380
+ unset: ['NO_COLOR'],
381
+ tmuxSetenvKeys: ['OPENAI_API_KEY', 'CODEX_API_KEY', 'CODEX_HOME'],
382
+ dockerExecEnvNames: ['OPENAI_API_KEY', 'CODEX_API_KEY'],
383
+ allowedPrefixes: ['CODEX_'],
384
+ allowedKeys: [],
385
+ },
386
+ capabilities: {
387
+ ...agentDefaults(),
388
+ transcript: 'codex-rollout',
389
+ altScreen: 'strip-full',
390
+ echo: { policy: 'predict', anchor: { kind: 'cursor' }, predictProfile: 'codex' },
391
+ wheelForward: { mode: 'never' }, // #227: codex ignores SGR wheel reports, never forward
392
+ maxFrameBytes: 32 * 1024,
393
+ // codex's own bare-spawn default (no config sent) is already safe (no bypass flag), so
394
+ // the multi-user clamp only needs to force an EXPLICITLY-SENT bypass back off.
395
+ //
396
+ // `param` names the REGISTRY param, like every other `param` in this file — the clamp
397
+ // resolves it through `legacyConfigAliases` on the way out, exactly as `configSetenv`
398
+ // does. codex is the entry where the two names differ (`bypassApprovals` here,
399
+ // `dangerouslyBypassApprovals` on the wire), so it is the one that would have caught a
400
+ // regression; `schema.ts` now rejects a name that is not a declared param.
401
+ privilegedParams: [{ param: 'bypassApprovals', clampTo: false }],
402
+ },
403
+ overlays: {
404
+ credStore: {
405
+ rel: '.codex',
406
+ shareDirs: ['sessions'],
407
+ shareFiles: ['history.jsonl'],
408
+ seedFiles: ['auth.json', 'config.toml'],
409
+ },
410
+ },
411
+ };
412
+ const GEMINI = {
413
+ id: 'gemini',
414
+ label: 'Gemini',
415
+ shortBadge: 'GM',
416
+ accent: '#4285f4',
417
+ enabled: true,
418
+ stock: true,
419
+ order: 30,
420
+ kind: 'agent',
421
+ discovery: {
422
+ binaries: ['gemini'],
423
+ searchDirs: [
424
+ '~/.gemini/bin',
425
+ HOME_DIRS.local,
426
+ HOME_DIRS.usrLocal,
427
+ HOME_DIRS.bunBin,
428
+ HOME_DIRS.npmGlobal,
429
+ HOME_DIRS.homeBin,
430
+ ],
431
+ version: { arg: '--version', regex: '(\\d+\\.\\d+\\.\\d+)' },
432
+ install: {
433
+ command: { linux: 'npm install -g @google/gemini-cli', darwin: 'npm install -g @google/gemini-cli' },
434
+ npmPackage: '@google/gemini-cli',
435
+ docsUrl: 'https://github.com/google-gemini/gemini-cli',
436
+ },
437
+ },
438
+ launch: {
439
+ params: {
440
+ approvalMode: { type: 'enum', values: ['default', 'auto_edit', 'yolo', 'plan'], default: 'yolo' },
441
+ model: { type: 'token', pattern: 'model' },
442
+ resumeId: { type: 'token', pattern: 'id-dotted' },
443
+ },
444
+ variants: [
445
+ {
446
+ id: 'default',
447
+ args: [
448
+ { lit: 'gemini' },
449
+ { flag: '--skip-trust' },
450
+ { flag: '--approval-mode', valueFrom: 'approvalMode' },
451
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
452
+ { flag: '--resume', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
453
+ ],
454
+ },
455
+ ],
456
+ legacyConfigAliases: { resumeId: 'resumeSession' },
457
+ legacyConfigField: 'geminiConfig',
458
+ resumeAppend: { style: 'flag', flag: '--resume' },
459
+ },
460
+ env: {
461
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
462
+ unset: ['NO_COLOR'],
463
+ tmuxSetenvKeys: [
464
+ 'GEMINI_API_KEY',
465
+ 'GEMINI_MODEL',
466
+ 'GOOGLE_API_KEY',
467
+ 'GOOGLE_CLOUD_PROJECT',
468
+ 'GOOGLE_CLOUD_LOCATION',
469
+ 'GOOGLE_APPLICATION_CREDENTIALS',
470
+ 'GOOGLE_GENAI_USE_VERTEXAI',
471
+ ],
472
+ dockerExecEnvNames: ['GEMINI_API_KEY', 'GOOGLE_API_KEY'],
473
+ allowedPrefixes: ['GEMINI_', 'GOOGLE_'],
474
+ allowedKeys: [],
475
+ },
476
+ capabilities: {
477
+ ...agentDefaults(),
478
+ altScreen: 'strip-full',
479
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
480
+ // gemini's builder defaults an ABSENT approvalMode to 'yolo', so the clamp must
481
+ // MATERIALIZE a config (not just touch an already-sent one) or a non-granted owner who
482
+ // sends no geminiConfig at all would still get yolo for free.
483
+ privilegedParams: [{ param: 'approvalMode', clampTo: 'auto_edit', materializeWhenAbsent: true }],
484
+ },
485
+ overlays: {
486
+ credStore: { rel: '.gemini', seedWhole: true }, // also covers antigravity — see its own entry
487
+ },
488
+ };
489
+ const ANTIGRAVITY = {
490
+ id: 'antigravity',
491
+ label: 'Antigravity',
492
+ shortBadge: 'AG',
493
+ accent: '#8b5cf6',
494
+ enabled: true,
495
+ stock: true,
496
+ order: 40,
497
+ kind: 'agent',
498
+ discovery: {
499
+ // Binary is `agy`, NOT `antigravity` — the mode-name/binary-name split that made
500
+ // probeDockerCliVersion wrong before this registry existed.
501
+ binaries: ['agy'],
502
+ searchDirs: [HOME_DIRS.local, '~/.antigravity/bin', HOME_DIRS.usrLocal, HOME_DIRS.homeBin],
503
+ version: { arg: '--version', regex: '(\\d+\\.\\d+\\.\\d+)' },
504
+ install: {
505
+ command: {
506
+ linux: 'curl -fsSL https://antigravity.google/cli/install.sh | bash',
507
+ darwin: 'curl -fsSL https://antigravity.google/cli/install.sh | bash',
508
+ },
509
+ docsUrl: 'https://antigravity.google/cli',
510
+ },
511
+ },
512
+ launch: {
513
+ params: {
514
+ dangerouslySkipPermissions: { type: 'bool' },
515
+ model: { type: 'token', pattern: 'model' },
516
+ resumeId: { type: 'token', pattern: 'id-dotted' },
517
+ },
518
+ variants: [
519
+ {
520
+ id: 'default',
521
+ args: [
522
+ { lit: 'agy' },
523
+ { flag: '--dangerously-skip-permissions', when: { param: 'dangerouslySkipPermissions', is: true } },
524
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
525
+ { flag: '--conversation', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
526
+ ],
527
+ },
528
+ ],
529
+ legacyConfigAliases: { resumeId: 'resumeConversationId' },
530
+ legacyConfigField: 'antigravityConfig',
531
+ resumeAppend: { style: 'flag', flag: '--conversation' },
532
+ },
533
+ env: {
534
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
535
+ unset: ['NO_COLOR'],
536
+ tmuxSetenvKeys: [],
537
+ dockerExecEnvNames: [],
538
+ allowedPrefixes: ['ANTIGRAVITY_'],
539
+ allowedKeys: [],
540
+ },
541
+ capabilities: {
542
+ ...agentDefaults(),
543
+ altScreen: 'strip-mux-only',
544
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
545
+ // Like codex: an ABSENT config already defaults safe (no bypass flag), so only a
546
+ // SENT config needs the flag forced off — nothing is materialized.
547
+ privilegedParams: [{ param: 'dangerouslySkipPermissions', clampTo: false }],
548
+ },
549
+ overlays: {
550
+ // No credStore of its own: agy nests its whole state under ~/.gemini/antigravity-cli/,
551
+ // which gemini's seedWhole entry already covers.
552
+ },
553
+ };
554
+ const PI = {
555
+ id: 'pi',
556
+ label: 'Pi',
557
+ shortBadge: 'PI',
558
+ accent: '#10b981',
559
+ enabled: true,
560
+ stock: true,
561
+ order: 50,
562
+ kind: 'agent',
563
+ discovery: {
564
+ binaries: ['pi'],
565
+ searchDirs: [HOME_DIRS.local, HOME_DIRS.usrLocal, HOME_DIRS.bunBin, HOME_DIRS.npmGlobal, HOME_DIRS.homeBin],
566
+ // pi is a generic binary name (Raspberry Pi tooling, personal scripts), so a `which`
567
+ // hit alone is not evidence of the right program — require the version match.
568
+ version: { arg: '--version', regex: '(?:^|\\s)(\\d+\\.\\d+\\.\\d+)', requireVersionMatch: true },
569
+ install: {
570
+ command: {
571
+ linux: 'npm install -g --ignore-scripts @earendil-works/pi-coding-agent',
572
+ darwin: 'npm install -g --ignore-scripts @earendil-works/pi-coding-agent',
573
+ },
574
+ npmPackage: '@earendil-works/pi-coding-agent',
575
+ docsUrl: 'https://pi.dev',
576
+ },
577
+ },
578
+ launch: {
579
+ params: {
580
+ approveProjectTrust: { type: 'bool' },
581
+ model: { type: 'token', pattern: 'model-pi' },
582
+ provider: { type: 'token', pattern: 'slug' },
583
+ thinking: { type: 'enum', values: ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] },
584
+ resumeId: { type: 'token', pattern: 'id-dotted' },
585
+ continueSession: { type: 'bool' },
586
+ },
587
+ variants: [
588
+ {
589
+ id: 'default',
590
+ args: [
591
+ { lit: 'pi' },
592
+ { flag: '--approve', when: { param: 'approveProjectTrust', is: true } },
593
+ { flag: '--no-approve', when: { param: 'approveProjectTrust', is: false } },
594
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
595
+ { flag: '--provider', valueFrom: 'provider', when: { param: 'provider', state: 'set' } },
596
+ { flag: '--thinking', valueFrom: 'thinking', when: { param: 'thinking', state: 'set' } },
597
+ { flag: '--session', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
598
+ {
599
+ lit: '-c',
600
+ when: {
601
+ allOf: [
602
+ { param: 'continueSession', is: true },
603
+ { param: 'resumeId', state: 'unset' },
604
+ ],
605
+ },
606
+ },
607
+ ],
608
+ },
609
+ ],
610
+ legacyConfigAliases: { resumeId: 'resumeSessionId' },
611
+ legacyConfigField: 'piConfig',
612
+ resumeAppend: { style: 'flag', flag: '--session' },
613
+ },
614
+ env: {
615
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
616
+ unset: ['NO_COLOR'],
617
+ // Pi's ~34 provider keys share no common prefix, so they are deliberately NOT
618
+ // allowlisted here — same reasoning as today's PI_ only prefix. Pi users authenticate
619
+ // via `/login` or the server process's own env.
620
+ tmuxSetenvKeys: [],
621
+ dockerExecEnvNames: [],
622
+ allowedPrefixes: ['PI_'],
623
+ allowedKeys: [],
624
+ },
625
+ capabilities: {
626
+ ...agentDefaults(),
627
+ altScreen: 'preserve', // pi's TUI renders into the main screen with terminal-owned scrollback
628
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
629
+ // pi's absent-config default is an interactive trust PROMPT the session user could
630
+ // just answer "yes" to, so omitting --approve is not itself a clamp — MATERIALIZE
631
+ // approveProjectTrust:false so buildPiCommand emits --no-approve outright.
632
+ privilegedParams: [{ param: 'approveProjectTrust', clampTo: false, materializeWhenAbsent: true }],
633
+ },
634
+ overlays: {
635
+ credStore: {
636
+ rel: '.pi/agent',
637
+ seedFiles: ['auth.json', 'settings.json', 'trust.json', 'models.json', 'models-store.json'],
638
+ },
639
+ },
640
+ };
641
+ // Grok Build (xAI, `grok`). Transcribed from the hand-written buildGrokCommand into
642
+ // registry data; enabled by default, like every other shipped mode.
643
+ const GROK = {
644
+ id: 'grok',
645
+ label: 'Grok',
646
+ shortBadge: 'GK',
647
+ // Upstream hand-authored a charcoal GRADIENT across 4+ CSS spots (welcome button, tab
648
+ // badge, run-mode dot, mobile skin overrides) rather than one flat colour; our registry's
649
+ // `accent` is a single hex, so this is the closest single value (the run-mode-dot colour,
650
+ // zinc-400). Nothing reads `accent` yet — the frontend is untouched in this change and
651
+ // keeps its own hand-authored CSS; the field is here so the entry is complete.
652
+ accent: '#a1a1aa',
653
+ enabled: true,
654
+ stock: true,
655
+ order: 70,
656
+ kind: 'agent',
657
+ discovery: {
658
+ binaries: ['grok'],
659
+ searchDirs: ['~/.grok/bin', HOME_DIRS.local, HOME_DIRS.usrLocal, HOME_DIRS.homeBin],
660
+ // `grok` has a known npm squatter (@vibe-kit/grok-cli also installs a `grok` bin), so a
661
+ // bare `which grok` hit is not evidence of the right program — same defence as pi,
662
+ // byte-identical regex.
663
+ version: { arg: '--version', regex: '(?:^|\\s)(\\d+\\.\\d+\\.\\d+)', requireVersionMatch: true },
664
+ install: {
665
+ command: {
666
+ linux: 'curl -fsSL https://x.ai/cli/install.sh | bash',
667
+ darwin: 'curl -fsSL https://x.ai/cli/install.sh | bash',
668
+ },
669
+ // Not on npm — xAI ships a standalone installer/binary, same shape as Antigravity.
670
+ docsUrl: 'https://github.com/xai-org/grok-build',
671
+ },
672
+ },
673
+ launch: {
674
+ params: {
675
+ alwaysApprove: { type: 'bool' },
676
+ model: { type: 'token', pattern: 'model' },
677
+ resumeId: { type: 'token', pattern: 'id-dotted' },
678
+ continueSession: { type: 'bool' },
679
+ },
680
+ variants: [
681
+ {
682
+ id: 'default',
683
+ args: [
684
+ { lit: 'grok' },
685
+ { flag: '--always-approve', when: { param: 'alwaysApprove', is: true } },
686
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
687
+ { flag: '--resume', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
688
+ {
689
+ lit: '--continue',
690
+ when: {
691
+ allOf: [
692
+ { param: 'continueSession', is: true },
693
+ { param: 'resumeId', state: 'unset' },
694
+ ],
695
+ },
696
+ },
697
+ ],
698
+ },
699
+ ],
700
+ legacyConfigAliases: { resumeId: 'resumeSessionId' },
701
+ legacyConfigField: 'grokConfig',
702
+ resumeAppend: { style: 'flag', flag: '--resume' },
703
+ },
704
+ env: {
705
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
706
+ unset: ['NO_COLOR'],
707
+ // No tmuxSetenvKeys: XAI_API_KEY (xAI's documented headless auth var) is covered by the
708
+ // XAI_ prefix allowlist below, same "rely on the prefix, not an explicit key list"
709
+ // reasoning as pi's ~34 provider keys.
710
+ tmuxSetenvKeys: [],
711
+ dockerExecEnvNames: [],
712
+ allowedPrefixes: ['GROK_', 'XAI_'],
713
+ allowedKeys: [],
714
+ },
715
+ capabilities: {
716
+ ...agentDefaults(),
717
+ // Fullscreen alt-screen TUI with mouse support — same shape as opencode/antigravity:
718
+ // only the tmux-attach-time smcup strip, not Ink's full erase-scrollback+DECSET strip.
719
+ altScreen: 'strip-mux-only',
720
+ // Buffer-policy fallthrough default, unmeasured against an authenticated grok composer
721
+ // (the existing hedge, preserved verbatim) — same as gemini/antigravity/pi.
722
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
723
+ // codex/antigravity-shaped clamp: grok's own bare-spawn default (no config sent) is
724
+ // already its safe interactive ask-mode, so the multi-user clamp only needs to force an
725
+ // EXPLICITLY-SENT bypass flag back off — nothing is materialized when config is absent.
726
+ privilegedParams: [{ param: 'alwaysApprove', clampTo: false }],
727
+ },
728
+ overlays: {
729
+ // ~/.grok also holds sessions/, memory/, downloads/ (the ~160MB binary), completions/,
730
+ // docs/, bin/ — per-file seeding like pi's credStore, not a whole-dir seedWhole copy.
731
+ credStore: { rel: '.grok', seedFiles: ['auth.json', 'config.toml', 'pager.toml'] },
732
+ // No remote/docker overlay needed: the defaults (exec grok / login-shell `grok`) are
733
+ // already correct — verified against upstream's own pinned test/grok-mode.test.ts
734
+ // expectation `exec "${SHELL:-/bin/sh}" -i -l -c 'grok'`.
735
+ },
736
+ };
737
+ // DeepSeek Harness (`dsh`, deepseek-ai/deepseek-harness). The awkward one, and worth
738
+ // reading before assuming it looks like its siblings — it breaks four of this catalog's
739
+ // normal assumptions at once, which is why the schema carries four extensions for it:
740
+ //
741
+ // 1. `dsh` is a PROFILE LAUNCHER, not the agent. It boots $DSH_HOME/profiles/<name>, and
742
+ // DeepSeek ships only `web`/`headless`/`base`, none of which can drive a terminal
743
+ // pane — so the terminal front door is ALWAYS third-party and "installed" is not
744
+ // "runnable". Hence `discovery.launcherProfile`.
745
+ // 2. Its permission switch is the `DSH_PERMISSION_MODE` ENV VAR, not a flag — the
746
+ // harness has none. Hence `env.configSetenv` (so the ordinary privilegedParams clamp
747
+ // still reaches it) plus `capabilities.privilegedEnvKeys` (so an envOverrides send
748
+ // cannot hand the privilege straight back).
749
+ // 3. It is the only non-claude mode with real hook signals, and for it alone that is a
750
+ // per-SESSION question. Hence `hooks: 'supervised'`.
751
+ // 4. Its transcript is zstd session files, one frame per write. Hence
752
+ // `transcript: 'deepseek-zstd'`.
753
+ //
754
+ // The identity probe is the strictest in the catalog for a sharper reason than pi's or
755
+ // grok's npm squatters: Debian ships an unrelated `dsh` (dancer's shell, `apt install
756
+ // dsh`) that would pass a version probe perfectly happily.
757
+ const DEEPSEEK = {
758
+ id: 'deepseek',
759
+ label: 'DeepSeek',
760
+ shortBadge: 'DS',
761
+ accent: '#4d6bfe',
762
+ enabled: true,
763
+ stock: true,
764
+ order: 80,
765
+ kind: 'agent',
766
+ discovery: {
767
+ binaries: ['dsh'],
768
+ searchDirs: [HOME_DIRS.local, HOME_DIRS.usrLocal, HOME_DIRS.npmGlobal, HOME_DIRS.homeBin],
769
+ // Checked BEFORE the version probe: dancer's shell answers --version happily, so a
770
+ // version match alone would accept it.
771
+ identity: { arg: '--help', regex: 'DeepSeek\\s+Harness' },
772
+ // Keeps the `-rc.2` prerelease tail — dsh ships them, and the `codeman doctor` row
773
+ // shares this regex so the two cannot disagree about what version a binary reports.
774
+ version: {
775
+ arg: '--version',
776
+ regex: '(?:^|\\s)v?(\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?)',
777
+ requireVersionMatch: true,
778
+ },
779
+ launcherProfile: 'deepseek-profile',
780
+ launcherTargetParam: 'profile',
781
+ install: {
782
+ command: {
783
+ linux: 'npm install -g @deepseek-ai/dsh',
784
+ darwin: 'npm install -g @deepseek-ai/dsh',
785
+ },
786
+ npmPackage: '@deepseek-ai/dsh',
787
+ docsUrl: 'https://github.com/deepseek-ai/deepseek-harness',
788
+ },
789
+ },
790
+ launch: {
791
+ params: {
792
+ // A single path segment: interpolated into the shell line AND joined into a
793
+ // filesystem path, so `path-segment` rather than the looser `id-dotted`.
794
+ profile: { type: 'token', pattern: 'path-segment' },
795
+ // Resolved at spawn time from what is actually installed — see launcherProfile.
796
+ defaultProfile: { type: 'engine', source: 'launcherDefaultTarget' },
797
+ resumeId: { type: 'token', pattern: 'id-dotted' },
798
+ resumeSession: { type: 'bool' },
799
+ // Never appears in argv. Declared so `configSetenv` can export it and, more to the
800
+ // point, so `privilegedParams` can clamp it — see capabilities below.
801
+ permissionMode: { type: 'enum', values: ['read-only', 'workspace-write', 'danger-full-access'] },
802
+ // Never appears in argv either; read by the status-bridge setenv profile.
803
+ statusReporting: { type: 'bool' },
804
+ },
805
+ variants: [
806
+ {
807
+ id: 'default',
808
+ args: [
809
+ { lit: 'dsh' },
810
+ { flag: '--profile', valueFrom: 'profile', when: { param: 'profile', state: 'set' } },
811
+ // An invalid profile name resolves to undefined, so `profile` reads as UNSET and
812
+ // this arm takes over — reproducing the hand-written builder's fall back to the
813
+ // resolved default rather than failing the spawn outright.
814
+ {
815
+ flag: '--profile',
816
+ valueFrom: 'defaultProfile',
817
+ when: {
818
+ allOf: [
819
+ { param: 'profile', state: 'unset' },
820
+ { param: 'defaultProfile', state: 'set' },
821
+ ],
822
+ },
823
+ },
824
+ // The launcher forwards everything after its own flags to the profile's app,
825
+ // which is where --resume is understood. An explicit id wins over the
826
+ // most-recent-session form, mirroring the sibling builders.
827
+ { flag: '--resume', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
828
+ {
829
+ flag: '--resume',
830
+ when: {
831
+ allOf: [
832
+ { param: 'resumeId', state: 'unset' },
833
+ { param: 'resumeSession', is: true },
834
+ ],
835
+ },
836
+ },
837
+ ],
838
+ },
839
+ ],
840
+ legacyConfigAliases: { resumeId: 'resumeSessionId' },
841
+ legacyConfigField: 'deepSeekConfig',
842
+ resumeAppend: { style: 'flag', flag: '--resume' },
843
+ },
844
+ env: {
845
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
846
+ unset: ['NO_COLOR'],
847
+ // DEEPSEEK_BASE_URL is forwarded from the SERVER's own env alongside the API key,
848
+ // which is exactly why a non-granted owner may not override it — see privilegedEnvKeys.
849
+ tmuxSetenvKeys: ['DEEPSEEK_API_KEY', 'DEEPSEEK_BASE_URL', 'DSH_HOME'],
850
+ dockerExecEnvNames: [],
851
+ configSetenv: [{ name: 'DSH_PERMISSION_MODE', fromParam: 'permissionMode' }],
852
+ // Only the vendor namespaces. A dsh settings.yaml can nominate ANY env var as a
853
+ // provider credential (`apiKeyEnv`), so admitting foreign provider keys here would
854
+ // widen one GLOBAL allowlist for every mode at once — the same lesson pi taught.
855
+ allowedPrefixes: ['DSH_', 'DEEPSEEK_'],
856
+ allowedKeys: [],
857
+ setenvProfile: 'deepseek-status-bridge',
858
+ },
859
+ capabilities: {
860
+ ...agentDefaults(),
861
+ // Definitive rather than inferred: the harness TUI reports idle/working/blocked to a
862
+ // supervisor and Codeman is that supervisor. 'supervised' rather than 'always' because
863
+ // the session can disarm the bridge, and docker/remote cannot reach it at all.
864
+ hooks: 'supervised',
865
+ transcript: 'deepseek-zstd',
866
+ altScreen: 'strip-mux-only',
867
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
868
+ // Model is NOT a session field for dsh — it is a profile composition entry.
869
+ model: { source: 'none' },
870
+ // Only-if-sent, like codex/antigravity/grok: an ABSENT permissionMode means the
871
+ // launcher's own default, `workspace-write`, which already asks. Clamping to
872
+ // `read-only` instead would break the workspace rather than protect it.
873
+ privilegedParams: [{ param: 'permissionMode', clampTo: 'workspace-write' }],
874
+ // The half no other CLI needs. `DSH_*` is an allowlisted envOverrides prefix and
875
+ // applyEnvOverrides() runs LAST, so without this a non-granted owner could send
876
+ // DSH_PERMISSION_MODE on the same request and land after the config clamp.
877
+ privilegedEnvKeys: ['DSH_PERMISSION_MODE', 'DSH_HOME', 'DEEPSEEK_BASE_URL'],
878
+ },
879
+ overlays: {
880
+ // No credStore: dsh keeps everything under $DSH_HOME (default ~/.dsh), which is
881
+ // forwarded as a plain env var above rather than seeded as a credential directory.
882
+ },
883
+ };
884
+ // OMP (`omp`, omp.sh). The plainest entry in the catalog after opencode: no permission
885
+ // flags at all — omp reads its model routing and hooks from `~/.omp/agent`, so the CLI's
886
+ // own config governs and there is deliberately nothing bypass-shaped to clamp. Its only
887
+ // privileged surface is a pair of ENV keys (see privilegedEnvKeys below).
888
+ const OMP = {
889
+ id: 'omp',
890
+ label: 'OMP',
891
+ shortBadge: 'OM',
892
+ accent: '#7c9cf5',
893
+ enabled: true,
894
+ stock: true,
895
+ order: 90,
896
+ kind: 'agent',
897
+ discovery: {
898
+ binaries: ['omp'],
899
+ // `~/.local/bin` leads: omp.sh's installer targets it with no `--dir` override
900
+ // (verified against a real `--no-cache` docker build); `~/.omp/bin` is a defensive
901
+ // fallback only.
902
+ searchDirs: [
903
+ HOME_DIRS.local,
904
+ '~/.omp/bin',
905
+ HOME_DIRS.usrLocal,
906
+ HOME_DIRS.bunBin,
907
+ HOME_DIRS.npmGlobal,
908
+ HOME_DIRS.homeBin,
909
+ ],
910
+ // A real `omp --version` prints `omp/<semver>`. `omp` is another short generic name, so
911
+ // the `omp/` prefix is what distinguishes the coding agent from anything else of that
912
+ // name — same defence as pi and grok, one notch stricter because the prefix is checked.
913
+ version: { arg: '--version', regex: '(?:^|\\s)omp/(\\d+\\.\\d+\\.\\d+)', requireVersionMatch: true },
914
+ install: {
915
+ command: {
916
+ linux: 'curl -fsSL https://omp.sh/install | sh',
917
+ darwin: 'brew install can1357/tap/omp',
918
+ },
919
+ docsUrl: 'https://omp.sh',
920
+ },
921
+ },
922
+ launch: {
923
+ params: {
924
+ model: { type: 'token', pattern: 'model' },
925
+ resumeId: { type: 'token', pattern: 'id-dotted' },
926
+ continueSession: { type: 'bool' },
927
+ },
928
+ variants: [
929
+ {
930
+ id: 'default',
931
+ args: [
932
+ { lit: 'omp' },
933
+ { flag: '--model', valueFrom: 'model', when: { param: 'model', state: 'set' } },
934
+ // `--resume` and `--continue` conflict; a valid explicit id wins, mirroring the
935
+ // sibling builders (grok/pi/opencode).
936
+ { flag: '--resume', valueFrom: 'resumeId', when: { param: 'resumeId', state: 'set' } },
937
+ {
938
+ lit: '--continue',
939
+ when: {
940
+ allOf: [
941
+ { param: 'continueSession', is: true },
942
+ { param: 'resumeId', state: 'unset' },
943
+ ],
944
+ },
945
+ },
946
+ ],
947
+ },
948
+ ],
949
+ legacyConfigAliases: { resumeId: 'resumeSessionId' },
950
+ legacyConfigField: 'ompConfig',
951
+ resumeAppend: { style: 'flag', flag: '--resume' },
952
+ },
953
+ env: {
954
+ exports: [{ name: 'COLORTERM', value: 'truecolor' }],
955
+ unset: ['NO_COLOR'],
956
+ // omp's provider credentials live in `~/.omp` config files, not env vars, so there is
957
+ // nothing for the server to forward into the pane.
958
+ tmuxSetenvKeys: [],
959
+ dockerExecEnvNames: [],
960
+ allowedPrefixes: ['OMP_'],
961
+ allowedKeys: [],
962
+ },
963
+ capabilities: {
964
+ ...agentDefaults(),
965
+ // Fullscreen alt-screen TUI, same shape as opencode/antigravity/grok: only the
966
+ // tmux-attach-time smcup strip, not Ink's full erase-scrollback+DECSET strip.
967
+ altScreen: 'strip-mux-only',
968
+ // Codeman reads omp's own `~/.omp/agent/sessions/**/*.jsonl` host-side, which is what
969
+ // makes an omp conversation survive a full session kill.
970
+ transcript: 'omp-jsonl',
971
+ echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
972
+ // No permission prompts and no bypass flag, so nothing config-shaped to clamp — the
973
+ // whole privileged surface here is env-shaped.
974
+ privilegedParams: [],
975
+ // Where omp resolves its auth from. No known concrete exfiltration path today (omp
976
+ // forwards no operator-held key into a pane), but a non-granted owner redirecting where
977
+ // a shared multi-tenant deployment resolves auth is not something to allow silently.
978
+ privilegedEnvKeys: ['OMP_AUTH_BROKER_URL', 'OMP_AUTH_BROKER_TOKEN'],
979
+ },
980
+ overlays: {
981
+ // `~/.omp/agent` also holds agent.db/history.db/models.db (SQLite caches) and
982
+ // terminal-sessions/blobs/cache (large, regenerable), so only the config files are
983
+ // seeded. UNLIKE pi/grok, `sessions/` is SHARED (RW) rather than host-invisible:
984
+ // Codeman reads it HOST-SIDE for history recovery and `--resume` pinning, the same
985
+ // reason codex's `sessions/` is shared — without it an in-container omp conversation
986
+ // would be invisible to Codeman's own resume logic.
987
+ credStore: {
988
+ rel: '.omp/agent',
989
+ shareDirs: ['sessions'],
990
+ seedFiles: ['config.yml', 'mcp.json', 'models.yml', 'settings.yml'],
991
+ },
992
+ },
993
+ };
994
+ /** The full stock catalog, in the order the run menu shows by default. */
995
+ export const STOCK_CLIS = [CLAUDE, SHELL, OPENCODE, CODEX, GEMINI, ANTIGRAVITY, PI, GROK, DEEPSEEK, OMP];
996
+ //# sourceMappingURL=stock.js.map