@quantiya/codevibe-core 2.0.91 → 2.0.93

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.
Files changed (81) hide show
  1. package/dist/adapter/registry.d.ts +2 -2
  2. package/dist/appsync/sync-deferred-inputs.d.ts +2 -2
  3. package/dist/appsync/sync-desktop-registration.d.ts +2 -2
  4. package/dist/appsync/sync-mobile-echo.d.ts +16 -16
  5. package/dist/appsync/sync-prompt-publication.d.ts +8 -8
  6. package/dist/appsync/sync-shell-source.d.ts +18 -18
  7. package/dist/appsync/sync-source-cursor.d.ts +30 -30
  8. package/dist/commerce/upgrade-handoff.d.ts +11 -0
  9. package/dist/companion-mode/codex-native-question-reply.d.ts +22 -0
  10. package/dist/companion-mode/codex-native-state.d.ts +132 -114
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +2605 -2306
  13. package/dist/local-executor/anchored-cas.d.ts +78 -6
  14. package/dist/local-executor/anchored-fs.d.ts +32 -0
  15. package/dist/local-executor/blob-store.d.ts +95 -0
  16. package/dist/local-executor/change-manifest-store.d.ts +32 -0
  17. package/dist/local-executor/change-manifest.d.ts +50 -0
  18. package/dist/local-executor/durable-store.d.ts +72 -15
  19. package/dist/local-executor/implementor-argv.d.ts +43 -7
  20. package/dist/local-executor/local-executor-impl.d.ts +57 -1
  21. package/dist/local-executor/nolink-apply.d.ts +22 -2
  22. package/dist/local-executor/revert-runner.d.ts +3 -0
  23. package/dist/local-executor/shadow-apply-capabilities.d.ts +31 -2
  24. package/dist/local-executor/shadow-free-space.d.ts +25 -0
  25. package/dist/local-executor/shadow-protocol.d.ts +117 -18
  26. package/dist/local-executor/shadow-recovery.d.ts +28 -3
  27. package/dist/local-executor/shadow-task-intent.d.ts +29 -0
  28. package/dist/local-executor/shadow-volume-probe.d.ts +0 -14
  29. package/dist/local-executor/sharded-list.d.ts +32 -0
  30. package/dist/local-executor/snapshot-merge.d.ts +18 -0
  31. package/dist/local-executor/spawn-sweep-binary.d.ts +33 -0
  32. package/dist/local-executor/spawn-sweep.d.ts +3 -0
  33. package/dist/local-executor/store-dir-anchor.d.ts +48 -0
  34. package/dist/local-executor/tier-self-test.d.ts +57 -0
  35. package/dist/local-executor/types.d.ts +26 -0
  36. package/dist/local-executor/verification-gates/diff-sanity.d.ts +6 -0
  37. package/dist/local-executor/workspace-materializer.d.ts +17 -1
  38. package/dist/local-executor/workspace-shadow.d.ts +209 -7
  39. package/dist/orchestration-shell/audit-summary.d.ts +22 -0
  40. package/dist/orchestration-shell/bounded-shutdown.d.ts +2 -0
  41. package/dist/orchestration-shell/cli.js +64053 -57609
  42. package/dist/orchestration-shell/components/OrchestrationApp.d.ts +6 -1
  43. package/dist/orchestration-shell/decision-attempt-recovery.d.ts +14 -0
  44. package/dist/orchestration-shell/engine-projection.d.ts +98 -0
  45. package/dist/orchestration-shell/exit-guard.d.ts +18 -0
  46. package/dist/orchestration-shell/gate-decision-submit.d.ts +2 -0
  47. package/dist/orchestration-shell/index.d.ts +18 -0
  48. package/dist/orchestration-shell/quorum-loop.d.ts +265 -21
  49. package/dist/orchestration-shell/review-bundle.d.ts +77 -0
  50. package/dist/orchestration-shell/review-receipts.d.ts +100 -0
  51. package/dist/orchestration-shell/reviewer-seat-self-test.d.ts +43 -0
  52. package/dist/orchestration-shell/team-decompose.d.ts +1 -0
  53. package/dist/orchestration-shell/types.d.ts +24 -0
  54. package/dist/reviewer/provider.d.ts +28 -0
  55. package/dist/reviewer/providers/antigravity.d.ts +4 -2
  56. package/dist/reviewer/providers/claude.d.ts +7 -2
  57. package/dist/reviewer/providers/codex.d.ts +1 -1
  58. package/dist/session-resume/cli-resume.d.ts +1 -0
  59. package/dist/substrate/bwrap.d.ts +85 -0
  60. package/dist/substrate/command-runner.d.ts +1 -0
  61. package/dist/substrate/docker.d.ts +21 -0
  62. package/dist/substrate/grant-rule.d.ts +71 -0
  63. package/dist/substrate/pasta-isolation-check.d.ts +60 -0
  64. package/dist/substrate/pasta.d.ts +8 -0
  65. package/dist/substrate/relay-channel.d.ts +80 -0
  66. package/dist/substrate/review-mask.d.ts +20 -0
  67. package/dist/substrate/sandbox-exec.d.ts +48 -1
  68. package/dist/substrate/seatbelt-grants.d.ts +46 -0
  69. package/dist/substrate/spawn-records.d.ts +95 -0
  70. package/dist/substrate/tier-relay-main.d.ts +1 -0
  71. package/dist/substrate/tier-relay.d.ts +7 -0
  72. package/dist/substrate/tier-relay.js +3 -0
  73. package/dist/substrate/types.d.ts +48 -1
  74. package/dist/substrate-launch/engage-substrate.d.ts +16 -2
  75. package/dist/substrate-launch/fallback-ladder.d.ts +109 -12
  76. package/dist/substrate-launch/index.d.ts +1 -1
  77. package/dist/substrate-launch/task-folder.d.ts +26 -0
  78. package/native/linux-arm64/codevibe-spawn-sweep +0 -0
  79. package/native/linux-x64/codevibe-spawn-sweep +0 -0
  80. package/native/spawn-sweep.json +8 -0
  81. package/package.json +10 -5
