@turns/dev 0.0.18 → 0.0.20
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 +69 -6
- package/package.json +1 -1
package/dist/agent-anchor.js
CHANGED
|
@@ -198,11 +198,22 @@ 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 {
|
|
211
|
+
nativeTurnActive = false;
|
|
202
212
|
sessionId;
|
|
203
213
|
permissionMode;
|
|
204
214
|
turnWaiter;
|
|
205
215
|
control;
|
|
216
|
+
heldResult = null;
|
|
206
217
|
constructor(anchor, createSession) {
|
|
207
218
|
const profile = { command: 'claude' };
|
|
208
219
|
const nativePermissionMode = anchor.permissionMode === 'default' ? 'manual' : anchor.permissionMode;
|
|
@@ -235,9 +246,35 @@ class ClaudeTransport extends LineTransport {
|
|
|
235
246
|
this.turnWaiter = null;
|
|
236
247
|
this.control = new Map();
|
|
237
248
|
}
|
|
249
|
+
setNativeTurnActive(active) {
|
|
250
|
+
if (this.nativeTurnActive === active)
|
|
251
|
+
return;
|
|
252
|
+
this.nativeTurnActive = active;
|
|
253
|
+
if (this.anchor.transport === this && this.anchor.status === 'idle')
|
|
254
|
+
this.anchor.scheduleExit();
|
|
255
|
+
}
|
|
238
256
|
onFrame(frame) {
|
|
239
257
|
if (!frame || typeof frame !== 'object')
|
|
240
258
|
return;
|
|
259
|
+
// Background notifications can start native turns after the queued prompt
|
|
260
|
+
// has completed. Retain the process until the root result, including quiet
|
|
261
|
+
// foreground tools and gaps between streamed messages. Child results do
|
|
262
|
+
// not complete the root turn, and child output alone does not start one.
|
|
263
|
+
if (!frame.parent_tool_use_id) {
|
|
264
|
+
if (frame.type === 'result')
|
|
265
|
+
this.setNativeTurnActive(false);
|
|
266
|
+
else if (frame.type === 'stream_event' ||
|
|
267
|
+
frame.type === 'assistant' ||
|
|
268
|
+
(frame.type === 'system' && frame.subtype === 'status' && frame.status === 'requesting'))
|
|
269
|
+
this.setNativeTurnActive(true);
|
|
270
|
+
}
|
|
271
|
+
// Another turn starting means a held notification result was not the
|
|
272
|
+
// prompt's: keep waiting for the result of the turn now running.
|
|
273
|
+
if (this.heldResult &&
|
|
274
|
+
((frame.type === 'system' && frame.subtype === 'init') || frame.type === 'stream_event' || frame.type === 'assistant')) {
|
|
275
|
+
clearTimeout(this.heldResult.timer);
|
|
276
|
+
this.heldResult = null;
|
|
277
|
+
}
|
|
241
278
|
if (frame.type === 'system' && frame.subtype === 'init' && frame.session_id) {
|
|
242
279
|
this.sessionId = String(frame.session_id);
|
|
243
280
|
if (this.sessionId !== this.anchor.agentSessionId)
|
|
@@ -245,11 +282,20 @@ class ClaudeTransport extends LineTransport {
|
|
|
245
282
|
return;
|
|
246
283
|
}
|
|
247
284
|
if (frame.type === 'result' && this.turnWaiter) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
285
|
+
if (frame.origin?.kind === 'task-notification') {
|
|
286
|
+
if (this.heldResult)
|
|
287
|
+
clearTimeout(this.heldResult.timer);
|
|
288
|
+
const timer = setTimeout(() => {
|
|
289
|
+
if (this.heldResult?.frame !== frame)
|
|
290
|
+
return;
|
|
291
|
+
this.heldResult = null;
|
|
292
|
+
this.settleTurn(frame);
|
|
293
|
+
}, CLAUDE_NOTIFICATION_RESULT_GRACE_MS);
|
|
294
|
+
timer.unref?.();
|
|
295
|
+
this.heldResult = { frame, timer };
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.settleTurn(frame);
|
|
253
299
|
return;
|
|
254
300
|
}
|
|
255
301
|
if (frame.type === 'control_response') {
|
|
@@ -266,7 +312,21 @@ class ClaudeTransport extends LineTransport {
|
|
|
266
312
|
if (frame.type === 'control_request')
|
|
267
313
|
void this.answerReverse(frame);
|
|
268
314
|
}
|
|
315
|
+
settleTurn(frame) {
|
|
316
|
+
const waiter = this.turnWaiter;
|
|
317
|
+
if (!waiter)
|
|
318
|
+
return;
|
|
319
|
+
this.turnWaiter = null;
|
|
320
|
+
frame.is_error
|
|
321
|
+
? waiter.reject(new Error(frame.result || frame.subtype || 'Claude turn failed.'))
|
|
322
|
+
: waiter.resolve(frame);
|
|
323
|
+
}
|
|
269
324
|
onClose(error) {
|
|
325
|
+
this.setNativeTurnActive(false);
|
|
326
|
+
if (this.heldResult) {
|
|
327
|
+
clearTimeout(this.heldResult.timer);
|
|
328
|
+
this.heldResult = null;
|
|
329
|
+
}
|
|
270
330
|
if (this.turnWaiter) {
|
|
271
331
|
this.turnWaiter.reject(error);
|
|
272
332
|
this.turnWaiter = null;
|
|
@@ -1615,7 +1675,10 @@ class Anchor {
|
|
|
1615
1675
|
}
|
|
1616
1676
|
scheduleExit() {
|
|
1617
1677
|
clearTimeout(this.idleTimer);
|
|
1618
|
-
if (this.status === 'running' ||
|
|
1678
|
+
if (this.status === 'running' ||
|
|
1679
|
+
this.pendingInteractions.size ||
|
|
1680
|
+
this.transport?.backgroundTasks?.active ||
|
|
1681
|
+
this.transport?.nativeTurnActive) {
|
|
1619
1682
|
this.idleTimer = null;
|
|
1620
1683
|
return;
|
|
1621
1684
|
}
|