@the-open-engine/zeroshot 6.37.0 → 6.38.1
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/lib/setup-scanner-worker.js +2 -9
- package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/claude-recovery.js +17 -3
- package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/codex.js +3 -1
- package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
- package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/index.js +7 -0
- package/lib/agent-cli-provider/adapters/index.js.map +1 -1
- package/lib/agent-cli-provider/adapters/pi.d.ts.map +1 -1
- package/lib/agent-cli-provider/adapters/pi.js +86 -97
- package/lib/agent-cli-provider/adapters/pi.js.map +1 -1
- package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -1
- package/lib/agent-cli-provider/contract-parse.js +5 -1
- package/lib/agent-cli-provider/contract-parse.js.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -1
- package/lib/agent-cli-provider/invoke-evidence.js +8 -1
- package/lib/agent-cli-provider/invoke-evidence.js.map +1 -1
- package/lib/agent-cli-provider/pi/credentials.d.ts +2 -0
- package/lib/agent-cli-provider/pi/credentials.d.ts.map +1 -0
- package/lib/agent-cli-provider/pi/credentials.js +51 -0
- package/lib/agent-cli-provider/pi/credentials.js.map +1 -0
- package/lib/agent-cli-provider/pi/events.d.ts +5 -0
- package/lib/agent-cli-provider/pi/events.d.ts.map +1 -0
- package/lib/agent-cli-provider/pi/events.js +124 -0
- package/lib/agent-cli-provider/pi/events.js.map +1 -0
- package/lib/agent-cli-provider/pi/protocol-state.d.ts +6 -0
- package/lib/agent-cli-provider/pi/protocol-state.d.ts.map +1 -0
- package/lib/agent-cli-provider/pi/protocol-state.js +138 -0
- package/lib/agent-cli-provider/pi/protocol-state.js.map +1 -0
- package/lib/agent-cli-provider/pi/release.d.ts +4 -0
- package/lib/agent-cli-provider/pi/release.d.ts.map +1 -0
- package/lib/agent-cli-provider/pi/release.js +7 -0
- package/lib/agent-cli-provider/pi/release.js.map +1 -0
- package/lib/agent-cli-provider/probe-availability.d.ts +5 -0
- package/lib/agent-cli-provider/probe-availability.d.ts.map +1 -0
- package/lib/agent-cli-provider/probe-availability.js +11 -0
- package/lib/agent-cli-provider/probe-availability.js.map +1 -0
- package/lib/agent-cli-provider/provider-registry.d.ts +13 -9
- package/lib/agent-cli-provider/provider-registry.d.ts.map +1 -1
- package/lib/agent-cli-provider/provider-registry.js +42 -8
- package/lib/agent-cli-provider/provider-registry.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +15 -5
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/agent-cli-provider/types.d.ts +3 -0
- package/lib/agent-cli-provider/types.d.ts.map +1 -1
- package/lib/agent-cli-provider/types.js.map +1 -1
- package/lib/cluster-worker/engine-adapter.js +9 -1
- package/lib/docker-config.js +6 -1
- package/lib/provider-credential-path.js +36 -0
- package/lib/start-cluster.js +8 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/agent/agent-task-executor.js +79 -20
- package/src/agent/output-extraction.js +115 -0
- package/src/agent/pi-terminal-lifecycle.js +242 -0
- package/src/agent/provider-control-plane.js +26 -0
- package/src/agent/provider-terminal-failure.js +49 -24
- package/src/agent-cli-provider/adapters/claude-recovery.ts +15 -2
- package/src/agent-cli-provider/adapters/codex.ts +3 -1
- package/src/agent-cli-provider/adapters/index.ts +6 -0
- package/src/agent-cli-provider/adapters/pi.ts +105 -125
- package/src/agent-cli-provider/contract-parse.ts +8 -1
- package/src/agent-cli-provider/invoke-evidence.ts +11 -1
- package/src/agent-cli-provider/pi/credentials.ts +47 -0
- package/src/agent-cli-provider/pi/events.ts +148 -0
- package/src/agent-cli-provider/pi/protocol-state.ts +163 -0
- package/src/agent-cli-provider/pi/release.ts +4 -0
- package/src/agent-cli-provider/probe-availability.ts +19 -0
- package/src/agent-cli-provider/provider-registry.ts +48 -9
- package/src/agent-cli-provider/single-agent-runtime.ts +27 -6
- package/src/agent-cli-provider/types.ts +3 -0
- package/src/config-validator.js +1 -3
- package/src/isolation-manager.js +43 -29
- package/src/orchestrator.js +33 -1
- package/task-lib/watcher-output-runtime.js +16 -5
|
@@ -3,12 +3,23 @@
|
|
|
3
3
|
const { getProvider } = require('../providers');
|
|
4
4
|
const { isCriticalAgent } = require('./critical-agent-policy');
|
|
5
5
|
const { extractCliFailure } = require('./output-extraction');
|
|
6
|
+
const { getPiTokenUsage, redactPiFailureForControlPlane } = require('./pi-terminal-lifecycle');
|
|
7
|
+
const { parseProviderEvent, providerFailureFields } = require('./provider-control-plane');
|
|
8
|
+
|
|
9
|
+
const AUTHENTICATION_FAILURE_PATTERNS = [
|
|
10
|
+
/invalid[_ -]?api[_ -]?key/i,
|
|
11
|
+
/api[_ -]?key.*invalid/i,
|
|
12
|
+
/(?:missing|no) api key/i,
|
|
13
|
+
/run\s*\/login/i,
|
|
14
|
+
/unauthori[sz]ed/i,
|
|
15
|
+
/forbidden|authentication|permission denied/i,
|
|
16
|
+
];
|
|
6
17
|
|
|
7
18
|
function categoryForProviderFailure(error, classification) {
|
|
8
19
|
const isPermanent = classification.retryable === false;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
if (isPermanent && AUTHENTICATION_FAILURE_PATTERNS.some((pattern) => pattern.test(error))) {
|
|
21
|
+
return 'authentication';
|
|
22
|
+
}
|
|
12
23
|
|
|
13
24
|
const quotaPattern = /(?:insufficient[_ -]?quota|quota exceeded|resource_exhausted)/i;
|
|
14
25
|
if (isPermanent && quotaPattern.test(error)) return 'quota';
|
|
@@ -30,6 +41,12 @@ function classifyProviderFailure(providerName, error) {
|
|
|
30
41
|
};
|
|
31
42
|
}
|
|
32
43
|
|
|
44
|
+
function providerFailureEvent(providerName) {
|
|
45
|
+
if (providerName === 'codex') return 'turn.failed';
|
|
46
|
+
if (providerName === 'pi') return 'agent_settled';
|
|
47
|
+
return 'terminal_error';
|
|
48
|
+
}
|
|
49
|
+
|
|
33
50
|
function extractProviderFailure(output, providerName) {
|
|
34
51
|
const cliError = extractCliFailure(output, providerName);
|
|
35
52
|
if (!cliError) return null;
|
|
@@ -39,41 +56,47 @@ function extractProviderFailure(output, providerName) {
|
|
|
39
56
|
return {
|
|
40
57
|
error: `Provider ${cliError.provider} failed (${category}; ${classification.kind})`,
|
|
41
58
|
provider: cliError.provider,
|
|
42
|
-
event: cliError.provider
|
|
59
|
+
event: providerFailureEvent(cliError.provider),
|
|
43
60
|
category,
|
|
44
61
|
classification,
|
|
45
62
|
diagnostic: cliError.diagnostic,
|
|
46
63
|
};
|
|
47
64
|
}
|
|
48
65
|
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
state.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const parsed = JSON.parse(content);
|
|
57
|
-
if (typeof parsed?.type === 'string') eventType = parsed.type;
|
|
58
|
-
} catch {
|
|
59
|
-
// extractProviderFailure already proved a supported terminal envelope.
|
|
66
|
+
function extractProviderCompletionFailure(output, providerName, state = {}) {
|
|
67
|
+
if (providerName !== 'pi') return extractProviderFailure(output, providerName);
|
|
68
|
+
if (state.providerFailure) return state.providerFailure;
|
|
69
|
+
if (state.piProtocolPrefixOmitted) return extractProviderFailure('', 'pi');
|
|
70
|
+
if (!state.piProtocolObserved) return extractProviderFailure(output, 'pi');
|
|
71
|
+
if (!state.piProtocolSettled || !state.piLatestAssistantObserved) {
|
|
72
|
+
return extractProviderFailure('', 'pi');
|
|
60
73
|
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function redactedFailureEnvelope(failure, providerName, eventType) {
|
|
61
78
|
return JSON.stringify({
|
|
62
79
|
type: eventType,
|
|
63
80
|
...(providerName === 'claude' ? { is_error: true } : {}),
|
|
64
81
|
...(providerName === 'gemini' ? { status: 'error', severity: 'error' } : {}),
|
|
65
|
-
|
|
66
|
-
zeroshot_failure: {
|
|
67
|
-
provider: failure.provider,
|
|
68
|
-
event: failure.event,
|
|
69
|
-
category: failure.category,
|
|
70
|
-
kind: failure.classification.kind,
|
|
71
|
-
retryable: failure.classification.retryable,
|
|
72
|
-
diagnostic: failure.diagnostic,
|
|
73
|
-
},
|
|
82
|
+
...providerFailureFields(failure),
|
|
74
83
|
});
|
|
75
84
|
}
|
|
76
85
|
|
|
86
|
+
function redactTerminalFailureForControlPlane(state, providerName, content) {
|
|
87
|
+
if (providerName === 'pi') {
|
|
88
|
+
return redactPiFailureForControlPlane(state, content, extractProviderFailure);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const failure = extractProviderFailure(content, providerName);
|
|
92
|
+
if (!failure) return content;
|
|
93
|
+
|
|
94
|
+
state.providerFailure = failure;
|
|
95
|
+
const parsed = parseProviderEvent(content);
|
|
96
|
+
const eventType = typeof parsed?.type === 'string' ? parsed.type : 'provider.failure';
|
|
97
|
+
return redactedFailureEnvelope(failure, providerName, eventType);
|
|
98
|
+
}
|
|
99
|
+
|
|
77
100
|
function decorateError(error, failure) {
|
|
78
101
|
if (!failure) return error;
|
|
79
102
|
error.provider = failure.provider || null;
|
|
@@ -176,7 +199,9 @@ function buildFinalFailureInfo({
|
|
|
176
199
|
module.exports = {
|
|
177
200
|
buildFinalFailureInfo,
|
|
178
201
|
decorateError,
|
|
202
|
+
extractProviderCompletionFailure,
|
|
179
203
|
extractProviderFailure,
|
|
204
|
+
getPiTokenUsage,
|
|
180
205
|
publishCriticalFailure,
|
|
181
206
|
receiptFields,
|
|
182
207
|
redactTerminalFailureForControlPlane,
|
|
@@ -25,8 +25,9 @@ export function detectProviderStreamingModeError(
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export function detectProviderFatalError(provider: string, line: unknown): string | null {
|
|
28
|
-
if (
|
|
29
|
-
return
|
|
28
|
+
if (isClaudeProvider(provider)) return detectClaudeFatalError(line);
|
|
29
|
+
if (provider.toLowerCase() === 'pi') return detectPiFatalError(line);
|
|
30
|
+
return null;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
export function recoverProviderStructuredOutput(
|
|
@@ -84,6 +85,18 @@ function detectClaudeFatalError(line: unknown): string | null {
|
|
|
84
85
|
return null;
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
function detectPiFatalError(line: unknown): string | null {
|
|
89
|
+
if (typeof line !== 'string') return null;
|
|
90
|
+
const message = line.trim();
|
|
91
|
+
if (/\bno api key found\b/i.test(message)) {
|
|
92
|
+
return 'Pi authentication required: run /login';
|
|
93
|
+
}
|
|
94
|
+
if (/\bmodel\b.*\bnot found\b/i.test(message)) {
|
|
95
|
+
return 'Pi model not found: run pi --list-models';
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
function findSessionJsonlPath(sessionId: string): string | null {
|
|
88
101
|
const claudeDir = process.env.CLAUDE_CONFIG_DIR || join(homedir(), '.claude');
|
|
89
102
|
const projectsDir = join(claudeDir, 'projects');
|
|
@@ -222,6 +222,8 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
|
|
|
222
222
|
: null;
|
|
223
223
|
addWebSearchArgs(args, options);
|
|
224
224
|
if (resumeSessionId) {
|
|
225
|
+
// Codex 0.147 parses --sandbox on `exec`, not on the `resume` subcommand.
|
|
226
|
+
addAutoApproveArgs(args, options);
|
|
225
227
|
args.push('resume');
|
|
226
228
|
}
|
|
227
229
|
|
|
@@ -229,8 +231,8 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
|
|
|
229
231
|
addModelArgs(args, options);
|
|
230
232
|
if (!resumeSessionId) {
|
|
231
233
|
addCwdArgs(args, options);
|
|
234
|
+
addAutoApproveArgs(args, options);
|
|
232
235
|
}
|
|
233
|
-
addAutoApproveArgs(args, options);
|
|
234
236
|
addSkipGitArgs(args, options);
|
|
235
237
|
const finalContext = applySchemaArgs(args, cleanup, context, options);
|
|
236
238
|
|
|
@@ -90,6 +90,12 @@ export function parseProviderChunk(
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
const finalEvent = adapter.finishParsing?.(state);
|
|
94
|
+
if (finalEvent) {
|
|
95
|
+
if (isOutputEventArray(finalEvent)) events.push(...finalEvent);
|
|
96
|
+
else events.push(finalEvent);
|
|
97
|
+
}
|
|
98
|
+
|
|
93
99
|
return events;
|
|
94
100
|
}
|
|
95
101
|
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { appendJsonSchemaPrompt } from '../schema';
|
|
2
2
|
import { contractError } from '../contract-errors';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
parseToolExecutionStart,
|
|
8
|
-
parseToolExecutionUpdate,
|
|
9
|
-
parseTurnEndResult,
|
|
10
|
-
resetAssistantSnapshot,
|
|
11
|
-
} from '../assistant-stream';
|
|
12
|
-
import { getRecord, getString, isRecord, tryParseJson, unknownToMessage } from '../json';
|
|
3
|
+
import { unknownToMessage } from '../json';
|
|
4
|
+
import { PI_CREDENTIAL_ENV_KEYS } from '../pi/credentials';
|
|
5
|
+
import { createPiParserState, finishPiParsing, parsePiEvent } from '../pi/events';
|
|
6
|
+
import { PI_SUPPORTED_VERSION } from '../pi/release';
|
|
13
7
|
import {
|
|
14
8
|
type BuildProviderCommandOptions,
|
|
15
9
|
type CommandSpec,
|
|
@@ -18,17 +12,15 @@ import {
|
|
|
18
12
|
type LevelOverrides,
|
|
19
13
|
type ModelCatalogEntry,
|
|
20
14
|
type ModelLevel,
|
|
21
|
-
type OutputEvent,
|
|
22
15
|
type PiCliFeatures,
|
|
23
16
|
type ProviderAdapter,
|
|
24
|
-
type ProviderParserState,
|
|
25
17
|
type ResolvedModelSpec,
|
|
26
18
|
type WarningMetadata,
|
|
27
19
|
} from '../types';
|
|
28
20
|
import {
|
|
29
21
|
classifyBaseProviderError,
|
|
30
22
|
commandSpec,
|
|
31
|
-
|
|
23
|
+
isCliVersionAtLeast,
|
|
32
24
|
optionFeatures,
|
|
33
25
|
resolveModelSpecWithConfig,
|
|
34
26
|
warning,
|
|
@@ -37,45 +29,104 @@ import {
|
|
|
37
29
|
const MODEL_CATALOG: Readonly<Record<string, ModelCatalogEntry>> = {};
|
|
38
30
|
|
|
39
31
|
const LEVEL_MAPPING: Readonly<Record<ModelLevel, LevelModelSpec>> = {
|
|
40
|
-
level1: { rank: 1, model: null },
|
|
41
|
-
level2: { rank: 2, model: null },
|
|
42
|
-
level3: { rank: 3, model: null },
|
|
32
|
+
level1: { rank: 1, model: null, reasoningEffort: 'low' },
|
|
33
|
+
level2: { rank: 2, model: null, reasoningEffort: 'medium' },
|
|
34
|
+
level3: { rank: 3, model: null, reasoningEffort: 'high' },
|
|
43
35
|
};
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'auto_retry_start',
|
|
54
|
-
'auto_retry_end',
|
|
55
|
-
]);
|
|
37
|
+
function supportsFlag(
|
|
38
|
+
help: string,
|
|
39
|
+
assumeCurrent: boolean,
|
|
40
|
+
versionMatches: boolean,
|
|
41
|
+
pattern: RegExp
|
|
42
|
+
): boolean {
|
|
43
|
+
return versionMatches && (assumeCurrent || pattern.test(help));
|
|
44
|
+
}
|
|
56
45
|
|
|
57
|
-
function detectCliFeatures(helpText?: string | null): PiCliFeatures {
|
|
46
|
+
function detectCliFeatures(helpText?: string | null, versionText?: string | null): PiCliFeatures {
|
|
58
47
|
const help = helpText ?? '';
|
|
59
|
-
const
|
|
48
|
+
const versionMatches = isCliVersionAtLeast(versionText, PI_SUPPORTED_VERSION);
|
|
49
|
+
const assumeCurrent = !help && versionMatches;
|
|
60
50
|
return {
|
|
61
51
|
provider: 'pi',
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
versionMatches,
|
|
53
|
+
supportsJsonMode:
|
|
54
|
+
supportsFlag(help, assumeCurrent, versionMatches, /--mode\b/) &&
|
|
55
|
+
(assumeCurrent || /\bjson\b/.test(help)),
|
|
56
|
+
supportsModel: supportsFlag(help, assumeCurrent, versionMatches, /--model\b/),
|
|
57
|
+
supportsThinking: supportsFlag(help, assumeCurrent, versionMatches, /--thinking\b/),
|
|
58
|
+
supportsNoSession: supportsFlag(help, assumeCurrent, versionMatches, /--no-session\b/),
|
|
59
|
+
supportsNoExtensions: supportsFlag(help, assumeCurrent, versionMatches, /--no-extensions\b/),
|
|
60
|
+
supportsNoSkills: supportsFlag(help, assumeCurrent, versionMatches, /--no-skills\b/),
|
|
61
|
+
supportsNoPromptTemplates: supportsFlag(
|
|
62
|
+
help,
|
|
63
|
+
assumeCurrent,
|
|
64
|
+
versionMatches,
|
|
65
|
+
/--no-prompt-templates\b/
|
|
66
|
+
),
|
|
67
|
+
supportsNoContextFiles: supportsFlag(
|
|
68
|
+
help,
|
|
69
|
+
assumeCurrent,
|
|
70
|
+
versionMatches,
|
|
71
|
+
/--no-context-files\b/
|
|
72
|
+
),
|
|
73
|
+
supportsNoApprove: supportsFlag(help, assumeCurrent, versionMatches, /--no-approve\b/),
|
|
74
|
+
unknown: !help || !(versionText ?? '').trim(),
|
|
71
75
|
};
|
|
72
76
|
}
|
|
73
77
|
|
|
78
|
+
function assertRequiredControls(options: BuildProviderCommandOptions): void {
|
|
79
|
+
const features = optionFeatures(options);
|
|
80
|
+
if (features.versionMatches !== true) {
|
|
81
|
+
throw contractError({
|
|
82
|
+
code: 'invalid-field',
|
|
83
|
+
field: 'options.cliFeatures.versionMatches',
|
|
84
|
+
exitCode: 2,
|
|
85
|
+
message:
|
|
86
|
+
`Pi ${PI_SUPPORTED_VERSION} or newer is required for the agent_settled JSON protocol. ` +
|
|
87
|
+
`Upgrade with: npm install -g --ignore-scripts ` +
|
|
88
|
+
`@earendil-works/pi-coding-agent@${PI_SUPPORTED_VERSION}`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const required = [
|
|
92
|
+
['supportsJsonMode', '--mode json'],
|
|
93
|
+
['supportsNoSession', '--no-session'],
|
|
94
|
+
['supportsNoSkills', '--no-skills'],
|
|
95
|
+
['supportsNoPromptTemplates', '--no-prompt-templates'],
|
|
96
|
+
['supportsNoContextFiles', '--no-context-files'],
|
|
97
|
+
['supportsNoApprove', '--no-approve'],
|
|
98
|
+
] as const;
|
|
99
|
+
for (const [field, flag] of required) {
|
|
100
|
+
if (features[field] !== false) continue;
|
|
101
|
+
throw contractError({
|
|
102
|
+
code: 'invalid-field',
|
|
103
|
+
field: `options.cliFeatures.${field}`,
|
|
104
|
+
exitCode: 2,
|
|
105
|
+
message: `Pi ${PI_SUPPORTED_VERSION}+ support for ${flag} is required for Zeroshot execution.`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (options.modelSpec?.model && features.supportsModel === false) {
|
|
109
|
+
throw contractError({
|
|
110
|
+
code: 'invalid-field',
|
|
111
|
+
field: 'options.cliFeatures.supportsModel',
|
|
112
|
+
exitCode: 2,
|
|
113
|
+
message: `Pi ${PI_SUPPORTED_VERSION}+ support for --model is required for explicit model selection.`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
if (options.modelSpec?.reasoningEffort && features.supportsThinking === false) {
|
|
117
|
+
throw contractError({
|
|
118
|
+
code: 'invalid-field',
|
|
119
|
+
field: 'options.cliFeatures.supportsThinking',
|
|
120
|
+
exitCode: 2,
|
|
121
|
+
message: `Pi ${PI_SUPPORTED_VERSION}+ support for --thinking is required for reasoning effort.`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
74
126
|
function addRequiredArgs(args: string[], options: BuildProviderCommandOptions): void {
|
|
75
127
|
const features = optionFeatures(options);
|
|
76
128
|
if (features.supportsJsonMode !== false) args.push('--mode', 'json');
|
|
77
129
|
if (features.supportsNoSession !== false) args.push('--no-session');
|
|
78
|
-
if (features.supportsNoExtensions !== false) args.push('--no-extensions');
|
|
79
130
|
if (features.supportsNoSkills !== false) args.push('--no-skills');
|
|
80
131
|
if (features.supportsNoPromptTemplates !== false) args.push('--no-prompt-templates');
|
|
81
132
|
if (features.supportsNoContextFiles !== false) args.push('--no-context-files');
|
|
@@ -87,6 +138,9 @@ function addOptionalArgs(args: string[], options: BuildProviderCommandOptions):
|
|
|
87
138
|
if (options.modelSpec?.model && features.supportsModel !== false) {
|
|
88
139
|
args.push('--model', options.modelSpec.model);
|
|
89
140
|
}
|
|
141
|
+
if (options.modelSpec?.reasoningEffort && features.supportsThinking !== false) {
|
|
142
|
+
args.push('--thinking', options.modelSpec.reasoningEffort);
|
|
143
|
+
}
|
|
90
144
|
}
|
|
91
145
|
|
|
92
146
|
function failClosedUnsupportedSessionControl(options: BuildProviderCommandOptions): void {
|
|
@@ -98,12 +152,11 @@ function failClosedUnsupportedSessionControl(options: BuildProviderCommandOption
|
|
|
98
152
|
field,
|
|
99
153
|
exitCode: 2,
|
|
100
154
|
message:
|
|
101
|
-
'Pi CLI does not support resume/continue session control
|
|
155
|
+
'Pi CLI does not support resume/continue session control with a task-owned identity boundary.',
|
|
102
156
|
});
|
|
103
157
|
}
|
|
104
158
|
|
|
105
159
|
function collectWarnings(options: BuildProviderCommandOptions): WarningMetadata[] {
|
|
106
|
-
const features = optionFeatures(options);
|
|
107
160
|
const warnings: WarningMetadata[] = [];
|
|
108
161
|
|
|
109
162
|
if (options.jsonSchema) {
|
|
@@ -115,16 +168,12 @@ function collectWarnings(options: BuildProviderCommandOptions): WarningMetadata[
|
|
|
115
168
|
)
|
|
116
169
|
);
|
|
117
170
|
}
|
|
118
|
-
if (features.supportsJsonMode === false) {
|
|
119
|
-
warnings.push(
|
|
120
|
-
warning('pi', 'pi-json-mode', 'Pi CLI does not advertise --mode json; continuing anyway.')
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
171
|
return warnings;
|
|
124
172
|
}
|
|
125
173
|
|
|
126
174
|
function buildCommand(context: string, options: BuildProviderCommandOptions = {}): CommandSpec {
|
|
127
175
|
failClosedUnsupportedSessionControl(options);
|
|
176
|
+
assertRequiredControls(options);
|
|
128
177
|
const finalContext = options.jsonSchema
|
|
129
178
|
? appendJsonSchemaPrompt(context, options.jsonSchema)
|
|
130
179
|
: context;
|
|
@@ -132,7 +181,9 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
|
|
|
132
181
|
|
|
133
182
|
addRequiredArgs(args, options);
|
|
134
183
|
addOptionalArgs(args, options);
|
|
135
|
-
|
|
184
|
+
// Pi has no `--` end-of-options sentinel and interprets leading `-`/`@` positional values as
|
|
185
|
+
// flags or file arguments. A leading space keeps arbitrary Zeroshot context in prompt mode.
|
|
186
|
+
args.push(` ${finalContext}`);
|
|
136
187
|
|
|
137
188
|
return commandSpec({
|
|
138
189
|
binary: 'pi',
|
|
@@ -143,80 +194,6 @@ function buildCommand(context: string, options: BuildProviderCommandOptions = {}
|
|
|
143
194
|
});
|
|
144
195
|
}
|
|
145
196
|
|
|
146
|
-
function createPiState(): ProviderParserState {
|
|
147
|
-
return {
|
|
148
|
-
...createParserState('pi'),
|
|
149
|
-
lastAssistantText: '',
|
|
150
|
-
lastAssistantThinking: '',
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function parsePiTurnEnd(
|
|
155
|
-
event: Record<string, unknown>,
|
|
156
|
-
state: ProviderParserState
|
|
157
|
-
): readonly OutputEvent[] {
|
|
158
|
-
const message = getRecord(event, 'message');
|
|
159
|
-
const events = message === null ? [] : [...emitAssistantSnapshot(message, state)];
|
|
160
|
-
events.push(parseTurnEndResult(event, { failureMessage: 'Pi turn failed' }));
|
|
161
|
-
return events;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function parseMessageEvent(
|
|
165
|
-
type: string,
|
|
166
|
-
event: Record<string, unknown>,
|
|
167
|
-
state: ProviderParserState
|
|
168
|
-
): readonly OutputEvent[] | OutputEvent | null | undefined {
|
|
169
|
-
if (type === 'message_start') {
|
|
170
|
-
resetAssistantSnapshot(getRecord(event, 'message'), state);
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (type === 'message_update') {
|
|
175
|
-
const assistantMessageEvent = getRecord(event, 'assistantMessageEvent');
|
|
176
|
-
if (assistantMessageEvent !== null) {
|
|
177
|
-
const assistantEvent = parseAssistantDelta(assistantMessageEvent, state);
|
|
178
|
-
if (assistantEvent !== null) return assistantEvent;
|
|
179
|
-
}
|
|
180
|
-
const message = getRecord(event, 'message');
|
|
181
|
-
return message === null ? null : emitAssistantSnapshot(message, state);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (type !== 'message_end') return undefined;
|
|
185
|
-
const message = getRecord(event, 'message');
|
|
186
|
-
return message === null ? null : emitAssistantSnapshot(message, state);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function parseToolEvent(
|
|
190
|
-
type: string,
|
|
191
|
-
event: Record<string, unknown>,
|
|
192
|
-
state: ProviderParserState
|
|
193
|
-
): OutputEvent | null | undefined {
|
|
194
|
-
if (type === 'tool_execution_start') return parseToolExecutionStart(event, state);
|
|
195
|
-
if (type === 'tool_execution_update') return parseToolExecutionUpdate(event, state);
|
|
196
|
-
if (type === 'tool_execution_end') return parseToolExecutionEnd(event, state);
|
|
197
|
-
return undefined;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function parseEvent(
|
|
201
|
-
line: string,
|
|
202
|
-
state: ProviderParserState
|
|
203
|
-
): readonly OutputEvent[] | OutputEvent | null {
|
|
204
|
-
const parsed = tryParseJson(line);
|
|
205
|
-
if (!isRecord(parsed)) return null;
|
|
206
|
-
|
|
207
|
-
const type = getString(parsed, 'type');
|
|
208
|
-
if (type === null || IGNORED_EVENT_TYPES.has(type)) return null;
|
|
209
|
-
|
|
210
|
-
const messageEvent = parseMessageEvent(type, parsed, state);
|
|
211
|
-
if (messageEvent !== undefined) return messageEvent;
|
|
212
|
-
|
|
213
|
-
const toolEvent = parseToolEvent(type, parsed, state);
|
|
214
|
-
if (toolEvent !== undefined) return toolEvent;
|
|
215
|
-
|
|
216
|
-
if (type === 'turn_end') return parsePiTurnEnd(parsed, state);
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
197
|
function resolveModelSpec(level: ModelLevel, overrides?: LevelOverrides): ResolvedModelSpec {
|
|
221
198
|
return resolveModelSpecWithConfig({
|
|
222
199
|
mapping: LEVEL_MAPPING,
|
|
@@ -250,7 +227,9 @@ function classifyError(error: unknown): ErrorClassification {
|
|
|
250
227
|
/\b(cancelled|canceled|aborted|interrupted)\b/i,
|
|
251
228
|
/\brun\s*\/login\b/i,
|
|
252
229
|
/\bmissing api key\b/i,
|
|
230
|
+
/\bno api key found\b/i,
|
|
253
231
|
/\bno valid authentication\b/i,
|
|
232
|
+
/\bmodel\b.*\bnot found\b/i,
|
|
254
233
|
/\bunknown option\b/i,
|
|
255
234
|
/\bfailed to load\b/i,
|
|
256
235
|
/\bcannot find module\b/i,
|
|
@@ -263,8 +242,8 @@ export const piAdapter: ProviderAdapter = {
|
|
|
263
242
|
id: 'pi',
|
|
264
243
|
displayName: 'Pi',
|
|
265
244
|
binary: 'pi',
|
|
266
|
-
adapterVersion: '
|
|
267
|
-
credentialEnvKeys:
|
|
245
|
+
adapterVersion: '2',
|
|
246
|
+
credentialEnvKeys: PI_CREDENTIAL_ENV_KEYS,
|
|
268
247
|
modelCatalog: MODEL_CATALOG,
|
|
269
248
|
levelMapping: LEVEL_MAPPING,
|
|
270
249
|
defaultLevel: 'level2',
|
|
@@ -272,8 +251,9 @@ export const piAdapter: ProviderAdapter = {
|
|
|
272
251
|
defaultMinLevel: 'level1',
|
|
273
252
|
detectCliFeatures,
|
|
274
253
|
buildCommand,
|
|
275
|
-
parseEvent,
|
|
276
|
-
|
|
254
|
+
parseEvent: parsePiEvent,
|
|
255
|
+
finishParsing: finishPiParsing,
|
|
256
|
+
createParserState: createPiParserState,
|
|
277
257
|
resolveModelSpec,
|
|
278
258
|
validateModelId,
|
|
279
259
|
classifyError,
|
|
@@ -62,9 +62,16 @@ export function parseOutputEvents(
|
|
|
62
62
|
}
|
|
63
63
|
): { readonly events: readonly OutputEvent[]; readonly diagnostics: readonly ContractEvidence[] } {
|
|
64
64
|
const diagnostics = [...collectParseDiagnostics(fragments.sources)];
|
|
65
|
+
const protocolChunk =
|
|
66
|
+
adapter.id === 'pi'
|
|
67
|
+
? ((
|
|
68
|
+
fragments.sources.find((source) => source.name === 'stdout') ??
|
|
69
|
+
fragments.sources.find((source) => source.name === 'jsonl')
|
|
70
|
+
)?.value ?? '')
|
|
71
|
+
: fragments.chunk;
|
|
65
72
|
try {
|
|
66
73
|
return {
|
|
67
|
-
events: parseProviderChunk(adapter.id,
|
|
74
|
+
events: parseProviderChunk(adapter.id, protocolChunk),
|
|
68
75
|
diagnostics,
|
|
69
76
|
};
|
|
70
77
|
} catch (error) {
|
|
@@ -20,6 +20,16 @@ function classifyParsedFailure(
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function classifyMissingPiSettlement(
|
|
24
|
+
adapter: ProviderAdapter,
|
|
25
|
+
events: readonly OutputEvent[]
|
|
26
|
+
): ErrorClassification | null {
|
|
27
|
+
if (adapter.id !== 'pi' || events.some((event) => event.type === 'result')) return null;
|
|
28
|
+
return classifyProviderError('pi', {
|
|
29
|
+
message: 'Pi JSON stream ended before agent_settled.',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
23
33
|
export function providerFailureClassification(
|
|
24
34
|
adapter: ProviderAdapter,
|
|
25
35
|
result: ProcessResult,
|
|
@@ -31,7 +41,7 @@ export function providerFailureClassification(
|
|
|
31
41
|
});
|
|
32
42
|
}
|
|
33
43
|
if (result.exitCode === 0 && result.signal === null) {
|
|
34
|
-
return classifyParsedFailure(adapter, events);
|
|
44
|
+
return classifyParsedFailure(adapter, events) ?? classifyMissingPiSettlement(adapter, events);
|
|
35
45
|
}
|
|
36
46
|
return classifyProviderError(adapter.id, {
|
|
37
47
|
message: result.stderr || result.stdout || `Provider exited with code ${result.exitCode ?? ''}`,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const PI_CREDENTIAL_ENV_KEYS = [
|
|
2
|
+
'ANTHROPIC_AUTH_TOKEN',
|
|
3
|
+
'ANTHROPIC_OAUTH_TOKEN',
|
|
4
|
+
'ANTHROPIC_API_KEY',
|
|
5
|
+
'ANT_LING_API_KEY',
|
|
6
|
+
'QWEN_TOKEN_PLAN_API_KEY',
|
|
7
|
+
'QWEN_TOKEN_PLAN_CN_API_KEY',
|
|
8
|
+
'OPENAI_API_KEY',
|
|
9
|
+
'AZURE_OPENAI_API_KEY',
|
|
10
|
+
'NVIDIA_API_KEY',
|
|
11
|
+
'DEEPSEEK_API_KEY',
|
|
12
|
+
'GEMINI_API_KEY',
|
|
13
|
+
'GOOGLE_CLOUD_API_KEY',
|
|
14
|
+
'GROQ_API_KEY',
|
|
15
|
+
'CEREBRAS_API_KEY',
|
|
16
|
+
'XAI_API_KEY',
|
|
17
|
+
'RADIUS_API_KEY',
|
|
18
|
+
'OPENROUTER_API_KEY',
|
|
19
|
+
'AI_GATEWAY_API_KEY',
|
|
20
|
+
'ZAI_API_KEY',
|
|
21
|
+
'ZAI_CODING_CN_API_KEY',
|
|
22
|
+
'MISTRAL_API_KEY',
|
|
23
|
+
'MINIMAX_API_KEY',
|
|
24
|
+
'MINIMAX_CN_API_KEY',
|
|
25
|
+
'MOONSHOT_API_KEY',
|
|
26
|
+
'HF_TOKEN',
|
|
27
|
+
'FIREWORKS_API_KEY',
|
|
28
|
+
'TOGETHER_API_KEY',
|
|
29
|
+
'BASETEN_API_KEY',
|
|
30
|
+
'OPENCODE_API_KEY',
|
|
31
|
+
'KIMI_API_KEY',
|
|
32
|
+
'CLOUDFLARE_API_KEY',
|
|
33
|
+
'XIAOMI_API_KEY',
|
|
34
|
+
'XIAOMI_TOKEN_PLAN_CN_API_KEY',
|
|
35
|
+
'XIAOMI_TOKEN_PLAN_AMS_API_KEY',
|
|
36
|
+
'XIAOMI_TOKEN_PLAN_SGP_API_KEY',
|
|
37
|
+
'COPILOT_GITHUB_TOKEN',
|
|
38
|
+
'AWS_ACCESS_KEY_ID',
|
|
39
|
+
'AWS_SECRET_ACCESS_KEY',
|
|
40
|
+
'AWS_SESSION_TOKEN',
|
|
41
|
+
'AWS_BEARER_TOKEN_BEDROCK',
|
|
42
|
+
'AWS_PROFILE',
|
|
43
|
+
'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI',
|
|
44
|
+
'AWS_CONTAINER_CREDENTIALS_FULL_URI',
|
|
45
|
+
'AWS_WEB_IDENTITY_TOKEN_FILE',
|
|
46
|
+
'GOOGLE_APPLICATION_CREDENTIALS',
|
|
47
|
+
] as const;
|