@@ -1,35 +1,102 @@
1
+ import { type BlobStore } from './blob-store';
2
+ import type { BlobRef } from './change-manifest';
1
3
  import { type AnchoredFsIdentity } from './anchored-fs';
2
4
  import { type WorkspaceRootAuthority } from './workspace-authority';
5
+ import { spawnIsolatedHelper } from './bounded-helper';
3
6
  import { CasHelperCrashStage, CasRecoveryAuthority } from './shadow-protocol';
4
7
  export declare function casRecoveryAuthorityFor(root: WorkspaceRootAuthority, rel: string, expectedHash: string | null, expectedMode: number | null, postHash: string | null, postMode: number | null): CasRecoveryAuthority;
8
+ /**
9
+ * P63 Slice 2 (§9.4) — byte cap for a post-image streamed from a blob: the capture cap. The helper copies and hashes in
10
+ * 1 MiB chunks, so this bounds disk, not memory.
11
+ */
12
+ export declare const MAX_PROMOTED_BLOB_BYTES: number;
5
13
  export interface AcceptedAlternateImage {
6
14
  hash: string | null;
7
15
  mode: number | null;
8
16
  }
17
+ export declare function markCasHelperStillRunning(err: unknown): void;
18
+ /** True when the failed request's helper was still running after its retirement returned (N1). */
19
+ export declare function casHelperStillRunning(err: unknown): boolean;
9
20
  export declare class WorkspaceCasUnresolvedError extends Error {
10
21
  readonly recoveryName?: string;
11
22
  constructor(recoveryName?: string, detail?: string);
12
23
  }
