@the-open-engine/zeroshot 6.4.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 (144) hide show
  1. package/cli/commands/providers.js +11 -5
  2. package/cli/index.js +79 -16
  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/docker-config.js +24 -13
  94. package/lib/provider-defaults.js +15 -4
  95. package/lib/provider-detection.js +2 -0
  96. package/lib/provider-names.js +32 -12
  97. package/lib/repo-settings.js +15 -1
  98. package/lib/settings/claude-auth.js +3 -6
  99. package/lib/settings.js +17 -2
  100. package/lib/setup-apply.js +300 -0
  101. package/lib/setup-journal.js +109 -0
  102. package/lib/setup-plan.js +406 -0
  103. package/lib/setup-undo.js +88 -0
  104. package/lib/start-cluster.js +12 -1
  105. package/lib/stream-json-parser.js +7 -5
  106. package/package.json +29 -1
  107. package/scripts/live-provider-smoke.js +221 -0
  108. package/src/agent/agent-lifecycle.js +5 -4
  109. package/src/agent/agent-quality-gate-schema.js +2 -2
  110. package/src/agent/agent-task-executor.js +41 -2
  111. package/src/agent-cli-provider/acp-stdio-runner.ts +314 -0
  112. package/src/agent-cli-provider/adapters/acp.ts +493 -0
  113. package/src/agent-cli-provider/adapters/claude.ts +2 -32
  114. package/src/agent-cli-provider/adapters/copilot-parser.ts +166 -0
  115. package/src/agent-cli-provider/adapters/copilot.ts +231 -0
  116. package/src/agent-cli-provider/adapters/gateway.ts +186 -0
  117. package/src/agent-cli-provider/adapters/gemini.ts +9 -2
  118. package/src/agent-cli-provider/adapters/index.ts +5 -36
  119. package/src/agent-cli-provider/adapters/opencode.ts +4 -1
  120. package/src/agent-cli-provider/adapters/pi.ts +410 -0
  121. package/src/agent-cli-provider/claude-command.ts +47 -0
  122. package/src/agent-cli-provider/contract-actions.ts +14 -17
  123. package/src/agent-cli-provider/contract-invoke.ts +12 -3
  124. package/src/agent-cli-provider/contract-options.ts +72 -3
  125. package/src/agent-cli-provider/contract-support.ts +30 -3
  126. package/src/agent-cli-provider/errors.ts +14 -4
  127. package/src/agent-cli-provider/gateway-client.ts +170 -0
  128. package/src/agent-cli-provider/gateway-runner.ts +353 -0
  129. package/src/agent-cli-provider/gateway-tools.ts +616 -0
  130. package/src/agent-cli-provider/index.ts +28 -0
  131. package/src/agent-cli-provider/invoke-evidence.ts +24 -3
  132. package/src/agent-cli-provider/provider-registry.ts +515 -0
  133. package/src/agent-cli-provider/single-agent-runtime.ts +192 -18
  134. package/src/agent-cli-provider/types.ts +145 -4
  135. package/src/config-validator.js +33 -15
  136. package/src/isolation-manager.js +124 -19
  137. package/src/orchestrator.js +23 -10
  138. package/src/preflight.js +83 -30
  139. package/src/providers/capabilities.js +15 -42
  140. package/src/providers/index.js +52 -72
  141. package/src/worktree-claude-config.js +17 -0
  142. package/task-lib/commands/run.js +1 -0
  143. package/task-lib/provider-helper-runtime.js +11 -0
  144. package/task-lib/runner.js +7 -0
