@parall/claude-agent 1.27.0 → 1.28.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/dist/dispatch.d.ts +15 -0
- package/dist/dispatch.d.ts.map +1 -1
- package/dist/dispatch.js +118 -0
- package/package.json +4 -4
- package/src/dispatch.ts +120 -0
package/dist/dispatch.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function buildSpawnEnv(parentEnv: NodeJS.ProcessEnv, claudeHome:
|
|
|
17
17
|
export declare class ClaudeCodeAdapter implements DispatchAdapter {
|
|
18
18
|
private readonly opts;
|
|
19
19
|
private readonly processes;
|
|
20
|
+
private readonly pendingInjections;
|
|
20
21
|
private shuttingDown;
|
|
21
22
|
private _model;
|
|
22
23
|
private _effortLevel;
|
|
@@ -27,12 +28,26 @@ export declare class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
27
28
|
model?: string | null;
|
|
28
29
|
effort?: string | null;
|
|
29
30
|
}): void;
|
|
31
|
+
enqueueDuringDispatch(sessionKey: string, body: string): boolean;
|
|
32
|
+
hasPendingInjections(sessionKey: string): boolean;
|
|
30
33
|
dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent>;
|
|
31
34
|
getBranchPoint(_sessionKey: string): string | undefined;
|
|
32
35
|
getSessionHistoryPath(sessionKey: string): string | undefined;
|
|
33
36
|
forkSession({ sessionKey }: ForkOpts): import("@parall/agent-core").ForkSessionHandle | null;
|
|
34
37
|
cleanupFork({ fork }: CleanupForkOpts): void;
|
|
35
38
|
shutdown(): Promise<void>;
|
|
39
|
+
private static readonly STEER_TURN_TIMEOUT_MS;
|
|
40
|
+
/**
|
|
41
|
+
* Consume a steer turn whose message was already written to stdin via
|
|
42
|
+
* enqueueDuringDispatch. Skip the stdin write — only read parser output.
|
|
43
|
+
* If no output arrives within STEER_TURN_TIMEOUT_MS, the steer was
|
|
44
|
+
* incorporated into the previous turn and there is nothing to consume.
|
|
45
|
+
*
|
|
46
|
+
* On timeout, the losing parser.next() promise is saved to
|
|
47
|
+
* state.steerReadPending so the next runTurn can drain it instead of
|
|
48
|
+
* silently losing the first event of the subsequent turn.
|
|
49
|
+
*/
|
|
50
|
+
private consumeSteerTurn;
|
|
36
51
|
private runTurn;
|
|
37
52
|
private ensureProcess;
|
|
38
53
|
private spawnProcess;
|
package/dist/dispatch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EAER,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,oBAAoB,EAErB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,iDAAiD;IACjD,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3D,CAAC;AAEF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,UAAU,CAuBnB;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../src/dispatch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,YAAY,EACZ,QAAQ,EAER,YAAY,EACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,EACL,oBAAoB,EAErB,MAAM,sBAAsB,CAAC;AAE9B,KAAK,wBAAwB,GAAG,IAAI,CAClC,iBAAiB,EACf,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,oBAAoB,GACpB,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,OAAO,GACP,gBAAgB,GAChB,cAAc,CACjB,GAAG;IACF,cAAc,EAAE,oBAAoB,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3D,iDAAiD;IACjD,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3D,CAAC;AAEF,wBAAgB,aAAa,CAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,EAChC,IAAI,EAAE;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACnD,MAAM,CAAC,UAAU,CAuBnB;AAiBD,qBAAa,iBAAkB,YAAW,eAAe;IAO3C,OAAO,CAAC,QAAQ,CAAC,IAAI;IANjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmC;IAC7D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAA6B;IAC/D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,YAAY,CAAqB;gBAEZ,IAAI,EAAE,wBAAwB;IAI3D,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAAwB;IAC9D,IAAI,aAAa,IAAI,MAAM,GAAG,SAAS,CAA8B;IAErE,YAAY,CAAC,MAAM,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI;IAY7E,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IAchE,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI1C,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;IAiCxG,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIvD,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ7D,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ;IAIpC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,eAAe;IAQ/B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAEvD;;;;;;;;;OASG;YACY,gBAAgB;YAsDhB,OAAO;IAwGtB,OAAO,CAAC,aAAa;IAyBrB,OAAO,CAAC,YAAY;IA8CpB,OAAO,CAAC,WAAW;IAgBnB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,SAAS;IAyCjB,OAAO,CAAC,uBAAuB;CAkBhC"}
|
package/dist/dispatch.js
CHANGED
|
@@ -31,6 +31,7 @@ export function buildSpawnEnv(parentEnv, claudeHome, context, opts) {
|
|
|
31
31
|
export class ClaudeCodeAdapter {
|
|
32
32
|
opts;
|
|
33
33
|
processes = new Map();
|
|
34
|
+
pendingInjections = new Map();
|
|
34
35
|
shuttingDown = false;
|
|
35
36
|
_model;
|
|
36
37
|
_effortLevel;
|
|
@@ -53,7 +54,35 @@ export class ClaudeCodeAdapter {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
enqueueDuringDispatch(sessionKey, body) {
|
|
58
|
+
const state = this.processes.get(sessionKey);
|
|
59
|
+
if (!state || state.done)
|
|
60
|
+
return false;
|
|
61
|
+
const { proc } = state.handle;
|
|
62
|
+
if (proc.exitCode !== null || proc.signalCode !== null || proc.stdin.destroyed)
|
|
63
|
+
return false;
|
|
64
|
+
try {
|
|
65
|
+
this.writeUserMessage(state.handle, body);
|
|
66
|
+
this.pendingInjections.set(sessionKey, (this.pendingInjections.get(sessionKey) ?? 0) + 1);
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
hasPendingInjections(sessionKey) {
|
|
74
|
+
return (this.pendingInjections.get(sessionKey) ?? 0) > 0;
|
|
75
|
+
}
|
|
56
76
|
async *dispatch({ event, bodyForAgent, sessionKey, context }) {
|
|
77
|
+
const pending = this.pendingInjections.get(sessionKey) ?? 0;
|
|
78
|
+
if (pending > 0) {
|
|
79
|
+
this.pendingInjections.delete(sessionKey);
|
|
80
|
+
context.log?.info?.(`claude-agent[${context.accountId}]: consuming ${pending} steer turn(s)`);
|
|
81
|
+
for (let i = 0; i < pending; i++) {
|
|
82
|
+
yield* this.consumeSteerTurn(sessionKey, context.log);
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
57
86
|
let promptBody = bodyForAgent;
|
|
58
87
|
let releasePreparedAttachments = () => { };
|
|
59
88
|
try {
|
|
@@ -103,6 +132,66 @@ export class ClaudeCodeAdapter {
|
|
|
103
132
|
this.processes.clear();
|
|
104
133
|
await this.opts.sessionManager.shutdownAll();
|
|
105
134
|
}
|
|
135
|
+
static STEER_TURN_TIMEOUT_MS = 10_000;
|
|
136
|
+
/**
|
|
137
|
+
* Consume a steer turn whose message was already written to stdin via
|
|
138
|
+
* enqueueDuringDispatch. Skip the stdin write — only read parser output.
|
|
139
|
+
* If no output arrives within STEER_TURN_TIMEOUT_MS, the steer was
|
|
140
|
+
* incorporated into the previous turn and there is nothing to consume.
|
|
141
|
+
*
|
|
142
|
+
* On timeout, the losing parser.next() promise is saved to
|
|
143
|
+
* state.steerReadPending so the next runTurn can drain it instead of
|
|
144
|
+
* silently losing the first event of the subsequent turn.
|
|
145
|
+
*/
|
|
146
|
+
async *consumeSteerTurn(sessionKey, log) {
|
|
147
|
+
const state = this.processes.get(sessionKey);
|
|
148
|
+
if (!state || state.done) {
|
|
149
|
+
throw new Error("claude-agent: process dead during steer consumption");
|
|
150
|
+
}
|
|
151
|
+
const groupKey = randomUUID();
|
|
152
|
+
const parserNext = state.parser.next();
|
|
153
|
+
const firstRead = await Promise.race([
|
|
154
|
+
parserNext.then((r) => ({ kind: "value", result: r })),
|
|
155
|
+
new Promise((resolve) => setTimeout(() => resolve({ kind: "timeout" }), ClaudeCodeAdapter.STEER_TURN_TIMEOUT_MS)),
|
|
156
|
+
]);
|
|
157
|
+
if (firstRead.kind === "timeout") {
|
|
158
|
+
state.steerReadPending = parserNext;
|
|
159
|
+
log?.info?.(`claude-agent: steer turn timeout — steer was incorporated into previous turn`);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
let next = firstRead.result;
|
|
163
|
+
// eslint-disable-next-line no-constant-condition
|
|
164
|
+
while (true) {
|
|
165
|
+
if (next.done) {
|
|
166
|
+
state.done = true;
|
|
167
|
+
this.processes.delete(sessionKey);
|
|
168
|
+
throw new Error("claude-agent: process exited while consuming steer turn");
|
|
169
|
+
}
|
|
170
|
+
const parsed = next.value;
|
|
171
|
+
if (parsed.type === "session_id") {
|
|
172
|
+
this.opts.sessionManager.recordSessionId(sessionKey, parsed.sessionId);
|
|
173
|
+
yield { type: "runtime_session", runtimeSessionId: parsed.sessionId, runtimeLaneKey: sessionKey };
|
|
174
|
+
}
|
|
175
|
+
else if (parsed.type === "turn_end") {
|
|
176
|
+
if (state.needsRestart)
|
|
177
|
+
this.killProcess(sessionKey, state);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
else if (parsed.type === "error") {
|
|
181
|
+
yield parsed;
|
|
182
|
+
}
|
|
183
|
+
else if (parsed.type === "text") {
|
|
184
|
+
yield { ...parsed, project: false, groupKey };
|
|
185
|
+
}
|
|
186
|
+
else if (parsed.type === "runtime_session") {
|
|
187
|
+
yield parsed;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
yield { ...parsed, groupKey };
|
|
191
|
+
}
|
|
192
|
+
next = await state.parser.next();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
106
195
|
async *runTurn(sessionKey, promptBody, log) {
|
|
107
196
|
let state;
|
|
108
197
|
try {
|
|
@@ -122,6 +211,35 @@ export class ClaudeCodeAdapter {
|
|
|
122
211
|
this.killProcess(sessionKey, state);
|
|
123
212
|
return;
|
|
124
213
|
}
|
|
214
|
+
if (state.steerReadPending) {
|
|
215
|
+
const orphaned = await state.steerReadPending;
|
|
216
|
+
state.steerReadPending = undefined;
|
|
217
|
+
if (!orphaned.done) {
|
|
218
|
+
const parsed = orphaned.value;
|
|
219
|
+
if (parsed.type === "session_id") {
|
|
220
|
+
this.opts.sessionManager.recordSessionId(sessionKey, parsed.sessionId);
|
|
221
|
+
yield { type: "runtime_session", runtimeSessionId: parsed.sessionId, runtimeLaneKey: sessionKey };
|
|
222
|
+
}
|
|
223
|
+
else if (parsed.type === "turn_end") {
|
|
224
|
+
if (state.needsRestart)
|
|
225
|
+
this.killProcess(sessionKey, state);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
else if (parsed.type === "error") {
|
|
229
|
+
sawError = true;
|
|
230
|
+
yield parsed;
|
|
231
|
+
}
|
|
232
|
+
else if (parsed.type === "text") {
|
|
233
|
+
yield { ...parsed, project: false, groupKey };
|
|
234
|
+
}
|
|
235
|
+
else if (parsed.type === "runtime_session") {
|
|
236
|
+
yield parsed;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
yield { ...parsed, groupKey };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
125
243
|
while (true) {
|
|
126
244
|
const next = await state.parser.next();
|
|
127
245
|
if (next.done) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/claude-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.1",
|
|
4
4
|
"description": "Claude Code bridge runtime for self-hosted Parall agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@parall/
|
|
29
|
-
"@parall/sdk": "1.
|
|
30
|
-
"@parall/
|
|
28
|
+
"@parall/agent-core": "1.28.1",
|
|
29
|
+
"@parall/sdk": "1.28.1",
|
|
30
|
+
"@parall/cli": "1.28.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.0.0",
|
package/src/dispatch.ts
CHANGED
|
@@ -85,10 +85,12 @@ type ProcessState = {
|
|
|
85
85
|
parser: AsyncGenerator<ClaudeParsedEvent>;
|
|
86
86
|
done: boolean;
|
|
87
87
|
needsRestart: boolean;
|
|
88
|
+
steerReadPending?: Promise<IteratorResult<ClaudeParsedEvent>>;
|
|
88
89
|
};
|
|
89
90
|
|
|
90
91
|
export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
91
92
|
private readonly processes = new Map<string, ProcessState>();
|
|
93
|
+
private readonly pendingInjections = new Map<string, number>();
|
|
92
94
|
private shuttingDown = false;
|
|
93
95
|
private _model: string | undefined;
|
|
94
96
|
private _effortLevel: string | undefined;
|
|
@@ -112,7 +114,35 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
117
|
+
enqueueDuringDispatch(sessionKey: string, body: string): boolean {
|
|
118
|
+
const state = this.processes.get(sessionKey);
|
|
119
|
+
if (!state || state.done) return false;
|
|
120
|
+
const { proc } = state.handle;
|
|
121
|
+
if (proc.exitCode !== null || proc.signalCode !== null || proc.stdin.destroyed) return false;
|
|
122
|
+
try {
|
|
123
|
+
this.writeUserMessage(state.handle, body);
|
|
124
|
+
this.pendingInjections.set(sessionKey, (this.pendingInjections.get(sessionKey) ?? 0) + 1);
|
|
125
|
+
return true;
|
|
126
|
+
} catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
hasPendingInjections(sessionKey: string): boolean {
|
|
132
|
+
return (this.pendingInjections.get(sessionKey) ?? 0) > 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
115
135
|
async *dispatch({ event, bodyForAgent, sessionKey, context }: DispatchOpts): AsyncIterable<RuntimeEvent> {
|
|
136
|
+
const pending = this.pendingInjections.get(sessionKey) ?? 0;
|
|
137
|
+
if (pending > 0) {
|
|
138
|
+
this.pendingInjections.delete(sessionKey);
|
|
139
|
+
context.log?.info?.(`claude-agent[${context.accountId}]: consuming ${pending} steer turn(s)`);
|
|
140
|
+
for (let i = 0; i < pending; i++) {
|
|
141
|
+
yield* this.consumeSteerTurn(sessionKey, context.log);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
116
146
|
let promptBody = bodyForAgent;
|
|
117
147
|
let releasePreparedAttachments = () => {};
|
|
118
148
|
try {
|
|
@@ -168,6 +198,72 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
168
198
|
await this.opts.sessionManager.shutdownAll();
|
|
169
199
|
}
|
|
170
200
|
|
|
201
|
+
private static readonly STEER_TURN_TIMEOUT_MS = 10_000;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Consume a steer turn whose message was already written to stdin via
|
|
205
|
+
* enqueueDuringDispatch. Skip the stdin write — only read parser output.
|
|
206
|
+
* If no output arrives within STEER_TURN_TIMEOUT_MS, the steer was
|
|
207
|
+
* incorporated into the previous turn and there is nothing to consume.
|
|
208
|
+
*
|
|
209
|
+
* On timeout, the losing parser.next() promise is saved to
|
|
210
|
+
* state.steerReadPending so the next runTurn can drain it instead of
|
|
211
|
+
* silently losing the first event of the subsequent turn.
|
|
212
|
+
*/
|
|
213
|
+
private async *consumeSteerTurn(
|
|
214
|
+
sessionKey: string,
|
|
215
|
+
log: GatewayLogger | undefined,
|
|
216
|
+
): AsyncGenerator<RuntimeEvent> {
|
|
217
|
+
const state = this.processes.get(sessionKey);
|
|
218
|
+
if (!state || state.done) {
|
|
219
|
+
throw new Error("claude-agent: process dead during steer consumption");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const groupKey = randomUUID();
|
|
223
|
+
|
|
224
|
+
const parserNext = state.parser.next();
|
|
225
|
+
const firstRead = await Promise.race([
|
|
226
|
+
parserNext.then((r) => ({ kind: "value" as const, result: r })),
|
|
227
|
+
new Promise<{ kind: "timeout" }>((resolve) =>
|
|
228
|
+
setTimeout(() => resolve({ kind: "timeout" }), ClaudeCodeAdapter.STEER_TURN_TIMEOUT_MS),
|
|
229
|
+
),
|
|
230
|
+
]);
|
|
231
|
+
|
|
232
|
+
if (firstRead.kind === "timeout") {
|
|
233
|
+
state.steerReadPending = parserNext;
|
|
234
|
+
log?.info?.(`claude-agent: steer turn timeout — steer was incorporated into previous turn`);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let next = firstRead.result;
|
|
239
|
+
// eslint-disable-next-line no-constant-condition
|
|
240
|
+
while (true) {
|
|
241
|
+
if (next.done) {
|
|
242
|
+
state.done = true;
|
|
243
|
+
this.processes.delete(sessionKey);
|
|
244
|
+
throw new Error("claude-agent: process exited while consuming steer turn");
|
|
245
|
+
}
|
|
246
|
+
const parsed = next.value;
|
|
247
|
+
|
|
248
|
+
if (parsed.type === "session_id") {
|
|
249
|
+
this.opts.sessionManager.recordSessionId(sessionKey, parsed.sessionId);
|
|
250
|
+
yield { type: "runtime_session", runtimeSessionId: parsed.sessionId, runtimeLaneKey: sessionKey };
|
|
251
|
+
} else if (parsed.type === "turn_end") {
|
|
252
|
+
if (state.needsRestart) this.killProcess(sessionKey, state);
|
|
253
|
+
return;
|
|
254
|
+
} else if (parsed.type === "error") {
|
|
255
|
+
yield parsed;
|
|
256
|
+
} else if (parsed.type === "text") {
|
|
257
|
+
yield { ...parsed, project: false, groupKey };
|
|
258
|
+
} else if (parsed.type === "runtime_session") {
|
|
259
|
+
yield parsed;
|
|
260
|
+
} else {
|
|
261
|
+
yield { ...parsed, groupKey };
|
|
262
|
+
}
|
|
263
|
+
next = await state.parser.next();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
171
267
|
private async *runTurn(
|
|
172
268
|
sessionKey: string,
|
|
173
269
|
promptBody: string,
|
|
@@ -191,6 +287,30 @@ export class ClaudeCodeAdapter implements DispatchAdapter {
|
|
|
191
287
|
return;
|
|
192
288
|
}
|
|
193
289
|
|
|
290
|
+
if (state.steerReadPending) {
|
|
291
|
+
const orphaned = await state.steerReadPending;
|
|
292
|
+
state.steerReadPending = undefined;
|
|
293
|
+
if (!orphaned.done) {
|
|
294
|
+
const parsed = orphaned.value;
|
|
295
|
+
if (parsed.type === "session_id") {
|
|
296
|
+
this.opts.sessionManager.recordSessionId(sessionKey, parsed.sessionId);
|
|
297
|
+
yield { type: "runtime_session", runtimeSessionId: parsed.sessionId, runtimeLaneKey: sessionKey };
|
|
298
|
+
} else if (parsed.type === "turn_end") {
|
|
299
|
+
if (state.needsRestart) this.killProcess(sessionKey, state);
|
|
300
|
+
return;
|
|
301
|
+
} else if (parsed.type === "error") {
|
|
302
|
+
sawError = true;
|
|
303
|
+
yield parsed;
|
|
304
|
+
} else if (parsed.type === "text") {
|
|
305
|
+
yield { ...parsed, project: false, groupKey };
|
|
306
|
+
} else if (parsed.type === "runtime_session") {
|
|
307
|
+
yield parsed;
|
|
308
|
+
} else {
|
|
309
|
+
yield { ...parsed, groupKey };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
194
314
|
while (true) {
|
|
195
315
|
const next = await state.parser.next();
|
|
196
316
|
|