round 1: implement bracket-first manual compaction

This commit is contained in:
Hypatia May
2026-07-30 17:40:25 +08:00
parent 86b95a3856
commit faac9b4fd5
101 files changed
+3452 -295

No files matched your search

+59 -11
View File
@@ -2,7 +2,8 @@
* Concrete Agent loop over two pending-input lists: queued prompts each open a
* turn that logs its admitted input after `turn/start` commits, while steering
* and injected context enter through the outbox at step boundaries. Every
* request is derived from the session log.
* request is derived from the session log. An idle turn-admission reservation
* can withhold the driver from the queue without touching its contents.
*
* @module dsh-agent-loop/agent
*/
@@ -68,6 +69,12 @@ export class ReactLoopAgent implements Agent {
private busy = false
/** Whether an idle waking send has deferred driver admission. */
private wakeScheduled = false
/**
* The live idle turn-admission reservation, holding the driver out of the
* queue until its owner releases. It settles idle waiters instead of
* {@link done} so lifecycle teardown never awaits the reserving operation.
*/
private admission: { readonly settled: Promise<void>; readonly settle: () => void } | undefined
/** Whether next-step input belongs to the current admission or open turn. */
acceptsNextStep = false
/** Abort owner for the current admission or turn. */
@@ -192,6 +199,32 @@ export class ReactLoopAgent implements Agent {
})
}
/**
* Hold the idle admission boundary so no queued prompt can open a turn until
* the returned release runs. Later sends keep their ordinary placement and
* `wakeup` facts; only the driver's claim waits.
* @returns the idempotent release, or `undefined` when the driver is active or already committed to waking work.
*/
reserveTurnAdmission(): (() => void) | undefined {
// `busy` covers every abort owner: kick() and run() mark the interval
// running before they install one. `wakeScheduled` is the same-tick state
// of an accepted waking prompt whose claim is still a pending microtask.
if (this.busy || this.wakeScheduled || this.admission !== undefined
|| this.queued.some(item => item.wakeup)) return undefined
const pending = Promise.withResolvers<void>()
const reservation = { settled: pending.promise, settle: pending.resolve }
this.admission = reservation
return () => {
// Idempotent, and inert once a later reservation owns the boundary.
if (this.admission !== reservation) return
this.admission = undefined
// Re-arm the ordinary path first, so an idle waiter released below
// re-reads live admission activity instead of settled state.
if (this.queued.some(item => item.wakeup)) this.scheduleKick()
reservation.settle()
}
}
/**
* Clear all pending work and abort the active turn; the first cause wins.
* The cause is signal payload for observers and the durable turn/end
@@ -225,19 +258,34 @@ export class ReactLoopAgent implements Agent {
/** Resolve at idle quiescence: no run driving and no waking prompt waiting. */
async whenIdle(): Promise<void> {
// `done` is replaced per activity, so re-reading it follows chained turns.
// Every driver failure today is contained before it can reject `done`,
// but the waiter must not gamble quiescence on that: a future escape
// still counts as settled activity.
/* v8 ignore next 3 -- the catch arm backstops rejection paths that are all currently contained */
while (this.busy || this.wakeScheduled || this.abort !== undefined || this.queued.some(item => item.wakeup)) {
await this.done.catch(() => undefined)
while (true) {
// `done` is replaced per activity, so re-reading it follows chained turns.
// Every driver failure today is contained before it can reject `done`,
// but the waiter must not gamble quiescence on that: a future escape
// still counts as settled activity.
/* v8 ignore next 3 -- the catch arm backstops rejection paths that are all currently contained */
while (this.busy || this.wakeScheduled || this.abort !== undefined || this.runnableWakingQueued) {
await this.done.catch(() => undefined)
}
// A reservation is unfinished activity even with an empty queue, and a
// prompt it withholds is not quiescent — but `done` never owns it, so
// waiting on the queue alone would spin on an already-settled promise.
const reservation = this.admission
if (reservation === undefined) return
await reservation.settled
}
}
/** Whether a queued waking prompt may claim the driver now. */
private get runnableWakingQueued(): boolean {
return this.admission === undefined && this.queued.some(item => item.wakeup)
}
/** Defer idle admission while keeping {@link done} as its quiescence owner. */
private scheduleKick(): void {
if (this.abort !== undefined || this.wakeScheduled) return
// A held reservation keeps the item queued with no scheduled claim; its
// release re-arms this path for whatever is queued by then.
if (this.abort !== undefined || this.wakeScheduled || this.admission !== undefined) return
this.wakeScheduled = true
const pending = Promise.withResolvers<void>()
const scheduled = pending.promise
@@ -259,7 +307,7 @@ export class ReactLoopAgent implements Agent {
/** Claim and admit the next queued prompt, then start its turn. */
private kick(): void {
if (this.abort !== undefined || !this.queued.some(item => item.wakeup)) return
if (this.abort !== undefined || !this.runnableWakingQueued) return
// The some() guard above proves the queue is non-empty; the non-null
// assertion expresses that invariant.
// oxlint-disable-next-line typescript/no-non-null-assertion
@@ -762,7 +810,7 @@ export class ReactLoopAgent implements Agent {
/** Continue with a waking prompt, or publish the idle status. */
private continueOrIdle(): void {
if (this.queued.some(item => item.wakeup)) {
if (this.runnableWakingQueued) {
this.kick()
} else {
// Every caller sits inside an admission or run whose install marked the