/** * crew#720 (operator ruling 2026-10-10): EVERY delivery — GitHub, Azure DevOps or local-only — * leaves a zip of the run's final codebase, whatever the outcome: delivered, push refused, * credentials missing, wrong account, lift conflict, or a run that failed after building. * * WHAT IS IN IT: the tree delivery would ship. HEAD, plus the worktree's changes to tracked files, * plus the untracked files the deliver phase's own classifier lets ride (`deliverExclusionReason`: * no `.env*` / key material / databases / scratch or tool-output dirs / oversize files) — gitignored * files never. It is snapshotted through a SCRATCH index (the worktree's own index is never * touched) and written with `git archive --format=zip `: tracked content only, no `.git`. * Tracked files ship as tracked, exactly as delivery treats them, and `git archive` honours the * repository's `export-ignore` / `export-subst` attributes. When the run's worktree is gone (the * engine reaps a cancelled run's, and a committed run's clean one) the `wicked/` branch in * the registered repository is archived instead. * * WHERE: `/artifacts/runs//codebase.zip` + `codebase.json` (the * record, the run id included). The record is durable on its own — {@link CodebaseArchiveStore.get} * reads it back after a restart — and served as `AgentSession.codebase_archive`; the bytes are * `GET /api/v1/runs/:id/artifacts/codebase.zip`. Re-archiving a run whose tree has not changed * rewrites nothing. * * Best-effort by construction: an archive that cannot be made is logged, never a run failure. */ import { type ReadStream } from 'node:fs'; /** api-types `CodebaseArchive` (crew#720). */ export interface CodebaseArchive { /** `GET` this (daemon-relative) for the zip. */ url: string; /** Hex sha256 of the zip's bytes. */ sha256: string; bytes: number; /** The git tree the zip was written from. */ tree: string; /** The commit the tree was taken on (the worktree's HEAD, or the run branch's tip). */ commit: string | null; /** Unix millis. */ created_at: number; /** What took it: the deliver phase (or a post-hoc deliver), or the run's end. */ trigger: 'deliver' | 'run_end'; /** Where the tree came from: the live worktree, or the retained run branch. */ source: 'worktree' | 'branch'; } /** Where the run's tree can be read: its worktree, and the registered repository (for the branch). */ export interface ArchiveSources { workdir?: string | null; repoRoot?: string | null; } /** `\0` — the key a deliver unit's dispatch and its output capture share. */ export declare function archiveKey(runId: string, ord: number): string; /** * The {@link archiveKey} of a `toolExecutorDispatched` frame that runs crew's deliver script — * recognised by its own refusal marker in the command (the structural mark `deliverUnitOf` reads, * crew#720 S3) — else `null`. The daemon archives on that unit's `unitOutputCaptured` only, so no * other unit's capture costs an engine read. */ export declare function deliverDispatchKey(event: { type?: unknown; session?: unknown; ord?: unknown; cmd?: unknown; }): string | null; /** The daemon-relative URL the zip is served at. */ export declare function codebaseArchiveUrl(runId: string): string; export declare function archiverGitEnv(env?: NodeJS.ProcessEnv): Record; /** * The tree delivery would ship from `workdir` (see the module doc), or throw. `scratch` is a * private temp dir for the scratch index and the pathspec file. */ export declare function worktreeShipTree(workdir: string, scratch: string): Promise<{ tree: string; commit: string | null; }>; export declare class CodebaseArchiveStore { readonly root: string; private readonly log; private readonly now; private readonly memo; private readonly chains; constructor(root?: string, log?: (msg: string) => void, now?: () => number); private dirOf; private stored; /** The run's archive record, or `undefined` — read from disk once, then memoized. */ get(runId: string): CodebaseArchive | undefined; /** The current zip's path, or `null`. */ zipFile(runId: string): string | null; /** * The record and a stream of EXACTLY its bytes, or `null`. The file is opened synchronously with * the record in hand, and every zip is content-addressed, so a newer archive landing meanwhile can * never put its bytes under this record's sha256 / length (codex r1). */ open(runId: string): { rec: CodebaseArchive; stream: ReadStream; } | null; /** * Archive the run's final tree (serialized per run). Answers the record — the existing one when * the tree is unchanged — or `null` when no tree could be read (logged). */ archive(runId: string, sources: ArchiveSources, trigger: CodebaseArchive['trigger']): Promise; private archiveNow; } //# sourceMappingURL=codebase-archive.d.ts.map