/** * Repository-local working artifacts. * * Artifacts are file trees that should survive an agent session but should not * become project records: screenshots, exports, audit dumps, rollback inputs, * and similar material. Each direct child of `.harnery/artifacts/` is one * managed unit with a small manifest. Cleanup fails closed: only a valid, * expired, inactive, untracked managed unit is deletable. */ import { type ArtifactActivity } from "./activity.js"; import { ARTIFACT_SCHEMA_VERSION } from "./constants.js"; export { ARTIFACT_MANIFEST, ARTIFACT_SCHEMA_VERSION, ARTIFACTS_DIR } from "./constants.js"; export type { ArtifactDeliveryCard, ArtifactDeliveryItem, ArtifactDeliveryManifest, ArtifactDeliveryPath, ArtifactDeliveryUrl, } from "./delivery-card.js"; export { ARTIFACT_DELIVERY_AUTO_ITEM_LIMIT, ARTIFACT_DELIVERY_MANIFEST, ARTIFACT_DELIVERY_SCHEMA_VERSION, parseArtifactDeliverySpec, readArtifactDeliveryManifest, renderArtifactDeliveryCard, resolveArtifactDeliveryManifest, writeArtifactDeliveryManifest, } from "./delivery-card.js"; export interface ArtifactActor { instance_id: string; session_id?: string; name?: string; } export interface ArtifactManifestV2 { schema_version: typeof ARTIFACT_SCHEMA_VERSION; holds: ArtifactHold[]; artifact_id: string; slug: string; purpose: string; created_at: string; created_by?: ArtifactActor; retention: { expires_at: string; renewed_at?: string; reason?: string; }; released_at?: string; released_by?: ArtifactActor; oversize_acknowledged?: boolean; activity?: ArtifactActivity; } export interface ArtifactHold { id: string; reason: string; set_by: ArtifactActor; set_at: string; } export interface ArtifactHoldInput { id: string; reason: string; } export declare function artifactCapabilities(): { readonly schema_version: 2; readonly holds: true; readonly atomic_create_holds: true; readonly owner_scoped_unhold: true; readonly explicit_v1_migration: true; readonly minute_retention: true; readonly discard_after_review: true; }; export type ArtifactClassification = "managed-held" | "managed-active" | "managed-current" | "managed-expired" | "managed-oversize" | "managed-over-budget" | "managed-tracked" | "invalid-manifest" | "unmanaged" | "symlink" | "unknown"; export interface ArtifactInventoryEntry { name: string; path: string; relative_path: string; classification: ArtifactClassification; reason: string; action: "keep" | "would-delete" | "deleted"; bytes: number | null; artifact_id: string | null; slug: string | null; created_at: string | null; last_modified_at: string | null; expires_at: string | null; owner_instance_id: string | null; oversize_acknowledged: boolean; } export interface ArtifactCreateInput { slug: string; purpose: string; retentionDays: number; /** Sub-day retention. When set, it replaces `retentionDays` for this unit * (a page review pack expires in minutes, not days). 1 to 5,256,000. */ retentionMinutes?: number; actor?: ArtifactActor; /** Holds are persisted with the first manifest; a valid actor is required. */ holds?: ArtifactHoldInput[]; now?: Date; id?: string; big?: boolean; } export interface ArtifactMutationInput { actor?: ArtifactActor; now?: Date; } export interface ArtifactAdoptionResult { candidates: Array<{ path: string; name: string; bytes: number; kind: "file" | "directory"; }>; candidate_bytes: number; requires_big: boolean; adopted_artifact_id: string | null; adopted_path: string | null; adopted_directories: number; } interface ParsedManifest { ok: true; manifest: ArtifactManifestV2; } interface ManifestError { ok: false; reason: string; } export declare function artifactsRoot(repoRoot: string): string; /** Public config-aware default for embedding hosts that create artifact units. */ export declare function configuredArtifactRetentionDays(repoRoot: string): number; export declare function createArtifact(repoRoot: string, input: ArtifactCreateInput): { path: string; manifest: ArtifactManifestV2; }; export declare function inventoryArtifacts(repoRoot: string, opts?: { now?: Date; freshnessSeconds?: number; }): ArtifactInventoryEntry[]; export declare function showArtifact(repoRoot: string, ref: string, opts?: { now?: Date; freshnessSeconds?: number; }): { entry: ArtifactInventoryEntry; manifest: ArtifactManifestV2; }; export declare function renewArtifact(repoRoot: string, ref: string, days: number | { minutes: number; }, reason: string, input?: ArtifactMutationInput): ArtifactManifestV2; export declare function releaseArtifact(repoRoot: string, ref: string, input?: ArtifactMutationInput): ArtifactManifestV2; /** Retire reviewed evidence without deleting it or extending an earlier deadline. */ export declare function discardArtifact(repoRoot: string, ref: string, reason: string, input?: ArtifactMutationInput & { minutes?: number; }): ArtifactManifestV2; /** Advice only: a successful check does not establish that its evidence is disposable. */ export declare function artifactReviewGuidance(repoRoot: string, ref: string): string; export declare function holdArtifact(repoRoot: string, ref: string, input: ArtifactHoldInput & { actor: ArtifactActor; now?: Date; }): ArtifactManifestV2; export declare function unholdArtifact(repoRoot: string, ref: string, id: string, input: { actor: ArtifactActor; now?: Date; }): ArtifactManifestV2; /** Adopt untracked loose files and legacy directories without changing directory paths. */ export declare function adoptUnmanagedArtifactFiles(repoRoot: string, input: { yes?: boolean; big?: boolean; purpose: string; retentionDays: number; actor?: ArtifactActor; now?: Date; }): ArtifactAdoptionResult; export declare function cleanArtifacts(repoRoot: string, opts?: { yes?: boolean; now?: Date; freshnessSeconds?: number; }): ArtifactInventoryEntry[]; export interface ArtifactAutoCleanResult { ran: boolean; reason: "swept" | "partial" | "failed" | "disabled" | "fresh" | "no-root"; deleted: number; bytes: number; } /** * Throttled expired-artifact sweep, fired at SessionStart and before new work. * * Retention was previously enforced only when someone remembered to run * `artifacts clean --yes`, so expired workspaces accumulated indefinitely on * busy hosts. This runs the exact same guarded deletion (expired or over-budget * managed entries, each re-classified immediately before removal; * unmanaged and legacy directories are never touched) at most once per * interval (default 1h after completion, 1m between partial/failed slices). * The owner-aware lock serializes callers; interrupted attempts can retry. * Disable with `artifacts.auto_clean: false` or * `HARNERY_ARTIFACT_AUTO_CLEAN=0`. */ export declare function autoCleanArtifacts(repoRoot: string, opts?: { now?: Date; maxDeletes?: number; timeBudgetMs?: number; }): ArtifactAutoCleanResult; export declare function resolveArtifactRef(repoRoot: string, ref: string): string; /** Validate only the current schema. Legacy conversion belongs to migrateArtifacts. */ export declare function parseArtifactManifest(value: unknown): ParsedManifest | ManifestError; export interface ArtifactMigrationEntry { path: string; action: "keep" | "would-migrate" | "migrated"; reason: string; preimage_path?: string; } /** Explicit, bounded v1 cutover. Preview never writes; every applied unit keeps its exact preimage. */ export declare function migrateArtifacts(repoRoot: string, opts?: { yes?: boolean; now?: Date; }): ArtifactMigrationEntry[]; export interface ArtifactActivityRepairEntry { path: string; action: "keep" | "would-repair" | "repaired"; reason: string; previous_expires_at?: string; repaired_expires_at?: string; receipt_path?: string; } /** Repair only a provable, untouched v1-to-v2 migration. Never delete payloads. * Old migrations saved the original manifest but not the original root stat. * The exact preimage and coincident root/manifest/receipt timestamps bound this * correction. Any later root change or metadata mutation requires manual review. */ export declare function repairArtifactActivity(repoRoot: string, opts?: { yes?: boolean; now?: Date; }): ArtifactActivityRepairEntry[]; //# sourceMappingURL=index.d.ts.map