13
- export declare function replacePromotedFile(workingDir: string, rel: string, content: string, mode: number, expectedHash: string | null, expectedMode: number | null, beforeCommit?: () => Promise<void> | undefined, afterValidation?: () => Promise<void> | undefined, afterDisplacement?: (recoveryName: string) => Promise<void> | undefined, beforeTargetQuarantine?: (quarantineName: string) => Promise<void> | undefined, beforeFinalUnlink?: (recoveryName: string) => Promise<void> | undefined, afterPrepared?: () => Promise<void> | undefined, expectedRootIdentity?: AnchoredFsIdentity | WorkspaceRootAuthority, helperOpts?: {
24
+ /**
25
+ * F-APPLY-300K-UNRESOLVED (e4bfix1): an unresolved promotion was logged with its paths only, so which of the ways a
26
+ * committed CAS can end unresolved had happened was unknowable. Maps the error text to a fixed code (no paths, no
27
+ * stderr, no user content) that is safe for the shape-only logs; an exit status is kept, nothing else of the text.
28
+ */
29
+ export declare function casUnresolvedReasonCode(message: string): string;
30
+ interface CasChannel {
31
+ child: ReturnType<typeof spawnIsolatedHelper>;
32
+ closedPromise: Promise<number | null>;
33
+ isClosed(): boolean;
34
+ closeCode(): number | null;
35
+ stderrText(): string;
36
+ waitForClose(timeoutMs: number): Promise<number | null>;
37
+ nextMessage(timeoutMs: number): Promise<Record<string, unknown>>;
38
+ /** F-APPLY-300K-UNRESOLVED: record one protocol line sent (a closed kind, never content). */
39
+ noteSent(kind: CasProtocolSentKind): void;
40
+ /** The last protocol events, oldest first: `s:<kind>@<ms>` / `r:<type>@<ms>` since the helper started. */
41
+ trail(): string[];
42
+ }
43
+ /**
44
+ * F-APPLY-300K-UNRESOLVED (E2E-4b): the closed protocol vocabulary a reply-timeout trail may carry. A timeout names
45
+ * the exact step at which the desktop and the helper stopped hearing each other, without any path or content.
46
+ */
47
+ export type CasProtocolSentKind = 'request' | 'VALIDATE_CHAIN' | 'COMMIT' | 'CONTINUE_PREPARED' | 'CONTINUE' | 'UNLINK' | 'QUARANTINE' | 'ABORT' | 'END';
48
+ /** The helper's own protocol trail carried by a reply-timeout message (closed tokens only), or null. */
49
+ export declare function casHelperTrail(message: string): string[] | null;
50
+ /** The protocol trail carried by a reply-timeout message (closed tokens only), or null. */
51
+ export declare function casProtocolTrail(message: string): string[] | null;
52
+ /**
53
+ * F-CAS-BATCH — one helper for a whole apply, instead of one process launch per file. Every request runs the same
54
+ * `execute()` with the same handshake and the same fsyncs; only the launch is shared. Any failure of a request retires
55
+ * the helper, and the next request starts a fresh one. `close()` belongs in a `finally`: it ends the helper whatever happened.
56
+ */
57
+ export interface AnchoredCasSession {
58
+ /** @internal the running helper, started if there is none. */
59
+ acquire(helperTimeoutMs: number): CasChannel;
60
+ /** @internal a request failed: end this helper, whatever state it is in. */
61
+ retire(channel: CasChannel): Promise<void>;
62
+ close(): Promise<void>;
63
+ }
64
+ export declare function createAnchoredCasSession(): AnchoredCasSession;
65
+ export declare function replacePromotedFile(workingDir: string, rel: string,
66
+ /** The post-image: inline text, or (P63 Slice 2 §9.4) a verified blob streamed to the helper in 1 MiB chunks. */
67
+ content: string | {
68
+ blob: BlobRef;
69
+ store: BlobStore;
70
+ }, mode: number, expectedHash: string | null, expectedMode: number | null, beforeCommit?: () => Promise<void> | undefined, afterValidation?: () => Promise<void> | undefined, afterDisplacement?: (recoveryName: string) => Promise<void> | undefined, beforeTargetQuarantine?: (quarantineName: string) => Promise<void> | undefined, beforeFinalUnlink?: (recoveryName: string) => Promise<void> | undefined, afterPrepared?: () => Promise<void> | undefined, expectedRootIdentity?: AnchoredFsIdentity | WorkspaceRootAuthority, helperOpts?: {
14
71
  helperTimeoutMs?: number;
15
- testHangStage?: 'root' | 'parent' | 'target';
72
+ testHangStage?: 'root' | 'parent' | 'target' | 'prepared';
16
73
  testCrashStage?: CasHelperCrashStage;
17
74
  allowPostImageRecovery?: boolean;
18
75
  acceptedAlternate?: AcceptedAlternateImage;
76
+ session?: AnchoredCasSession;
77
+ /** F-CAS-BATCH: the target is a disposable copy, not the user's folder: no fsyncs. */
78
+ disposableTarget?: boolean;
19
79
  }): Promise<boolean>;
20
80
  export declare function removePromotedFile(workingDir: string, rel: string, expectedHash: string | null, expectedMode: number | null, beforeCommit?: () => Promise<void> | undefined, afterValidation?: () => Promise<void> | undefined, afterDisplacement?: (recoveryName: string) => Promise<void> | undefined, beforeTargetQuarantine?: (quarantineName: string) => Promise<void> | undefined, beforeFinalUnlink?: (recoveryName: string) => Promise<void> | undefined, expectedRootIdentity?: AnchoredFsIdentity | WorkspaceRootAuthority, helperOpts?: {
21
81
  helperTimeoutMs?: number;
22
- testHangStage?: 'root' | 'parent' | 'target';
82
+ testHangStage?: 'root' | 'parent' | 'target' | 'prepared';
23
83
  testCrashStage?: CasHelperCrashStage;
24
84
  allowPostImageRecovery?: boolean;
25
85
  acceptedAlternate?: AcceptedAlternateImage;
86
+ session?: AnchoredCasSession;
87
+ /** F-CAS-BATCH: the target is a disposable copy, not the user's folder: no fsyncs. */
88
+ disposableTarget?: boolean;
26
89
  }): Promise<boolean>;
27
90
  /** Hardened reverse-apply surface shared by promotion and durable revert. */
28
91
  export declare function replaceWorkspaceFileIfMatches(args: {
29
92
  workingDir: string;
30
93
  workspaceRootAuthority: WorkspaceRootAuthority;
31
94
  path: string;
32
- content: string;
95
+ /** Inline text, or (P63 Slice 2 §9.4) a verified blob streamed to the helper — a team revert's pre-image. */
96
+ content: string | {
97
+ blob: BlobRef;
98
+ store: BlobStore;
99
+ };
33
100
  mode: number;
34
101
  expectedHash: string | null;
35
102
  expectedMode: number | null;
@@ -46,13 +113,15 @@ export declare function replaceWorkspaceFileIfMatches(args: {
46
113
  /** @internal lower deterministic CAS-helper deadline for tests. */
47
114
  helperTimeoutMs?: number;
48
115
  /** @internal deterministic CAS-helper target-filesystem stall seam. */
49
- testHangHelperStage?: 'root' | 'parent' | 'target';
116
+ testHangHelperStage?: 'root' | 'parent' | 'target' | 'prepared';
50
117
  /** @internal deterministic helper death after COMMIT/mutation. */
51
118
  testCrashHelperStage?: CasHelperCrashStage;
52
119
  /** Re-drive deterministic recovery names owned by an existing durable journal. */
53
120
  allowPostImageRecovery?: boolean;
54
121
  /** Exact terminal image that authorizes cleanup-only replay for same-path chains. */
55
122
  acceptedAlternate?: AcceptedAlternateImage;
123
+ /** F-CAS-BATCH: run in this session's helper. */
124
+ session?: AnchoredCasSession;
56
125
  }): Promise<boolean>;
57
126
  /** Hardened reverse-delete surface shared by promotion and durable revert. */
58
127
  export declare function removeWorkspaceFileIfMatches(args: {
@@ -74,11 +143,14 @@ export declare function removeWorkspaceFileIfMatches(args: {
74
143
  /** @internal lower deterministic CAS-helper deadline for tests. */
75
144
  helperTimeoutMs?: number;
76
145
  /** @internal deterministic CAS-helper target-filesystem stall seam. */
77
- testHangHelperStage?: 'root' | 'parent' | 'target';
146
+ testHangHelperStage?: 'root' | 'parent' | 'target' | 'prepared';
78
147
  /** @internal deterministic helper death after COMMIT/mutation. */
79
148
  testCrashHelperStage?: CasHelperCrashStage;
80
149
  /** Re-drive deterministic recovery names owned by an existing durable journal. */
81
150
  allowPostImageRecovery?: boolean;
82
151
  /** Exact terminal image that authorizes cleanup-only replay for same-path chains. */
83
152
  acceptedAlternate?: AcceptedAlternateImage;
153
+ /** F-CAS-BATCH: run in this session's helper. */
154
+ session?: AnchoredCasSession;
84
155
  }): Promise<boolean>;
156
+ export {};
@@ -114,6 +114,38 @@ export declare function anchoredHashFileWithinRoot(root: string, relativePath: s
114
114
  testHangStage?: 'open' | 'read' | 'close';
115
115
  signal?: AbortSignal;
116
116
  }): Promise<AnchoredHashedFile | null>;
117
+ /**
118
+ * P63 Slice 2 (§9.4) — copy one file, anchored exactly as `anchoredHashFileWithinRoot` reads it, into `outFd` (an open
119
+ * writable descriptor the caller owns) in 1 MiB chunks, returning its hash, size and identity. The bytes never cross
120
+ * the helper protocol and are never held whole. The caller must treat what it finds in `outFd` as unverified until it
121
+ * has re-hashed it against the returned `sha256` and `size` (`putBlobFromAnchoredCopy` does).
122
+ */
123
+ export declare function anchoredCopyFileWithinRoot(root: string, relativePath: string, opts: {
124
+ maxBytes: number;
125
+ outFd: number;
126
+ expectedRoot?: AnchoredFsIdentity | WorkspaceRootAuthority;
127
+ helperTimeoutMs?: number;
128
+ signal?: AbortSignal;
129
+ }): Promise<AnchoredHashedFile | null>;
130
+ /** F-CAS-BATCH: bytes returned with a session read's hash, when the file is at most this size. */
131
+ export declare const READ_SESSION_CONTENT_MAX_BYTES: number;
132
+ /**
133
+ * F-CAS-BATCH — one read helper for a whole pass over a change, instead of one process launch per file: the same
134
+ * `readFile` with the same root, chain, publication-marker and identity checks as `anchoredHashFileWithinRoot`, whose
135
+ * errors it reports in the same words. Calls run one at a time. `close()` belongs in a `finally`.
136
+ */
137
+ export interface AnchoredReadSession {
138
+ hashFile(root: string, relativePath: string, opts: {
139
+ maxBytes: number;
140
+ expectedRoot: AnchoredFsIdentity | WorkspaceRootAuthority;
141
+ contentMaxBytes?: number;
142
+ helperTimeoutMs?: number;
143
+ }): Promise<(AnchoredHashedFile & {
144
+ bytes?: Buffer;
145
+ }) | null>;
146
+ close(): Promise<void>;
147
+ }
148
+ export declare function createAnchoredReadSession(): AnchoredReadSession;
117
149
  export type AnchoredWritePrecondition = {
118
150
  kind: 'absent';
119
151
  } | {
@@ -0,0 +1,95 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import type { BlobRef } from './change-manifest';
3
+ import { type StoreAnchor } from './store-dir-anchor';
4
+ export declare const BLOB_CHUNK_BYTES: number;
5
+ /** One instance's blob namespace in a task state directory. */
6
+ export interface BlobStore {
7
+ dir: string;
8
+ prefix: string;
9
+ }
10
+ export declare function blobName(store: BlobStore, sha256: string): string;
11
+ /**
12
+ * R2-F3 (Stage 2 R2): run `fn` with the content-store directory held (`store-dir-anchor.ts`), creating it (0700, its
13
+ * parent's entry fsynced) on first use. A store path that is anything but a real directory is refused, never followed.
14
+ */
15
+ export declare function withStoreAnchor<T>(dir: string, fn: (anchor: StoreAnchor) => Promise<T>): Promise<T>;
16
+ /**
17
+ * F-S2-4 / S2-C5 / R2-F3: create an exclusive temp file in the HELD store directory. On Linux the name is created
18
+ * through the held descriptor, so it cannot land anywhere else; on macOS a symlink anywhere in the path refuses the
19
+ * open. Either way the file must then be in the same directory, still named by the store path — otherwise it is
20
+ * removed (through the held directory) and refused.
21
+ */
22
+ export declare function createStoreTemp(anchor: StoreAnchor, name: string,
23
+ /** Test-only: runs between the directory being held and the open (the replacement race). */
24
+ testBeforeOpen?: () => Promise<void>): Promise<fs.FileHandle>;
25
+ /**
26
+ * Publish a written, fsynced temp file under `finalName` in the held directory by `link()` (never replacing a name).
27
+ * `'exists'` when the name was already there — the caller must verify it holds the same bytes. The temp name is
28
+ * always removed.
29
+ */
30
+ export declare function publishStoreTemp(anchor: StoreAnchor, tmp: string, finalName: string): Promise<'published' | 'exists'>;
31
+ /** Remove a temp name from the held directory (a failed write). */
32
+ export declare function dropStoreTemp(anchor: StoreAnchor, tmp: string): Promise<void>;
33
+ /**
34
+ * Store the bytes produced by `chunks` and return their reference. Streaming: at most one chunk is held. When a blob
35
+ * with the same hash already exists it is verified (size and hash) and kept; the new copy is dropped.
36
+ */
37
+ export declare function putBlob(store: BlobStore, chunks: AsyncIterable<Buffer | Uint8Array>,
38
+ /**
39
+ * P63 Slice 2 (§9.4 capture at scale): skip the per-blob directory fsync — the caller MUST call
40
+ * {@link syncBlobStore} once before any record references the blobs it published (the file itself is still fsynced).
41
+ */
42
+ opts?: {
43
+ deferDirSync?: boolean;
44
+ }): Promise<BlobRef>;
45
+ /** P63 Slice 2: the one directory fsync owed by blobs published with `deferDirSync`. */
46
+ export declare function syncBlobStore(store: BlobStore): Promise<void>;
47
+ /** Store an in-memory buffer or string. */
48
+ export declare function putBlobBytes(store: BlobStore, data: Buffer | string, opts?: {
49
+ deferDirSync?: boolean;
50
+ }): Promise<BlobRef>;
51
+ /**
52
+ * Store a file copied by an anchored helper (`anchoredCopyFileWithinRoot`): `copy` receives an exclusive temp file's
53
+ * descriptor and returns the hash and size the helper read, or null when the file is absent. The temp file is
54
+ * fsynced and re-hashed here before it is published, so a blob name never holds bytes other than the ones the anchored
55
+ * read hashed. Returns null (and leaves nothing behind) when `copy` does.
56
+ */
57
+ export declare function putBlobFromAnchoredCopy<T extends {
58
+ sha256: string;
59
+ size: number;
60
+ }>(store: BlobStore, copy: (fd: number) => Promise<T | null>): Promise<{
61
+ ref: BlobRef;
62
+ read: T;
63
+ } | null>;
64
+ /** Store a file's bytes by streaming it (opened no-follow; the caller owns the path's authority). */
65
+ /**
66
+ * §9.4 — copy one verified blob into another store (a team track's capture into its group's content store, a merge
67
+ * plan's result into the integration copy's store): streamed in 1 MiB chunks, hashed while written, published by link
68
+ * (never replacing a name). A source that does not match its reference throws before anything is published; a
69
+ * destination that already holds the name is verified, never overwritten.
70
+ */
71
+ export declare function copyBlob(src: BlobStore, dst: BlobStore, ref: BlobRef): Promise<BlobRef>;
72
+ export declare function putBlobFromFile(store: BlobStore, file: string): Promise<BlobRef>;
73
+ /**
74
+ * Stream a blob in chunks of at most 1 MiB. The size is checked before the first byte and the hash after the last;
75
+ * a mismatch throws from the iterator, so a consumer that finishes the loop has read exactly the referenced bytes.
76
+ */
77
+ export declare function readBlobChunks(store: BlobStore, ref: BlobRef): AsyncGenerator<Buffer>;
78
+ /**
79
+ * Open a blob for a consumer that streams and verifies it itself (the CAS helper hashes while it copies and refuses a
80
+ * mismatch before anything is published). Opened no-follow; must be a regular file of exactly `ref.size` bytes.
81
+ */
82
+ export declare function openBlobForStreaming(store: BlobStore, ref: BlobRef): Promise<fs.FileHandle>;
83
+ /**
84
+ * P63 Slice 2 (§9.4) — the strict-text rule of `decodeStrictUtf8` (valid UTF-8, no NUL byte) checked while streaming,
85
+ * for a file too large to decode whole. Throws the same "binary file changes are not supported safely" refusal.
86
+ */
87
+ export declare function assertBlobStrictUtf8(store: BlobStore, ref: BlobRef, rel: string): Promise<void>;
88
+ /** Verify a blob end to end without keeping its bytes. */
89
+ export declare function verifyBlob(store: BlobStore, ref: BlobRef): Promise<void>;
90
+ /** Read a blob whole (only for references at most `maxBytes`), verified. */
91
+ export declare function readBlobBytes(store: BlobStore, ref: BlobRef, maxBytes: number): Promise<Buffer>;
92
+ /** The first `maxBytes` of a blob (not hash-verified: a partial read cannot be). For display excerpts only. */
93
+ export declare function readBlobHead(store: BlobStore, ref: BlobRef, maxBytes: number): Promise<Buffer>;
94
+ /** This instance's blob names (and stray temp files). */
95
+ export declare function listBlobFiles(store: BlobStore): Promise<string[]>;
@@ -0,0 +1,32 @@
1
+ import { CHANGE_MANIFEST_VERSION, type ChangeManifest, type ChangeManifestEntry } from './change-manifest';
2
+ /** What a marker records for a reviewed change: the ordered shard hashes and the root (§9.4). */
3
+ export interface ChangeManifestRecord {
4
+ changeManifest: typeof CHANGE_MANIFEST_VERSION;
5
+ entries: number;
6
+ shards: Array<{
7
+ sha256: string;
8
+ entries: number;
9
+ }>;
10
+ rootHash: string;
11
+ }
12
+ /** One instance's manifest namespace in a task state directory. */
13
+ export interface ManifestStore {
14
+ dir: string;
15
+ prefix: string;
16
+ }
17
+ /** A shard holds at most 10,000 entries of a few hundred bytes each; anything far larger is not a shard. */
18
+ export declare const MAX_MANIFEST_SHARD_BYTES: number;
19
+ export declare function manifestShardName(store: ManifestStore, sha256: string): string;
20
+ /**
21
+ * Write every shard of `manifest` (each once; an existing name must already hold exactly those bytes) and return the
22
+ * record a marker stores. The directory is fsynced before returning, so a marker written afterwards never names a
23
+ * shard that a crash could lose.
24
+ */
25
+ export declare function writeChangeManifest(store: ManifestStore, manifest: ChangeManifest): Promise<ChangeManifestRecord>;
26
+ /** Validate a record's shape and root hash (no file access). */
27
+ export declare function assertChangeManifestRecord(value: unknown): asserts value is ChangeManifestRecord;
28
+ /**
29
+ * Read every entry a record names, verifying each shard's bytes and count, the record's root hash, and that the
30
+ * entries are in strictly increasing byte order of path across shards (so no path appears twice).
31
+ */
32
+ export declare function readChangeManifest(store: ManifestStore, record: unknown): Promise<ChangeManifestEntry[]>;
@@ -0,0 +1,50 @@
1
+ import type { ShadowChangeKind } from './shadow-protocol';
2
+ export declare const CHANGE_MANIFEST_VERSION = 1;
3
+ export declare const CHANGE_MANIFEST_SHARD_ENTRIES = 10000;
4
+ /** A content-addressed blob in the task's store (§9.4). Never on the engine wire. */
5
+ export interface BlobRef {
6
+ blob: string;
7
+ size: number;
8
+ }
9
+ export interface ChangeManifestEntry {
10
+ path: string;
11
+ change_kind: ShadowChangeKind;
12
+ /** POSIX mode of the reviewed post-image; null for a deletion. */
13
+ mode: number | null;
14
+ /** SHA-256 of the pre-task file, or null when the path was absent. */
15
+ base_hash: string | null;
16
+ /** The reviewed post-image; null for a deletion. */
17
+ content: BlobRef | null;
18
+ /**
19
+ * The pre-task file's POSIX mode (null when absent). Optional: a team track bundle records it (a merge needs every
20
+ * track's declared base mode); a single task's manifest omits it and its lines are unchanged.
21
+ */
22
+ base_mode?: number | null;
23
+ }
24
+ export interface ChangeManifestShard {
25
+ /** SHA-256 (hex) of `bytes`; the shard's name in the store. */
26
+ sha256: string;
27
+ entries: number;
28
+ bytes: Buffer;
29
+ }
30
+ export interface ChangeManifest {
31
+ changeManifest: typeof CHANGE_MANIFEST_VERSION;
32
+ entries: number;
33
+ shards: ChangeManifestShard[];
34
+ /** SHA-256 of the ordered shard hashes, one per line. */
35
+ rootHash: string;
36
+ }
37
+ export declare function manifestRootHash(shardHashes: readonly string[]): string;
38
+ /**
39
+ * Build the manifest for a change. Entries are sorted by path (byte order) and must be unique; shards hold at most
40
+ * `entriesPerShard` entries. Pure — the caller writes the shards (§9.4: each written once, named by its SHA-256).
41
+ */
42
+ export declare function buildChangeManifest(entries: readonly ChangeManifestEntry[], entriesPerShard?: number): ChangeManifest;
43
+ /**
44
+ * Parse one shard read back from the store, verifying it against its recorded hash and entry count. A shard whose
45
+ * bytes, count or any entry does not match is refused — the manifest is authority for what promotion writes.
46
+ */
47
+ export declare function parseChangeManifestShard(bytes: Buffer, expected: {
48
+ sha256: string;
49
+ entries: number;
50
+ }): ChangeManifestEntry[];
@@ -1,4 +1,6 @@
1
1
  import { type ReviewedShadowDiffFile, type ShadowChangeKind, type ShadowDiffFile } from './workspace-shadow';
2
+ import { type BlobStore } from './blob-store';
3
+ import type { BlobRef } from './change-manifest';
2
4
  import { type SnapshotBundlesView, type SnapshotTrackBundle } from './snapshot-merge';
3
5
  import type { ImageAttachment } from '../orchestration-shell/types';
4
6
  import { type WorkspaceRootAuthority } from './workspace-authority';
@@ -52,12 +54,15 @@ export interface RevertManifestEntry {
52
54
  /** How the promote changed the file. */
53
55
  changeKind: ShadowChangeKind;
54
56
  /**
55
- * Where the pre-image CONTENT lives — a `preimages/<sha>.blob` relative ref, OR
56
- * the {@link ABSENT_PREIMAGE} marker for a `created` file (nothing to restore).
57
+ * Where the pre-image CONTENT lives — `content/<blob name>` in the group's content store (§9.4, streamed), a
58
+ * `preimages/<sha>.blob` relative ref (a record written before Slice 2), OR the {@link ABSENT_PREIMAGE} marker for a
59
+ * `created` file (nothing to restore).
57
60
  */
58
61
  preImageRef: string;
59
62
  /** SHA-256 of the pre-image content; `null` for an absent (created) file. */
60
63
  preImageHash: string | null;
64
+ /** §9.4 — the pre-image's byte size, for a `content/` reference (its blob reference is `{ preImageHash, size }`). */
65
+ preImageSize?: number;
61
66
  /** POSIX permission bits of the pre-image; `null` when absent. */
62
67
  preImageMode: number | null;
63
68
  /** SHA-256 of the applied content; `null` for a `deleted` file. */
@@ -201,9 +206,15 @@ export interface PromoteManifestInput {
201
206
  promoteOrder: number;
202
207
  path: string;
203
208
  changeKind: ShadowChangeKind;
204
- /** The pre-image content (the file currently on disk); `null` = absent (created). */
205
- preImageContent: string | null;
206
- /** SHA-256 of `preImageContent`; `null` when absent. */
209
+ /**
210
+ * §9.4 — the pre-image (the file currently on disk) as a verified blob in the promoting instance's store; `null` =
211
+ * absent (created). The store stream-copies it into the group's content store — never as text in memory.
212
+ */
213
+ preImage: {
214
+ store: BlobStore;
215
+ ref: BlobRef;
216
+ } | null;
217
+ /** SHA-256 of the pre-image; `null` when absent. */
207
218
  preImageHash: string | null;
208
219
  preImageMode: number | null;
209
220
  /** SHA-256 of the applied content; `null` for a deleted file. */
@@ -237,8 +248,9 @@ export interface TrackTipsView {
237
248
  export declare class RevertManifestStore {
238
249
  private readonly rootDir;
239
250
  private readonly maxStoreJsonBytes;
240
- private readonly maxPreImageAggregateBytes;
241
251
  private readonly maxMetadataEntries;
252
+ /** §9.4 — the bound on a sharded per-path list (bundle file occurrences); the change-set ceiling by default. */
253
+ private readonly maxChangeSetEntries;
242
254
  private readonly maxMetadataPathBytes;
243
255
  private readonly beforePreImageOpen?;
244
256
  private readonly afterPreImageStat?;
@@ -251,11 +263,9 @@ export declare class RevertManifestStore {
251
263
  private readonly failRootDirectorySyncStage;
252
264
  private readonly helperTimeoutMs;
253
265
  private readonly testHangStoreHelperBeforeReady;
254
- private readonly testHangPreImageEnumerationStat;
255
266
  private readonly testHangPreImageReadStage;
256
267
  private readonly afterAnchoredWriteValidation?;
257
268
  private readonly afterJsonAuthorityRead?;
258
- private readonly afterPreImageAbsentRead?;
259
269
  private readonly beforeMergeFenceAcquire?;
260
270
  private anchoredWriteCount;
261
271
  /**
@@ -277,10 +287,9 @@ export declare class RevertManifestStore {
277
287
  rootDir?: string;
278
288
  /** @internal lower deterministic limit for serialized-authority boundary tests. */
279
289
  maxStoreJsonBytes?: number;
280
- /** @internal lower deterministic aggregate pre-image quota for tests. */
281
- maxPreImageAggregateBytes?: number;
282
290
  /** @internal lower deterministic shared metadata ceilings for tests. */
283
291
  maxMetadataEntries?: number;
292
+ maxChangeSetEntries?: number;
284
293
  maxMetadataPathBytes?: number;
285
294
  /** @internal deterministic read-race seam. */
286
295
  beforePreImageOpen?: (blobPath: string) => Promise<void> | void;
@@ -306,15 +315,12 @@ export declare class RevertManifestStore {
306
315
  helperTimeoutMs?: number;
307
316
  /** @internal deterministic blocked store-helper seam. */
308
317
  testHangStoreHelperBeforeReady?: boolean;
309
- /** @internal deterministic bounded pre-image enumeration/read seams. */
310
- testHangPreImageEnumerationStat?: boolean;
318
+ /** @internal deterministic bounded legacy pre-image read seam. */
311
319
  testHangPreImageReadStage?: 'open' | 'read' | 'close';
312
320
  /** @internal deterministic prepared-inode replacement seam. */
313
321
  afterAnchoredWriteValidation?: (filePath: string) => Promise<void> | void;
314
322
  /** @internal deterministic independent-store RMW seam. */
315
323
  afterJsonAuthorityRead?: (filePath: string) => Promise<void> | void;
316
- /** @internal deterministic content-addressed create race seam. */
317
- afterPreImageAbsentRead?: (blobPath: string) => Promise<void> | void;
318
324
  /** @internal causal cross-process fence test seam. */
319
325
  beforeMergeFenceAcquire?: (taskGroupId: string) => Promise<void> | void;
320
326
  });
@@ -325,6 +331,13 @@ export declare class RevertManifestStore {
325
331
  listSessionOwnedGroups(): string[];
326
332
  /** The per-group directory `<root>/<taskGroupId>/`. */
327
333
  private groupDir;
334
+ /**
335
+ * P63 Slice 2 (F-S2-5, SB-31) — the group's private state directory for a revert on a no-link volume: the §9.3
336
+ * journal, the probed apply capabilities and the pre-images as blobs live here so a crashed revert resumes from them.
337
+ * Created through the same secure, identity-checked mkdir as the group directory and removed with it (GC removes the
338
+ * whole group tree); `revertGroup` retires its contents once nothing is open.
339
+ */
340
+ noLinkRevertStateDir(taskGroupId: string): Promise<string>;
328
341
  /**
329
342
  * Validate `taskGroupId` is a safe SINGLE path segment so a malicious id can't
330
343
  * escape `<root>/` (defense-in-depth; ids are ULIDs server-side, but never
@@ -365,6 +378,19 @@ export declare class RevertManifestStore {
365
378
  * NEVER logs file content.
366
379
  */
367
380
  appendPromote(taskGroupId: string, _trackIndex: number, entries: PromoteManifestInput[]): Promise<void>;
381
+ /** P63 Slice 2 (§9.4) — the group's blob namespace in its content store. */
382
+ groupBlobStore(taskGroupId: string): BlobStore;
383
+ private groupListStore;
384
+ /**
385
+ * §9.4 — the record as written: each per-path list (revert promotes, promotion files, reviewed snapshot) replaced by
386
+ * its sharded-list record, the shards written (and fsynced) first. Validation always runs on the full lists.
387
+ */
388
+ private shardRecordLists;
389
+ /** §9.4 — a record read back: each sharded-list record replaced by its verified list (legacy inline lists as-is). */
390
+ private expandRecordLists;
391
+ /** §9.4 — track bundles as written: each bundle's files as a sharded list. */
392
+ private shardBundleLists;
393
+ private expandBundleLists;
368
394
  private writeRecord;
369
395
  /** Read the full unified record, or `null` if none exists. */
370
396
  readRecord(taskGroupId: string): Promise<RevertRecordV1 | null>;
@@ -390,7 +416,13 @@ export declare class RevertManifestStore {
390
416
  * record is idempotent on `trackIndex`, serialized against sibling tracks,
391
417
  * atomic, and removed with the group lifecycle.
392
418
  */
393
- appendTrackBundle(taskGroupId: string, bundle: SnapshotTrackBundle): Promise<void>;
419
+ appendTrackBundle(taskGroupId: string, bundle: SnapshotTrackBundle,
420
+ /**
421
+ * §9.4 — the track instance's blob store holding the bundle's reference post-images. Each is stream-copied into the
422
+ * group's content store BEFORE the bundle record names it, so the merge gate never depends on the track's own copy
423
+ * (retired with the track). Required when the bundle has reference files.
424
+ */
425
+ sourceStore?: BlobStore): Promise<void>;
394
426
  readTrackBundles(taskGroupId: string): Promise<SnapshotBundlesView>;
395
427
  /**
396
428
  * Serialize the complete merge-gate body (including hosted verdict submit)
@@ -421,6 +453,21 @@ export declare class RevertManifestStore {
421
453
  * manifest hash and the content-addressed filename must agree before any
422
454
  * reverse CAS is allowed to consume these bytes.
423
455
  */
456
+ /**
457
+ * §9.4 — a revert entry's pre-image as a stream source for the CAS: a `content/` reference resolves to the group's
458
+ * content store and its verified blob reference (never read whole here; every reader verifies size and hash); a
459
+ * pre-Slice 2 `preimages/` reference is read through the bounded anchored path as before.
460
+ */
461
+ preImageSource(taskGroupId: string, entry: Pick<RevertManifestEntry, 'preImageRef' | 'preImageHash' | 'preImageSize'>): Promise<string | {
462
+ blob: BlobRef;
463
+ store: BlobStore;
464
+ }>;
465
+ /**
466
+ * §9.4 — a `content/` pre-image read whole (a legacy caller of `readPreImage`; the revert runner streams through
467
+ * `preImageSource`): opened no-follow, size and SHA-256 verified before the text is returned, so a replaced, swapped,
468
+ * grown or symlinked blob is refused.
469
+ */
470
+ private readStreamedPreImage;
424
471
  readPreImage(taskGroupId: string, preImageRef: string, expectedHash: string): Promise<string>;
425
472
  private setLifecycle;
426
473
  /** Group accepted/kept → terminal lifecycle `accepted` (GC-eligible). */
@@ -481,6 +528,11 @@ export declare class RevertManifestStore {
481
528
  * cannot skip a reverse CAS. Idempotent on the entry key; atomic + append-only.
482
529
  */
483
530
  recordReverted(taskGroupId: string, relPath: string, promoteOrder: number): Promise<void>;
531
+ /** {@link recordReverted} for several entries in one journal write (F-CAS-BATCH: a large revert's checkpoint). */
532
+ recordRevertedMany(taskGroupId: string, entries: ReadonlyArray<{
533
+ path: string;
534
+ promoteOrder: number;
535
+ }>): Promise<void>;
484
536
  /** Mark the journal complete (the revert finished this pass). */
485
537
  endRevert(taskGroupId: string): Promise<void>;
486
538
  /** List every group id with a durable record under `<root>/`. */
@@ -494,6 +546,11 @@ export declare class RevertManifestStore {
494
546
  * Returns the ids it removed.
495
547
  */
496
548
  gc(now?: number, ttlMs?: number, protectedTaskGroupIds?: ReadonlySet<string>): Promise<string[]>;
549
+ /**
550
+ * g3paths3: a removal that failed part-way leaves its exact tombstone "for retry", and this is the retry. The orphan
551
+ * limit is far longer than any helper's deadline, so a tombstone this old has no live removal behind it.
552
+ */
553
+ private gcTreeTombstones;
497
554
  private gcOneGroup;
498
555
  /**
499
556
  * Same-session terminal reclamation for one group. This is intentionally
@@ -175,7 +175,12 @@ export declare function buildImplementorArgv(agent: AgentKind, mode: Implementor
175
175
  * summary is an audit-only nicety; the captured diff, not the summary, is the
176
176
  * round's contract).
177
177
  */
178
- export declare function readImplementorLastMessage(capturePath: string, workdir?: string): string;
178
+ export declare function readImplementorLastMessage(capturePath: string, workdir?: string,
179
+ /**
180
+ * P63 Slice 2 (§8.2) — the host path of the spawn's capture-dir file (`<capture dir>/last-message.txt`) when the
181
+ * spawn ran with a read-only view; read FIRST. After it is read, its (task-owned, now empty) capture dir is removed.
182
+ */
183
+ captureHostPath?: string): string;
179
184
  /**
180
185
  * Positive runtime evidence that this process is ACTUALLY inside a container —
181
186
  * the confinement boundary that `CODEVIBE_TRUSTED_CONTAINER` asserts. Docker
@@ -206,6 +211,23 @@ export declare function isInsideContainer(): boolean;
206
211
  * container check is injectable for tests.
207
212
  */
208
213
  export declare function isTrustedContainerBoundary(env?: NodeJS.ProcessEnv, inContainer?: () => boolean): boolean;
214
+ /**
215
+ * P63 Slice 2 (§8.2) — the outer boundary around one spawn, built from the engager's OWN result (never a
216
+ * caller-supplied handle): `seatbelt` / `docker` = the CP-7 tier that engaged; `trusted-container` = the marked
217
+ * dogfood container with no substrate; `bubblewrap` = the Linux tier (typed; the ladder does not select it yet);
218
+ * `none` = unconfined. `readOnlyView` = that substrate enforces a read-only view of the working directory.
219
+ */
220
+ export type OuterBoundaryKind = 'none' | 'trusted-container' | 'seatbelt' | 'docker' | 'bubblewrap';
221
+ export interface OuterBoundary {
222
+ kind: OuterBoundaryKind;
223
+ readOnlyView: boolean;
224
+ }
225
+ /**
226
+ * P63 Slice 2 (§8.2) — is this implementor argv a PLAN-mode (read-only) run? Read from the argv itself so a caller
227
+ * cannot get a writable view for a plan run by omitting the mode: Claude with no write tool in its tool lists, Codex
228
+ * `--sandbox read-only`, agy `--mode plan`.
229
+ */
230
+ export declare function isPlanModeImplementorArgv(agent: AgentKind, argv: readonly string[]): boolean;
209
231
  /**
210
232
  * CP-7 substrate-aware Codex sandbox selection. When an OUTER confinement
211
233
  * boundary already isolates the implementor — the CP-7 substrate engaged (Docker
@@ -224,14 +246,21 @@ export declare function isTrustedContainerBoundary(env?: NodeJS.ProcessEnv, inCo
224
246
  *
225
247
  * NARROW BY DESIGN:
226
248
  * - Only CODEX is affected (Claude + Antigravity have no bwrap inner sandbox).
227
- * - Only `workspace-write` (the EXECUTE variant) is rewritten. `read-only`
228
- * (PLAN mode) is left untouched — it is a DELIBERATE write restriction, and
229
- * `danger-full-access` would let a plan-mode agent write, violating the plan
230
- * contract.
249
+ * - `workspace-write` (the EXECUTE variant) is rewritten for any boundary.
250
+ * `read-only` (PLAN mode) is a DELIBERATE write restriction and is rewritten
251
+ * ONLY when the outer boundary itself enforces a read-only view (P63 Slice 2
252
+ * §8.2: Seatbelt trailing write deny / Docker `:ro`, kind `seatbelt` or
253
+ * `docker` with `readOnlyView`) — there Codex's own read-only sandbox cannot
254
+ * start, and the boundary keeps the plan contract instead.
231
255
  * - No-op when `outerBoundaryConfines` is false.
232
256
  * Pure; returns a NEW array when it rewrites, the same array otherwise.
233
257
  */
234
- export declare function applyOuterBoundarySandbox(argv: readonly string[], agent: AgentKind, outerBoundaryConfines: boolean): string[];
258
+ export declare function applyOuterBoundarySandbox(argv: readonly string[], agent: AgentKind,
259
+ /**
260
+ * P63 Slice 2 (§8.2) — the boundary KIND the engager itself reported. A boolean is the pre-Slice-2 form
261
+ * (`true` = some confining boundary, no read-only view) and keeps its shipped behaviour.
262
+ */
263
+ boundary: boolean | OuterBoundary): string[];
235
264
  /**
236
265
  * A1b (Codex summary never-read fix, WRITE side) — substrate-aware relocation
237
266
  * of the Codex `--output-last-message` value. Mirrors the reviewer provider's
@@ -262,7 +291,14 @@ export declare function applyOuterBoundarySandbox(argv: readonly string[], agent
262
291
  * rewritten argv still passes `enforceCommand` (drift-guard-tested).
263
292
  * Pure; returns a NEW array when it rewrites, a copy otherwise.
264
293
  */
265
- export declare function relocateCodexLastMessageForSubstrate(argv: readonly string[], agent: AgentKind, substrateEngaged: boolean): string[];
294
+ export declare function relocateCodexLastMessageForSubstrate(argv: readonly string[], agent: AgentKind, substrateEngaged: boolean,
295
+ /**
296
+ * P63 Slice 2 (§8.2) — with a read-only view the workdir cannot take the file: the ABSOLUTE path codex should write
297
+ * instead, inside the spawn's capture dir as the sandbox sees it (Docker: `/codevibe-capture/last-message.txt`).
298
+ */
299
+ captureTarget?: string): string[];
300
+ /** P63 Slice 2 (§8.2) — the plan file's name inside a spawn's capture dir. */
301
+ export declare const CAPTURE_LAST_MESSAGE_FILE = "last-message.txt";
266
302
  /**
267
303
  * Authority `commandAllowlist` entries covering EVERY (agent, mode) argv
268
304
  * `buildImplementorArgv` can produce. The single-impl + team LEs seed this