praxis-agent 0.55.2 → 0.55.3
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/dist/cli/protocol.d.ts +29 -6
- package/dist/cli/protocol.js +147 -39
- package/dist/cli-runtime.js +46 -9
- package/package.json +1 -1
package/dist/cli/protocol.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ModelDocument, ModelDocumentMediaType, ModelImage, ModelImageMediaType, ModelUsage, RuntimeEventSink } from '../core/runtime.js';
|
|
1
|
+
import type { ModelDocument, ModelDocumentMediaType, ModelImage, ModelImageMediaType, ModelTerminalReason, ModelUsage, RuntimeEventSink } from '../core/runtime.js';
|
|
2
2
|
export type CliInputFormat = 'text' | 'stream-json';
|
|
3
3
|
export type CliOutputFormat = 'text' | 'json' | 'stream-json';
|
|
4
4
|
export type CliPermissionMode = 'acceptEdits' | 'auto' | 'bypassPermissions' | 'manual' | 'dontAsk' | 'plan' | 'default';
|
|
@@ -225,8 +225,25 @@ export interface ProtocolResult {
|
|
|
225
225
|
*/
|
|
226
226
|
modelCostUsd?: Readonly<Record<string, number>>;
|
|
227
227
|
}
|
|
228
|
-
export
|
|
229
|
-
|
|
228
|
+
export interface ProtocolSuccessProjectionContext {
|
|
229
|
+
localCommand?: boolean;
|
|
230
|
+
stopReason?: ModelTerminalReason;
|
|
231
|
+
ttftMs?: number;
|
|
232
|
+
ttftStreamMs?: number;
|
|
233
|
+
timeToRequestMs?: number;
|
|
234
|
+
}
|
|
235
|
+
export interface ProtocolErrorProjectionContext {
|
|
236
|
+
providerApiError?: boolean;
|
|
237
|
+
apiErrorStatus?: number | null;
|
|
238
|
+
}
|
|
239
|
+
export interface ProtocolTimingProjection {
|
|
240
|
+
ttftMs?: number;
|
|
241
|
+
ttftStreamMs?: number;
|
|
242
|
+
timeToRequestMs?: number;
|
|
243
|
+
}
|
|
244
|
+
export declare function projectProtocolTimings(startedAt: number, requestAt?: number, outputAt?: number): ProtocolTimingProjection;
|
|
245
|
+
export declare function createSuccessResult(result: ProtocolResult, info: CliRuntimeInfo, startedAt: number, modelTurns: number, context?: ProtocolSuccessProjectionContext): Record<string, unknown>;
|
|
246
|
+
export declare function createErrorResult(message: string, sessionId: string, startedAt: number, modelTurns: number, context?: ProtocolErrorProjectionContext): Record<string, unknown>;
|
|
230
247
|
/**
|
|
231
248
|
* Returns the argument text of a headless `/color` prompt, or undefined when
|
|
232
249
|
* the prompt is not a bare `/color` command. A trailing space matches with an
|
|
@@ -259,6 +276,11 @@ export declare class StreamJsonOutput {
|
|
|
259
276
|
private activeThinkingIndex;
|
|
260
277
|
private nextContentIndex;
|
|
261
278
|
private modelTurns;
|
|
279
|
+
private projectionStartedAt;
|
|
280
|
+
private firstRequestAt;
|
|
281
|
+
private firstOutputAt;
|
|
282
|
+
private turnHasRealContent;
|
|
283
|
+
private pendingFailureMessage;
|
|
262
284
|
private compacting;
|
|
263
285
|
private sessionState;
|
|
264
286
|
private readonly emitSessionStateEvents;
|
|
@@ -272,7 +294,7 @@ export declare class StreamJsonOutput {
|
|
|
272
294
|
private readonly includePartialMessages;
|
|
273
295
|
private readonly includeHookEvents;
|
|
274
296
|
private write;
|
|
275
|
-
init(): void;
|
|
297
|
+
init(startedAt?: number): void;
|
|
276
298
|
replayUser(message: StreamUserMessage['message']): void;
|
|
277
299
|
/**
|
|
278
300
|
* Emits the synthetic assistant message for a provider-free local command
|
|
@@ -285,13 +307,14 @@ export declare class StreamJsonOutput {
|
|
|
285
307
|
request: Record<string, unknown>;
|
|
286
308
|
}): void;
|
|
287
309
|
readonly sink: RuntimeEventSink;
|
|
288
|
-
result(result: ProtocolResult, startedAt: number): void;
|
|
310
|
+
result(result: ProtocolResult, startedAt: number, context?: ProtocolSuccessProjectionContext): void;
|
|
289
311
|
promptSuggestion(suggestion: string): void;
|
|
290
|
-
error(message: string, startedAt: number): void;
|
|
312
|
+
error(message: string, startedAt: number, context?: ProtocolErrorProjectionContext): void;
|
|
291
313
|
private onEvent;
|
|
292
314
|
private writeSessionState;
|
|
293
315
|
private writeTerminalIdle;
|
|
294
316
|
private ensureTurn;
|
|
317
|
+
private observeOutput;
|
|
295
318
|
private discardTurn;
|
|
296
319
|
private startTurn;
|
|
297
320
|
private writePartialMessageStart;
|
package/dist/cli/protocol.js
CHANGED
|
@@ -1,32 +1,74 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
-
export function
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const usage = {
|
|
6
|
-
input_tokens: result.usage.inputTokens,
|
|
7
|
-
output_tokens: result.usage.outputTokens,
|
|
8
|
-
...(result.usage.cacheReadInputTokens === undefined
|
|
2
|
+
export function projectProtocolTimings(startedAt, requestAt, outputAt) {
|
|
3
|
+
return {
|
|
4
|
+
...(requestAt === undefined
|
|
9
5
|
? {}
|
|
10
|
-
: {
|
|
11
|
-
...(
|
|
6
|
+
: { timeToRequestMs: requestAt - startedAt }),
|
|
7
|
+
...(outputAt === undefined
|
|
12
8
|
? {}
|
|
13
9
|
: {
|
|
14
|
-
|
|
10
|
+
ttftMs: outputAt - startedAt,
|
|
11
|
+
ttftStreamMs: outputAt - startedAt,
|
|
15
12
|
}),
|
|
16
13
|
};
|
|
14
|
+
}
|
|
15
|
+
function protocolUsage(usage) {
|
|
16
|
+
return {
|
|
17
|
+
input_tokens: usage.inputTokens,
|
|
18
|
+
cache_creation_input_tokens: usage.cacheCreationInputTokens ?? 0,
|
|
19
|
+
cache_read_input_tokens: usage.cacheReadInputTokens ?? 0,
|
|
20
|
+
output_tokens: usage.outputTokens,
|
|
21
|
+
server_tool_use: {
|
|
22
|
+
web_search_requests: usage.webSearchRequests ?? 0,
|
|
23
|
+
web_fetch_requests: 0,
|
|
24
|
+
},
|
|
25
|
+
service_tier: 'standard',
|
|
26
|
+
cache_creation: {
|
|
27
|
+
ephemeral_1h_input_tokens: 0,
|
|
28
|
+
ephemeral_5m_input_tokens: 0,
|
|
29
|
+
},
|
|
30
|
+
inference_geo: '',
|
|
31
|
+
iterations: [],
|
|
32
|
+
speed: 'standard',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function normalizeApiError(message, status) {
|
|
36
|
+
const body = message.replace(/^API Error:\s*\d+\s*/u, '');
|
|
37
|
+
return `API Error: ${status} ${body}`;
|
|
38
|
+
}
|
|
39
|
+
export function createSuccessResult(result, info, startedAt, modelTurns, context = {}) {
|
|
40
|
+
const duration = Date.now() - startedAt;
|
|
41
|
+
const localCommand = context.localCommand === true;
|
|
42
|
+
const modelUsage = result.modelUsage ?? (localCommand ? {} : { [info.model]: result.usage });
|
|
43
|
+
const usage = protocolUsage(result.usage);
|
|
17
44
|
return {
|
|
18
45
|
type: 'result',
|
|
19
46
|
subtype: 'success',
|
|
20
47
|
is_error: false,
|
|
21
48
|
duration_ms: duration,
|
|
22
|
-
|
|
23
|
-
?
|
|
24
|
-
:
|
|
25
|
-
|
|
49
|
+
...(localCommand
|
|
50
|
+
? {}
|
|
51
|
+
: {
|
|
52
|
+
api_error_status: null,
|
|
53
|
+
...(context.ttftMs === undefined ? {} : { ttft_ms: context.ttftMs }),
|
|
54
|
+
...(context.ttftStreamMs === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { ttft_stream_ms: context.ttftStreamMs }),
|
|
57
|
+
...(context.timeToRequestMs === undefined
|
|
58
|
+
? {}
|
|
59
|
+
: { time_to_request_ms: context.timeToRequestMs }),
|
|
60
|
+
}),
|
|
61
|
+
duration_api_ms: localCommand
|
|
62
|
+
? 0
|
|
63
|
+
: result.durationApiMs === undefined
|
|
64
|
+
? 0
|
|
65
|
+
: Math.round(result.durationApiMs),
|
|
66
|
+
num_turns: localCommand ? 0 : modelTurns,
|
|
26
67
|
result: result.text,
|
|
27
|
-
stop_reason: null,
|
|
68
|
+
stop_reason: localCommand ? null : (context.stopReason ?? null),
|
|
69
|
+
...(localCommand ? {} : { terminal_reason: 'completed' }),
|
|
28
70
|
session_id: result.sessionId,
|
|
29
|
-
total_cost_usd: result.costUsd ??
|
|
71
|
+
total_cost_usd: result.costUsd ?? 0,
|
|
30
72
|
usage,
|
|
31
73
|
modelUsage: Object.fromEntries(Object.entries(modelUsage).map(([model, modelUsage]) => [
|
|
32
74
|
model,
|
|
@@ -35,11 +77,9 @@ export function createSuccessResult(result, info, startedAt, modelTurns) {
|
|
|
35
77
|
outputTokens: modelUsage.outputTokens,
|
|
36
78
|
cacheReadInputTokens: modelUsage.cacheReadInputTokens ?? 0,
|
|
37
79
|
cacheCreationInputTokens: modelUsage.cacheCreationInputTokens ?? 0,
|
|
80
|
+
webSearchRequests: modelUsage.webSearchRequests ?? 0,
|
|
38
81
|
costUSD: result.modelCostUsd?.[model] ??
|
|
39
|
-
(model === info.model ? (result.costUsd ??
|
|
40
|
-
...(modelUsage.webSearchRequests === undefined
|
|
41
|
-
? {}
|
|
42
|
-
: { webSearchRequests: modelUsage.webSearchRequests }),
|
|
82
|
+
(model === info.model ? (result.costUsd ?? 0) : 0),
|
|
43
83
|
contextWindow: modelUsage.contextWindow ??
|
|
44
84
|
(model === info.model ? (info.contextWindowTokens ?? 0) : 0),
|
|
45
85
|
maxOutputTokens: modelUsage.maxOutputTokens ??
|
|
@@ -63,24 +103,45 @@ function errorResultSubtype(message) {
|
|
|
63
103
|
return 'error_max_structured_output_retries';
|
|
64
104
|
return 'error_during_execution';
|
|
65
105
|
}
|
|
66
|
-
export function createErrorResult(message, sessionId, startedAt, modelTurns) {
|
|
106
|
+
export function createErrorResult(message, sessionId, startedAt, modelTurns, context = {}) {
|
|
67
107
|
const duration = Date.now() - startedAt;
|
|
108
|
+
const providerApiError = context.providerApiError === true;
|
|
109
|
+
const apiErrorStatus = context.apiErrorStatus ?? null;
|
|
110
|
+
const normalizedMessage = providerApiError && typeof context.apiErrorStatus === 'number'
|
|
111
|
+
? normalizeApiError(message, context.apiErrorStatus)
|
|
112
|
+
: message;
|
|
113
|
+
if (providerApiError) {
|
|
114
|
+
return {
|
|
115
|
+
type: 'result',
|
|
116
|
+
subtype: 'success',
|
|
117
|
+
is_error: true,
|
|
118
|
+
api_error_status: apiErrorStatus,
|
|
119
|
+
duration_ms: duration,
|
|
120
|
+
duration_api_ms: 0,
|
|
121
|
+
num_turns: modelTurns,
|
|
122
|
+
result: normalizedMessage,
|
|
123
|
+
stop_reason: 'stop_sequence',
|
|
124
|
+
terminal_reason: 'api_error',
|
|
125
|
+
session_id: sessionId,
|
|
126
|
+
total_cost_usd: 0,
|
|
127
|
+
usage: protocolUsage({ inputTokens: 0, outputTokens: 0 }),
|
|
128
|
+
modelUsage: {},
|
|
129
|
+
permission_denials: [],
|
|
130
|
+
fast_mode_state: 'off',
|
|
131
|
+
uuid: randomUUID(),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
68
134
|
return {
|
|
69
135
|
type: 'result',
|
|
70
136
|
subtype: errorResultSubtype(message),
|
|
71
137
|
is_error: true,
|
|
72
138
|
duration_ms: duration,
|
|
73
|
-
duration_api_ms:
|
|
139
|
+
duration_api_ms: 0,
|
|
74
140
|
num_turns: modelTurns,
|
|
75
141
|
stop_reason: null,
|
|
76
142
|
session_id: sessionId,
|
|
77
|
-
total_cost_usd:
|
|
78
|
-
usage: {
|
|
79
|
-
input_tokens: 0,
|
|
80
|
-
output_tokens: 0,
|
|
81
|
-
cache_creation_input_tokens: 0,
|
|
82
|
-
cache_read_input_tokens: 0,
|
|
83
|
-
},
|
|
143
|
+
total_cost_usd: 0,
|
|
144
|
+
usage: protocolUsage({ inputTokens: 0, outputTokens: 0 }),
|
|
84
145
|
modelUsage: {},
|
|
85
146
|
permission_denials: [],
|
|
86
147
|
errors: [message],
|
|
@@ -1710,6 +1771,11 @@ export class StreamJsonOutput {
|
|
|
1710
1771
|
activeThinkingIndex;
|
|
1711
1772
|
nextContentIndex = 0;
|
|
1712
1773
|
modelTurns = 0;
|
|
1774
|
+
projectionStartedAt;
|
|
1775
|
+
firstRequestAt;
|
|
1776
|
+
firstOutputAt;
|
|
1777
|
+
turnHasRealContent = false;
|
|
1778
|
+
pendingFailureMessage;
|
|
1713
1779
|
compacting = false;
|
|
1714
1780
|
sessionState;
|
|
1715
1781
|
emitSessionStateEvents;
|
|
@@ -1732,7 +1798,11 @@ export class StreamJsonOutput {
|
|
|
1732
1798
|
session_id: value.session_id ?? this.sessionId,
|
|
1733
1799
|
});
|
|
1734
1800
|
}
|
|
1735
|
-
init() {
|
|
1801
|
+
init(startedAt = Date.now()) {
|
|
1802
|
+
this.projectionStartedAt = startedAt;
|
|
1803
|
+
this.firstRequestAt = undefined;
|
|
1804
|
+
this.firstOutputAt = undefined;
|
|
1805
|
+
this.pendingFailureMessage = undefined;
|
|
1736
1806
|
this.writeSessionState('running');
|
|
1737
1807
|
this.write({
|
|
1738
1808
|
type: 'system',
|
|
@@ -1783,9 +1853,21 @@ export class StreamJsonOutput {
|
|
|
1783
1853
|
this.write({ type: 'control_request', ...request });
|
|
1784
1854
|
}
|
|
1785
1855
|
sink = (event) => this.onEvent(event);
|
|
1786
|
-
result(result, startedAt) {
|
|
1856
|
+
result(result, startedAt, context = {}) {
|
|
1787
1857
|
this.finishTurn();
|
|
1788
|
-
|
|
1858
|
+
const requestAt = this.firstRequestAt;
|
|
1859
|
+
const outputAt = this.firstOutputAt;
|
|
1860
|
+
const stopReason = context.stopReason ?? this.turnTerminalReason;
|
|
1861
|
+
const timing = projectProtocolTimings(this.projectionStartedAt ?? startedAt, requestAt, outputAt);
|
|
1862
|
+
this.write(createSuccessResult(result, this.info, startedAt, this.modelTurns, {
|
|
1863
|
+
...context,
|
|
1864
|
+
...(context.localCommand
|
|
1865
|
+
? {}
|
|
1866
|
+
: {
|
|
1867
|
+
...(stopReason === undefined ? {} : { stopReason }),
|
|
1868
|
+
...timing,
|
|
1869
|
+
}),
|
|
1870
|
+
}));
|
|
1789
1871
|
this.writeTerminalIdle();
|
|
1790
1872
|
this.modelTurns = 0;
|
|
1791
1873
|
}
|
|
@@ -1797,9 +1879,15 @@ export class StreamJsonOutput {
|
|
|
1797
1879
|
session_id: this.sessionId,
|
|
1798
1880
|
});
|
|
1799
1881
|
}
|
|
1800
|
-
error(message, startedAt) {
|
|
1882
|
+
error(message, startedAt, context = {}) {
|
|
1883
|
+
if (this.pendingFailureMessage !== undefined) {
|
|
1884
|
+
this.turnText =
|
|
1885
|
+
context.providerApiError && typeof context.apiErrorStatus === 'number'
|
|
1886
|
+
? normalizeApiError(message, context.apiErrorStatus)
|
|
1887
|
+
: message;
|
|
1888
|
+
}
|
|
1801
1889
|
this.finishTurn();
|
|
1802
|
-
this.write(createErrorResult(message, this.sessionId, startedAt, this.modelTurns));
|
|
1890
|
+
this.write(createErrorResult(message, this.sessionId, startedAt, this.modelTurns, context));
|
|
1803
1891
|
this.writeTerminalIdle();
|
|
1804
1892
|
this.modelTurns = 0;
|
|
1805
1893
|
}
|
|
@@ -1841,7 +1929,9 @@ export class StreamJsonOutput {
|
|
|
1841
1929
|
event.state === 'completed' ||
|
|
1842
1930
|
event.state === 'cancelled' ||
|
|
1843
1931
|
event.state === 'failed') {
|
|
1844
|
-
|
|
1932
|
+
if (event.state !== 'failed' ||
|
|
1933
|
+
this.pendingFailureMessage === undefined)
|
|
1934
|
+
this.flushAssistant();
|
|
1845
1935
|
if (event.state === 'awaiting-permission')
|
|
1846
1936
|
this.writeSessionState('requires_action');
|
|
1847
1937
|
}
|
|
@@ -1849,12 +1939,14 @@ export class StreamJsonOutput {
|
|
|
1849
1939
|
}
|
|
1850
1940
|
if (event.type === 'text-delta') {
|
|
1851
1941
|
this.ensureTurn();
|
|
1942
|
+
this.observeOutput();
|
|
1852
1943
|
this.turnText += event.delta;
|
|
1853
1944
|
this.partialText(event.delta);
|
|
1854
1945
|
return;
|
|
1855
1946
|
}
|
|
1856
1947
|
if (event.type === 'thinking-start') {
|
|
1857
1948
|
this.ensureTurn();
|
|
1949
|
+
this.observeOutput();
|
|
1858
1950
|
if (this.includePartialMessages) {
|
|
1859
1951
|
if (this.activeThinkingIndex !== undefined) {
|
|
1860
1952
|
throw new Error('Thinking content blocks cannot overlap');
|
|
@@ -1885,6 +1977,7 @@ export class StreamJsonOutput {
|
|
|
1885
1977
|
if (event.type === 'thinking-delta' ||
|
|
1886
1978
|
event.type === 'thinking-signature-delta') {
|
|
1887
1979
|
this.ensureTurn();
|
|
1980
|
+
this.observeOutput();
|
|
1888
1981
|
if (this.includePartialMessages) {
|
|
1889
1982
|
if (this.activeThinkingIndex === undefined) {
|
|
1890
1983
|
throw new Error('Thinking delta arrived without an active block');
|
|
@@ -1906,6 +1999,7 @@ export class StreamJsonOutput {
|
|
|
1906
1999
|
}
|
|
1907
2000
|
if (event.type === 'thinking-stop') {
|
|
1908
2001
|
this.ensureTurn();
|
|
2002
|
+
this.observeOutput();
|
|
1909
2003
|
this.turnThinking.push(event.block);
|
|
1910
2004
|
if (this.includePartialMessages) {
|
|
1911
2005
|
if (this.activeThinkingIndex === undefined) {
|
|
@@ -1930,6 +2024,7 @@ export class StreamJsonOutput {
|
|
|
1930
2024
|
}
|
|
1931
2025
|
if (event.type === 'tool-call') {
|
|
1932
2026
|
this.ensureTurn();
|
|
2027
|
+
this.observeOutput();
|
|
1933
2028
|
this.turnCalls.push(event.call);
|
|
1934
2029
|
if (this.includePartialMessages) {
|
|
1935
2030
|
const index = this.nextContentIndex++;
|
|
@@ -2165,13 +2260,16 @@ export class StreamJsonOutput {
|
|
|
2165
2260
|
}
|
|
2166
2261
|
if (event.type === 'failed') {
|
|
2167
2262
|
this.ensureTurn();
|
|
2168
|
-
if (this.
|
|
2263
|
+
if (!this.turnHasRealContent &&
|
|
2264
|
+
this.turnText.length === 0 &&
|
|
2169
2265
|
this.turnThinking.length === 0 &&
|
|
2170
2266
|
this.turnCalls.length === 0) {
|
|
2171
|
-
this.
|
|
2172
|
-
this.
|
|
2267
|
+
this.pendingFailureMessage = event.message;
|
|
2268
|
+
this.assistantFlushed = false;
|
|
2269
|
+
}
|
|
2270
|
+
else {
|
|
2271
|
+
this.flushAssistant();
|
|
2173
2272
|
}
|
|
2174
|
-
this.flushAssistant();
|
|
2175
2273
|
}
|
|
2176
2274
|
}
|
|
2177
2275
|
writeSessionState(state) {
|
|
@@ -2204,6 +2302,11 @@ export class StreamJsonOutput {
|
|
|
2204
2302
|
if (!this.turnActive)
|
|
2205
2303
|
this.startTurn();
|
|
2206
2304
|
}
|
|
2305
|
+
observeOutput() {
|
|
2306
|
+
this.turnHasRealContent = true;
|
|
2307
|
+
if (this.firstOutputAt === undefined)
|
|
2308
|
+
this.firstOutputAt = Date.now();
|
|
2309
|
+
}
|
|
2207
2310
|
discardTurn(reason) {
|
|
2208
2311
|
this.write({
|
|
2209
2312
|
type: 'system',
|
|
@@ -2225,6 +2328,8 @@ export class StreamJsonOutput {
|
|
|
2225
2328
|
this.nextContentIndex = 0;
|
|
2226
2329
|
this.partialEvents = [];
|
|
2227
2330
|
this.pendingPartialStop = undefined;
|
|
2331
|
+
this.turnHasRealContent = false;
|
|
2332
|
+
this.pendingFailureMessage = undefined;
|
|
2228
2333
|
}
|
|
2229
2334
|
startTurn() {
|
|
2230
2335
|
if (this.turnActive)
|
|
@@ -2243,6 +2348,9 @@ export class StreamJsonOutput {
|
|
|
2243
2348
|
this.nextContentIndex = 0;
|
|
2244
2349
|
this.partialEvents = [];
|
|
2245
2350
|
this.pendingPartialStop = undefined;
|
|
2351
|
+
this.turnHasRealContent = false;
|
|
2352
|
+
if (this.firstRequestAt === undefined)
|
|
2353
|
+
this.firstRequestAt = Date.now();
|
|
2246
2354
|
this.modelTurns += 1;
|
|
2247
2355
|
if (this.includePartialMessages) {
|
|
2248
2356
|
this.write({
|
package/dist/cli-runtime.js
CHANGED
|
@@ -19,7 +19,7 @@ import { isSessionId } from './core/session.js';
|
|
|
19
19
|
import { assertNativeDataPlane, resolveDataPlane, resolveDataPlaneRoot, resolveDataPlanePaths, } from './persistence/data-plane.js';
|
|
20
20
|
import { loadNativeContextResources, loadNativeSettings, loadNativeSharedResources, } from './persistence/native-resources.js';
|
|
21
21
|
import { resolveProjectIdentity } from './platform/project-identity.js';
|
|
22
|
-
import { AgentRunCancelledError, } from './core/runtime.js';
|
|
22
|
+
import { AgentRunCancelledError, ModelProviderError, } from './core/runtime.js';
|
|
23
23
|
import { persistTuiPermissionUpdates } from './cli/tui/permission-settings.js';
|
|
24
24
|
import { loadClaudeReleaseNotes } from './cli/tui/release-notes.js';
|
|
25
25
|
import { canonicalClaudeCostModelName, formatCostSummary, } from './cli/tui/cost-summary.js';
|
|
@@ -65,7 +65,7 @@ import { WorkspaceContext } from './application/session-worktree.js';
|
|
|
65
65
|
import { launchTmuxWorktree } from './platform/tmux-worktree.js';
|
|
66
66
|
import { claudeSandboxRuntime } from './sandbox/claude-sandbox-runtime.js';
|
|
67
67
|
import { nativeSandboxTempDirectory, loadClaudeSandboxSettings, } from './sandbox/claude-sandbox-settings.js';
|
|
68
|
-
import { createErrorResult, createSuccessResult, isHeadlessCostCommand, matchHeadlessColorCommand, parseCliInvocation, readStreamJsonMessages, StreamJsonOutput, } from './cli/protocol.js';
|
|
68
|
+
import { createErrorResult, createSuccessResult, isHeadlessCostCommand, matchHeadlessColorCommand, parseCliInvocation, projectProtocolTimings, readStreamJsonMessages, StreamJsonOutput, } from './cli/protocol.js';
|
|
69
69
|
import { executeProviderAuthCommand } from './cli/provider-auth-command.js';
|
|
70
70
|
import { describeClaudePlugin, initClaudePlugin, installClaudePlugin, loadClaudePlugins, readPluginRegistry, setClaudePluginEnabled, uninstallClaudePlugin, updateClaudePlugin, validateClaudePlugin, } from './plugins/claude-plugin-runtime.js';
|
|
71
71
|
import { addClaudeMarketplace, disableAllNativePlugins, installClaudeMarketplacePlugin, listClaudeMarketplaceAvailablePlugins, listNativePluginRecords, readClaudeKnownMarketplaces, removeClaudeMarketplace, setNativePluginEnabled, saveClaudePluginConfig, uninstallNativePlugin, updateClaudeMarketplace, updateNativePlugin, validateClaudeMarketplace, } from './plugins/claude-plugin-marketplace.js';
|
|
@@ -5412,6 +5412,9 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5412
5412
|
}
|
|
5413
5413
|
let streamOutput;
|
|
5414
5414
|
let jsonModelTurns = 0;
|
|
5415
|
+
let jsonRequestAt;
|
|
5416
|
+
let jsonOutputAt;
|
|
5417
|
+
let jsonTerminalReason;
|
|
5415
5418
|
const pendingEvents = [];
|
|
5416
5419
|
let streamIterator;
|
|
5417
5420
|
let firstStreamMessage;
|
|
@@ -5615,10 +5618,10 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5615
5618
|
output.init();
|
|
5616
5619
|
output.sink({ type: 'text-delta', delta: text });
|
|
5617
5620
|
output.sink({ type: 'usage', usage: result.usage });
|
|
5618
|
-
output.result(result, startedAt);
|
|
5621
|
+
output.result(result, startedAt, { localCommand: true });
|
|
5619
5622
|
}
|
|
5620
5623
|
else if (outputFormat === 'json' || invocation.legacyJson) {
|
|
5621
|
-
writeJson(io, createSuccessResult(result, info, startedAt, 0));
|
|
5624
|
+
writeJson(io, createSuccessResult(result, info, startedAt, 0, { localCommand: true }));
|
|
5622
5625
|
}
|
|
5623
5626
|
else {
|
|
5624
5627
|
io.stdout(`${text}\n`);
|
|
@@ -5663,6 +5666,20 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5663
5666
|
? (event) => {
|
|
5664
5667
|
if (event.type === 'state' && event.state === 'awaiting-model') {
|
|
5665
5668
|
jsonModelTurns += 1;
|
|
5669
|
+
if (jsonRequestAt === undefined)
|
|
5670
|
+
jsonRequestAt = Date.now();
|
|
5671
|
+
}
|
|
5672
|
+
if (event.type === 'terminal') {
|
|
5673
|
+
jsonTerminalReason = event.reason;
|
|
5674
|
+
}
|
|
5675
|
+
if ((event.type === 'text-delta' ||
|
|
5676
|
+
event.type === 'thinking-start' ||
|
|
5677
|
+
event.type === 'thinking-delta' ||
|
|
5678
|
+
event.type === 'thinking-signature-delta' ||
|
|
5679
|
+
event.type === 'thinking-stop' ||
|
|
5680
|
+
event.type === 'tool-call') &&
|
|
5681
|
+
jsonOutputAt === undefined) {
|
|
5682
|
+
jsonOutputAt = Date.now();
|
|
5666
5683
|
}
|
|
5667
5684
|
if (event.type === 'warning') {
|
|
5668
5685
|
io.stderr(`Warning: ${redactSensitiveText(event.message, sensitiveEnvironmentValues(process.env))}\n`);
|
|
@@ -5816,8 +5833,12 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5816
5833
|
currentTurnAbort = streamIterator ? turnAbort : undefined;
|
|
5817
5834
|
const runSignal = streamIterator ? turnAbort.signal : signal;
|
|
5818
5835
|
await ensureCostBaseline(activeSessionId);
|
|
5836
|
+
jsonModelTurns = 0;
|
|
5837
|
+
jsonRequestAt = undefined;
|
|
5838
|
+
jsonOutputAt = undefined;
|
|
5839
|
+
jsonTerminalReason = undefined;
|
|
5819
5840
|
if (streamOutput) {
|
|
5820
|
-
streamOutput.init();
|
|
5841
|
+
streamOutput.init(startedAt);
|
|
5821
5842
|
if (isFirstTurn) {
|
|
5822
5843
|
for (const event of pendingEvents)
|
|
5823
5844
|
streamOutput.sink(event);
|
|
@@ -5902,10 +5923,20 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5902
5923
|
throw error;
|
|
5903
5924
|
}
|
|
5904
5925
|
const message = redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveEnvironmentValues(process.env));
|
|
5926
|
+
const providerApiError = error instanceof ModelProviderError &&
|
|
5927
|
+
(error.status !== undefined || error.kind === 'api_error');
|
|
5928
|
+
const errorContext = providerApiError
|
|
5929
|
+
? {
|
|
5930
|
+
providerApiError: true,
|
|
5931
|
+
apiErrorStatus: error instanceof ModelProviderError
|
|
5932
|
+
? (error.status ?? null)
|
|
5933
|
+
: null,
|
|
5934
|
+
}
|
|
5935
|
+
: {};
|
|
5905
5936
|
if (streamOutput)
|
|
5906
|
-
streamOutput.error(message, startedAt);
|
|
5937
|
+
streamOutput.error(message, startedAt, errorContext);
|
|
5907
5938
|
else if (outputFormat === 'json') {
|
|
5908
|
-
writeJson(io, createErrorResult(message, activeSessionId, startedAt, jsonModelTurns));
|
|
5939
|
+
writeJson(io, createErrorResult(message, activeSessionId, startedAt, jsonModelTurns, errorContext));
|
|
5909
5940
|
}
|
|
5910
5941
|
else {
|
|
5911
5942
|
if (currentTurnAbort === turnAbort)
|
|
@@ -5925,11 +5956,17 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
5925
5956
|
if (localCommand) {
|
|
5926
5957
|
streamOutput.syntheticAssistant(result.text);
|
|
5927
5958
|
}
|
|
5928
|
-
streamOutput.result(result, startedAt);
|
|
5959
|
+
streamOutput.result(result, startedAt, { localCommand });
|
|
5929
5960
|
}
|
|
5930
5961
|
else if (outputFormat === 'json') {
|
|
5931
5962
|
const resultRuntimeInfo = service.runtimeInfo?.() ?? runtimeInfo;
|
|
5932
|
-
writeJson(io, createSuccessResult(result, resultRuntimeInfo, startedAt, localCommand ? 0 : Math.max(1, jsonModelTurns)
|
|
5963
|
+
writeJson(io, createSuccessResult(result, resultRuntimeInfo, startedAt, localCommand ? 0 : Math.max(1, jsonModelTurns), {
|
|
5964
|
+
localCommand,
|
|
5965
|
+
...(jsonTerminalReason === undefined
|
|
5966
|
+
? {}
|
|
5967
|
+
: { stopReason: jsonTerminalReason }),
|
|
5968
|
+
...projectProtocolTimings(startedAt, jsonRequestAt, jsonOutputAt),
|
|
5969
|
+
}));
|
|
5933
5970
|
}
|
|
5934
5971
|
else if (outputFormat !== 'text')
|
|
5935
5972
|
writeJson(io, { type: 'result', ...result });
|