/** * Session regeneration worker activities: ARCHIVE and DISTILL * (docs/proposals/session-regen-and-footprint.md §4, §9). * * Both are idempotent per ATTEMPT (never per epoch): every artifact name * carries the attempt id, so a later attempt in the same epoch structurally * cannot pick up a stale predecessor's output — correctness never depends on * cleanup having run. Within one attempt, a retried activity short-circuits * on its own already-uploaded artifact. * * The Distiller is an ephemeral, fresh-context SDK session — never the * degraded session summarizing itself. Hygiene the title-summarizer pattern * lacks: its own COPILOT_HOME under a temp dir, and deleteSession + rm in a * finally, so nothing leaks per invocation. * * Injection posture: the transcript tail and the handoff are ATTACKER- * INFLUENCEABLE text. They are rendered inside fenced quote blocks as data; * the distiller is instructed that imperatives inside them carry no * authority; and the bootstrap renders every LLM-generated field as quoted * distiller output to verify — never as instructions. */ import type { SessionCatalog } from "./cms.js"; import type { ArtifactStore } from "./session-store.js"; export interface RegenArchiveInput { /** Selection strategy name; defaults to exchange-clustered. */ selectionStrategy?: string; sessionId: string; epoch: number; attemptId: string; } export interface RegenArchiveResult { /** First chunk — also the whole archive when it fits in one artifact. */ archiveArtifactId: string; /** Every chunk in order; length 1 for a single-artifact archive. */ archiveChunkIds: string[]; /** Which strategy chose the archived messages, and what it dropped. */ selectionStrategy: string; selectionStats: Record; elidedCount: number; turnsArchived: number; compactionsArchived: number; archiveMs: number; } export interface RegenDistillInput { sessionId: string; epoch: number; attemptId: string; /** Untrusted, length-capped freeform handoff from the requester. */ handoff?: string; /** Untrusted distilling instructions (HOW to distill), length-capped. */ instructions?: string; /** Session's own model ref (default distiller model — capability parity). */ sessionModel?: string; /** Per-call override (operator) or deployment distillerModel config. */ distillerModel?: string; archiveArtifactId?: string; } export interface RegenDistillResult { packageArtifactId: string; bootstrap: string; distillMs: number; distillerModel: string; packageBytes: number; } export interface RegenWorkerDeps { catalog: SessionCatalog; artifactStore: ArtifactStore | null; /** * Resolve a model ref to SDK session options ({model, provider} or * {githubToken}). Ref undefined = the deployment default. Returns null * when the ref doesn't resolve (dead model) — the caller falls back. */ resolveModelOptions(ref?: string): { model?: string; provider?: unknown; gitHubToken?: string; } | null; /** Deployment-configured fallback distiller model (mandatory per §9). */ fallbackDistillerModel?: string; trace(message: string): void; } export declare function archiveName(epoch: number, attemptId: string): string; /** * Chunk N of a multi-part archive. The archive is written as several * artifacts because a text artifact is capped at 1 MiB * (TEXT_ARTIFACT_MAX_BYTES) — a real 1.8 MB transcript threw * ARTIFACT_TOO_LARGE and killed regeneration outright on the session that * needed it most. */ export declare function archiveChunkName(epoch: number, attemptId: string, part: number): string; /** * Chunk payloads so each stays under the artifact ceiling. Splits on line * boundaries only — a JSONL record is never divided — and a single line that * exceeds the limit on its own becomes its own oversized chunk rather than * being silently dropped (the store will reject it and the caller reports a * real failure instead of losing a message). */ export declare function chunkArchiveLines(lines: string[], maxBytes: number): string[]; export declare function packageName(epoch: number, attemptId: string): string; /** Exact distiller input as sent — attempt-scoped dump artifact (§9 dumps). */ export declare function distillInputName(epoch: number, attemptId: string): string; /** Raw pre-parse distiller output — attempt-scoped dump artifact (§9 dumps). */ export declare function distillOutputName(epoch: number, attemptId: string): string; export declare function artifactExists(store: ArtifactStore, sessionId: string, filename: string): Promise; export declare function runRegenArchive(deps: RegenWorkerDeps, input: RegenArchiveInput): Promise; export interface ResumePackage { version: number; mission: string; standingInstructions: string[]; currentState: string; workingSet: string[]; commitments: string[]; childRoster: Array<{ id: string; role?: string; status?: string; }>; factsMap: string[]; artifactsMap: Array<{ id: string; what?: string; }>; workspaceMap: Array<{ path: string; what?: string; recreate?: string; }>; pitfalls: string[]; openQuestions: string[]; recentTail: string; /** Verbatim opening of the session — the mission as it was actually stated. */ openingContext?: string; /** * Requester's distilling instructions, embedded verbatim when the * DETERMINISTIC path ran (no LLM to honor them) so the reborn agent still * sees them. LLM distillations honor them in-prompt instead. */ requesterInstructions?: string; } /** Render the bootstrap the reborn session wakes to. Verified pointers, quoted summaries. */ export declare function renderBootstrap(pkg: ResumePackage, meta: { epoch: number; archiveArtifactId?: string; packageArtifactId: string; }): string; export interface RegenClosure { tail: string; /** First OPENING_MESSAGE_COUNT user/assistant messages, ≤ OPENING_CLIP chars. */ opening: string; firstUserMessage: string | null; childRoster: Array<{ id: string; status?: string; }>; artifactNames: string[]; } export declare function assembleRegenClosure(deps: Pick, sessionId: string): Promise; /** * Deterministic package from the closure alone (no LLM). The guaranteed * floor: what the reborn session boots from if the distiller model is * unavailable, hangs, or returns junk — a degraded-but-real resume package * always beats blocking the regeneration that exists to escape a broken * transcript. Requester instructions are embedded verbatim (§9) since no * LLM ran to honor them. */ export declare function deterministicPackage(closure: RegenClosure, opts?: { instructions?: string; }): ResumePackage; /** Parse + schema-normalize a distiller's raw response (throws on junk). */ export declare function parseDistillerResponse(text: string): ResumePackage; export declare function runRegenDistill(deps: RegenWorkerDeps, input: RegenDistillInput): Promise; /** * System message for the regen-distiller service session. The session is * read-only machinery: one seed prompt in, one ResumePackage JSON out. */ export declare const DISTILLER_SYSTEM_MESSAGE: string; /** * Seed prompt for the service-session distiller: page the WHOLE archived * transcript via read_transcript_page (map), then emit the ResumePackage * JSON (reduce). Untrusted inputs ride in explicit fences. */ export declare function buildMapReduceSeedPrompt(args: { servedSessionId: string; epoch: number; attemptId: string; archiveArtifactId: string; /** Full chunk list when the archive spans several artifacts. */ archiveChunkIds?: string[]; closure: RegenClosure; handoff?: string; instructions?: string; }, nonce?: string): string; //# sourceMappingURL=regen-worker.d.ts.map