@the-open-engine/zeroshot 6.3.0 → 6.5.0

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 (149) hide show
  1. package/cli/commands/providers.js +11 -5
  2. package/cli/index.js +83 -17
  3. package/cli/lib/first-run.js +14 -3
  4. package/docker/zeroshot-cluster/Dockerfile +11 -1
  5. package/lib/agent-cli-provider/acp-stdio-runner.d.ts +4 -0
  6. package/lib/agent-cli-provider/acp-stdio-runner.d.ts.map +1 -0
  7. package/lib/agent-cli-provider/acp-stdio-runner.js +259 -0
  8. package/lib/agent-cli-provider/acp-stdio-runner.js.map +1 -0
  9. package/lib/agent-cli-provider/adapters/acp.d.ts +23 -0
  10. package/lib/agent-cli-provider/adapters/acp.d.ts.map +1 -0
  11. package/lib/agent-cli-provider/adapters/acp.js +373 -0
  12. package/lib/agent-cli-provider/adapters/acp.js.map +1 -0
  13. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
  14. package/lib/agent-cli-provider/adapters/claude.js +2 -29
  15. package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
  16. package/lib/agent-cli-provider/adapters/copilot-parser.d.ts +3 -0
  17. package/lib/agent-cli-provider/adapters/copilot-parser.d.ts.map +1 -0
  18. package/lib/agent-cli-provider/adapters/copilot-parser.js +154 -0
  19. package/lib/agent-cli-provider/adapters/copilot-parser.js.map +1 -0
  20. package/lib/agent-cli-provider/adapters/copilot.d.ts +3 -0
  21. package/lib/agent-cli-provider/adapters/copilot.d.ts.map +1 -0
  22. package/lib/agent-cli-provider/adapters/copilot.js +176 -0
  23. package/lib/agent-cli-provider/adapters/copilot.js.map +1 -0
  24. package/lib/agent-cli-provider/adapters/gateway.d.ts +6 -0
  25. package/lib/agent-cli-provider/adapters/gateway.d.ts.map +1 -0
  26. package/lib/agent-cli-provider/adapters/gateway.js +161 -0
  27. package/lib/agent-cli-provider/adapters/gateway.js.map +1 -0
  28. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -1
  29. package/lib/agent-cli-provider/adapters/gemini.js +9 -2
  30. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -1
  31. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
  32. package/lib/agent-cli-provider/adapters/index.js +6 -34
  33. package/lib/agent-cli-provider/adapters/index.js.map +1 -1
  34. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
  35. package/lib/agent-cli-provider/adapters/opencode.js +5 -1
  36. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
  37. package/lib/agent-cli-provider/adapters/pi.d.ts +3 -0
  38. package/lib/agent-cli-provider/adapters/pi.d.ts.map +1 -0
  39. package/lib/agent-cli-provider/adapters/pi.js +331 -0
  40. package/lib/agent-cli-provider/adapters/pi.js.map +1 -0
  41. package/lib/agent-cli-provider/claude-command.d.ts +6 -0
  42. package/lib/agent-cli-provider/claude-command.d.ts.map +1 -0
  43. package/lib/agent-cli-provider/claude-command.js +43 -0
  44. package/lib/agent-cli-provider/claude-command.js.map +1 -0
  45. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
  46. package/lib/agent-cli-provider/contract-actions.js +13 -17
  47. package/lib/agent-cli-provider/contract-actions.js.map +1 -1
  48. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
  49. package/lib/agent-cli-provider/contract-invoke.js +10 -3
  50. package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
  51. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
  52. package/lib/agent-cli-provider/contract-options.js +57 -2
  53. package/lib/agent-cli-provider/contract-options.js.map +1 -1
  54. package/lib/agent-cli-provider/contract-support.d.ts +1 -0
  55. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
  56. package/lib/agent-cli-provider/contract-support.js +25 -3
  57. package/lib/agent-cli-provider/contract-support.js.map +1 -1
  58. package/lib/agent-cli-provider/errors.d.ts +1 -0
  59. package/lib/agent-cli-provider/errors.d.ts.map +1 -1
  60. package/lib/agent-cli-provider/errors.js +14 -5
  61. package/lib/agent-cli-provider/errors.js.map +1 -1
  62. package/lib/agent-cli-provider/gateway-client.d.ts +32 -0
  63. package/lib/agent-cli-provider/gateway-client.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/gateway-client.js +135 -0
  65. package/lib/agent-cli-provider/gateway-client.js.map +1 -0
  66. package/lib/agent-cli-provider/gateway-runner.d.ts +9 -0
  67. package/lib/agent-cli-provider/gateway-runner.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/gateway-runner.js +294 -0
  69. package/lib/agent-cli-provider/gateway-runner.js.map +1 -0
  70. package/lib/agent-cli-provider/gateway-tools.d.ts +14 -0
  71. package/lib/agent-cli-provider/gateway-tools.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/gateway-tools.js +495 -0
  73. package/lib/agent-cli-provider/gateway-tools.js.map +1 -0
  74. package/lib/agent-cli-provider/index.d.ts +3 -2
  75. package/lib/agent-cli-provider/index.d.ts.map +1 -1
  76. package/lib/agent-cli-provider/index.js +14 -1
  77. package/lib/agent-cli-provider/index.js.map +1 -1
  78. package/lib/agent-cli-provider/invoke-evidence.d.ts +2 -2
  79. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -1
  80. package/lib/agent-cli-provider/invoke-evidence.js +17 -3
  81. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -1
  82. package/lib/agent-cli-provider/provider-registry.d.ts +437 -0
  83. package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/provider-registry.js +411 -0
  85. package/lib/agent-cli-provider/provider-registry.js.map +1 -0
  86. package/lib/agent-cli-provider/single-agent-runtime.d.ts +7 -0
  87. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
  88. package/lib/agent-cli-provider/single-agent-runtime.js +139 -19
  89. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
  90. package/lib/agent-cli-provider/types.d.ts +106 -4
  91. package/lib/agent-cli-provider/types.d.ts.map +1 -1
  92. package/lib/agent-cli-provider/types.js.map +1 -1
  93. package/lib/detached-startup.js +4 -2
  94. package/lib/docker-config.js +24 -13
  95. package/lib/provider-defaults.js +15 -4
  96. package/lib/provider-detection.js +2 -0
  97. package/lib/provider-names.js +32 -12
  98. package/lib/repo-settings.js +15 -1
  99. package/lib/run-mode.js +18 -6
  100. package/lib/run-plan.js +32 -0
  101. package/lib/settings/claude-auth.js +3 -6
  102. package/lib/settings.js +17 -2
  103. package/lib/setup-apply.js +300 -0
  104. package/lib/setup-journal.js +109 -0
  105. package/lib/setup-plan.js +406 -0
  106. package/lib/setup-undo.js +88 -0
  107. package/lib/start-cluster.js +34 -9
  108. package/lib/stream-json-parser.js +7 -5
  109. package/package.json +33 -3
  110. package/scripts/live-provider-smoke.js +221 -0
  111. package/src/agent/agent-lifecycle.js +7 -4
  112. package/src/agent/agent-quality-gate-schema.js +2 -2
  113. package/src/agent/agent-task-executor.js +41 -2
  114. package/src/agent-cli-provider/acp-stdio-runner.ts +314 -0
  115. package/src/agent-cli-provider/adapters/acp.ts +493 -0
  116. package/src/agent-cli-provider/adapters/claude.ts +2 -32
  117. package/src/agent-cli-provider/adapters/copilot-parser.ts +166 -0
  118. package/src/agent-cli-provider/adapters/copilot.ts +231 -0
  119. package/src/agent-cli-provider/adapters/gateway.ts +186 -0
  120. package/src/agent-cli-provider/adapters/gemini.ts +9 -2
  121. package/src/agent-cli-provider/adapters/index.ts +5 -36
  122. package/src/agent-cli-provider/adapters/opencode.ts +4 -1
  123. package/src/agent-cli-provider/adapters/pi.ts +410 -0
  124. package/src/agent-cli-provider/claude-command.ts +47 -0
  125. package/src/agent-cli-provider/contract-actions.ts +14 -17
  126. package/src/agent-cli-provider/contract-invoke.ts +12 -3
  127. package/src/agent-cli-provider/contract-options.ts +72 -3
  128. package/src/agent-cli-provider/contract-support.ts +30 -3
  129. package/src/agent-cli-provider/errors.ts +14 -4
  130. package/src/agent-cli-provider/gateway-client.ts +170 -0
  131. package/src/agent-cli-provider/gateway-runner.ts +353 -0
  132. package/src/agent-cli-provider/gateway-tools.ts +616 -0
  133. package/src/agent-cli-provider/index.ts +28 -0
  134. package/src/agent-cli-provider/invoke-evidence.ts +24 -3
  135. package/src/agent-cli-provider/provider-registry.ts +515 -0
  136. package/src/agent-cli-provider/single-agent-runtime.ts +192 -18
  137. package/src/agent-cli-provider/types.ts +145 -4
  138. package/src/config-validator.js +33 -15
  139. package/src/isolation-manager.js +124 -19
  140. package/src/ledger.js +3 -0
  141. package/src/message-bus.js +12 -2
  142. package/src/orchestrator.js +37 -17
  143. package/src/preflight.js +83 -30
  144. package/src/providers/capabilities.js +15 -42
  145. package/src/providers/index.js +52 -72
  146. package/src/worktree-claude-config.js +17 -0
  147. package/task-lib/commands/run.js +1 -0
  148. package/task-lib/provider-helper-runtime.js +11 -0
  149. package/task-lib/runner.js +7 -0
