@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.
- package/dist/adapter/registry.d.ts +2 -2
- package/dist/appsync/sync-deferred-inputs.d.ts +2 -2
- package/dist/appsync/sync-desktop-registration.d.ts +2 -2
- package/dist/appsync/sync-mobile-echo.d.ts +16 -16
- package/dist/appsync/sync-prompt-publication.d.ts +8 -8
- package/dist/appsync/sync-shell-source.d.ts +18 -18
- package/dist/appsync/sync-source-cursor.d.ts +30 -30
- package/dist/commerce/upgrade-handoff.d.ts +11 -0
- package/dist/companion-mode/codex-native-question-reply.d.ts +22 -0
- package/dist/companion-mode/codex-native-state.d.ts +132 -114
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2605 -2306
- package/dist/local-executor/anchored-cas.d.ts +78 -6
- package/dist/local-executor/anchored-fs.d.ts +32 -0
- package/dist/local-executor/blob-store.d.ts +95 -0
- package/dist/local-executor/change-manifest-store.d.ts +32 -0
- package/dist/local-executor/change-manifest.d.ts +50 -0
- package/dist/local-executor/durable-store.d.ts +72 -15
- package/dist/local-executor/implementor-argv.d.ts +43 -7
- package/dist/local-executor/local-executor-impl.d.ts +57 -1
- package/dist/local-executor/nolink-apply.d.ts +22 -2
- package/dist/local-executor/revert-runner.d.ts +3 -0
- package/dist/local-executor/shadow-apply-capabilities.d.ts +31 -2
- package/dist/local-executor/shadow-free-space.d.ts +25 -0
- package/dist/local-executor/shadow-protocol.d.ts +117 -18
- package/dist/local-executor/shadow-recovery.d.ts +28 -3
- package/dist/local-executor/shadow-task-intent.d.ts +29 -0
- package/dist/local-executor/shadow-volume-probe.d.ts +0 -14
- package/dist/local-executor/sharded-list.d.ts +32 -0
- package/dist/local-executor/snapshot-merge.d.ts +18 -0
- package/dist/local-executor/spawn-sweep-binary.d.ts +33 -0
- package/dist/local-executor/spawn-sweep.d.ts +3 -0
- package/dist/local-executor/store-dir-anchor.d.ts +48 -0
- package/dist/local-executor/tier-self-test.d.ts +57 -0
- package/dist/local-executor/types.d.ts +26 -0
- package/dist/local-executor/verification-gates/diff-sanity.d.ts +6 -0
- package/dist/local-executor/workspace-materializer.d.ts +17 -1
- package/dist/local-executor/workspace-shadow.d.ts +209 -7
- package/dist/orchestration-shell/audit-summary.d.ts +22 -0
- package/dist/orchestration-shell/bounded-shutdown.d.ts +2 -0
- package/dist/orchestration-shell/cli.js +64053 -57609
- package/dist/orchestration-shell/components/OrchestrationApp.d.ts +6 -1
- package/dist/orchestration-shell/decision-attempt-recovery.d.ts +14 -0
- package/dist/orchestration-shell/engine-projection.d.ts +98 -0
- package/dist/orchestration-shell/exit-guard.d.ts +18 -0
- package/dist/orchestration-shell/gate-decision-submit.d.ts +2 -0
- package/dist/orchestration-shell/index.d.ts +18 -0
- package/dist/orchestration-shell/quorum-loop.d.ts +265 -21
- package/dist/orchestration-shell/review-bundle.d.ts +77 -0
- package/dist/orchestration-shell/review-receipts.d.ts +100 -0
- package/dist/orchestration-shell/reviewer-seat-self-test.d.ts +43 -0
- package/dist/orchestration-shell/team-decompose.d.ts +1 -0
- package/dist/orchestration-shell/types.d.ts +24 -0
- package/dist/reviewer/provider.d.ts +28 -0
- package/dist/reviewer/providers/antigravity.d.ts +4 -2
- package/dist/reviewer/providers/claude.d.ts +7 -2
- package/dist/reviewer/providers/codex.d.ts +1 -1
- package/dist/session-resume/cli-resume.d.ts +1 -0
- package/dist/substrate/bwrap.d.ts +85 -0
- package/dist/substrate/command-runner.d.ts +1 -0
- package/dist/substrate/docker.d.ts +21 -0
- package/dist/substrate/grant-rule.d.ts +71 -0
- package/dist/substrate/pasta-isolation-check.d.ts +60 -0
- package/dist/substrate/pasta.d.ts +8 -0
- package/dist/substrate/relay-channel.d.ts +80 -0
- package/dist/substrate/review-mask.d.ts +20 -0
- package/dist/substrate/sandbox-exec.d.ts +48 -1
- package/dist/substrate/seatbelt-grants.d.ts +46 -0
- package/dist/substrate/spawn-records.d.ts +95 -0
- package/dist/substrate/tier-relay-main.d.ts +1 -0
- package/dist/substrate/tier-relay.d.ts +7 -0
- package/dist/substrate/tier-relay.js +3 -0
- package/dist/substrate/types.d.ts +48 -1
- package/dist/substrate-launch/engage-substrate.d.ts +16 -2
- package/dist/substrate-launch/fallback-ladder.d.ts +109 -12
- package/dist/substrate-launch/index.d.ts +1 -1
- package/dist/substrate-launch/task-folder.d.ts +26 -0
- package/native/linux-arm64/codevibe-spawn-sweep +0 -0
- package/native/linux-x64/codevibe-spawn-sweep +0 -0
- package/native/spawn-sweep.json +8 -0
- 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
|
-
|
|
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
|
-
|
|
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 —
|
|
56
|
-
* the {@link ABSENT_PREIMAGE} marker for a
|
|
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
|
-
/**
|
|
205
|
-
|
|
206
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
* -
|
|
228
|
-
* (PLAN mode) is
|
|
229
|
-
*
|
|
230
|
-
*
|
|
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,
|
|
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
|
|
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
|