@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.
- package/cli/commands/providers.js +11 -5
- package/cli/index.js +83 -17
- package/cli/lib/first-run.js +14 -3
- package/docker/zeroshot-cluster/Dockerfile +11 -1
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts +4 -0
- package/lib/agent-cli-provider/acp-stdio-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js +259 -0
- package/lib/agent-cli-provider/acp-stdio-runner.js.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts +23 -0
- package/lib/agent-cli-provider/adapters/acp.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/acp.js +373 -0
- package/lib/agent-cli-provider/adapters/acp.js.map +1 -0
- package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude.js +2 -29
- package/lib/agent-cli-provider/adapters/claude.js.map +1 -1
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js +154 -0
- package/lib/agent-cli-provider/adapters/copilot-parser.js.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/copilot.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/copilot.js +176 -0
- package/lib/agent-cli-provider/adapters/copilot.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts +6 -0
- package/lib/agent-cli-provider/adapters/gateway.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/gateway.js +161 -0
- package/lib/agent-cli-provider/adapters/gateway.js.map +1 -0
- package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/gemini.js +9 -2
- package/lib/agent-cli-provider/adapters/gemini.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +6 -34
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/opencode.js +5 -1
- package/lib/agent-cli-provider/adapters/opencode.js.map +1 -1
- package/lib/agent-cli-provider/adapters/pi.d.ts +3 -0
- package/lib/agent-cli-provider/adapters/pi.d.ts.map +1 -0
- package/lib/agent-cli-provider/adapters/pi.js +331 -0
- package/lib/agent-cli-provider/adapters/pi.js.map +1 -0
- package/lib/agent-cli-provider/claude-command.d.ts +6 -0
- package/lib/agent-cli-provider/claude-command.d.ts.map +1 -0
- package/lib/agent-cli-provider/claude-command.js +43 -0
- package/lib/agent-cli-provider/claude-command.js.map +1 -0
- package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-actions.js +13 -17
- package/lib/agent-cli-provider/contract-actions.js.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-invoke.js +10 -3
- package/lib/agent-cli-provider/contract-invoke.js.map +1 -1
- package/lib/agent-cli-provider/contract-options.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-options.js +57 -2
- package/lib/agent-cli-provider/contract-options.js.map +1 -1
- package/lib/agent-cli-provider/contract-support.d.ts +1 -0
- package/lib/agent-cli-provider/contract-support.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-support.js +25 -3
- package/lib/agent-cli-provider/contract-support.js.map +1 -1
- package/lib/agent-cli-provider/errors.d.ts +1 -0
- package/lib/agent-cli-provider/errors.d.ts.map +1 -1
- package/lib/agent-cli-provider/errors.js +14 -5
- package/lib/agent-cli-provider/errors.js.map +1 -1
- package/lib/agent-cli-provider/gateway-client.d.ts +32 -0
- package/lib/agent-cli-provider/gateway-client.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-client.js +135 -0
- package/lib/agent-cli-provider/gateway-client.js.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts +9 -0
- package/lib/agent-cli-provider/gateway-runner.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-runner.js +294 -0
- package/lib/agent-cli-provider/gateway-runner.js.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts +14 -0
- package/lib/agent-cli-provider/gateway-tools.d.ts.map +1 -0
- package/lib/agent-cli-provider/gateway-tools.js +495 -0
- package/lib/agent-cli-provider/gateway-tools.js.map +1 -0
- package/lib/agent-cli-provider/index.d.ts +3 -2
- package/lib/agent-cli-provider/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/index.js +14 -1
- package/lib/agent-cli-provider/index.js.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.d.ts +2 -2
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.js +17 -3
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -1
- package/lib/agent-cli-provider/provider-registry.d.ts +437 -0
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -0
- package/lib/agent-cli-provider/provider-registry.js +411 -0
- package/lib/agent-cli-provider/provider-registry.js.map +1 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts +7 -0
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +139 -19
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +106 -4
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/detached-startup.js +4 -2
- package/lib/docker-config.js +24 -13
- package/lib/provider-defaults.js +15 -4
- package/lib/provider-detection.js +2 -0
- package/lib/provider-names.js +32 -12
- package/lib/repo-settings.js +15 -1
- package/lib/run-mode.js +18 -6
- package/lib/run-plan.js +32 -0
- package/lib/settings/claude-auth.js +3 -6
- package/lib/settings.js +17 -2
- package/lib/setup-apply.js +300 -0
- package/lib/setup-journal.js +109 -0
- package/lib/setup-plan.js +406 -0
- package/lib/setup-undo.js +88 -0
- package/lib/start-cluster.js +34 -9
- package/lib/stream-json-parser.js +7 -5
- package/package.json +33 -3
- package/scripts/live-provider-smoke.js +221 -0
- package/src/agent/agent-lifecycle.js +7 -4
- package/src/agent/agent-quality-gate-schema.js +2 -2
- package/src/agent/agent-task-executor.js +41 -2
- package/src/agent-cli-provider/acp-stdio-runner.ts +314 -0
- package/src/agent-cli-provider/adapters/acp.ts +493 -0
- package/src/agent-cli-provider/adapters/claude.ts +2 -32
- package/src/agent-cli-provider/adapters/copilot-parser.ts +166 -0
- package/src/agent-cli-provider/adapters/copilot.ts +231 -0
- package/src/agent-cli-provider/adapters/gateway.ts +186 -0
- package/src/agent-cli-provider/adapters/gemini.ts +9 -2
- package/src/agent-cli-provider/adapters/index.ts +5 -36
- package/src/agent-cli-provider/adapters/opencode.ts +4 -1
- package/src/agent-cli-provider/adapters/pi.ts +410 -0
- package/src/agent-cli-provider/claude-command.ts +47 -0
- package/src/agent-cli-provider/contract-actions.ts +14 -17
- package/src/agent-cli-provider/contract-invoke.ts +12 -3
- package/src/agent-cli-provider/contract-options.ts +72 -3
- package/src/agent-cli-provider/contract-support.ts +30 -3
- package/src/agent-cli-provider/errors.ts +14 -4
- package/src/agent-cli-provider/gateway-client.ts +170 -0
- package/src/agent-cli-provider/gateway-runner.ts +353 -0
- package/src/agent-cli-provider/gateway-tools.ts +616 -0
- package/src/agent-cli-provider/index.ts +28 -0
- package/src/agent-cli-provider/invoke-evidence.ts +24 -3
- package/src/agent-cli-provider/provider-registry.ts +515 -0
- package/src/agent-cli-provider/single-agent-runtime.ts +192 -18
- package/src/agent-cli-provider/types.ts +145 -4
- package/src/config-validator.js +33 -15
- package/src/isolation-manager.js +124 -19
- package/src/ledger.js +3 -0
- package/src/message-bus.js +12 -2
- package/src/orchestrator.js +37 -17
- package/src/preflight.js +83 -30
- package/src/providers/capabilities.js +15 -42
- package/src/providers/index.js +52 -72
- package/src/worktree-claude-config.js +17 -0
- package/task-lib/commands/run.js +1 -0
- package/task-lib/provider-helper-runtime.js +11 -0
- 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.
|
|
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 {
|
|
19
|
-
import {
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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,
|