@yu_robotics/remote-cli 1.6.139 → 1.6.145
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/README.md +53 -13
- package/dist/client/MessageHandler.d.ts +2 -0
- package/dist/client/MessageHandler.d.ts.map +1 -1
- package/dist/client/MessageHandler.js +86 -9
- package/dist/client/MessageHandler.js.map +1 -1
- package/dist/client/WebSocketClient.d.ts.map +1 -1
- package/dist/client/WebSocketClient.js +1 -0
- package/dist/client/WebSocketClient.js.map +1 -1
- package/dist/delegation/DelegatedWorkspaceManager.d.ts.map +1 -1
- package/dist/delegation/DelegatedWorkspaceManager.js +38 -4
- package/dist/delegation/DelegatedWorkspaceManager.js.map +1 -1
- package/dist/delegation/DelegationManager.d.ts +4 -0
- package/dist/delegation/DelegationManager.d.ts.map +1 -1
- package/dist/delegation/DelegationManager.js +195 -93
- package/dist/delegation/DelegationManager.js.map +1 -1
- package/dist/delegation/DelegationStatusFormatter.d.ts.map +1 -1
- package/dist/delegation/DelegationStatusFormatter.js +6 -5
- package/dist/delegation/DelegationStatusFormatter.js.map +1 -1
- package/dist/delegation/GitCheckpoint.d.ts +10 -2
- package/dist/delegation/GitCheckpoint.d.ts.map +1 -1
- package/dist/delegation/GitCheckpoint.js +286 -41
- package/dist/delegation/GitCheckpoint.js.map +1 -1
- package/dist/delegation/WorkerPolicy.d.ts +4 -1
- package/dist/delegation/WorkerPolicy.d.ts.map +1 -1
- package/dist/delegation/WorkerPolicy.js +17 -8
- package/dist/delegation/WorkerPolicy.js.map +1 -1
- package/dist/delegation/contract.d.ts +2 -2
- package/dist/delegation/contract.d.ts.map +1 -1
- package/dist/delegation/contract.js +3 -3
- package/dist/delegation/contract.js.map +1 -1
- package/dist/executor/AcpExecutor.d.ts +1 -0
- package/dist/executor/AcpExecutor.d.ts.map +1 -1
- package/dist/executor/AcpExecutor.js +21 -1
- package/dist/executor/AcpExecutor.js.map +1 -1
- package/dist/executor/Activity.d.ts +39 -0
- package/dist/executor/Activity.d.ts.map +1 -0
- package/dist/executor/Activity.js +266 -0
- package/dist/executor/Activity.js.map +1 -0
- package/dist/executor/AgyExecutor.d.ts +1 -0
- package/dist/executor/AgyExecutor.d.ts.map +1 -1
- package/dist/executor/AgyExecutor.js +8 -0
- package/dist/executor/AgyExecutor.js.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.d.ts +4 -1
- package/dist/executor/ClaudePersistentExecutor.d.ts.map +1 -1
- package/dist/executor/ClaudePersistentExecutor.js +8 -0
- package/dist/executor/ClaudePersistentExecutor.js.map +1 -1
- package/dist/executor/CodexAppServerExecutor.d.ts +1 -0
- package/dist/executor/CodexAppServerExecutor.d.ts.map +1 -1
- package/dist/executor/CodexAppServerExecutor.js +33 -9
- package/dist/executor/CodexAppServerExecutor.js.map +1 -1
- package/dist/executor/IExecutor.d.ts +3 -1
- package/dist/executor/IExecutor.d.ts.map +1 -1
- package/dist/executor/PiExecutor.d.ts +1 -0
- package/dist/executor/PiExecutor.d.ts.map +1 -1
- package/dist/executor/PiExecutor.js +10 -0
- package/dist/executor/PiExecutor.js.map +1 -1
- package/dist/executor/index.d.ts +1 -0
- package/dist/executor/index.d.ts.map +1 -1
- package/dist/executor/index.js +7 -1
- package/dist/executor/index.js.map +1 -1
- package/dist/maintenance/release-notes.json +1 -1
- package/dist/types/index.d.ts +12 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/ActivityProgress.d.ts +20 -0
- package/dist/utils/ActivityProgress.d.ts.map +1 -0
- package/dist/utils/ActivityProgress.js +69 -0
- package/dist/utils/ActivityProgress.js.map +1 -0
- package/package.json +1 -1
|
@@ -21,11 +21,13 @@ const WorkerPolicy_1 = require("./WorkerPolicy");
|
|
|
21
21
|
const DelegationNotice_1 = require("./DelegationNotice");
|
|
22
22
|
const WorkingDirectory_1 = require("../utils/WorkingDirectory");
|
|
23
23
|
const DelegatedWorkspaceManager_1 = require("./DelegatedWorkspaceManager");
|
|
24
|
+
const ActivityProgress_1 = require("../utils/ActivityProgress");
|
|
25
|
+
const Activity_1 = require("../executor/Activity");
|
|
24
26
|
exports.DELEGATION_LIMITS = { launches: 12, concurrent: 5, closeoutRounds: 2,
|
|
25
27
|
idleTimeoutMs: 15 * 60_000, toolIdleTimeoutMs: 45 * 60_000,
|
|
26
28
|
queueTimeoutMs: 60 * 60_000,
|
|
27
29
|
resultBytes: 32 * 1024, continuationBytes: 64 * 1024,
|
|
28
|
-
storageTimeoutMs: 10_000, calls: 500 };
|
|
30
|
+
storageTimeoutMs: 10_000, toolResponseWaitMs: 20_000, calls: 500 };
|
|
29
31
|
/** Bounds and cadence for optional display-only delegated-worker text. */
|
|
30
32
|
exports.DELEGATION_TEXT_PROGRESS = {
|
|
31
33
|
flushMs: 2_500,
|
|
@@ -255,6 +257,7 @@ class DelegationManager {
|
|
|
255
257
|
if (task.record.state === 'running')
|
|
256
258
|
captureWorkerMetadata(task);
|
|
257
259
|
return parent.onProgress?.({ taskId: task.record.id, backend: task.record.backend, ...progress,
|
|
260
|
+
...(parent.onActivityProgress && task.activity ? { activity: { ...task.activity } } : {}),
|
|
258
261
|
...(task.lane ? { workerContext: { laneId: task.lane.id, generation: task.lane.contextGeneration ?? 0 } } : {}),
|
|
259
262
|
...(task.executionMetadata ? { executionMetadata: { ...task.executionMetadata } } : {}) }) === true;
|
|
260
263
|
}
|
|
@@ -265,6 +268,8 @@ class DelegationManager {
|
|
|
265
268
|
};
|
|
266
269
|
const reportTextProgress = (task, latestText) => {
|
|
267
270
|
try {
|
|
271
|
+
if (task.executor?.isWaitingInput?.())
|
|
272
|
+
return false;
|
|
268
273
|
captureWorkerMetadata(task);
|
|
269
274
|
return parent.onTextProgress?.({
|
|
270
275
|
taskId: task.record.id,
|
|
@@ -272,6 +277,7 @@ class DelegationManager {
|
|
|
272
277
|
phase: 'text',
|
|
273
278
|
latestText,
|
|
274
279
|
startedAt: task.record.startedAt,
|
|
280
|
+
...(parent.onActivityProgress && task.executor?.isWaitingInput?.() === false ? { waitingForInput: false } : {}),
|
|
275
281
|
...(task.executionMetadata ? { executionMetadata: { ...task.executionMetadata } } : {}),
|
|
276
282
|
}) === true;
|
|
277
283
|
}
|
|
@@ -286,8 +292,28 @@ class DelegationManager {
|
|
|
286
292
|
if (discardPending)
|
|
287
293
|
task.textBuffer = undefined;
|
|
288
294
|
};
|
|
295
|
+
const recordActivity = (task, value) => {
|
|
296
|
+
if (!parent.onActivityProgress || closed || task.record.state !== 'running' || task.executor?.isWaitingInput?.())
|
|
297
|
+
return;
|
|
298
|
+
const activity = (0, Activity_1.normalizeActivity)(value);
|
|
299
|
+
if (!activity)
|
|
300
|
+
return;
|
|
301
|
+
if (task.executor?.isWaitingInput?.() === false)
|
|
302
|
+
task.waitingInputText = undefined;
|
|
303
|
+
task.activity = activity;
|
|
304
|
+
task.activityProgress ??= new ActivityProgress_1.ActivityProgress(snapshot => {
|
|
305
|
+
if (closed || task.record.state !== 'running' || task.executor?.isWaitingInput?.())
|
|
306
|
+
return false;
|
|
307
|
+
captureWorkerMetadata(task);
|
|
308
|
+
return parent.onActivityProgress?.({ taskId: task.record.id, backend: task.record.backend,
|
|
309
|
+
phase: 'text', activity: snapshot, startedAt: task.record.startedAt,
|
|
310
|
+
...(task.executor?.isWaitingInput?.() === false ? { waitingForInput: false } : {}),
|
|
311
|
+
...(task.executionMetadata ? { executionMetadata: { ...task.executionMetadata } } : {}) }) === true;
|
|
312
|
+
});
|
|
313
|
+
task.activityProgress.offer(activity);
|
|
314
|
+
};
|
|
289
315
|
const flushTextProgress = (task) => {
|
|
290
|
-
if (closed || task.record.state !== 'running' || !task.textProgressEnabled)
|
|
316
|
+
if (closed || task.record.state !== 'running' || !task.textProgressEnabled || task.executor?.isWaitingInput?.())
|
|
291
317
|
return;
|
|
292
318
|
const latestText = task.textBuffer ? latestDisplayText(task.textBuffer) : undefined;
|
|
293
319
|
if (!latestText || latestText === task.lastReportedText)
|
|
@@ -312,6 +338,8 @@ class DelegationManager {
|
|
|
312
338
|
const recordDisplayText = (task, text) => {
|
|
313
339
|
if (!task.textProgressEnabled || !text)
|
|
314
340
|
return;
|
|
341
|
+
if (task.executor?.isWaitingInput?.() === false)
|
|
342
|
+
task.waitingInputText = undefined;
|
|
315
343
|
task.textBuffer = tailText(`${task.textBuffer ?? ''}${text}`, exports.DELEGATION_TEXT_PROGRESS.bufferBytes);
|
|
316
344
|
scheduleTextProgress(task);
|
|
317
345
|
};
|
|
@@ -349,6 +377,7 @@ class DelegationManager {
|
|
|
349
377
|
};
|
|
350
378
|
const stop = (task) => task.stop ??= (async () => {
|
|
351
379
|
clearTextProgress(task, true);
|
|
380
|
+
task.activityProgress?.dispose();
|
|
352
381
|
if (!task.executor) {
|
|
353
382
|
// A cancellation can race with durable lane acquisition. No worker
|
|
354
383
|
// process owns this lane yet, so it must not become a reusable empty
|
|
@@ -404,6 +433,7 @@ class DelegationManager {
|
|
|
404
433
|
};
|
|
405
434
|
const finishTask = async (task) => {
|
|
406
435
|
clearTimeout(task.queueTimer);
|
|
436
|
+
task.activityProgress?.dispose();
|
|
407
437
|
task.record.finishedAt = Date.now();
|
|
408
438
|
await deadline(async () => { await save(task); await this.store.prune(); }, this.cleanupMs)
|
|
409
439
|
.catch(() => console.warn('[Delegation] Final task metadata could not be saved or pruned'));
|
|
@@ -466,7 +496,7 @@ class DelegationManager {
|
|
|
466
496
|
}
|
|
467
497
|
assertWorkspace();
|
|
468
498
|
};
|
|
469
|
-
const
|
|
499
|
+
const prepareWorkerWorkspace = async () => {
|
|
470
500
|
assertStartable();
|
|
471
501
|
if (!task.lane) {
|
|
472
502
|
const acquired = await this.laneStore.acquire(identity, source
|
|
@@ -480,17 +510,25 @@ class DelegationManager {
|
|
|
480
510
|
await this.laneStore.markDirty(acquired.lane.id, 'Worker setup completed after its task ended.', true).catch(() => undefined);
|
|
481
511
|
}
|
|
482
512
|
}
|
|
513
|
+
assertStartable();
|
|
514
|
+
if (source && !task.workspace) {
|
|
515
|
+
baseline ??= this.workspaceManager.baseline(source);
|
|
516
|
+
const input = await baseline;
|
|
517
|
+
assertStartable();
|
|
518
|
+
task.workspacePreparationStarted = true;
|
|
519
|
+
task.workspace = await this.workspaceManager.prepare(task.lane, source, input, task.record.id);
|
|
520
|
+
assertStartable();
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const createWorker = async () => {
|
|
483
524
|
let worker;
|
|
484
525
|
try {
|
|
526
|
+
// Shared-directory acquisition keeps its existing startup deadline.
|
|
527
|
+
if (!source)
|
|
528
|
+
await prepareWorkerWorkspace();
|
|
485
529
|
assertStartable();
|
|
486
|
-
if (
|
|
487
|
-
|
|
488
|
-
const input = await baseline;
|
|
489
|
-
assertStartable();
|
|
490
|
-
task.workspacePreparationStarted = true;
|
|
491
|
-
task.workspace = await this.workspaceManager.prepare(task.lane, source, input, task.record.id);
|
|
492
|
-
assertStartable();
|
|
493
|
-
}
|
|
530
|
+
if (!task.lane)
|
|
531
|
+
throw new Error('Worker lane is unavailable before native startup');
|
|
494
532
|
const executionCwd = task.workspace?.cwd ?? cwd;
|
|
495
533
|
const workerGuard = task.workspace ? new DirectoryGuard_1.DirectoryGuard([...this.guard.getAllowedDirectories(), executionCwd]) : this.guard;
|
|
496
534
|
worker = this.factory(workerGuard, configuration, executionCwd, task.lane.executorThreadId, (0, ThreadExecutorPool_1.resolveThreadModel)(parent.thread, configuration), parent.thread.efforts?.[backend], { lifecycleHooks: false, delegationWorker: true });
|
|
@@ -546,6 +584,11 @@ class DelegationManager {
|
|
|
546
584
|
}
|
|
547
585
|
let result;
|
|
548
586
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
587
|
+
// Owned Git work and repository-lock waits are not native startup.
|
|
588
|
+
// Await them even on cancellation: no mutation may outlive its slot
|
|
589
|
+
// or source lease, and the final startable check prevents late launch.
|
|
590
|
+
if (source)
|
|
591
|
+
await prepareWorkerWorkspace();
|
|
549
592
|
let setupPending = true;
|
|
550
593
|
const setup = createWorker().finally(() => { setupPending = false; });
|
|
551
594
|
let worker;
|
|
@@ -572,6 +615,7 @@ class DelegationManager {
|
|
|
572
615
|
}
|
|
573
616
|
// Native session IDs are distinct; policy was resolved against the real
|
|
574
617
|
// parent thread before passing the synthetic worker identity to the factory.
|
|
618
|
+
let acceptingActivity = true;
|
|
575
619
|
const execution = worker.execute(`You are executing one delegated task in an isolated worker session. This session may contain context from earlier delegated tasks in this thread and workspace. Complete only this objective and return a concise result with verification and remaining issues. Do not delegate to other agents.${task.workspace
|
|
576
620
|
? `\nThis task runs in an owned Git worktree at ${task.workspace.cwd}, with a fresh detached task checkout based on checkpoint ${task.workspace.input.commit}. Previous task changes are NOT implicitly present. Re-read current files; earlier conversation context may be stale. Stay within this checkout. Do not merge into or edit the delivery directory, push, change branches, or remove the worktree. Your file changes will be captured as an artifact for explicit coordinator integration. Ignored files and dependencies are not copied from the delivery directory. A worktree is not an OS sandbox.` : ''}
|
|
577
621
|
|
|
@@ -580,6 +624,10 @@ ${objective}`, {
|
|
|
580
624
|
// backend-local limits that would otherwise ignore tool callbacks.
|
|
581
625
|
timeout: 0,
|
|
582
626
|
inactivityTimeout: 0,
|
|
627
|
+
onActivity: parent.onActivityProgress ? value => {
|
|
628
|
+
if (acceptingActivity && task.executor === worker)
|
|
629
|
+
recordActivity(task, value);
|
|
630
|
+
} : undefined,
|
|
583
631
|
onStream: text => {
|
|
584
632
|
// Generic streams may include reasoning or backend notices. Use
|
|
585
633
|
// this channel only to detect interactive input requests.
|
|
@@ -588,6 +636,7 @@ ${objective}`, {
|
|
|
588
636
|
return;
|
|
589
637
|
if (task.executor?.isWaitingInput?.()) {
|
|
590
638
|
clearTextProgress(task, true);
|
|
639
|
+
task.activityProgress?.clear();
|
|
591
640
|
const prompt = bounded(text, 4000).text;
|
|
592
641
|
if (task.waitingInputText === prompt)
|
|
593
642
|
return;
|
|
@@ -633,20 +682,41 @@ ${prompt}`));
|
|
|
633
682
|
tool_use_id: toolResult.tool_use_id, content: '', is_error: toolResult.is_error,
|
|
634
683
|
}, startedAt: task.record.startedAt });
|
|
635
684
|
},
|
|
636
|
-
onApprovalRequest: request =>
|
|
637
|
-
|
|
638
|
-
|
|
685
|
+
onApprovalRequest: request => {
|
|
686
|
+
if (closed || task.record.state !== 'running')
|
|
687
|
+
return false;
|
|
688
|
+
task.activityProgress?.clear();
|
|
689
|
+
if (parent.onActivityProgress)
|
|
690
|
+
reportProgress(task, { phase: 'waiting_input', summary: 'Approval required', startedAt: task.record.startedAt });
|
|
691
|
+
return parent.onApproval({ ...request,
|
|
692
|
+
description: `[${backend} delegated task] ${request.description}`, canRemember: false }, task.executor);
|
|
693
|
+
},
|
|
694
|
+
onApprovalResolved: (id, status) => {
|
|
695
|
+
parent.onApprovalResolved(id, status);
|
|
696
|
+
if (acceptingActivity && task.executor === worker && worker.isWaitingInput?.() === false) {
|
|
697
|
+
task.waitingInputText = undefined;
|
|
698
|
+
recordActivity(task, { source: 'state', text: 'Working' });
|
|
699
|
+
}
|
|
700
|
+
},
|
|
639
701
|
});
|
|
640
702
|
// Start the inactivity window only once the worker has accepted the
|
|
641
703
|
// objective and can emit its first real progress callback.
|
|
642
704
|
task.signalDispatch();
|
|
643
705
|
armIdleTimer(task);
|
|
644
|
-
|
|
706
|
+
try {
|
|
707
|
+
result = await Promise.race([task.interrupted, execution]);
|
|
708
|
+
}
|
|
709
|
+
finally {
|
|
710
|
+
acceptingActivity = false;
|
|
711
|
+
}
|
|
645
712
|
captureWorkerMetadata(task);
|
|
646
713
|
metadataCaptured = true;
|
|
647
714
|
if (!result.success && attempt === 0 && worker.consumeSessionResumeFailure?.()) {
|
|
648
715
|
clearTaskTimer(task);
|
|
649
716
|
clearTextProgress(task, true);
|
|
717
|
+
task.activityProgress?.dispose();
|
|
718
|
+
task.activityProgress = undefined;
|
|
719
|
+
task.activity = undefined;
|
|
650
720
|
task.lastReportedText = undefined;
|
|
651
721
|
if (await discardMissingSessionLane())
|
|
652
722
|
continue;
|
|
@@ -675,76 +745,97 @@ ${prompt}`));
|
|
|
675
745
|
captureWorkerMetadata(task);
|
|
676
746
|
clearTaskTimer(task);
|
|
677
747
|
clearTextProgress(task, true);
|
|
748
|
+
task.activityProgress?.dispose();
|
|
678
749
|
if (task.record.startedAt === undefined)
|
|
679
750
|
task.discardLane = !task.preserveUnstartedLane;
|
|
680
751
|
let released = !task.quarantineWorkspace;
|
|
752
|
+
let nativeExitConfirmed = false;
|
|
681
753
|
let readyWorkspaceLane;
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
754
|
+
const finalizeLane = async () => {
|
|
755
|
+
if (task.stop)
|
|
756
|
+
await deadline(() => task.stop, this.cleanupMs);
|
|
757
|
+
else if (task.executor)
|
|
758
|
+
await deadline(() => destroyWorker(task.executor), this.cleanupMs);
|
|
759
|
+
else if (task.lane && !task.preserveUnstartedLane) {
|
|
760
|
+
// A scope can close after durable acquisition but before construction.
|
|
761
|
+
// No backend process was started, so discard the lane rather than
|
|
762
|
+
// reserving the workspace indefinitely.
|
|
763
|
+
task.discardLane = true;
|
|
764
|
+
}
|
|
765
|
+
nativeExitConfirmed = true;
|
|
766
|
+
if (released && task.lane && task.preserveUnstartedLane) {
|
|
767
|
+
if (!task.workspacePreparationStarted) {
|
|
768
|
+
// Only acquisition changed: no checkout or executor was touched.
|
|
769
|
+
await this.laneStore.markReady(task.lane.id);
|
|
693
770
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
771
|
+
else {
|
|
772
|
+
await this.workspaceManager.preserveLane(task.lane.id);
|
|
773
|
+
await this.laneStore.markDirty(task.lane.id, `${task.workspace ? 'Worker process setup did not complete' : 'Worker checkout preparation failed'}; existing context and files were retained for manual recovery.`);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (released && task.lane && task.discardLane) {
|
|
777
|
+
const lane = task.lane;
|
|
778
|
+
await this.laneStore.markDirty(lane.id, 'Delegated worker setup ended before a reusable context was established.', true).catch(() => undefined);
|
|
779
|
+
await discardLane(lane);
|
|
780
|
+
task.lane = undefined;
|
|
781
|
+
}
|
|
782
|
+
if (released && task.lane && !task.preserveUnstartedLane) {
|
|
783
|
+
try {
|
|
784
|
+
if (task.workspace) {
|
|
785
|
+
task.artifact = await this.workspaceManager.collect(task.workspace, task.record.state === 'succeeded');
|
|
786
|
+
readyWorkspaceLane = task.lane;
|
|
698
787
|
}
|
|
699
788
|
else {
|
|
700
|
-
await this.
|
|
701
|
-
await this.laneStore.markDirty(task.lane.id, `${task.workspace ? 'Worker process setup did not complete' : 'Worker checkout preparation failed'}; existing context and files were retained for manual recovery.`);
|
|
789
|
+
await this.laneStore.markReady(task.lane.id);
|
|
702
790
|
}
|
|
703
791
|
}
|
|
704
|
-
|
|
705
|
-
const
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
task.artifact = await this.workspaceManager.collect(task.workspace, task.record.state === 'succeeded');
|
|
714
|
-
readyWorkspaceLane = task.lane;
|
|
715
|
-
}
|
|
716
|
-
else {
|
|
717
|
-
await this.laneStore.markReady(task.lane.id);
|
|
718
|
-
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
const detail = error instanceof Error ? error.message : 'Worker lane persistence failed';
|
|
794
|
+
if (task.workspace) {
|
|
795
|
+
// Exit was confirmed. Preserve files and context for recovery without blocking unrelated lanes.
|
|
796
|
+
await this.workspaceManager.preserveLane(task.lane.id).catch(() => undefined);
|
|
797
|
+
await this.laneStore.markDirty(task.lane.id, detail).catch(() => undefined);
|
|
798
|
+
if (task.record.state === 'succeeded')
|
|
799
|
+
task.record.state = 'failed';
|
|
800
|
+
task.record.error = bounded(`Worker artifact could not be finalized: ${detail}. Files were retained for manual recovery.`, 4000).text;
|
|
719
801
|
}
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
// Exit was confirmed. Preserve files and context for recovery without blocking unrelated lanes.
|
|
724
|
-
await this.workspaceManager.preserveLane(task.lane.id).catch(() => undefined);
|
|
725
|
-
await this.laneStore.markDirty(task.lane.id, detail).catch(() => undefined);
|
|
726
|
-
if (task.record.state === 'succeeded')
|
|
727
|
-
task.record.state = 'failed';
|
|
728
|
-
task.record.error = bounded(`Worker artifact could not be finalized: ${detail}. Files were retained for manual recovery.`, 4000).text;
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
await this.laneStore.markDirty(task.lane.id, detail, true).catch(() => undefined);
|
|
732
|
-
await discardLane(task.lane);
|
|
733
|
-
}
|
|
734
|
-
console.warn('[Delegation] Worker lane could not be retained:', detail);
|
|
802
|
+
else {
|
|
803
|
+
await this.laneStore.markDirty(task.lane.id, detail, true).catch(() => undefined);
|
|
804
|
+
await discardLane(task.lane);
|
|
735
805
|
}
|
|
806
|
+
console.warn('[Delegation] Worker lane could not be retained:', detail);
|
|
736
807
|
}
|
|
737
|
-
}
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
try {
|
|
811
|
+
// Collection owns Git mutations and must settle before releasing the
|
|
812
|
+
// lane, slot, or source lease. Individual Git commands remain bounded.
|
|
813
|
+
if (source)
|
|
814
|
+
await finalizeLane();
|
|
815
|
+
else
|
|
816
|
+
await deadline(finalizeLane, this.cleanupMs);
|
|
738
817
|
}
|
|
739
818
|
catch (error) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
819
|
+
const detail = error instanceof Error ? error.message : 'Worker cleanup could not be confirmed';
|
|
820
|
+
if (source && released && nativeExitConfirmed) {
|
|
821
|
+
readyWorkspaceLane = undefined;
|
|
822
|
+
if (task.lane) {
|
|
823
|
+
await this.workspaceManager.preserveLane(task.lane.id).catch(() => undefined);
|
|
824
|
+
await this.laneStore.markDirty(task.lane.id, detail).catch(() => undefined);
|
|
825
|
+
}
|
|
826
|
+
if (task.record.state === 'succeeded')
|
|
827
|
+
task.record.state = 'failed';
|
|
828
|
+
task.record.error = bounded(`Worker lane could not be finalized: ${detail}. Files were retained for manual recovery.`, 4000).text;
|
|
829
|
+
}
|
|
830
|
+
else {
|
|
831
|
+
released = false;
|
|
832
|
+
task.quarantineWorkspace = true;
|
|
833
|
+
if (task.lane) {
|
|
834
|
+
await deadline(() => this.laneStore.markDirty(task.lane.id, detail), this.cleanupMs).catch(() => undefined);
|
|
835
|
+
}
|
|
836
|
+
task.record.state = 'interrupted';
|
|
837
|
+
task.record.error = 'Worker cleanup could not be confirmed. This workspace is blocked until the worker is stopped and the CLI restarts.';
|
|
745
838
|
}
|
|
746
|
-
task.record.state = 'interrupted';
|
|
747
|
-
task.record.error = 'Worker cleanup could not be confirmed. This workspace is blocked until the worker is stopped and the CLI restarts.';
|
|
748
839
|
console.warn('[Delegation] Worker cleanup failed:', error instanceof Error ? error.message : 'Cleanup failure');
|
|
749
840
|
}
|
|
750
841
|
if (released && readyWorkspaceLane) {
|
|
@@ -815,6 +906,18 @@ ${prompt}`));
|
|
|
815
906
|
if (closed)
|
|
816
907
|
throw new Error('Delegation turn has ended');
|
|
817
908
|
const requestedDuring = execution;
|
|
909
|
+
const responseDeadline = Date.now() + exports.DELEGATION_LIMITS.toolResponseWaitMs;
|
|
910
|
+
const waitForResponse = async (pending) => {
|
|
911
|
+
let timer;
|
|
912
|
+
try {
|
|
913
|
+
await Promise.race([pending, new Promise(resolve => {
|
|
914
|
+
timer = setTimeout(resolve, Math.max(0, responseDeadline - Date.now()));
|
|
915
|
+
})]);
|
|
916
|
+
}
|
|
917
|
+
finally {
|
|
918
|
+
clearTimeout(timer);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
818
921
|
if (!callId || callId.length > 200)
|
|
819
922
|
throw new Error('Invalid tool call ID');
|
|
820
923
|
const signature = JSON.stringify({ name, args });
|
|
@@ -830,25 +933,8 @@ ${prompt}`));
|
|
|
830
933
|
if (name === 'remote_cli_list_backends') {
|
|
831
934
|
await discoverWorkspace();
|
|
832
935
|
const backends = await this.registry.list(parent.config);
|
|
833
|
-
return { backends: backends.map(item =>
|
|
834
|
-
|
|
835
|
-
try {
|
|
836
|
-
configure('inherit');
|
|
837
|
-
}
|
|
838
|
-
catch (error) {
|
|
839
|
-
return { ...item, worker: false, readOnly: false, reason: error.message };
|
|
840
|
-
}
|
|
841
|
-
let readOnly = item.readOnly;
|
|
842
|
-
if (readOnly) {
|
|
843
|
-
try {
|
|
844
|
-
configure('read_only');
|
|
845
|
-
}
|
|
846
|
-
catch {
|
|
847
|
-
readOnly = false;
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
return { ...item, readOnly };
|
|
851
|
-
}), workspace: cwd, maxConcurrentChildren: maxChildren,
|
|
936
|
+
return { backends: backends.map(item => (0, WorkerPolicy_1.workerAvailability)(parent.config, this.guard, parent.thread.id, parent.backend, item)),
|
|
937
|
+
workspace: cwd, maxConcurrentChildren: maxChildren,
|
|
852
938
|
maxTasksPerRequest: exports.DELEGATION_LIMITS.launches, scheduling: source ? 'isolated-worktrees' : 'serial',
|
|
853
939
|
queueTimeoutSeconds: this.timeouts.queueTimeoutMs / 1000 };
|
|
854
940
|
}
|
|
@@ -870,7 +956,16 @@ ${prompt}`));
|
|
|
870
956
|
}
|
|
871
957
|
if (name === 'remote_cli_cancel') {
|
|
872
958
|
const task = getTask(args.taskId);
|
|
873
|
-
|
|
959
|
+
const cancelling = cancel(task);
|
|
960
|
+
if (source)
|
|
961
|
+
await waitForResponse(cancelling);
|
|
962
|
+
else
|
|
963
|
+
await cancelling;
|
|
964
|
+
if (source && task.record.finishedAt === undefined) {
|
|
965
|
+
return { ...receive(task, requestedDuring), statusDetail: task.record.state === 'cancelled'
|
|
966
|
+
? 'Cancellation requested; owned operations are still settling. Poll this taskId with remote_cli_result. The workspace remains reserved until cleanup completes.'
|
|
967
|
+
: 'Task finalization is still in progress. Poll this taskId with remote_cli_result. The workspace remains reserved until cleanup completes.' };
|
|
968
|
+
}
|
|
874
969
|
return receive(task, requestedDuring);
|
|
875
970
|
}
|
|
876
971
|
if (name === 'remote_cli_integrate') {
|
|
@@ -1015,9 +1110,16 @@ ${prompt}`));
|
|
|
1015
1110
|
start();
|
|
1016
1111
|
}
|
|
1017
1112
|
if (task.ownsSlot) {
|
|
1018
|
-
// Preserve
|
|
1019
|
-
//
|
|
1020
|
-
|
|
1113
|
+
// Preserve prompt dispatch responses, but do not let slow managed Git
|
|
1114
|
+
// work outlive the tool adapter's HTTP timeout. Returning an accepted
|
|
1115
|
+
// task ID never cancels its work or releases its lease/execution slot.
|
|
1116
|
+
if (source)
|
|
1117
|
+
await waitForResponse(task.dispatched);
|
|
1118
|
+
else
|
|
1119
|
+
await task.dispatched;
|
|
1120
|
+
if (source && task.record.finishedAt === undefined && task.record.startedAt === undefined) {
|
|
1121
|
+
return { ...receive(task, requestedDuring), statusDetail: 'Task accepted; workspace preparation is still in progress. Poll this taskId with remote_cli_result instead of submitting the task again.' };
|
|
1122
|
+
}
|
|
1021
1123
|
}
|
|
1022
1124
|
return receive(task, requestedDuring);
|
|
1023
1125
|
})();
|