refactor(agent): run maintenance between turns
This commit is contained in:
@@ -27,6 +27,8 @@ function expectedFailure(error: ManualCompactionError): CommandResult {
|
||||
kind: 'error',
|
||||
text: 'Compaction is unavailable because this process has an active compaction, or the agent is not idle.',
|
||||
}
|
||||
case 'cancelled':
|
||||
return { kind: 'error', text: 'Compaction cancelled.' }
|
||||
case 'changed':
|
||||
return {
|
||||
kind: 'error',
|
||||
|
||||
@@ -365,44 +365,54 @@ export class BasicCompactService extends CompactService {
|
||||
* Force one useful idle-session compaction below the pressure threshold, and
|
||||
* resolve only after its standalone marker pair is durably checkpointed.
|
||||
* @param agent - idle agent whose next-turn admission this call reserves.
|
||||
* @param signal - command-owned cancellation forwarded to summarization.
|
||||
* @param signal - cancellation scoped to this compaction request.
|
||||
* @returns the committed result, or `null` when no safe useful range exists.
|
||||
*/
|
||||
override async compactNow(
|
||||
agent: Agent,
|
||||
signal: AbortSignal,
|
||||
): Promise<CompactionResult | null> {
|
||||
override compactNow(agent: Agent, signal: AbortSignal): Promise<CompactionResult | null> {
|
||||
signal.throwIfAborted()
|
||||
const releaseTurnAdmission = agent.reserveTurnAdmission()
|
||||
if (releaseTurnAdmission === undefined) {
|
||||
try {
|
||||
return agent.runMaintenance(async (agentSignal) => {
|
||||
const operationSignal = AbortSignal.any([agentSignal, signal])
|
||||
try {
|
||||
operationSignal.throwIfAborted()
|
||||
const range = selectCompactableRange(
|
||||
agent.session,
|
||||
this.ctx.tokenMeter.measure(agent.session),
|
||||
0,
|
||||
)
|
||||
if (range === null) return null
|
||||
return await compactSurfaceRegion(
|
||||
this.regionDependencies(),
|
||||
agent.session,
|
||||
range.start,
|
||||
range.end,
|
||||
agent,
|
||||
{
|
||||
owner: null,
|
||||
stability: 'selected-span',
|
||||
flush: () => this.ctx.sessions.flush(agent.session),
|
||||
},
|
||||
operationSignal,
|
||||
)
|
||||
} catch (error: unknown) {
|
||||
if (agentSignal.aborted && operationSignal.reason === agentSignal.reason) {
|
||||
throw new ManualCompactionError(
|
||||
'cancelled',
|
||||
'manual compaction was cancelled',
|
||||
{ cause: error },
|
||||
)
|
||||
}
|
||||
operationSignal.throwIfAborted()
|
||||
throw error
|
||||
}
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
throw new ManualCompactionError(
|
||||
'busy',
|
||||
'manual compaction requires an idle agent with no waking queued work',
|
||||
{ cause: error },
|
||||
)
|
||||
}
|
||||
try {
|
||||
const range = selectCompactableRange(
|
||||
agent.session,
|
||||
this.ctx.tokenMeter.measure(agent.session),
|
||||
0,
|
||||
)
|
||||
if (range === null) return null
|
||||
return await compactSurfaceRegion(
|
||||
this.regionDependencies(),
|
||||
agent.session,
|
||||
range.start,
|
||||
range.end,
|
||||
agent,
|
||||
{
|
||||
owner: null,
|
||||
stability: 'selected-span',
|
||||
flush: () => this.ctx.sessions.flush(agent.session),
|
||||
},
|
||||
signal,
|
||||
)
|
||||
} finally {
|
||||
releaseTurnAdmission()
|
||||
}
|
||||
}
|
||||
|
||||
/** Bind the effective token meter and dynamically dispatched summarizer hook. */
|
||||
|
||||
@@ -22,7 +22,13 @@ export { COMPACT_CHECKPOINT_SOURCE, isCompactCheckpointSource } from './checkpoi
|
||||
export type CompactionTrigger = 'pressure' | 'context-overflow'
|
||||
|
||||
/** Expected failure classes for an explicit idle-session compaction request. */
|
||||
export type ManualCompactionErrorCode = 'busy' | 'changed' | 'summary' | 'commit' | 'persistence'
|
||||
export type ManualCompactionErrorCode =
|
||||
| 'busy'
|
||||
| 'cancelled'
|
||||
| 'changed'
|
||||
| 'summary'
|
||||
| 'commit'
|
||||
| 'persistence'
|
||||
|
||||
/**
|
||||
* Expected manual-compaction failure suitable for a direct human-command result.
|
||||
@@ -59,7 +65,14 @@ export interface CompactAgentContext {
|
||||
* other compaction transactions.
|
||||
*/
|
||||
export interface ManualCompactAgentContext extends CompactAgentContext {
|
||||
reserveTurnAdmission(): (() => void) | undefined
|
||||
/**
|
||||
* Run a non-turn maintenance operation only while the agent is idle, withholding later
|
||||
* waking input until it settles.
|
||||
* @param task - operation whose fulfillment or rejection is preserved, with an agent-owned cancellation signal.
|
||||
* @throws synchronously when the agent is already active.
|
||||
* @returns the task promise.
|
||||
*/
|
||||
runMaintenance<T>(task: (signal: AbortSignal) => Promise<T>): Promise<T>
|
||||
}
|
||||
|
||||
declare module 'cordis' {
|
||||
@@ -102,21 +115,22 @@ export abstract class CompactService extends Service {
|
||||
|
||||
/**
|
||||
* Explicitly compact useful history even below automatic pressure thresholds.
|
||||
* Implementations reserve idle turn admission synchronously before any
|
||||
* asynchronous work, select a useful range without writing on a no-op, then
|
||||
* Implementations synchronously start an idle task before any asynchronous
|
||||
* work, select a useful range without writing on a no-op, then
|
||||
* append a standalone `compact/start` before summarization. That durable
|
||||
* marker is the compaction lock until one `compact/end` attempt. Later waking
|
||||
* prompts remain accepted in FIFO order and start only after the optional
|
||||
* durability checkpoint and admission release. Context injected while the
|
||||
* durability checkpoint and idle-task settlement. Context injected while the
|
||||
* summary runs may sit between the marker pair; only the selected span must
|
||||
* remain stable.
|
||||
*
|
||||
* @param agent - idle agent whose durable history should be compacted.
|
||||
* @param signal - command-owned cancellation forwarded to summarization.
|
||||
* @param signal - cancellation scoped to this compaction request.
|
||||
* @returns the compaction result, or `null` when no safe useful range exists.
|
||||
* @throws {@link ManualCompactionError} for expected busy, changed-span,
|
||||
* summarization/shrink, commit-stage, or persistence failures, and the exact
|
||||
* abort reason when cancelled. Failed attempts remain visible in the log.
|
||||
* @throws {@link ManualCompactionError} for expected busy, agent-cancellation,
|
||||
* changed-span, summarization/shrink, commit-stage, or persistence failures;
|
||||
* an aborted request preserves its exact abort reason. Failed attempts remain
|
||||
* visible in the log.
|
||||
*/
|
||||
abstract compactNow(
|
||||
agent: ManualCompactAgentContext,
|
||||
|
||||
Reference in New Issue
Block a user