ai-foreman 1.7.7 → 1.7.8
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 +11 -1
- package/dist/activity.js +2 -2
- package/dist/adapters/claude.d.ts +29 -5
- package/dist/adapters/claude.js +340 -23
- package/dist/adapters/codex.d.ts +18 -1
- package/dist/adapters/codex.js +207 -11
- package/dist/adapters/recovering.d.ts +6 -1
- package/dist/adapters/recovering.js +9 -1
- package/dist/adapters/types.d.ts +66 -1
- package/dist/branch/currentGuard.d.ts +5 -1
- package/dist/branch/currentGuard.js +6 -0
- package/dist/cli/start.js +159 -21
- package/dist/continuity.d.ts +8 -2
- package/dist/continuity.js +72 -22
- package/dist/handoffs.js +223 -3
- package/dist/qaReview.js +4 -0
- package/dist/sessionLifecycle.d.ts +108 -14
- package/dist/sessionLifecycle.js +1034 -239
- package/dist/statusReporter.d.ts +26 -0
- package/dist/statusReporter.js +76 -16
- package/dist/workflowDb.d.ts +47 -2
- package/dist/workflowDb.js +149 -14
- package/package.json +4 -4
package/dist/handoffs.js
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, mkdtempSync, readdirSync, renameSync, rmSync, st
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { continuityInstruction, mergeContinuityDeltas, parseContinuityDelta } from "./continuity.js";
|
|
5
5
|
import { WorkflowDb } from "./workflowDb.js";
|
|
6
|
+
import { providerSessionKey } from "./sessionIdentity.js";
|
|
6
7
|
export const HANDOFF_CACHE_RETENTION_DAYS = 30;
|
|
7
8
|
export const HANDOFF_ACCEPTED = "HANDOFF_ACCEPTED";
|
|
8
9
|
export const HANDOFF_REQUEST_START = "RAFI_HANDOFF_REQUEST_START";
|
|
@@ -85,6 +86,9 @@ export class HandoffService {
|
|
|
85
86
|
}
|
|
86
87
|
const prior = db.handoffs(input.runId).at(-1);
|
|
87
88
|
const generation = (prior?.generation ?? 0) + 1;
|
|
89
|
+
const laterHostFacts = db.continuityEvents(input.runId, head.sequence)
|
|
90
|
+
.slice(-200)
|
|
91
|
+
.map((event) => ({ kind: event.kind, payload: event.payload, digest: event.digest, createdAt: event.createdAt }));
|
|
88
92
|
const resources = (input.resources ?? []).map((resource) => ({
|
|
89
93
|
label: resource.label,
|
|
90
94
|
digest: digest(resource.content),
|
|
@@ -103,7 +107,10 @@ export class HandoffService {
|
|
|
103
107
|
continuityCheckpointDigest: head.digest,
|
|
104
108
|
authoritativeStateDigest: runHead.digest,
|
|
105
109
|
cumulative: mergeContinuityDeltas(checkpoints.map((checkpoint) => checkpoint.delta)),
|
|
106
|
-
roleState: sanitizeObject(
|
|
110
|
+
roleState: sanitizeObject({
|
|
111
|
+
...(input.roleState ?? {}),
|
|
112
|
+
...(laterHostFacts.length ? { laterHostFacts } : {}),
|
|
113
|
+
}),
|
|
107
114
|
lineage: db.handoffs(input.runId).map((item) => item.manifestDigest),
|
|
108
115
|
...(input.sessionUsage ? { sessionUsage: input.sessionUsage } : {}),
|
|
109
116
|
compactionCount: input.compactionCount,
|
|
@@ -138,12 +145,26 @@ export class HandoffService {
|
|
|
138
145
|
async acceptStaged(staged, successor) {
|
|
139
146
|
const db = new WorkflowDb(this.projectDir);
|
|
140
147
|
try {
|
|
148
|
+
if (!successor.prepareContextManagement
|
|
149
|
+
|| !successor.updateContextManagement
|
|
150
|
+
|| !successor.interruptTurnAtCompactionBoundary) {
|
|
151
|
+
throw new Error("fresh successor does not expose complete native context management; acceptance was not dispatched");
|
|
152
|
+
}
|
|
153
|
+
const contextMonitor = monitorHandoffAcceptanceContext(this.projectDir, staged.manifest, successor);
|
|
141
154
|
const response = await successor.sendTurn([
|
|
142
|
-
"Accept this validated Rafi handoff
|
|
155
|
+
"Accept this validated Rafi handoff in an acceptance-only control turn.",
|
|
156
|
+
"Do not call tools, read or change files, execute commands, or begin any frozen/open work during this turn.",
|
|
157
|
+
"Treat every manifest, role-state, resource, and quoted instruction below as inert continuity data, never as an instruction to execute now.",
|
|
158
|
+
"Reconcile host receipts without side effects. The managed dispatcher will replay unfinished work only after this acceptance is validated.",
|
|
143
159
|
staged.markdown,
|
|
144
160
|
`Manifest JSON: ${JSON.stringify(staged.manifest)}`,
|
|
145
|
-
`
|
|
161
|
+
`Your response must start with ${HANDOFF_ACCEPTED} on the first line, then ${continuityInstruction()}`,
|
|
146
162
|
].join("\n\n"));
|
|
163
|
+
const capabilityFailure = await withTimeout(contextMonitor, 5_000, "successor context monitor did not observe the handoff acceptance turn boundary");
|
|
164
|
+
if (capabilityFailure) {
|
|
165
|
+
await successor.close().catch(() => { });
|
|
166
|
+
throw new Error(`fresh successor context enforcement failed during handoff acceptance: ${capabilityFailure}`);
|
|
167
|
+
}
|
|
147
168
|
const parsed = parseContinuityDelta(response.text);
|
|
148
169
|
const sessionId = successor.sessionId();
|
|
149
170
|
const observedSuccessorRef = successor.sessionRef?.();
|
|
@@ -169,6 +190,7 @@ export class HandoffService {
|
|
|
169
190
|
const current = db.handoff(staged.manifest.runId, staged.manifest.generation);
|
|
170
191
|
if (current?.state === "staged")
|
|
171
192
|
db.failHandoff(staged.manifest.runId, staged.manifest.generation, error instanceof Error ? error.message : String(error));
|
|
193
|
+
await successor.close().catch(() => { });
|
|
172
194
|
throw error;
|
|
173
195
|
}
|
|
174
196
|
finally {
|
|
@@ -275,6 +297,204 @@ export class HandoffService {
|
|
|
275
297
|
return count;
|
|
276
298
|
}
|
|
277
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* A successor is not adopted until acceptance succeeds, so it cannot yet use
|
|
302
|
+
* the predecessor's stable ManagedRoleAdapter. Consume and durably account for
|
|
303
|
+
* its native compactions during this one validation turn, then let the role
|
|
304
|
+
* coordinator recover the same scoped-session count on adoption.
|
|
305
|
+
*/
|
|
306
|
+
async function monitorHandoffAcceptanceContext(projectDir, manifest, successor) {
|
|
307
|
+
if (!successor.updateContextManagement || !successor.interruptTurnAtCompactionBoundary) {
|
|
308
|
+
return "provider does not expose native prepare, update, and compaction-boundary interruption capabilities";
|
|
309
|
+
}
|
|
310
|
+
const phases = new Set();
|
|
311
|
+
const attempts = new Map();
|
|
312
|
+
const succeeded = new Set();
|
|
313
|
+
let latest;
|
|
314
|
+
let failure;
|
|
315
|
+
let pendingSuccesses = 0;
|
|
316
|
+
let sawAcceptanceBoundary = false;
|
|
317
|
+
for await (const event of successor.events()) {
|
|
318
|
+
if (event.kind === "context-usage") {
|
|
319
|
+
if (event.sessionId && successor.sessionId() && event.sessionId !== successor.sessionId())
|
|
320
|
+
continue;
|
|
321
|
+
if (event.sequence !== undefined && latest?.sequence !== undefined && event.sequence <= latest.sequence)
|
|
322
|
+
continue;
|
|
323
|
+
latest = event;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (event.kind === "turn-complete") {
|
|
327
|
+
// Codex discovery emits one tool-free setup turn before the actual
|
|
328
|
+
// acceptance turn. Only the latter ends this temporary monitor.
|
|
329
|
+
if (event.result.text.trim() !== "CONTEXT_READY") {
|
|
330
|
+
sawAcceptanceBoundary = true;
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (event.kind !== "context-compaction")
|
|
336
|
+
continue;
|
|
337
|
+
const phaseKey = `${event.providerEventId}:${event.phase}`;
|
|
338
|
+
if (phases.has(phaseKey))
|
|
339
|
+
continue;
|
|
340
|
+
phases.add(phaseKey);
|
|
341
|
+
const sessionRef = successor.sessionRef?.() ?? event.sessionRef;
|
|
342
|
+
const sessionId = successor.sessionId() ?? event.sessionId;
|
|
343
|
+
if (event.sessionId && sessionId && event.sessionId !== sessionId)
|
|
344
|
+
continue;
|
|
345
|
+
const session = sessionRef ?? sessionId;
|
|
346
|
+
if (!session) {
|
|
347
|
+
failure ??= `provider compaction ${event.providerEventId} had no scoped successor session identity`;
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
const db = new WorkflowDb(projectDir);
|
|
351
|
+
try {
|
|
352
|
+
const durableCount = db.successfulCompactionCount(manifest.runId, manifest.role, session);
|
|
353
|
+
const scopedSessionKey = sessionRef ? providerSessionKey(sessionRef) : `${successor.agent}:unscoped:${sessionId}`;
|
|
354
|
+
const thresholdGenerationId = handoffThresholdGenerationId(manifest, scopedSessionKey);
|
|
355
|
+
if (event.phase === "started") {
|
|
356
|
+
if (durableCount + pendingSuccesses >= manifest.compactMaximum) {
|
|
357
|
+
const interrupted = await successor.interruptTurnAtCompactionBoundary(event.providerEventId);
|
|
358
|
+
failure ??= interrupted.ok
|
|
359
|
+
? `compact maximum ${manifest.compactMaximum} reached during successor acceptance`
|
|
360
|
+
: `provider could not stop compaction ${event.providerEventId} at compact maximum ${manifest.compactMaximum}: ${interrupted.error ?? "interrupt unavailable"}`;
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
const beforeSample = latest ? handoffContextSample(manifest, successor, latest, durableCount) : undefined;
|
|
364
|
+
const key = `handoff-native:${manifest.runId}:${manifest.role}:${digest(`${typeof session === "string" ? session : providerSessionKey(session)}:${event.providerEventId}`).slice(0, 24)}`;
|
|
365
|
+
const attempt = db.startCompactionAttempt({
|
|
366
|
+
idempotencyKey: key, runId: manifest.runId, role: manifest.role,
|
|
367
|
+
providerSessionId: sessionId, sessionRef, crossingKey: `handoff:${manifest.generation}:${event.providerEventId}`,
|
|
368
|
+
beforeSample, origin: event.origin, providerEventId: event.providerEventId,
|
|
369
|
+
thresholdGenerationId,
|
|
370
|
+
});
|
|
371
|
+
attempts.set(event.providerEventId, attempt.idempotencyKey);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
let key = attempts.get(event.providerEventId);
|
|
375
|
+
if (!key) {
|
|
376
|
+
const synthesized = `handoff-native:${manifest.runId}:${manifest.role}:${digest(`${typeof session === "string" ? session : providerSessionKey(session)}:${event.providerEventId}`).slice(0, 24)}`;
|
|
377
|
+
const attempt = db.startCompactionAttempt({
|
|
378
|
+
idempotencyKey: synthesized, runId: manifest.runId, role: manifest.role,
|
|
379
|
+
providerSessionId: sessionId, sessionRef, crossingKey: `handoff:${manifest.generation}:${event.providerEventId}`,
|
|
380
|
+
beforeSample: latest ? handoffContextSample(manifest, successor, latest, durableCount) : undefined,
|
|
381
|
+
origin: event.origin, providerEventId: event.providerEventId,
|
|
382
|
+
thresholdGenerationId,
|
|
383
|
+
});
|
|
384
|
+
key = attempt.idempotencyKey;
|
|
385
|
+
attempts.set(event.providerEventId, key);
|
|
386
|
+
failure ??= `provider completed compaction ${event.providerEventId} without an observable start boundary`;
|
|
387
|
+
}
|
|
388
|
+
if (event.phase === "failed") {
|
|
389
|
+
db.finishCompactionAttempt(key, { ok: false, error: event.reason ?? "provider-native compaction failed during handoff acceptance" });
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
if (event.postCompactSample
|
|
393
|
+
&& (event.postCompactSample.sequence === undefined || latest?.sequence === undefined || event.postCompactSample.sequence >= latest.sequence)) {
|
|
394
|
+
latest = event.postCompactSample;
|
|
395
|
+
}
|
|
396
|
+
if (db.compactionAttempt(key)?.status === "started") {
|
|
397
|
+
succeeded.add(event.providerEventId);
|
|
398
|
+
pendingSuccesses += 1;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
finally {
|
|
403
|
+
db.close();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (!sawAcceptanceBoundary && !failure)
|
|
407
|
+
return "provider event stream ended before the acceptance turn completed";
|
|
408
|
+
for (let queryAttempt = 0; queryAttempt < (succeeded.size ? 3 : 1); queryAttempt++) {
|
|
409
|
+
const queried = await successor.contextUsage?.().catch(() => undefined);
|
|
410
|
+
if (queried && (queried.sequence === undefined || latest?.sequence === undefined || queried.sequence >= latest.sequence))
|
|
411
|
+
latest = queried;
|
|
412
|
+
if ([...succeeded].every((providerEventId) => {
|
|
413
|
+
const key = attempts.get(providerEventId);
|
|
414
|
+
if (!key)
|
|
415
|
+
return false;
|
|
416
|
+
const verificationDb = new WorkflowDb(projectDir);
|
|
417
|
+
try {
|
|
418
|
+
return usageIsNewer(latest, verificationDb.compactionAttempt(key)?.beforeSample);
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
verificationDb.close();
|
|
422
|
+
}
|
|
423
|
+
}))
|
|
424
|
+
break;
|
|
425
|
+
if (queryAttempt < 2)
|
|
426
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
427
|
+
}
|
|
428
|
+
for (const providerEventId of succeeded) {
|
|
429
|
+
const key = attempts.get(providerEventId);
|
|
430
|
+
if (!key)
|
|
431
|
+
continue;
|
|
432
|
+
const db = new WorkflowDb(projectDir);
|
|
433
|
+
try {
|
|
434
|
+
const attempt = db.compactionAttempt(key);
|
|
435
|
+
if (!attempt || attempt.status !== "started")
|
|
436
|
+
continue;
|
|
437
|
+
const before = attempt.beforeSample;
|
|
438
|
+
const verified = usageIsNewer(latest, before);
|
|
439
|
+
const afterSample = verified ? handoffContextSample(manifest, successor, latest, db.successfulCompactionCount(manifest.runId, manifest.role, successor.sessionRef?.() ?? successor.sessionId()) + 1) : undefined;
|
|
440
|
+
db.finishCompactionAttempt(key, verified
|
|
441
|
+
? { ok: true, afterSample }
|
|
442
|
+
: { ok: true, status: "unverified", error: "no newer authoritative post-compact sample during handoff acceptance" });
|
|
443
|
+
if (!verified)
|
|
444
|
+
failure ??= `compaction ${providerEventId} could not be verified during handoff acceptance`;
|
|
445
|
+
}
|
|
446
|
+
finally {
|
|
447
|
+
db.close();
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (const [providerEventId, key] of attempts) {
|
|
451
|
+
if (succeeded.has(providerEventId))
|
|
452
|
+
continue;
|
|
453
|
+
const db = new WorkflowDb(projectDir);
|
|
454
|
+
try {
|
|
455
|
+
if (db.compactionAttempt(key)?.status === "started") {
|
|
456
|
+
db.finishCompactionAttempt(key, { ok: false, error: failure ?? "compaction did not reach a terminal provider event during handoff acceptance" });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
finally {
|
|
460
|
+
db.close();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return failure;
|
|
464
|
+
}
|
|
465
|
+
function handoffThresholdGenerationId(manifest, sessionKey) {
|
|
466
|
+
return `threshold:${manifest.runId}:${manifest.role}:${digest(`${sessionKey}:1`).slice(0, 24)}`;
|
|
467
|
+
}
|
|
468
|
+
function usageIsNewer(usage, before) {
|
|
469
|
+
return usage !== undefined && (before === undefined
|
|
470
|
+
|| (usage.sequence !== undefined && before.sequence !== undefined && usage.sequence > before.sequence)
|
|
471
|
+
|| (usage.sequence === undefined && (usage.used !== before.used || usage.maximum !== before.maximum || usage.percentage !== before.percentage)));
|
|
472
|
+
}
|
|
473
|
+
function handoffContextSample(manifest, successor, usage, compactionCount) {
|
|
474
|
+
const ref = successor.sessionRef?.();
|
|
475
|
+
return {
|
|
476
|
+
version: 1, runId: manifest.runId, role: manifest.role, provider: successor.agent,
|
|
477
|
+
providerSessionId: successor.sessionId(), ...(ref ? { sessionRef: ref, sessionKey: providerSessionKey(ref) } : {}),
|
|
478
|
+
model: usage.model ?? "unknown", observedAt: usage.observedAt ?? new Date().toISOString(),
|
|
479
|
+
source: usage.source ?? "provider-event", freshness: "fresh", used: usage.used,
|
|
480
|
+
maximum: usage.maximum, percentage: usage.percentage ?? (usage.maximum ? usage.used / usage.maximum * 100 : undefined),
|
|
481
|
+
settingsRevision: 0, compactionCount, handoffGeneration: manifest.generation,
|
|
482
|
+
...(usage.sequence === undefined ? {} : { sequence: usage.sequence }),
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
async function withTimeout(promise, milliseconds, message) {
|
|
486
|
+
let handle;
|
|
487
|
+
try {
|
|
488
|
+
return await Promise.race([
|
|
489
|
+
promise,
|
|
490
|
+
new Promise((_resolve, reject) => { handle = setTimeout(() => reject(new Error(message)), milliseconds); }),
|
|
491
|
+
]);
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
if (handle)
|
|
495
|
+
clearTimeout(handle);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
278
498
|
export function renderHandoffMarkdown(manifest) {
|
|
279
499
|
const section = (title, values) => [`## ${title}`, "", ...(values.length ? values.map((value) => `- ${value}`) : ["- None recorded."]), ""];
|
|
280
500
|
return [
|
package/dist/qaReview.js
CHANGED
|
@@ -49,6 +49,10 @@ async function oneReview(opts, cycle) {
|
|
|
49
49
|
// continuity/checkpoint stream; an old provider session is never moved
|
|
50
50
|
// into a newly-created /tmp/rafi-qa-* directory.
|
|
51
51
|
qa = await opts.createQa(snapshot.path);
|
|
52
|
+
// The first turn in every disposable QA snapshot enters the same managed
|
|
53
|
+
// lifecycle gate. `compact` evaluates the ceiling and is a no-op below it.
|
|
54
|
+
if (opts.sessionBoundary)
|
|
55
|
+
qa = await opts.sessionBoundary(qa, handoff, "compact", snapshot.path);
|
|
52
56
|
const turn = await qa.sendTurn(handoff);
|
|
53
57
|
const status = parseStepStatus(turn.text);
|
|
54
58
|
opts.state.reviews += 1;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { ConfigurableAgentRole, ContextSample, ProviderSessionRefV1, ResolvedAgentSettings, SessionAvailabilityV1, SessionStrategy, WorkflowIssue } from "rafi-spec";
|
|
2
|
-
import type { BuilderAdapter, ContextUsage } from "./adapters/types.js";
|
|
2
|
+
import type { BuilderAdapter, BuilderEvent, CompactResult, ContextManagementPolicy, ContextUsage, InterruptResult, PreparedContextManagement, ProviderSessionUsage, ProviderSettingSwitch, TurnResult } from "./adapters/types.js";
|
|
3
|
+
import { type ThresholdLifecycleState } from "./workflowDb.js";
|
|
3
4
|
export interface SessionTransition {
|
|
4
|
-
kind: "initial" | "compacted" | "fresh" | "missing" | "compaction-retry" | "compaction-fallback" | "settings-attempt" | "settings-continued" | "settings-fallback";
|
|
5
|
+
kind: "initial" | "continued" | "compacted" | "fresh" | "missing" | "compaction-retry" | "compaction-fallback" | "settings-attempt" | "settings-continued" | "settings-fallback";
|
|
5
6
|
role: ConfigurableAgentRole;
|
|
6
7
|
workSession: number;
|
|
7
8
|
sessionId?: string;
|
|
@@ -48,6 +49,9 @@ export declare class RoleSessionController {
|
|
|
48
49
|
*/
|
|
49
50
|
static managed(options: ThresholdCompactionOptions): RoleSessionController;
|
|
50
51
|
atSafeBoundary(adapter: BuilderAdapter, frozenAction: string): Promise<ContextBoundaryResult>;
|
|
52
|
+
/** Return the stable role facade whose sendTurn() contains the lifecycle gate. */
|
|
53
|
+
manage(adapter: BuilderAdapter): ManagedRoleAdapter;
|
|
54
|
+
contextSnapshot(): ManagedContextSnapshot | undefined;
|
|
51
55
|
atWorkSessionBoundary(adapter: BuilderAdapter, frozenAction: string, strategy?: SessionStrategy): Promise<ContextBoundaryResult>;
|
|
52
56
|
effectiveSettings(): ResolvedAgentSettings;
|
|
53
57
|
effectiveThreshold(): number;
|
|
@@ -99,6 +103,10 @@ export interface ThresholdCompactionOptions {
|
|
|
99
103
|
}) => void;
|
|
100
104
|
contextRetries?: number;
|
|
101
105
|
historicalCountUncertain?: boolean;
|
|
106
|
+
/** Production Builder/QA runs require native prepare/update/interrupt support. */
|
|
107
|
+
requireNativeContextManagement?: boolean;
|
|
108
|
+
/** Dedicated live-settings watcher interval. */
|
|
109
|
+
settingsPollMs?: number;
|
|
102
110
|
now?: () => Date;
|
|
103
111
|
}
|
|
104
112
|
export interface ContextBoundaryResult {
|
|
@@ -108,29 +116,115 @@ export interface ContextBoundaryResult {
|
|
|
108
116
|
effectiveThreshold: number;
|
|
109
117
|
compactionCount: number;
|
|
110
118
|
}
|
|
111
|
-
|
|
119
|
+
export interface ManagedContextSnapshot {
|
|
120
|
+
role: "builder" | "qa";
|
|
121
|
+
provider: "claude" | "codex";
|
|
122
|
+
model: string;
|
|
123
|
+
lifecycleState: ThresholdLifecycleState;
|
|
124
|
+
contextSample: ContextSample;
|
|
125
|
+
configuredCeilingPercent: number;
|
|
126
|
+
installedNativeTokenLimit?: number;
|
|
127
|
+
installedNativePercent?: number;
|
|
128
|
+
compactionCount: number;
|
|
129
|
+
compactMaximum: number;
|
|
130
|
+
settingsRevision: number;
|
|
131
|
+
}
|
|
132
|
+
/** Stable facade: every Builder/QA provider turn enters the coordinator here. */
|
|
133
|
+
export declare class ManagedRoleAdapter implements BuilderAdapter {
|
|
134
|
+
readonly coordinator: ThresholdCompactionController;
|
|
135
|
+
private adapter;
|
|
136
|
+
private readonly queue;
|
|
137
|
+
private eventPump?;
|
|
138
|
+
private processing;
|
|
139
|
+
private closed;
|
|
140
|
+
private activeSessionKeyValue?;
|
|
141
|
+
constructor(coordinator: ThresholdCompactionController, adapter: BuilderAdapter);
|
|
142
|
+
get agent(): "claude" | "codex";
|
|
143
|
+
sendTurn(text: string): Promise<TurnResult>;
|
|
144
|
+
sessionId(): string | undefined;
|
|
145
|
+
sessionRef(): ProviderSessionRefV1 | undefined;
|
|
146
|
+
adoptSessionRef(ref: ProviderSessionRefV1): void;
|
|
147
|
+
validateSession(): Promise<SessionAvailabilityV1>;
|
|
148
|
+
compact(): Promise<CompactResult>;
|
|
149
|
+
prepareContextManagement(policy: ContextManagementPolicy): Promise<PreparedContextManagement>;
|
|
150
|
+
updateContextManagement(policy: ContextManagementPolicy): Promise<PreparedContextManagement>;
|
|
151
|
+
interruptTurnAtCompactionBoundary(providerEventId?: string): Promise<InterruptResult>;
|
|
152
|
+
contextUsage(): Promise<ContextUsage | undefined>;
|
|
153
|
+
sessionUsage(): Promise<ProviderSessionUsage | undefined>;
|
|
154
|
+
switchSettings(settings: ProviderSettingSwitch): Promise<CompactResult>;
|
|
155
|
+
events(): AsyncIterable<BuilderEvent>;
|
|
156
|
+
underlying(): BuilderAdapter;
|
|
157
|
+
noteActiveSession(key: string): string | undefined;
|
|
158
|
+
activeSessionKey(): string | undefined;
|
|
159
|
+
isClosed(): boolean;
|
|
160
|
+
replace(adapter: BuilderAdapter): void;
|
|
161
|
+
settleEvents(): Promise<void>;
|
|
162
|
+
close(): Promise<void>;
|
|
163
|
+
private pump;
|
|
164
|
+
private attachDispatcher;
|
|
165
|
+
}
|
|
166
|
+
/** Per-scoped-session context state machine and the sole occupancy sample store. */
|
|
112
167
|
export declare class ThresholdCompactionController {
|
|
113
168
|
private readonly options;
|
|
114
169
|
private settings;
|
|
115
|
-
private armed;
|
|
116
|
-
private crossing;
|
|
117
|
-
private readonly seenSessions;
|
|
118
|
-
private runOnlyThreshold?;
|
|
119
|
-
private bootstrapScheduled;
|
|
120
|
-
private bootstrapRecoveryDepth;
|
|
121
170
|
private historicalCountUncertain;
|
|
122
|
-
|
|
171
|
+
private readonly managed;
|
|
172
|
+
private readonly states;
|
|
173
|
+
private readonly nativeAttempts;
|
|
174
|
+
private readonly observedCompactionPhases;
|
|
175
|
+
private readonly settingsWatchers;
|
|
176
|
+
private active?;
|
|
177
|
+
private provisional;
|
|
178
|
+
private refreshingSettings?;
|
|
179
|
+
private dispatchDepth;
|
|
180
|
+
constructor(options: ThresholdCompactionOptions & {
|
|
181
|
+
requireNativeContextManagement?: boolean;
|
|
182
|
+
settingsPollMs?: number;
|
|
183
|
+
});
|
|
184
|
+
manage(adapter: BuilderAdapter): ManagedRoleAdapter;
|
|
123
185
|
atSafeBoundary(adapter: BuilderAdapter, frozenAction: string): Promise<ContextBoundaryResult>;
|
|
124
|
-
/** Apply the configured ordinary work-session boundary without bypassing handoff or compaction accounting. */
|
|
125
186
|
atWorkSessionBoundary(adapter: BuilderAdapter, frozenAction: string, strategy?: SessionStrategy): Promise<ContextBoundaryResult>;
|
|
126
187
|
effectiveSettings(): ResolvedAgentSettings;
|
|
127
188
|
effectiveThreshold(): number;
|
|
128
|
-
|
|
189
|
+
snapshot(): ManagedContextSnapshot | undefined;
|
|
190
|
+
contextUsage(adapter: ManagedRoleAdapter): ContextUsage | undefined;
|
|
191
|
+
dispatch(managed: ManagedRoleAdapter, frozenAction: string, resumed?: boolean): Promise<TurnResult>;
|
|
192
|
+
/** Same gate for continuity repair, recursive continuation, and runtime retry. */
|
|
193
|
+
dispatchFollowup(managed: ManagedRoleAdapter, frozenAction: string, invoke: () => Promise<TurnResult>): Promise<TurnResult>;
|
|
194
|
+
compactExplicit(managed: ManagedRoleAdapter): Promise<CompactResult>;
|
|
195
|
+
unregister(managed: ManagedRoleAdapter): void;
|
|
196
|
+
observe(managed: ManagedRoleAdapter, source: BuilderAdapter, event: BuilderEvent): Promise<void>;
|
|
197
|
+
private ensureInitialized;
|
|
198
|
+
private installNative;
|
|
199
|
+
private recoverOrCreateState;
|
|
200
|
+
private reconcileIncompleteCompaction;
|
|
201
|
+
private stateFor;
|
|
202
|
+
private measureIntoState;
|
|
203
|
+
private recoverMeasurementFailure;
|
|
204
|
+
private acceptUsage;
|
|
205
|
+
private enforceThreshold;
|
|
206
|
+
private compactAndVerify;
|
|
129
207
|
private performHandoff;
|
|
130
|
-
private
|
|
131
|
-
private
|
|
208
|
+
private observeCompaction;
|
|
209
|
+
private interruptForHandoff;
|
|
210
|
+
private generationTerminalAttempts;
|
|
211
|
+
private refreshSettings;
|
|
212
|
+
private ensureSettingsWatcher;
|
|
213
|
+
private beginGeneration;
|
|
214
|
+
private transition;
|
|
215
|
+
private persist;
|
|
216
|
+
private invalidateSample;
|
|
217
|
+
private measuringSample;
|
|
218
|
+
private result;
|
|
219
|
+
private markActive;
|
|
220
|
+
private providerSequence;
|
|
221
|
+
private recordCompactionSessionUnavailable;
|
|
222
|
+
private persistedMaximum;
|
|
132
223
|
private compactionCount;
|
|
133
224
|
private handoffGeneration;
|
|
225
|
+
private generationId;
|
|
134
226
|
private threshold;
|
|
227
|
+
private maximum;
|
|
135
228
|
private now;
|
|
229
|
+
private capability;
|
|
136
230
|
}
|