ptbk 0.119.0-67 → 0.119.0-70
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/engine/agenda.d.ts
CHANGED
|
@@ -870,6 +870,8 @@ export declare class Agenda {
|
|
|
870
870
|
* concurrent units; the loop never waits for one of them.
|
|
871
871
|
*/
|
|
872
872
|
private iteration;
|
|
873
|
+
/** Waits for scheduled work or an internal retry after the current evaluation has finished. */
|
|
874
|
+
private waitForNextWork;
|
|
873
875
|
/** Whether the last loop wait was skipped by the operator. */
|
|
874
876
|
private waiterSkipped;
|
|
875
877
|
/**
|
package/dist/engine/agenda.js
CHANGED
|
@@ -1031,15 +1031,19 @@ export class Agenda {
|
|
|
1031
1031
|
}
|
|
1032
1032
|
/** Tells waiting chats about finished work, once each, also after a restart. */
|
|
1033
1033
|
async deliverTaskNotifications() {
|
|
1034
|
-
|
|
1034
|
+
// An active writer's source may be incomplete, or carry a completion stamp its check can still reject.
|
|
1035
|
+
const waiting = this.taskControlStore.waiting().filter((notification) => !this.ownership.ownerOf(notification.source));
|
|
1035
1036
|
if (!waiting.length)
|
|
1036
1037
|
return;
|
|
1037
1038
|
const { tasks } = await this.readQueue();
|
|
1038
1039
|
for (const notification of waiting) {
|
|
1040
|
+
// A run can acquire the source while the asynchronous queue scan is in progress.
|
|
1041
|
+
if (this.ownership.ownerOf(notification.source))
|
|
1042
|
+
continue;
|
|
1039
1043
|
const task = tasks.find((candidate) => candidate.source.relativePath === notification.source);
|
|
1040
1044
|
const result = this.recentResults.find((candidate) => nameKey(candidate.title) === nameKey(notification.task));
|
|
1041
1045
|
const outcome = !task
|
|
1042
|
-
? '
|
|
1046
|
+
? 'no longer exists'
|
|
1043
1047
|
: task.cycle > notification.cycle || task.status === 'done'
|
|
1044
1048
|
? `is done${result?.summary ? `: ${result.summary}` : ''}${result?.commit ? ` (result commit ${result.commit.slice(0, 8)})` : ''}`
|
|
1045
1049
|
: task.status === 'cancelled'
|
|
@@ -2992,12 +2996,18 @@ export class Agenda {
|
|
|
2992
2996
|
await this.runEngineUpdate();
|
|
2993
2997
|
return;
|
|
2994
2998
|
}
|
|
2999
|
+
await this.waitForNextWork(evaluated);
|
|
3000
|
+
}
|
|
3001
|
+
/** Waits for scheduled work or an internal retry after the current evaluation has finished. */
|
|
3002
|
+
async waitForNextWork(evaluated) {
|
|
3003
|
+
const waitingForTools = this.tools.waiting;
|
|
2995
3004
|
const waiting = evaluated.filter((item) => item.readiness.kind === 'waiting-until').map((item) => item.readiness.until);
|
|
2996
|
-
|
|
2997
|
-
const
|
|
3005
|
+
/** Internal deadlines can become due while evaluation runs; they still require a prompt next iteration. */
|
|
3006
|
+
const earliest = (times) => times.filter(Number.isFinite).reduce((first, until) => (first === undefined || until < first ? until : first), undefined);
|
|
3007
|
+
const nextTime = earliest([...waiting, ...(this.nextKnownTime ? [this.nextKnownTime] : [])].filter((until) => until > Date.now()));
|
|
2998
3008
|
this.nextKnownTime = nextTime;
|
|
2999
3009
|
// Internal retries (resumable runs, deferred claims, a failed brief's recheck) also wake the loop, without being
|
|
3000
|
-
// presented as scheduled work.
|
|
3010
|
+
// presented as scheduled work. Overdue retries must not fall through to the ten-minute idle wait.
|
|
3001
3011
|
const wakeAt = earliest([...(nextTime ? [nextTime] : []), ...this.resumeAfter.values(), ...this.claimDeferred.values(), ...this.recoveryDue.values(), ...(this.reportRecheck ? [Date.parse(this.reportRecheck.nextAt)] : []), ...(this.updater.wakeAt() ? [this.updater.wakeAt()] : [])]);
|
|
3002
3012
|
const blocked = evaluated.filter((item) => ['blocked', 'needs-recovery', 'failed'].includes(item.readiness.kind));
|
|
3003
3013
|
const reason = this.executing
|
|
@@ -3010,7 +3020,7 @@ export class Agenda {
|
|
|
3010
3020
|
? `No unfinished work. Waiting for tools: ${waitingForTools.reason}`
|
|
3011
3021
|
: (this.idleReason ?? 'No unfinished work.');
|
|
3012
3022
|
this.phase = 'idle';
|
|
3013
|
-
await this.wait(wakeAt ? Math.min(Math.max(1_000, wakeAt - Date.now()), IDLE_RECHECK_MS) : IDLE_RECHECK_MS, { kind: nextTime ? 'schedule' : 'idle', reason, skippable: false });
|
|
3023
|
+
await this.wait(wakeAt !== undefined ? Math.min(Math.max(1_000, wakeAt - Date.now()), IDLE_RECHECK_MS) : IDLE_RECHECK_MS, { kind: nextTime ? 'schedule' : 'idle', reason, skippable: false });
|
|
3014
3024
|
}
|
|
3015
3025
|
/** Whether the last loop wait was skipped by the operator. */
|
|
3016
3026
|
waiterSkipped = false;
|