@parall/claude-agent 1.58.2 → 1.60.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/dist/busy-state.d.ts +56 -0
- package/dist/busy-state.d.ts.map +1 -0
- package/dist/busy-state.js +140 -0
- package/dist/compact.d.ts +29 -0
- package/dist/compact.d.ts.map +1 -0
- package/dist/compact.js +98 -0
- package/dist/dispatch.d.ts +54 -6
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +193 -180
- package/dist/index.js +6 -2
- package/dist/input-lifecycle.d.ts +31 -1
- package/dist/input-lifecycle.d.ts.map +1 -1
- package/dist/input-lifecycle.js +50 -2
- package/dist/output-parser.d.ts +32 -0
- package/dist/output-parser.d.ts.map +1 -1
- package/dist/output-parser.js +106 -10
- package/dist/process-pump.d.ts +105 -0
- package/dist/process-pump.d.ts.map +1 -0
- package/dist/process-pump.js +436 -0
- package/dist/runtime-turn.d.ts +19 -0
- package/dist/runtime-turn.d.ts.map +1 -0
- package/dist/runtime-turn.js +59 -0
- package/dist/session-manager.d.ts +1 -0
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +3 -0
- package/dist/turn-sink.d.ts +49 -0
- package/dist/turn-sink.d.ts.map +1 -0
- package/dist/turn-sink.js +9 -0
- package/package.json +4 -4
- package/src/busy-state.ts +159 -0
- package/src/compact.ts +132 -0
- package/src/dispatch.ts +217 -209
- package/src/index.ts +6 -1
- package/src/input-lifecycle.ts +61 -1
- package/src/output-parser.ts +140 -10
- package/src/process-pump.ts +493 -0
- package/src/runtime-turn.ts +80 -0
- package/src/session-manager.ts +4 -0
- package/src/turn-sink.ts +59 -0
package/dist/input-lifecycle.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { newTurnEvidence, newTurnSink, } from './turn-sink.js';
|
|
2
3
|
/**
|
|
3
4
|
* Per-process registry that maps Parall WorkItem batches to Claude stdin
|
|
4
5
|
* UUIDs. It owns lifecycle transition rules; process/stdout orchestration
|
|
@@ -28,17 +29,35 @@ export class ClaudeInputRegistry {
|
|
|
28
29
|
hasPendingInjections() {
|
|
29
30
|
return [...this.byKey.values()].some((delivery) => delivery.injected && !delivery.drained);
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Injected inputs whose lifecycle has NOT reached a terminal state. A
|
|
34
|
+
* terminal delivery still counts as pending (its buffered copy owes frame
|
|
35
|
+
* boundary bookkeeping) but owes the server nothing — the lane complete
|
|
36
|
+
* defers on this predicate, never on hasPendingInjections, so a bookkeeping
|
|
37
|
+
* copy that never drains cannot hold the lane open past its lease. Drained
|
|
38
|
+
* is deliberately NOT part of this predicate: a discarded bookkeeping copy
|
|
39
|
+
* (drained, non-terminal) is still a running input — completing its lane
|
|
40
|
+
* would release the member for redrive mid-processing.
|
|
41
|
+
*/
|
|
42
|
+
hasUnsettledInjections() {
|
|
43
|
+
return [...this.byKey.values()].some((delivery) => delivery.injected && !delivery.terminal);
|
|
44
|
+
}
|
|
45
|
+
register(deliveryKey, lifecycle, injected, noteActivity, log) {
|
|
32
46
|
if (this.byKey.has(deliveryKey)) {
|
|
33
47
|
throw new Error(`duplicate Claude delivery key ${deliveryKey}`);
|
|
34
48
|
}
|
|
49
|
+
const commandUuid = randomUUID();
|
|
35
50
|
const delivery = {
|
|
36
51
|
deliveryKey,
|
|
37
|
-
commandUuid
|
|
52
|
+
commandUuid,
|
|
38
53
|
lifecycle,
|
|
39
54
|
injected,
|
|
40
55
|
drained: !injected,
|
|
41
56
|
resultFailed: false,
|
|
57
|
+
sink: newTurnSink(`delivery ${deliveryKey}`, log),
|
|
58
|
+
evidence: newTurnEvidence(),
|
|
59
|
+
noteActivity,
|
|
60
|
+
sessionAnnounced: false,
|
|
42
61
|
};
|
|
43
62
|
this.byKey.set(deliveryKey, delivery);
|
|
44
63
|
this.byCommand.set(delivery.commandUuid, delivery);
|
|
@@ -51,6 +70,26 @@ export class ClaudeInputRegistry {
|
|
|
51
70
|
if (this.byCommand.get(delivery.commandUuid) === delivery) {
|
|
52
71
|
this.byCommand.delete(delivery.commandUuid);
|
|
53
72
|
}
|
|
73
|
+
// The sink stays open: apply() removes a drained delivery at its
|
|
74
|
+
// terminal BEFORE the pump pushes the terminal envelope its drain is
|
|
75
|
+
// waiting for. Only the pump closes sinks (process gone).
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The buffered copy backing this delivery was discarded without a
|
|
79
|
+
* dispatch (already rendered into a frame the model saw), so the normal
|
|
80
|
+
* bookkeeping drain never comes. Its frame-boundary obligation is void:
|
|
81
|
+
* mark it drained so nothing waits on it, and drop it once terminal. A
|
|
82
|
+
* still-running input keeps its registration — byCommand must keep routing
|
|
83
|
+
* its lifecycle frames — and is dropped when its terminal state arrives
|
|
84
|
+
* (see apply/fail).
|
|
85
|
+
*/
|
|
86
|
+
discardBookkeeping(deliveryKey) {
|
|
87
|
+
const delivery = this.byKey.get(deliveryKey);
|
|
88
|
+
if (!delivery)
|
|
89
|
+
return;
|
|
90
|
+
delivery.drained = true;
|
|
91
|
+
if (delivery.terminal)
|
|
92
|
+
this.remove(delivery);
|
|
54
93
|
}
|
|
55
94
|
async apply(delivery, state) {
|
|
56
95
|
if (delivery.terminal || state === 'queued')
|
|
@@ -71,12 +110,19 @@ export class ClaudeInputRegistry {
|
|
|
71
110
|
delivery.reportedState = 'completed';
|
|
72
111
|
}
|
|
73
112
|
delivery.terminal = 'completed';
|
|
113
|
+
// A discarded bookkeeping copy (drained without a dispatch) has no
|
|
114
|
+
// drain left to remove this entry — drop it at its terminal so it can
|
|
115
|
+
// never shadow a redelivered batch in findOverlapping.
|
|
116
|
+
if (delivery.drained)
|
|
117
|
+
this.remove(delivery);
|
|
74
118
|
}
|
|
75
119
|
async fail(delivery) {
|
|
76
120
|
if (delivery.terminal)
|
|
77
121
|
return;
|
|
78
122
|
if (delivery.suppressFailReport) {
|
|
79
123
|
delivery.terminal = 'settled';
|
|
124
|
+
if (delivery.drained)
|
|
125
|
+
this.remove(delivery);
|
|
80
126
|
return;
|
|
81
127
|
}
|
|
82
128
|
try {
|
|
@@ -88,6 +134,8 @@ export class ClaudeInputRegistry {
|
|
|
88
134
|
}
|
|
89
135
|
finally {
|
|
90
136
|
delivery.terminal ??= 'failed';
|
|
137
|
+
if (delivery.drained)
|
|
138
|
+
this.remove(delivery);
|
|
91
139
|
}
|
|
92
140
|
}
|
|
93
141
|
async failBestEffort(delivery, log) {
|
package/dist/output-parser.d.ts
CHANGED
|
@@ -25,10 +25,38 @@ export type ClaudeResultMeta = {
|
|
|
25
25
|
/** First model key of `modelUsage` — the model the CLI actually used. */
|
|
26
26
|
model?: string;
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* The CLI's background-work frames (`system` subtypes). Every field is
|
|
30
|
+
* optional except the subtype.
|
|
31
|
+
* Observed on 2.1.227/2.1.258: `background_tasks_changed` (REPLACE
|
|
32
|
+
* semantics — every live task after the change), `task_started`,
|
|
33
|
+
* `task_progress`, `task_updated`, `task_notification` (a task finished or
|
|
34
|
+
* was stopped), `status`, `session_state_changed` (env-gated).
|
|
35
|
+
*/
|
|
36
|
+
export type ClaudeRuntimeTaskEvent = {
|
|
37
|
+
type: 'runtime_task';
|
|
38
|
+
subtype: 'background_tasks_changed' | 'task_started' | 'task_progress' | 'task_updated' | 'task_notification' | 'status' | 'session_state_changed';
|
|
39
|
+
taskId?: string;
|
|
40
|
+
toolUseId?: string;
|
|
41
|
+
description?: string;
|
|
42
|
+
taskType?: string;
|
|
43
|
+
status?: string;
|
|
44
|
+
summary?: string;
|
|
45
|
+
isBackgrounded?: boolean;
|
|
46
|
+
tasks?: Array<{
|
|
47
|
+
taskId: string;
|
|
48
|
+
taskType?: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
ambient?: boolean;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
28
53
|
export type ClaudeParsedEvent = RuntimeEvent | {
|
|
29
54
|
type: 'runtime_init';
|
|
30
55
|
sessionId?: string;
|
|
31
56
|
capabilities: string[];
|
|
57
|
+
} | ClaudeRuntimeTaskEvent | {
|
|
58
|
+
type: 'user_text';
|
|
59
|
+
text: string;
|
|
32
60
|
} | {
|
|
33
61
|
type: 'runtime_activity';
|
|
34
62
|
} | {
|
|
@@ -38,6 +66,10 @@ export type ClaudeParsedEvent = RuntimeEvent | {
|
|
|
38
66
|
} | {
|
|
39
67
|
type: 'assistant_error';
|
|
40
68
|
message: string;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'compact_boundary';
|
|
71
|
+
trigger?: string;
|
|
72
|
+
preTokens?: number;
|
|
41
73
|
} | {
|
|
42
74
|
type: 'turn_end';
|
|
43
75
|
sessionId?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../src/output-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"output-parser.d.ts","sourceRoot":"","sources":["../src/output-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EACH,0BAA0B,GAC1B,cAAc,GACd,eAAe,GACf,cAAc,GACd,mBAAmB,GACnB,QAAQ,GACR,uBAAuB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/F,CAAC;AAYF,MAAM,MAAM,iBAAiB,GACzB,YAAY,GACZ;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GACpE,sBAAsB,GAItB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAKnC;IAAE,IAAI,EAAE,kBAAkB,CAAA;CAAE,GAC5B;IACE,IAAI,EAAE,mBAAmB,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC;CACvE,GAID;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAI5C;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAqBlE;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AAgFN,wBAAuB,qBAAqB,CAC1C,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAAC,iBAAiB,CAAC,CA6NnC"}
|
package/dist/output-parser.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
const RUNTIME_TASK_SUBTYPES = new Set([
|
|
2
|
+
'background_tasks_changed',
|
|
3
|
+
'task_started',
|
|
4
|
+
'task_progress',
|
|
5
|
+
'task_updated',
|
|
6
|
+
'task_notification',
|
|
7
|
+
'status',
|
|
8
|
+
'session_state_changed',
|
|
9
|
+
]);
|
|
1
10
|
function asTrimmedString(value) {
|
|
2
11
|
if (typeof value !== 'string')
|
|
3
12
|
return undefined;
|
|
@@ -91,20 +100,44 @@ export async function* parseClaudeStreamJson(readable) {
|
|
|
91
100
|
const now = Date.now();
|
|
92
101
|
const eventTimestampMs = parseEventTimestampMs(event) ?? now;
|
|
93
102
|
const eventRecord = event;
|
|
94
|
-
if (eventRecord.type === 'system' && eventRecord.subtype === '
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
: [];
|
|
103
|
+
if (eventRecord.type === 'system' && eventRecord.subtype === 'compact_boundary') {
|
|
104
|
+
const meta = eventRecord.compact_metadata && typeof eventRecord.compact_metadata === 'object'
|
|
105
|
+
? eventRecord.compact_metadata
|
|
106
|
+
: undefined;
|
|
107
|
+
const trigger = asTrimmedString(meta?.trigger);
|
|
108
|
+
const preTokens = asFiniteNumber(meta?.pre_tokens);
|
|
101
109
|
yield {
|
|
102
|
-
type: '
|
|
103
|
-
...(
|
|
104
|
-
|
|
110
|
+
type: 'compact_boundary',
|
|
111
|
+
...(trigger ? { trigger } : {}),
|
|
112
|
+
...(preTokens !== undefined ? { preTokens } : {}),
|
|
105
113
|
};
|
|
106
114
|
continue;
|
|
107
115
|
}
|
|
116
|
+
if (eventRecord.type === 'system') {
|
|
117
|
+
if (eventRecord.subtype === 'init') {
|
|
118
|
+
const sessionId = asTrimmedString(eventRecord.session_id);
|
|
119
|
+
const capabilities = Array.isArray(eventRecord.capabilities)
|
|
120
|
+
? eventRecord.capabilities
|
|
121
|
+
.map((capability) => asTrimmedString(capability))
|
|
122
|
+
.filter((capability) => Boolean(capability))
|
|
123
|
+
: [];
|
|
124
|
+
yield {
|
|
125
|
+
type: 'runtime_init',
|
|
126
|
+
...(sessionId ? { sessionId } : {}),
|
|
127
|
+
capabilities,
|
|
128
|
+
};
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const subtype = asTrimmedString(eventRecord.subtype);
|
|
132
|
+
if (subtype && RUNTIME_TASK_SUBTYPES.has(subtype)) {
|
|
133
|
+
yield parseRuntimeTask(subtype, eventRecord);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// hook_started / hook_response / thinking_tokens /
|
|
137
|
+
// ...: valid CLI progress with no projection.
|
|
138
|
+
yield { type: 'runtime_activity' };
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
108
141
|
if (eventRecord.type === 'command_lifecycle') {
|
|
109
142
|
const commandUuid = asTrimmedString(eventRecord.command_uuid);
|
|
110
143
|
const state = asTrimmedString(eventRecord.state);
|
|
@@ -190,8 +223,20 @@ export async function* parseClaudeStreamJson(readable) {
|
|
|
190
223
|
const content = message && typeof message === 'object'
|
|
191
224
|
? message.content
|
|
192
225
|
: undefined;
|
|
226
|
+
if (typeof content === 'string') {
|
|
227
|
+
const text = content.trim();
|
|
228
|
+
if (text)
|
|
229
|
+
yield { type: 'user_text', text };
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
193
232
|
if (!Array.isArray(content))
|
|
194
233
|
continue;
|
|
234
|
+
if (!content.some((block) => block?.type === 'tool_result')) {
|
|
235
|
+
const text = stringifyContent(content).trim();
|
|
236
|
+
if (text)
|
|
237
|
+
yield { type: 'user_text', text };
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
195
240
|
for (const block of content) {
|
|
196
241
|
if (!block || typeof block !== 'object')
|
|
197
242
|
continue;
|
|
@@ -239,8 +284,59 @@ export async function* parseClaudeStreamJson(readable) {
|
|
|
239
284
|
...(numTurns !== undefined ? { numTurns } : {}),
|
|
240
285
|
resultMeta: extractResultMeta(eventRecord, isError, numTurns),
|
|
241
286
|
};
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
// Any other well-formed frame (rate_limit_event, stream_event, ...) is
|
|
290
|
+
// CLI progress: refresh activity, never project.
|
|
291
|
+
yield { type: 'runtime_activity' };
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function parseRuntimeTask(subtype, frame) {
|
|
295
|
+
const event = { type: 'runtime_task', subtype };
|
|
296
|
+
const taskId = asTrimmedString(frame.task_id);
|
|
297
|
+
if (taskId)
|
|
298
|
+
event.taskId = taskId;
|
|
299
|
+
const toolUseId = asTrimmedString(frame.tool_use_id);
|
|
300
|
+
if (toolUseId)
|
|
301
|
+
event.toolUseId = toolUseId;
|
|
302
|
+
const description = asTrimmedString(frame.description);
|
|
303
|
+
if (description)
|
|
304
|
+
event.description = description;
|
|
305
|
+
const taskType = asTrimmedString(frame.task_type);
|
|
306
|
+
if (taskType)
|
|
307
|
+
event.taskType = taskType;
|
|
308
|
+
const status = asTrimmedString(frame.status) ??
|
|
309
|
+
asTrimmedString(frame.patch?.status) ??
|
|
310
|
+
asTrimmedString(frame.state);
|
|
311
|
+
if (status)
|
|
312
|
+
event.status = status;
|
|
313
|
+
const summary = asTrimmedString(frame.summary);
|
|
314
|
+
if (summary)
|
|
315
|
+
event.summary = summary;
|
|
316
|
+
if (typeof frame.is_backgrounded === 'boolean')
|
|
317
|
+
event.isBackgrounded = frame.is_backgrounded;
|
|
318
|
+
if (Array.isArray(frame.tasks)) {
|
|
319
|
+
event.tasks = [];
|
|
320
|
+
for (const task of frame.tasks) {
|
|
321
|
+
if (!task || typeof task !== 'object')
|
|
322
|
+
continue;
|
|
323
|
+
const record = task;
|
|
324
|
+
const id = asTrimmedString(record.task_id);
|
|
325
|
+
if (!id)
|
|
326
|
+
continue;
|
|
327
|
+
const entry = { taskId: id };
|
|
328
|
+
const entryType = asTrimmedString(record.task_type);
|
|
329
|
+
if (entryType)
|
|
330
|
+
entry.taskType = entryType;
|
|
331
|
+
const entryDescription = asTrimmedString(record.description);
|
|
332
|
+
if (entryDescription)
|
|
333
|
+
entry.description = entryDescription;
|
|
334
|
+
if (record.ambient === true)
|
|
335
|
+
entry.ambient = true;
|
|
336
|
+
event.tasks.push(entry);
|
|
242
337
|
}
|
|
243
338
|
}
|
|
339
|
+
return event;
|
|
244
340
|
}
|
|
245
341
|
/** Snapshot the result frame's discriminator + usage fields (all optional). */
|
|
246
342
|
function extractResultMeta(frame, isError, numTurns) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type GatewayLogger, type RuntimeBusyState } from '@parall/agent-core';
|
|
2
|
+
import { ClaudeBusyTracker } from './busy-state.js';
|
|
3
|
+
import type { ClaudeInputDelivery, ClaudeInputRegistry } from './input-lifecycle.js';
|
|
4
|
+
import type { ClaudeParsedEvent } from './output-parser.js';
|
|
5
|
+
import { ClaudeRuntimeTurn } from './runtime-turn.js';
|
|
6
|
+
import type { ClaudeProcessHandle } from './session-manager.js';
|
|
7
|
+
import type { ClaudeTurnEndReason } from './turn-sink.js';
|
|
8
|
+
export type ClaudeProcessPumpHooks = {
|
|
9
|
+
/** system.init: capabilities + session id. Return a message to fail the process. */
|
|
10
|
+
onRuntimeInit(init: {
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
capabilities: string[];
|
|
13
|
+
}): string | undefined;
|
|
14
|
+
/** Unrecoverable protocol state: the adapter kills the process. */
|
|
15
|
+
onFatal(): void;
|
|
16
|
+
/** stdout reached EOF (process gone). */
|
|
17
|
+
onEof(): void;
|
|
18
|
+
onRuntimeTurnOpened(turn: ClaudeRuntimeTurn): void;
|
|
19
|
+
onRuntimeTurnClosed(): void;
|
|
20
|
+
};
|
|
21
|
+
export type ClaudeProcessPumpOptions = {
|
|
22
|
+
sessionKey: string;
|
|
23
|
+
parser: AsyncGenerator<ClaudeParsedEvent>;
|
|
24
|
+
handle: ClaudeProcessHandle;
|
|
25
|
+
inputs: ClaudeInputRegistry;
|
|
26
|
+
hooks: ClaudeProcessPumpHooks;
|
|
27
|
+
log?: GatewayLogger;
|
|
28
|
+
followUpHoldMs?: number;
|
|
29
|
+
now?: () => number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Single owner of one Claude subprocess's stdout. Reads every frame the CLI
|
|
33
|
+
* emits — during dispatches AND between them — and routes it:
|
|
34
|
+
*
|
|
35
|
+
* - `command_lifecycle` frames go to the delivery they name (stdin uuid).
|
|
36
|
+
* - Content frames (thinking / text / tool_call / tool_result / error) go to
|
|
37
|
+
* the delivery whose lifecycle is `started` and not yet terminal
|
|
38
|
+
* (claude-soft-steer-design.md: the lifecycle terminal, not `result`,
|
|
39
|
+
* bounds a delivery); with no started delivery they go to the open
|
|
40
|
+
* runtime-initiated turn, opening one if needed. A `queued` delivery never
|
|
41
|
+
* owns frames.
|
|
42
|
+
* - `result` frames attach turn-outcome evidence to the owner and close a
|
|
43
|
+
* runtime-initiated turn; a poison `num_turns: 0` resume artifact never
|
|
44
|
+
* opens or closes one.
|
|
45
|
+
* - task frames feed the busy ledger; `runtime_activity` only refreshes
|
|
46
|
+
* inactivity deadlines.
|
|
47
|
+
*
|
|
48
|
+
* Reading continuously also removes the latent stall where an unread
|
|
49
|
+
* self-started turn filled the OS pipe and blocked the CLI.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ClaudeProcessPump {
|
|
52
|
+
private readonly opts;
|
|
53
|
+
readonly busy: ClaudeBusyTracker;
|
|
54
|
+
capabilities: Set<string>;
|
|
55
|
+
sessionId: string | undefined;
|
|
56
|
+
runtimeTurn: ClaudeRuntimeTurn | null;
|
|
57
|
+
private closed;
|
|
58
|
+
private idleWaiters;
|
|
59
|
+
private idleTimer;
|
|
60
|
+
private activeDrain;
|
|
61
|
+
private readonly now;
|
|
62
|
+
constructor(opts: ClaudeProcessPumpOptions);
|
|
63
|
+
start(): void;
|
|
64
|
+
/** The dispatch generator currently draining `delivery` (owner preference). */
|
|
65
|
+
setActiveDrain(delivery: ClaudeInputDelivery, noteActivity?: () => void): void;
|
|
66
|
+
clearActiveDrain(delivery: ClaudeInputDelivery): void;
|
|
67
|
+
hasStartedDelivery(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The CLI has (or is about to start) work no dispatch asked for: a
|
|
70
|
+
* runtime-initiated turn is open, or a finished background task's
|
|
71
|
+
* follow-up hold is pending. Lazy restarts, dispatch aborts and
|
|
72
|
+
* `whenIdle` all defer to it.
|
|
73
|
+
*/
|
|
74
|
+
hasOwnWork(now?: number): boolean;
|
|
75
|
+
busyState(now?: number): RuntimeBusyState;
|
|
76
|
+
/** Dispatch abort: fail every live delivery and release its drain. */
|
|
77
|
+
abortDeliveries(message: string): void;
|
|
78
|
+
/**
|
|
79
|
+
* The adapter is terminating the process (kill / reset / shutdown): every
|
|
80
|
+
* drain and the open runtime turn end now, before stdout EOF is observed.
|
|
81
|
+
*/
|
|
82
|
+
close(reason: ClaudeTurnEndReason, message: string): void;
|
|
83
|
+
/**
|
|
84
|
+
* Run `cb` once the CLI has nothing left to do on its own: no
|
|
85
|
+
* runtime-initiated turn open and no follow-up hold pending (a hold that
|
|
86
|
+
* turns into a follow-up turn is waited for as well). Immediate when
|
|
87
|
+
* already idle or closed.
|
|
88
|
+
*/
|
|
89
|
+
whenIdle(cb: () => void): void;
|
|
90
|
+
private checkIdle;
|
|
91
|
+
closeRuntimeTurn(reason: ClaudeTurnEndReason, message?: string): void;
|
|
92
|
+
private run;
|
|
93
|
+
private route;
|
|
94
|
+
private touchAll;
|
|
95
|
+
private liveDeliveries;
|
|
96
|
+
private ownerFor;
|
|
97
|
+
private openRuntimeTurn;
|
|
98
|
+
private handleInit;
|
|
99
|
+
private handleLifecycle;
|
|
100
|
+
private handleTask;
|
|
101
|
+
private handleTurnEnd;
|
|
102
|
+
private pushErrorToDeliveries;
|
|
103
|
+
private handleEof;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=process-pump.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-pump.d.ts","sourceRoot":"","sources":["../src/process-pump.ts"],"names":[],"mappings":"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"}
|