@turns/dev 0.0.17 → 0.0.19
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/agent-anchor.js +66 -5
- package/package.json +1 -1
package/dist/agent-anchor.js
CHANGED
|
@@ -198,11 +198,21 @@ class LineTransport {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* How long a background notification's `result` is held before it is taken as
|
|
203
|
+
* the end of the prompt's turn. Claude answers a task notification with a turn
|
|
204
|
+
* of its own, stamped `origin.kind: "task-notification"`; when a prompt is
|
|
205
|
+
* waiting behind it, that turn's `result` arrives first and the prompt's turn
|
|
206
|
+
* starts right after it. If Claude instead folded the prompt into the
|
|
207
|
+
* notification's turn, nothing follows, and the held result is the prompt's.
|
|
208
|
+
*/
|
|
209
|
+
const CLAUDE_NOTIFICATION_RESULT_GRACE_MS = 2_000;
|
|
201
210
|
class ClaudeTransport extends LineTransport {
|
|
202
211
|
sessionId;
|
|
203
212
|
permissionMode;
|
|
204
213
|
turnWaiter;
|
|
205
214
|
control;
|
|
215
|
+
heldResult = null;
|
|
206
216
|
constructor(anchor, createSession) {
|
|
207
217
|
const profile = { command: 'claude' };
|
|
208
218
|
const nativePermissionMode = anchor.permissionMode === 'default' ? 'manual' : anchor.permissionMode;
|
|
@@ -238,6 +248,13 @@ class ClaudeTransport extends LineTransport {
|
|
|
238
248
|
onFrame(frame) {
|
|
239
249
|
if (!frame || typeof frame !== 'object')
|
|
240
250
|
return;
|
|
251
|
+
// Another turn starting means a held notification result was not the
|
|
252
|
+
// prompt's: keep waiting for the result of the turn now running.
|
|
253
|
+
if (this.heldResult &&
|
|
254
|
+
((frame.type === 'system' && frame.subtype === 'init') || frame.type === 'stream_event' || frame.type === 'assistant')) {
|
|
255
|
+
clearTimeout(this.heldResult.timer);
|
|
256
|
+
this.heldResult = null;
|
|
257
|
+
}
|
|
241
258
|
if (frame.type === 'system' && frame.subtype === 'init' && frame.session_id) {
|
|
242
259
|
this.sessionId = String(frame.session_id);
|
|
243
260
|
if (this.sessionId !== this.anchor.agentSessionId)
|
|
@@ -245,11 +262,20 @@ class ClaudeTransport extends LineTransport {
|
|
|
245
262
|
return;
|
|
246
263
|
}
|
|
247
264
|
if (frame.type === 'result' && this.turnWaiter) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
265
|
+
if (frame.origin?.kind === 'task-notification') {
|
|
266
|
+
if (this.heldResult)
|
|
267
|
+
clearTimeout(this.heldResult.timer);
|
|
268
|
+
const timer = setTimeout(() => {
|
|
269
|
+
if (this.heldResult?.frame !== frame)
|
|
270
|
+
return;
|
|
271
|
+
this.heldResult = null;
|
|
272
|
+
this.settleTurn(frame);
|
|
273
|
+
}, CLAUDE_NOTIFICATION_RESULT_GRACE_MS);
|
|
274
|
+
timer.unref?.();
|
|
275
|
+
this.heldResult = { frame, timer };
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.settleTurn(frame);
|
|
253
279
|
return;
|
|
254
280
|
}
|
|
255
281
|
if (frame.type === 'control_response') {
|
|
@@ -266,7 +292,20 @@ class ClaudeTransport extends LineTransport {
|
|
|
266
292
|
if (frame.type === 'control_request')
|
|
267
293
|
void this.answerReverse(frame);
|
|
268
294
|
}
|
|
295
|
+
settleTurn(frame) {
|
|
296
|
+
const waiter = this.turnWaiter;
|
|
297
|
+
if (!waiter)
|
|
298
|
+
return;
|
|
299
|
+
this.turnWaiter = null;
|
|
300
|
+
frame.is_error
|
|
301
|
+
? waiter.reject(new Error(frame.result || frame.subtype || 'Claude turn failed.'))
|
|
302
|
+
: waiter.resolve(frame);
|
|
303
|
+
}
|
|
269
304
|
onClose(error) {
|
|
305
|
+
if (this.heldResult) {
|
|
306
|
+
clearTimeout(this.heldResult.timer);
|
|
307
|
+
this.heldResult = null;
|
|
308
|
+
}
|
|
270
309
|
if (this.turnWaiter) {
|
|
271
310
|
this.turnWaiter.reject(error);
|
|
272
311
|
this.turnWaiter = null;
|
|
@@ -330,6 +369,28 @@ class ClaudeTransport extends LineTransport {
|
|
|
330
369
|
timer.unref?.();
|
|
331
370
|
return waiter.promise.finally(() => clearTimeout(timer));
|
|
332
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Claude Code folds a stdin `user` frame that arrives during a turn into that
|
|
374
|
+
* turn: the model reads it after the tool calls in flight finish, within the
|
|
375
|
+
* same `result`, and the transcript records it as a `queued_command`
|
|
376
|
+
* attachment rather than a new user row (verified on 2.1.280). `priority`
|
|
377
|
+
* selects what happens to the running work. `next` is the fold described
|
|
378
|
+
* above and matches what the interactive TUI does when the user types while
|
|
379
|
+
* Claude works. `now` would end the running turn with `aborted_tools` and
|
|
380
|
+
* answer the message as a fresh turn, which is the interrupt fallback the
|
|
381
|
+
* queue already offers, so it is not used here.
|
|
382
|
+
*/
|
|
383
|
+
steer(text, _messageId = '') {
|
|
384
|
+
if (!this.turnWaiter)
|
|
385
|
+
throw new Error('Claude has no active turn to steer.');
|
|
386
|
+
this.emit({
|
|
387
|
+
type: 'user',
|
|
388
|
+
session_id: this.sessionId,
|
|
389
|
+
message: { role: 'user', content: [{ type: 'text', text }] },
|
|
390
|
+
parent_tool_use_id: null,
|
|
391
|
+
priority: 'next',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
333
394
|
async interrupt() {
|
|
334
395
|
try {
|
|
335
396
|
await this.controlRequest('interrupt');
|