@@ -1,17 +1,38 @@
1
1
  import { classifyProviderError } from './adapters';
2
2
  import type { ProcessResult } from './process-runner';
3
- import type { ErrorClassification, ProviderAdapter } from './types';
3
+ import type { ErrorClassification, OutputEvent, ProviderAdapter, ResultEvent } from './types';
4
+
5
+ function isFailedResultEvent(event: OutputEvent): event is ResultEvent {
6
+ return event.type === 'result' && event.success === false;
7
+ }
8
+
9
+ function classifyParsedFailure(
10
+ adapter: ProviderAdapter,
11
+ events: readonly OutputEvent[]
12
+ ): ErrorClassification | null {
13
+ const failedResult = [...events].reverse().find(isFailedResultEvent);
14
+ if (failedResult === undefined) return null;
15
+ return classifyProviderError(adapter.id, {
16
+ message:
17
+ typeof failedResult.error === 'string' && failedResult.error.trim()
18
+ ? failedResult.error
19
+ : 'Provider reported a failed result event.',
20
+ });
21
+ }
4
22
 
5
23
  export function providerFailureClassification(
6
24
  adapter: ProviderAdapter,
7
- result: ProcessResult
25
+ result: ProcessResult,
26
+ events: readonly OutputEvent[] = []
8
27
  ): ErrorClassification | null {
9
28
  if (result.timedOut) {
10
29
  return classifyProviderError(adapter.id, {
11
30
  message: `Provider timed out after ${result.timeoutMs ?? 'unknown'}ms`,
12
31
  });
13
32
  }
14
- if (result.exitCode === 0 && result.signal === null) return null;
33
+ if (result.exitCode === 0 && result.signal === null) {
34
+ return classifyParsedFailure(adapter, events);
35
+ }
15
36
  return classifyProviderError(adapter.id, {
16
37
  message: result.stderr || result.stdout || `Provider exited with code ${result.exitCode ?? ''}`,
17
38
  });
@@ -0,0 +1,515 @@
1
+ import { createAcpAdapter } from './adapters/acp';
2
+ import { claudeAdapter } from './adapters/claude';
3
+ import { codexAdapter } from './adapters/codex';
4
+ import { copilotAdapter } from './adapters/copilot';
5
+ import { gatewayAdapter, gatewaySettingsDefaults, validateGatewaySettings } from './adapters/gateway';
6
+ import { geminiAdapter } from './adapters/gemini';
7
+ import { opencodeAdapter } from './adapters/opencode';
8
+ import { piAdapter } from './adapters/pi';
9
+ import { resolveClaudeCommand } from './claude-command';
10
+ import type { ModelLevel, ProviderAdapter } from './types';
11
+
12
+ export type ProviderCapabilityState = boolean | 'experimental';
13
+
14
+ export interface ProviderCapabilities {
15
+ readonly dockerIsolation: ProviderCapabilityState;
16
+ readonly worktreeIsolation: ProviderCapabilityState;
17
+ readonly mcpServers: ProviderCapabilityState;
18
+ readonly jsonSchema: ProviderCapabilityState;
19
+ readonly streamJson: ProviderCapabilityState;
20
+ readonly thinkingMode: ProviderCapabilityState;
21
+ readonly reasoningEffort: ProviderCapabilityState;
22
+ }
23
+
24
+ interface FixedProviderCommandSpec {
25
+ readonly kind: 'fixed';
26
+ readonly command: string;
27
+ readonly args: readonly string[];
28
+ }
29
+
30
+ interface ConfiguredClaudeCommandSpec {
31
+ readonly kind: 'configured-claude';
32
+ }
33
+
34
+ export interface SpawnProviderInvokeSpec {
35
+ readonly lane: 'spawn';
36
+ }
37
+
38
+ export interface AcpStdioProviderInvokeSpec {
39
+ readonly lane: 'acp-stdio';
40
+ readonly transport: 'stdio';
41
+ }
42
+
43
+ export type ProviderInvokeSpec = SpawnProviderInvokeSpec | AcpStdioProviderInvokeSpec;
44
+
45
+ export type ProviderCommandSpec = FixedProviderCommandSpec | ConfiguredClaudeCommandSpec;
46
+
47
+ export interface ProviderDocsMetadata {
48
+ readonly label: string;
49
+ readonly setupHeading: string;
50
+ }
51
+
52
+ export interface ProviderDockerMountPreset {
53
+ readonly host: string;
54
+ readonly container: string;
55
+ readonly readonly: boolean;
56
+ }
57
+
58
+ export interface ProviderDockerMetadata {
59
+ readonly mount: ProviderDockerMountPreset;
60
+ readonly envPassthrough: readonly string[];
61
+ // False when the mounted dir doesn't hold the secret (auth is via an envPassthrough token).
62
+ readonly credentialInMount?: boolean;
63
+ // Shell command that installs this provider's CLI inside the Debian-based cluster image, run as
64
+ // a docker-cached build layer for the per-provider image variant. Omit for providers already
65
+ // baked into the base image (e.g. Claude) or not installable via a single command.
66
+ readonly install?: string;
67
+ }
68
+
69
+ export interface ProviderRegistryEntry {
70
+ readonly id: string;
71
+ readonly aliases: readonly string[];
72
+ readonly displayName: string;
73
+ readonly binary: string;
74
+ readonly command: ProviderCommandSpec;
75
+ readonly invoke: ProviderInvokeSpec;
76
+ readonly installInstructions: string;
77
+ readonly authInstructions: string;
78
+ readonly credentialPaths: readonly string[];
79
+ readonly credentialEnvKeys: readonly string[];
80
+ readonly settingsFields: readonly string[];
81
+ readonly settingsDefaults?: Readonly<Record<string, unknown>>;
82
+ readonly settingsValidator?: (settings: Record<string, unknown>) => string | null;
83
+ readonly availabilityProbe?: 'command' | 'help-or-version';
84
+ readonly capabilities: ProviderCapabilities;
85
+ readonly docs: ProviderDocsMetadata;
86
+ readonly docker: ProviderDockerMetadata;
87
+ readonly defaultLevels: Readonly<{
88
+ readonly min: ModelLevel;
89
+ readonly default: ModelLevel;
90
+ readonly max: ModelLevel;
91
+ }>;
92
+ readonly adapter: ProviderAdapter;
93
+ }
94
+
95
+ const STANDARD_CAPABILITIES: Readonly<
96
+ Pick<ProviderCapabilities, 'dockerIsolation' | 'worktreeIsolation' | 'mcpServers' | 'streamJson' | 'thinkingMode'>
97
+ > = {
98
+ dockerIsolation: true,
99
+ worktreeIsolation: true,
100
+ mcpServers: true,
101
+ streamJson: true,
102
+ thinkingMode: true,
103
+ };
104
+
105
+ const CLAUDE_DOCKER_ENV_PASSTHROUGH = [
106
+ 'ANTHROPIC_API_KEY',
107
+ 'AWS_BEARER_TOKEN_BEDROCK',
108
+ 'AWS_REGION',
109
+ 'CLAUDE_CODE_USE_BEDROCK',
110
+ ] as const;
111
+
112
+ const SPAWN_INVOKE = Object.freeze({ lane: 'spawn' }) as SpawnProviderInvokeSpec;
113
+ const ACP_STDIO_INVOKE = Object.freeze({
114
+ lane: 'acp-stdio',
115
+ transport: 'stdio',
116
+ }) as AcpStdioProviderInvokeSpec;
117
+
118
+ const kiroAdapter = createAcpAdapter({
119
+ provider: 'kiro',
120
+ displayName: 'Kiro',
121
+ binary: 'kiro-cli',
122
+ commandArgs: ['acp'],
123
+ credentialEnvKeys: ['KIRO_API_KEY'],
124
+ supportsPromptImages: true,
125
+ supportsLoadSession: false,
126
+ supportsSessionCancel: true,
127
+ supportsSessionSetModel: false,
128
+ supportsSessionSetMode: false,
129
+ retryableErrorPatterns: [
130
+ /\brate(?:[ _])?limit\b/i,
131
+ /\btemporar(?:y|ily)\b/i,
132
+ /\btimeout\b/i,
133
+ /\bunavailable\b/i,
134
+ ],
135
+ permanentErrorPatterns: [
136
+ /\bauth(?:entication)?\b/i,
137
+ /\bapi[_ -]?key\b/i,
138
+ /\bforbidden\b/i,
139
+ /\bunauthorized\b/i,
140
+ /\bcancelled\b/i,
141
+ /\bmalformed\b/i,
142
+ /\bunsupported\b/i,
143
+ ],
144
+ });
145
+
146
+ export const providerRegistry = [
147
+ {
148
+ id: 'claude',
149
+ aliases: ['anthropic'],
150
+ displayName: 'Claude',
151
+ binary: 'claude',
152
+ command: { kind: 'configured-claude' },
153
+ invoke: SPAWN_INVOKE,
154
+ installInstructions:
155
+ 'npm install -g @anthropic-ai/claude-code\nOr (macOS): brew install claude',
156
+ authInstructions: 'claude login',
157
+ credentialPaths: ['~/.claude'],
158
+ credentialEnvKeys: claudeAdapter.credentialEnvKeys,
159
+ settingsFields: ['anthropicApiKey', 'bedrockApiKey', 'bedrockRegion'],
160
+ capabilities: {
161
+ ...STANDARD_CAPABILITIES,
162
+ jsonSchema: true,
163
+ reasoningEffort: false,
164
+ },
165
+ docs: {
166
+ label: 'Claude',
167
+ setupHeading: 'Claude Setup',
168
+ },
169
+ docker: {
170
+ mount: {
171
+ host: '~/.claude',
172
+ container: '$HOME/.claude',
173
+ readonly: true,
174
+ },
175
+ envPassthrough: CLAUDE_DOCKER_ENV_PASSTHROUGH,
176
+ },
177
+ defaultLevels: {
178
+ min: claudeAdapter.defaultMinLevel,
179
+ default: claudeAdapter.defaultLevel,
180
+ max: claudeAdapter.defaultMaxLevel,
181
+ },
182
+ adapter: claudeAdapter,
183
+ },
184
+ {
185
+ id: 'codex',
186
+ aliases: ['openai'],
187
+ displayName: 'Codex',
188
+ binary: 'codex',
189
+ command: { kind: 'fixed', command: 'codex', args: ['exec'] },
190
+ invoke: SPAWN_INVOKE,
191
+ installInstructions: 'npm install -g @openai/codex',
192
+ authInstructions: 'codex login',
193
+ credentialPaths: ['~/.config/codex', '~/.codex'],
194
+ credentialEnvKeys: codexAdapter.credentialEnvKeys,
195
+ settingsFields: [],
196
+ capabilities: {
197
+ ...STANDARD_CAPABILITIES,
198
+ jsonSchema: true,
199
+ reasoningEffort: true,
200
+ },
201
+ docs: {
202
+ label: 'Codex',
203
+ setupHeading: 'Codex Setup',
204
+ },
205
+ docker: {
206
+ mount: {
207
+ host: '~/.config/codex',
208
+ container: '$HOME/.config/codex',
209
+ readonly: true,
210
+ },
211
+ install: 'npm install -g @openai/codex',
212
+ envPassthrough: [],
213
+ },
214
+ defaultLevels: {
215
+ min: codexAdapter.defaultMinLevel,
216
+ default: codexAdapter.defaultLevel,
217
+ max: codexAdapter.defaultMaxLevel,
218
+ },
219
+ adapter: codexAdapter,
220
+ },
221
+ {
222
+ id: 'gateway',
223
+ aliases: [],
224
+ displayName: 'Gateway',
225
+ binary: 'node',
226
+ command: { kind: 'fixed', command: 'node', args: [] },
227
+ invoke: SPAWN_INVOKE,
228
+ installInstructions: 'Bundled with Zeroshot; no external provider CLI install is required.',
229
+ authInstructions:
230
+ 'Configure providerSettings.gateway.baseUrl, apiKey, model, and toolPolicy in Zeroshot settings.',
231
+ credentialPaths: [],
232
+ credentialEnvKeys: gatewayAdapter.credentialEnvKeys,
233
+ settingsFields: ['baseUrl', 'apiKey', 'headers', 'model', 'toolPolicy'],
234
+ settingsDefaults: gatewaySettingsDefaults,
235
+ settingsValidator: validateGatewaySettings,
236
+ capabilities: {
237
+ ...STANDARD_CAPABILITIES,
238
+ mcpServers: false,
239
+ jsonSchema: false,
240
+ reasoningEffort: false,
241
+ },
242
+ docs: {
243
+ label: 'Gateway',
244
+ setupHeading: 'Gateway Setup',
245
+ },
246
+ docker: {
247
+ mount: {
248
+ host: '~/.zeroshot',
249
+ container: '$HOME/.zeroshot',
250
+ readonly: true,
251
+ },
252
+ envPassthrough: [],
253
+ },
254
+ defaultLevels: {
255
+ min: gatewayAdapter.defaultMinLevel,
256
+ default: gatewayAdapter.defaultLevel,
257
+ max: gatewayAdapter.defaultMaxLevel,
258
+ },
259
+ adapter: gatewayAdapter,
260
+ },
261
+ {
262
+ id: 'gemini',
263
+ aliases: ['google'],
264
+ displayName: 'Gemini',
265
+ binary: 'gemini',
266
+ command: { kind: 'fixed', command: 'gemini', args: [] },
267
+ invoke: SPAWN_INVOKE,
268
+ installInstructions: 'npm install -g @google/gemini-cli',
269
+ authInstructions: 'gemini auth login',
270
+ credentialPaths: ['~/.config/gcloud', '~/.config/gemini', '~/.gemini'],
271
+ credentialEnvKeys: geminiAdapter.credentialEnvKeys,
272
+ settingsFields: [],
273
+ capabilities: {
274
+ ...STANDARD_CAPABILITIES,
275
+ jsonSchema: 'experimental',
276
+ reasoningEffort: false,
277
+ },
278
+ docs: {
279
+ label: 'Gemini',
280
+ setupHeading: 'Gemini Setup',
281
+ },
282
+ docker: {
283
+ mount: {
284
+ host: '~/.config/gemini',
285
+ container: '$HOME/.config/gemini',
286
+ readonly: true,
287
+ },
288
+ install: 'npm install -g @google/gemini-cli',
289
+ envPassthrough: [],
290
+ },
291
+ defaultLevels: {
292
+ min: geminiAdapter.defaultMinLevel,
293
+ default: geminiAdapter.defaultLevel,
294
+ max: geminiAdapter.defaultMaxLevel,
295
+ },
296
+ adapter: geminiAdapter,
297
+ },
298
+ {
299
+ id: 'opencode',
300
+ aliases: [],
301
+ displayName: 'Opencode',
302
+ binary: 'opencode',
303
+ command: { kind: 'fixed', command: 'opencode', args: ['run'] },
304
+ invoke: SPAWN_INVOKE,
305
+ installInstructions: 'See https://opencode.ai for installation instructions.',
306
+ authInstructions: 'opencode auth login',
307
+ credentialPaths: ['~/.local/share/opencode'],
308
+ credentialEnvKeys: opencodeAdapter.credentialEnvKeys,
309
+ settingsFields: [],
310
+ capabilities: {
311
+ ...STANDARD_CAPABILITIES,
312
+ jsonSchema: 'experimental',
313
+ reasoningEffort: true,
314
+ },
315
+ docs: {
316
+ label: 'Opencode',
317
+ setupHeading: 'Opencode Setup',
318
+ },
319
+ docker: {
320
+ mount: {
321
+ host: '~/.local/share/opencode',
322
+ container: '$HOME/.local/share/opencode',
323
+ readonly: true,
324
+ },
325
+ envPassthrough: [],
326
+ },
327
+ defaultLevels: {
328
+ min: opencodeAdapter.defaultMinLevel,
329
+ default: opencodeAdapter.defaultLevel,
330
+ max: opencodeAdapter.defaultMaxLevel,
331
+ },
332
+ adapter: opencodeAdapter,
333
+ },
334
+ {
335
+ id: 'pi',
336
+ aliases: [],
337
+ displayName: 'Pi',
338
+ binary: 'pi',
339
+ command: { kind: 'fixed', command: 'pi', args: [] },
340
+ invoke: SPAWN_INVOKE,
341
+ installInstructions:
342
+ 'npm install -g --ignore-scripts @earendil-works/pi-coding-agent@0.80.3',
343
+ authInstructions: 'pi\n/login',
344
+ credentialPaths: ['~/.pi'],
345
+ credentialEnvKeys: piAdapter.credentialEnvKeys,
346
+ settingsFields: [],
347
+ availabilityProbe: 'help-or-version',
348
+ capabilities: {
349
+ ...STANDARD_CAPABILITIES,
350
+ mcpServers: false,
351
+ jsonSchema: false,
352
+ reasoningEffort: false,
353
+ },
354
+ docs: {
355
+ label: 'Pi',
356
+ setupHeading: 'Pi Setup',
357
+ },
358
+ docker: {
359
+ mount: {
360
+ host: '~/.pi',
361
+ container: '$HOME/.pi',
362
+ readonly: true,
363
+ },
364
+ envPassthrough: [],
365
+ },
366
+ defaultLevels: {
367
+ min: piAdapter.defaultMinLevel,
368
+ default: piAdapter.defaultLevel,
369
+ max: piAdapter.defaultMaxLevel,
370
+ },
371
+ adapter: piAdapter,
372
+ },
373
+ {
374
+ id: 'kiro',
375
+ aliases: [],
376
+ displayName: 'Kiro',
377
+ binary: 'kiro-cli',
378
+ command: { kind: 'fixed', command: 'kiro-cli', args: ['acp'] },
379
+ invoke: ACP_STDIO_INVOKE,
380
+ installInstructions: 'See https://kiro.dev/docs/cli/',
381
+ authInstructions: 'See https://kiro.dev/docs/cli/authentication/',
382
+ credentialPaths: ['~/.kiro'],
383
+ credentialEnvKeys: kiroAdapter.credentialEnvKeys,
384
+ settingsFields: [],
385
+ capabilities: {
386
+ ...STANDARD_CAPABILITIES,
387
+ mcpServers: false,
388
+ jsonSchema: false,
389
+ reasoningEffort: false,
390
+ },
391
+ docs: {
392
+ label: 'Kiro',
393
+ setupHeading: 'Kiro Setup',
394
+ },
395
+ docker: {
396
+ mount: {
397
+ host: '~/.kiro',
398
+ container: '$HOME/.kiro',
399
+ readonly: true,
400
+ },
401
+ envPassthrough: ['KIRO_API_KEY'],
402
+ },
403
+ defaultLevels: {
404
+ min: kiroAdapter.defaultMinLevel,
405
+ default: kiroAdapter.defaultLevel,
406
+ max: kiroAdapter.defaultMaxLevel,
407
+ },
408
+ adapter: kiroAdapter,
409
+ },
410
+ {
411
+ id: 'copilot',
412
+ aliases: [],
413
+ displayName: 'Copilot',
414
+ binary: 'copilot',
415
+ command: { kind: 'fixed', command: 'copilot', args: [] },
416
+ invoke: SPAWN_INVOKE,
417
+ installInstructions: 'npm install -g @github/copilot',
418
+ // Docker/CI can't use the keychain token; export COPILOT_GITHUB_TOKEN instead.
419
+ authInstructions: 'copilot\n/login\n(Docker/CI: export COPILOT_GITHUB_TOKEN=<token>)',
420
+ credentialPaths: ['~/.copilot'],
421
+ credentialEnvKeys: copilotAdapter.credentialEnvKeys,
422
+ settingsFields: [],
423
+ availabilityProbe: 'help-or-version',
424
+ // MCP servers pass through via the `--additional-mcp-config` CLI flag (see copilot adapter
425
+ // addMcpArgs). No native output-schema or reasoning-effort flag.
426
+ capabilities: {
427
+ ...STANDARD_CAPABILITIES,
428
+ mcpServers: true,
429
+ jsonSchema: false,
430
+ reasoningEffort: false,
431
+ },
432
+ docs: {
433
+ label: 'Copilot',
434
+ setupHeading: 'Copilot Setup',
435
+ },
436
+ docker: {
437
+ mount: {
438
+ host: '~/.copilot',
439
+ container: '$HOME/.copilot',
440
+ readonly: true,
441
+ },
442
+ install: 'npm install -g @github/copilot',
443
+ envPassthrough: ['COPILOT_GITHUB_TOKEN', 'GH_TOKEN', 'GITHUB_TOKEN'],
444
+ credentialInMount: false, // token is in the OS keychain, not ~/.copilot
445
+ },
446
+ defaultLevels: {
447
+ min: copilotAdapter.defaultMinLevel,
448
+ default: copilotAdapter.defaultLevel,
449
+ max: copilotAdapter.defaultMaxLevel,
450
+ },
451
+ adapter: copilotAdapter,
452
+ },
453
+ ] as const satisfies readonly ProviderRegistryEntry[];
454
+
455
+ type RegistryProviderId = (typeof providerRegistry)[number]['id'];
456
+ type RegistryProviderAlias = (typeof providerRegistry)[number]['aliases'][number];
457
+
458
+ export const providerIds = providerRegistry.map((entry) => entry.id) as readonly RegistryProviderId[];
459
+ export const providerAliases = providerRegistry.flatMap((entry) => entry.aliases) as readonly RegistryProviderAlias[];
460
+ export const knownProviderNames = providerRegistry.flatMap((entry) => [entry.id, ...entry.aliases]) as readonly (
461
+ | RegistryProviderId
462
+ | RegistryProviderAlias
463
+ )[];
464
+
465
+ export const providerAliasMap: Readonly<Record<string, RegistryProviderId>> = Object.freeze(
466
+ providerRegistry.reduce<Record<string, RegistryProviderId>>((result, entry) => {
467
+ result[entry.id] = entry.id;
468
+ for (const alias of entry.aliases) {
469
+ result[alias] = entry.id;
470
+ }
471
+ return result;
472
+ }, {})
473
+ );
474
+
475
+ export function normalizeProviderName(name: string): RegistryProviderId | string {
476
+ const normalized = name.toLowerCase();
477
+ return providerAliasMap[normalized] ?? name;
478
+ }
479
+
480
+ export function listProviderRegistryEntries(): readonly ProviderRegistryEntry[] {
481
+ return providerRegistry;
482
+ }
483
+
484
+ export function findProviderRegistryEntry(name: string | null | undefined): ProviderRegistryEntry | undefined {
485
+ if (!name) return undefined;
486
+ const normalized = normalizeProviderName(name);
487
+ return providerRegistry.find((entry) => entry.id === normalized);
488
+ }
489
+
490
+ export function getProviderRegistryEntry(name: string): ProviderRegistryEntry {
491
+ const entry = findProviderRegistryEntry(name);
492
+ if (entry) return entry;
493
+ throw new Error(`Unknown provider: ${name}. Valid: ${providerIds.join(', ')}`);
494
+ }
495
+
496
+ export function resolveProviderCommand(name: string): {
497
+ readonly command: string;
498
+ readonly args: readonly string[];
499
+ } {
500
+ const entry = getProviderRegistryEntry(name);
501
+ if (entry.command.kind === 'configured-claude') {
502
+ return resolveClaudeCommand();
503
+ }
504
+ return {
505
+ command: entry.command.command,
506
+ args: entry.command.args,
507
+ };
508
+ }
509
+
510
+ export function supportsProviderCapability(
511
+ name: string,
512
+ capability: keyof ProviderCapabilities
513
+ ): boolean {
514
+ return getProviderRegistryEntry(name).capabilities[capability] === true;
515
+ }