@@ -84,6 +84,7 @@ function detectCliFeatures(helpText?: string | null): OpencodeCliFeatures {
84
84
  supportsJson: unknown ? true : /--format\b/.test(help),
85
85
  supportsModel: unknown ? true : /--model\b/.test(help),
86
86
  supportsVariant: unknown ? true : /--variant\b/.test(help),
87
+ supportsDir: unknown ? false : /--dir\b/.test(help),
87
88
  supportsCwd: unknown ? false : /--cwd\b/.test(help),
88
89
  supportsAutoApprove: false,
89
90
  unknown,
@@ -107,7 +108,9 @@ function addOpencodeOptionalArgs(args: string[], options: BuildProviderCommandOp
107
108
  args.push('--variant', options.modelSpec.reasoningEffort);
108
109
  }
109
110
 
110
- if (options.cwd && features.supportsCwd) {
111
+ if (options.cwd && features.supportsDir) {
112
+ args.push('--dir', options.cwd);
113
+ } else if (options.cwd && features.supportsCwd) {
111
114
  args.push('--cwd', options.cwd);
112
115
  }
113
116
  }
@@ -0,0 +1,410 @@
1
+ import { appendJsonSchemaPrompt } from '../schema';
2
+ import { contractError } from '../contract-errors';
3
+ import {
4
+ getArray,
5
+ getBoolean,
6
+ getNumber,
7
+ getOptionalString,
8
+ getRecord,
9
+ getString,
10
+ isRecord,
11
+ tryParseJson,
12
+ unknownToMessage,
13
+ } from '../json';
14
+ import {
15
+ type BuildProviderCommandOptions,
16
+ type CommandSpec,
17
+ type ErrorClassification,
18
+ type LevelModelSpec,
19
+ type LevelOverrides,
20
+ type ModelCatalogEntry,
21
+ type ModelLevel,
22
+ type OutputEvent,
23
+ type PiCliFeatures,
24
+ type ProviderAdapter,
25
+ type ProviderParserState,
26
+ type ResolvedModelSpec,
27
+ type WarningMetadata,
28
+ } from '../types';
29
+ import {
30
+ classifyBaseProviderError,
31
+ commandSpec,
32
+ createParserState,
33
+ optionFeatures,
34
+ resolveModelSpecWithConfig,
35
+ warning,
36
+ } from './common';
37
+
38
+ const MODEL_CATALOG: Readonly<Record<string, ModelCatalogEntry>> = {};
39
+
40
+ const LEVEL_MAPPING: Readonly<Record<ModelLevel, LevelModelSpec>> = {
41
+ level1: { rank: 1, model: null },
42
+ level2: { rank: 2, model: null },
43
+ level3: { rank: 3, model: null },
44
+ };
45
+
46
+ const IGNORED_EVENT_TYPES = new Set([
47
+ 'session',
48
+ 'agent_start',
49
+ 'agent_end',
50
+ 'turn_start',
51
+ 'queue_update',
52
+ 'compaction_start',
53
+ 'compaction_end',
54
+ 'auto_retry_start',
55
+ 'auto_retry_end',
56
+ ]);
57
+
58
+ function detectCliFeatures(helpText?: string | null): PiCliFeatures {
59
+ const help = helpText ?? '';
60
+ const unknown = !help;
61
+ return {
62
+ provider: 'pi',
63
+ supportsJsonMode: unknown ? true : /--mode\b/.test(help) && /\bjson\b/.test(help),
64
+ supportsModel: unknown ? true : /--model\b/.test(help),
65
+ supportsNoSession: unknown ? true : /--no-session\b/.test(help),
66
+ supportsNoExtensions: unknown ? true : /--no-extensions\b/.test(help),
67
+ supportsNoSkills: unknown ? true : /--no-skills\b/.test(help),
68
+ supportsNoPromptTemplates: unknown ? true : /--no-prompt-templates\b/.test(help),
69
+ supportsNoContextFiles: unknown ? true : /--no-context-files\b/.test(help),
70
+ supportsNoApprove: unknown ? true : /--no-approve\b/.test(help),
71
+ unknown,
72
+ };
73
+ }
74
+
75
+ function addRequiredArgs(args: string[], options: BuildProviderCommandOptions): void {
76
+ const features = optionFeatures(options);
77
+ if (features.supportsJsonMode !== false) args.push('--mode', 'json');
78
+ if (features.supportsNoSession !== false) args.push('--no-session');
79
+ if (features.supportsNoExtensions !== false) args.push('--no-extensions');
80
+ if (features.supportsNoSkills !== false) args.push('--no-skills');
81
+ if (features.supportsNoPromptTemplates !== false) args.push('--no-prompt-templates');
82
+ if (features.supportsNoContextFiles !== false) args.push('--no-context-files');
83
+ if (features.supportsNoApprove !== false) args.push('--no-approve');
84
+ }
85
+
86
+ function addOptionalArgs(args: string[], options: BuildProviderCommandOptions): void {
87
+ const features = optionFeatures(options);
88
+ if (options.modelSpec?.model && features.supportsModel !== false) {
89
+ args.push('--model', options.modelSpec.model);
90
+ }
91
+ }
92
+
93
+ function failClosedUnsupportedSessionControl(options: BuildProviderCommandOptions): void {
94
+ const hasResumeSessionId = options.resumeSessionId !== undefined;
95
+ if (!hasResumeSessionId && !options.continueSession) return;
96
+ const field = hasResumeSessionId ? 'options.resumeSessionId' : 'options.continueSession';
97
+ throw contractError({
98
+ code: 'invalid-field',
99
+ field,
100
+ exitCode: 2,
101
+ message:
102
+ 'Pi CLI does not support resume/continue session control; fail closed and start a fresh run instead.',
103
+ });
104
+ }
105
+
106
+ function collectWarnings(options: BuildProviderCommandOptions): WarningMetadata[] {
107
+ const features = optionFeatures(options);
108
+ const warnings: WarningMetadata[] = [];
109
+
110
+ if (options.jsonSchema) {
111
+ warnings.push(
112
+ warning(
113
+ 'pi',
114
+ 'pi-jsonschema',
115
+ 'Pi CLI does not support provider-native JSON schema; appending schema instructions to the prompt.'
116
+ )
117
+ );
118
+ }
119
+ if (features.supportsJsonMode === false) {
120
+ warnings.push(
121
+ warning('pi', 'pi-json-mode', 'Pi CLI does not advertise --mode json; continuing anyway.')
122
+ );
123
+ }
124
+ return warnings;
125
+ }
126
+
127
+ function buildCommand(context: string, options: BuildProviderCommandOptions = {}): CommandSpec {
128
+ failClosedUnsupportedSessionControl(options);
129
+ const finalContext = options.jsonSchema
130
+ ? appendJsonSchemaPrompt(context, options.jsonSchema)
131
+ : context;
132
+ const args: string[] = [];
133
+
134
+ addRequiredArgs(args, options);
135
+ addOptionalArgs(args, options);
136
+ args.push(finalContext);
137
+
138
+ return commandSpec({
139
+ binary: 'pi',
140
+ args,
141
+ env: {},
142
+ ...(options.cwd === undefined ? {} : { cwd: options.cwd }),
143
+ warnings: collectWarnings(options),
144
+ });
145
+ }
146
+
147
+ function createPiState(): ProviderParserState {
148
+ return {
149
+ ...createParserState('pi'),
150
+ lastAssistantText: '',
151
+ lastAssistantThinking: '',
152
+ };
153
+ }
154
+
155
+ function assistantSnapshot(message: Record<string, unknown>): { text: string; thinking: string } {
156
+ if (getString(message, 'role') !== 'assistant') return { text: '', thinking: '' };
157
+
158
+ let text = '';
159
+ let thinking = '';
160
+ for (const item of getArray(message, 'content')) {
161
+ if (!isRecord(item)) continue;
162
+ const type = getString(item, 'type');
163
+ if (type === 'text') {
164
+ text += getString(item, 'text') ?? '';
165
+ } else if (type === 'thinking') {
166
+ thinking += getString(item, 'thinking') ?? '';
167
+ }
168
+ }
169
+
170
+ return { text, thinking };
171
+ }
172
+
173
+ function snapshotDelta(previous: string, current: string): string | null {
174
+ if (!current) return null;
175
+ if (previous === current) return null;
176
+ if (current.startsWith(previous)) return current.slice(previous.length) || null;
177
+ return current;
178
+ }
179
+
180
+ function emitAssistantSnapshot(
181
+ message: Record<string, unknown>,
182
+ state: ProviderParserState
183
+ ): readonly OutputEvent[] {
184
+ const snapshot = assistantSnapshot(message);
185
+ const events: OutputEvent[] = [];
186
+
187
+ const textDelta = snapshotDelta(state.lastAssistantText ?? '', snapshot.text);
188
+ if (textDelta) events.push({ type: 'text', text: textDelta });
189
+ state.lastAssistantText = snapshot.text;
190
+
191
+ const thinkingDelta = snapshotDelta(state.lastAssistantThinking ?? '', snapshot.thinking);
192
+ if (thinkingDelta) events.push({ type: 'thinking', text: thinkingDelta });
193
+ state.lastAssistantThinking = snapshot.thinking;
194
+
195
+ return events;
196
+ }
197
+
198
+ function parseAssistantEvent(
199
+ event: Record<string, unknown>,
200
+ state: ProviderParserState
201
+ ): OutputEvent | null {
202
+ const type = getString(event, 'type');
203
+ if (type === 'text_delta') {
204
+ const delta = getString(event, 'delta');
205
+ if (!delta) return null;
206
+ state.lastAssistantText += delta;
207
+ return { type: 'text', text: delta };
208
+ }
209
+ if (type === 'thinking_delta') {
210
+ const delta = getString(event, 'delta');
211
+ if (!delta) return null;
212
+ state.lastAssistantThinking += delta;
213
+ return { type: 'thinking', text: delta };
214
+ }
215
+ return null;
216
+ }
217
+
218
+ function parseToolExecutionStart(
219
+ event: Record<string, unknown>,
220
+ state: ProviderParserState
221
+ ): OutputEvent {
222
+ const toolId = getOptionalString(event, 'toolCallId');
223
+ state.lastToolId = toolId;
224
+ return {
225
+ type: 'tool_call',
226
+ toolName: getOptionalString(event, 'toolName'),
227
+ toolId,
228
+ input: event.args ?? {},
229
+ };
230
+ }
231
+
232
+ function parseToolExecutionUpdate(
233
+ event: Record<string, unknown>,
234
+ state: ProviderParserState
235
+ ): OutputEvent | null {
236
+ const toolId = getOptionalString(event, 'toolCallId') ?? state.lastToolId;
237
+ if (toolId !== undefined) state.lastToolId = toolId;
238
+ if (!Object.prototype.hasOwnProperty.call(event, 'partialResult')) return null;
239
+ return {
240
+ type: 'tool_result',
241
+ toolId,
242
+ content: event.partialResult,
243
+ isError: false,
244
+ };
245
+ }
246
+
247
+ function parseToolExecutionEnd(
248
+ event: Record<string, unknown>,
249
+ state: ProviderParserState
250
+ ): OutputEvent {
251
+ const toolId = getOptionalString(event, 'toolCallId') ?? state.lastToolId;
252
+ return {
253
+ type: 'tool_result',
254
+ toolId,
255
+ content: event.result ?? '',
256
+ isError: getBoolean(event, 'isError') ?? false,
257
+ };
258
+ }
259
+
260
+ function parseTurnEnd(
261
+ event: Record<string, unknown>,
262
+ state: ProviderParserState
263
+ ): readonly OutputEvent[] {
264
+ const message = getRecord(event, 'message');
265
+ const events: OutputEvent[] = [];
266
+ if (message !== null) {
267
+ events.push(...emitAssistantSnapshot(message, state));
268
+ }
269
+
270
+ const usage = message ? getRecord(message, 'usage') ?? {} : {};
271
+ const stopReason = message ? getString(message, 'stopReason') : null;
272
+ const errorMessage = message ? getString(message, 'errorMessage') : null;
273
+ const snapshot = message ? assistantSnapshot(message) : { text: '', thinking: '' };
274
+ const success = stopReason !== 'error' && stopReason !== 'aborted' && !errorMessage;
275
+ events.push({
276
+ type: 'result',
277
+ success,
278
+ result: success ? snapshot.text || null : null,
279
+ error: success ? null : (errorMessage ?? stopReason ?? 'Pi turn failed'),
280
+ inputTokens: getNumber(usage, 'input') ?? 0,
281
+ outputTokens: getNumber(usage, 'output') ?? 0,
282
+ cacheReadInputTokens: getNumber(usage, 'cacheRead') ?? 0,
283
+ cacheCreationInputTokens: getNumber(usage, 'cacheWrite') ?? 0,
284
+ cost: getRecord(usage, 'cost') ?? null,
285
+ modelUsage: usage,
286
+ });
287
+ return events;
288
+ }
289
+
290
+ function parseMessageEvent(
291
+ type: string,
292
+ event: Record<string, unknown>,
293
+ state: ProviderParserState
294
+ ): readonly OutputEvent[] | OutputEvent | null | undefined {
295
+ if (type === 'message_start') {
296
+ const message = getRecord(event, 'message');
297
+ if (message !== null && getString(message, 'role') === 'assistant') {
298
+ state.lastAssistantText = '';
299
+ state.lastAssistantThinking = '';
300
+ }
301
+ return null;
302
+ }
303
+
304
+ if (type === 'message_update') {
305
+ const assistantMessageEvent = getRecord(event, 'assistantMessageEvent');
306
+ if (assistantMessageEvent !== null) {
307
+ const assistantEvent = parseAssistantEvent(assistantMessageEvent, state);
308
+ if (assistantEvent !== null) return assistantEvent;
309
+ }
310
+ const message = getRecord(event, 'message');
311
+ return message === null ? null : emitAssistantSnapshot(message, state);
312
+ }
313
+
314
+ if (type !== 'message_end') return undefined;
315
+ const message = getRecord(event, 'message');
316
+ return message === null ? null : emitAssistantSnapshot(message, state);
317
+ }
318
+
319
+ function parseToolEvent(
320
+ type: string,
321
+ event: Record<string, unknown>,
322
+ state: ProviderParserState
323
+ ): OutputEvent | null | undefined {
324
+ if (type === 'tool_execution_start') return parseToolExecutionStart(event, state);
325
+ if (type === 'tool_execution_update') return parseToolExecutionUpdate(event, state);
326
+ if (type === 'tool_execution_end') return parseToolExecutionEnd(event, state);
327
+ return undefined;
328
+ }
329
+
330
+ function parseEvent(
331
+ line: string,
332
+ state: ProviderParserState
333
+ ): readonly OutputEvent[] | OutputEvent | null {
334
+ const parsed = tryParseJson(line);
335
+ if (!isRecord(parsed)) return null;
336
+
337
+ const type = getString(parsed, 'type');
338
+ if (type === null || IGNORED_EVENT_TYPES.has(type)) return null;
339
+
340
+ const messageEvent = parseMessageEvent(type, parsed, state);
341
+ if (messageEvent !== undefined) return messageEvent;
342
+
343
+ const toolEvent = parseToolEvent(type, parsed, state);
344
+ if (toolEvent !== undefined) return toolEvent;
345
+
346
+ if (type === 'turn_end') return parseTurnEnd(parsed, state);
347
+ return null;
348
+ }
349
+
350
+ function resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec {
351
+ return resolveModelSpecWithConfig({
352
+ mapping: LEVEL_MAPPING,
353
+ defaultLevel: 'level2',
354
+ level,
355
+ overrides,
356
+ validateModelId,
357
+ });
358
+ }
359
+
360
+ function validateModelId(modelId: string | null | undefined): string | null | undefined {
361
+ if (modelId === undefined || modelId === null) return modelId;
362
+ if (typeof modelId !== 'string') {
363
+ throw new Error(`Invalid model "${unknownToMessage(modelId)}" for provider "pi".`);
364
+ }
365
+ return modelId;
366
+ }
367
+
368
+ function classifyError(error: unknown): ErrorClassification {
369
+ return classifyBaseProviderError(
370
+ error,
371
+ [
372
+ /\brate(?:[_ -]?limit| limited)\b/i,
373
+ /\bquota\b/i,
374
+ /\bresource[_ -]?exhausted\b/i,
375
+ /\btemporar(?:y|ily)\b/i,
376
+ /\boverloaded\b/i,
377
+ /\bservice unavailable\b/i,
378
+ ],
379
+ [
380
+ /\b(cancelled|canceled|aborted|interrupted)\b/i,
381
+ /\brun\s*\/login\b/i,
382
+ /\bmissing api key\b/i,
383
+ /\bno valid authentication\b/i,
384
+ /\bunknown option\b/i,
385
+ /\bfailed to load\b/i,
386
+ /\bcannot find module\b/i,
387
+ /\bno such file or directory\b/i,
388
+ ]
389
+ );
390
+ }
391
+
392
+ export const piAdapter: ProviderAdapter = {
393
+ id: 'pi',
394
+ displayName: 'Pi',
395
+ binary: 'pi',
396
+ adapterVersion: '1',
397
+ credentialEnvKeys: [],
398
+ modelCatalog: MODEL_CATALOG,
399
+ levelMapping: LEVEL_MAPPING,
400
+ defaultLevel: 'level2',
401
+ defaultMaxLevel: 'level3',
402
+ defaultMinLevel: 'level1',
403
+ detectCliFeatures,
404
+ buildCommand,
405
+ parseEvent,
406
+ createParserState: createPiState,
407
+ resolveModelSpec,
408
+ validateModelId,
409
+ classifyError,
410
+ };
@@ -0,0 +1,47 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+
5
+ function settingsFilePath(): string {
6
+ return process.env.ZEROSHOT_SETTINGS_FILE || join(homedir(), '.zeroshot', 'settings.json');
7
+ }
8
+
9
+ export function readConfiguredClaudeCommand(): string {
10
+ if (process.env.ZEROSHOT_CLAUDE_COMMAND?.trim()) {
11
+ return process.env.ZEROSHOT_CLAUDE_COMMAND;
12
+ }
13
+
14
+ const settingsPath = settingsFilePath();
15
+ if (!existsSync(settingsPath)) return 'claude';
16
+
17
+ try {
18
+ const settings: unknown = JSON.parse(readFileSync(settingsPath, 'utf8'));
19
+ if (
20
+ settings !== null &&
21
+ typeof settings === 'object' &&
22
+ 'claudeCommand' in settings &&
23
+ typeof settings.claudeCommand === 'string' &&
24
+ settings.claudeCommand.trim()
25
+ ) {
26
+ return settings.claudeCommand;
27
+ }
28
+ } catch {
29
+ return 'claude';
30
+ }
31
+
32
+ return 'claude';
33
+ }
34
+
35
+ export function resolveClaudeCommand(): {
36
+ readonly command: string;
37
+ readonly args: readonly string[];
38
+ } {
39
+ const parts = readConfiguredClaudeCommand()
40
+ .trim()
41
+ .split(/\s+/)
42
+ .filter((part) => part.length > 0);
43
+ return {
44
+ command: parts[0] ?? 'claude',
45
+ args: parts.slice(1),
46
+ };
47
+ }
@@ -15,8 +15,9 @@ import {
15
15
  schemaMode,
16
16
  type RequestData,
17
17
  } from './contract-support';
18
- import { detectRuntimeProviderCliFeatures } from './single-agent-runtime';
19
- import { getNumber, getRecord, getString, isRecord, unknownToMessage } from './json';
18
+ import { extractErrorStatus } from './errors';
19
+ import { probeRuntimeProviderCli } from './single-agent-runtime';
20
+ import { getString, isRecord, unknownToMessage } from './json';
20
21
  import type { ErrorClassification } from './types';
21
22
  import type { ProcessRunner } from './process-runner';
22
23
 
@@ -42,8 +43,11 @@ function runBuildCommand(request: RequestData): ContractEnvelope {
42
43
  function runProbe(request: RequestData): ContractEnvelope {
43
44
  const adapter = adapterForProvider(request.provider);
44
45
  const helpText = typeof request.raw.helpText === 'string' ? request.raw.helpText : null;
46
+ const runtimeProbe = helpText === null ? probeRuntimeProviderCli(adapter.id) : null;
45
47
  const capabilities =
46
- helpText === null ? detectRuntimeProviderCliFeatures(adapter.id) : adapter.detectCliFeatures(helpText);
48
+ runtimeProbe === null
49
+ ? adapter.detectCliFeatures(helpText)
50
+ : runtimeProbe.capabilities;
47
51
  return successEnvelope({
48
52
  command: request.command ?? 'probe',
49
53
  adapter,
@@ -56,6 +60,9 @@ function runProbe(request: RequestData): ContractEnvelope {
56
60
  },
57
61
  contractVersion: providerExecutableSchemaVersion,
58
62
  adapterVersion: adapter.adapterVersion,
63
+ available: runtimeProbe?.available ?? true,
64
+ helpText: runtimeProbe?.helpText ?? helpText,
65
+ versionText: runtimeProbe?.versionText ?? null,
59
66
  capabilities,
60
67
  credentials: adapter.credentialEnvKeys.map((key) => ({
61
68
  key,
@@ -65,24 +72,14 @@ function runProbe(request: RequestData): ContractEnvelope {
65
72
  });
66
73
  }
67
74
 
68
- function statusFromError(error: unknown): number | null {
69
- if (!isRecord(error)) return null;
70
- const directStatus = getNumber(error, 'status') ?? getNumber(error, 'statusCode');
71
- if (directStatus !== null) return directStatus;
72
-
73
- const response = getRecord(error, 'response');
74
- if (response === null) return null;
75
- return getNumber(response, 'status') ?? getNumber(response, 'statusCode');
76
- }
77
-
78
75
  function categoryForClassification(classification: ErrorClassification, error: unknown): string {
79
- const status = statusFromError(error);
76
+ const status = extractErrorStatus(error);
80
77
  if (status === 401 || status === 403) return 'auth';
81
78
  if (status === 429) return 'rate-limit';
82
79
  const message = unknownToMessage(error);
83
80
  if (/auth|api[_ -]?key|unauthorized|forbidden|permission/i.test(message)) return 'auth';
84
81
  if (/rate|429|quota|resource_exhausted/i.test(message)) return 'rate-limit';
85
- if (/schema|json|parse|format/i.test(message)) return 'schema';
82
+ if (/schema|json|parse|format|malformed/i.test(message)) return 'schema';
86
83
  return classification.retryable ? 'retryable' : 'permanent';
87
84
  }
88
85
 
@@ -90,10 +87,10 @@ function errorEvidence(error: unknown): ContractEvidence {
90
87
  const evidence: Record<string, unknown> = {
91
88
  message: unknownToMessage(error),
92
89
  };
90
+ const status = extractErrorStatus(error);
91
+ if (status !== null) evidence.status = status;
93
92
  if (isRecord(error)) {
94
- const status = statusFromError(error);
95
93
  const code = getString(error, 'code');
96
- if (status !== null) evidence.status = status;
97
94
  if (code !== null) evidence.code = code;
98
95
  }
99
96
  return evidence;
@@ -1,10 +1,13 @@
1
1
  import { unlink } from 'node:fs/promises';
2
+ import { buildAcpPrompt } from './adapters/acp';
3
+ import { runAcpStdioPrompt } from './acp-stdio-runner';
2
4
  import { commandRedactions } from './contract-env';
3
5
  import { successEnvelope, type ContractEnvelope } from './contract-envelope';
4
6
  import { buildCommandSpec, optionalNumber, schemaMode, type RequestData } from './contract-support';
5
7
  import { providerFailureClassification } from './invoke-evidence';
6
8
  import { parseOutputEvents } from './contract-parse';
7
9
  import { unknownToMessage } from './json';
10
+ import { getProviderRegistryEntry } from './provider-registry';
8
11
  import type { CommandSpec } from './types';
9
12
  import type { ProcessResult, ProcessRunner, ProcessRunnerOptions } from './process-runner';
10
13
 
@@ -51,10 +54,16 @@ export async function runInvoke(
51
54
  request: RequestData,
52
55
  runner: ProcessRunner
53
56
  ): Promise<ContractEnvelope> {
54
- const { adapter, commandSpec, options } = buildCommandSpec(request);
57
+ const { adapter, commandSpec, context, options } = buildCommandSpec(request);
55
58
  const timeoutMs = optionalNumber(request.raw, 'timeoutMs');
56
59
  const runnerOptions = timeoutMs === undefined ? {} : { timeoutMs };
57
- const { result, cleanup } = await runAndCleanup(commandSpec, runner, runnerOptions);
60
+ const invokeSpec = getProviderRegistryEntry(adapter.id).invoke;
61
+ const invokeRunner =
62
+ invokeSpec.lane === 'acp-stdio'
63
+ ? (spec: CommandSpec, invokeOptions?: ProcessRunnerOptions): Promise<ProcessResult> =>
64
+ runAcpStdioPrompt(adapter.id, spec, buildAcpPrompt(context, options), invokeOptions)
65
+ : runner;
66
+ const { result, cleanup } = await runAndCleanup(commandSpec, invokeRunner, runnerOptions);
58
67
  const parsed = parseOutputEvents(adapter, {
59
68
  chunk: [result.stdout, result.stderr].join('\n'),
60
69
  sources: [
@@ -62,7 +71,7 @@ export async function runInvoke(
62
71
  { name: 'stderr', value: result.stderr },
63
72
  ],
64
73
  });
65
- const classification = providerFailureClassification(adapter, result);
74
+ const classification = providerFailureClassification(adapter, result, parsed.events);
66
75
  return successEnvelope({
67
76
  command: request.command ?? 'invoke',
68
77
  adapter,