@quantiya/codevibe-core 2.0.92 → 2.0.94

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 +10 -1
  10. package/dist/companion-mode/codex-native-state.d.ts +110 -110
  11. package/dist/index.d.ts +1 -1
  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
@@ -15,6 +15,7 @@ import type { ConflictResolverContext } from './merge-conflict-resolve';
15
15
  import { type ContentCipher } from './conflict-resolution-durable-store';
16
16
  import type { PromoteManifestInput, SnapshotPromotionJournalV1, TrackTipsView } from './durable-store';
17
17
  import { type ReviewedShadowDiffFile, type ShadowEnv } from './workspace-shadow';
18
+ import type { BlobStore } from './blob-store';
18
19
  /**
19
20
  * CP-7 W2 Stage-2 HIGH (Finding 2) — STRICT delivery sink for the
20
21
  * `tier:'none'` (moat-OFF) reduced-trust badge.
@@ -74,7 +75,7 @@ export interface MergeGateContentEncryptor {
74
75
  */
75
76
  export type SubstrateEngageResult = {
76
77
  mode: 'substrate';
77
- tier: 'docker' | 'sandbox_exec';
78
+ tier: 'docker' | 'sandbox_exec' | 'bubblewrap';
78
79
  reducedTrust: boolean;
79
80
  reducedTrustReason?: string;
80
81
  substrateHandle: SubstrateHandle;
@@ -113,9 +114,13 @@ export type SubstrateEngager = (input: {
113
114
  */
114
115
  requireConfined?: boolean;
115
116
  writableWorkdir?: boolean;
117
+ /** P63 Slice 2 (§8.2) — the spawn's task-owned capture dir, granted read-write beside a read-only view. */
118
+ captureDir?: string;
116
119
  webBrowsingActive?: boolean;
117
120
  /** F-P46a-4 — the spawn's process-tree id, folded into the sandbox environment. */
118
121
  processTreeId?: string;
122
+ /** P63 §6.1 / §6.2 — the user's real project folder (`SpawnArgs.projectRoot`). */
123
+ projectRoot?: string;
119
124
  }) => Promise<SubstrateEngageResult>;
120
125
  /** Backend lease is 15 minutes; three renewal opportunities fit inside it. */
121
126
  export declare const TEAM_TRACK_HEARTBEAT_INTERVAL_MS: number;
@@ -253,6 +258,27 @@ export interface LocalExecutorImplDeps {
253
258
  * + the GEMINI reduced-trust case.
254
259
  */
255
260
  substrateEngager?: SubstrateEngager;
261
+ /**
262
+ * P63 Slice 2 (§8.2) — the per-task folder (`taskFolderFor`, Stream A's layout): a read-only-view Codex spawn's
263
+ * capture dir is created at `<task folder>/capture/<spawn>/`. Absent (tests, embedders) → a fresh `os.tmpdir()`
264
+ * folder, so no test writes the real cache.
265
+ */
266
+ taskFolder?: (taskId: string) => string;
267
+ /**
268
+ * P63 §6.4 — the per-mode tier self-test: before the first real implementor of an agent version runs in a tier in a
269
+ * mode, the same engage runs it against a throwaway one-file view; a failure refuses the spawn (§6.4). Production
270
+ * wires it (`cli.ts`); absent → no self-test (tests and embedders with fake substrates).
271
+ */
272
+ tierSelfTest?: {
273
+ /** Where results are cached (`~/.codevibe/substrate-checks`). */
274
+ cacheDir: string;
275
+ /** Where the throwaway view is made (the real copy location). */
276
+ viewParent: () => Promise<string>;
277
+ /** `<agent> --version` (default: runs the binary; tests inject it). */
278
+ readAgentVersion?: (binary: string) => Promise<string>;
279
+ /** The Linux bubblewrap tier's key parts (filter SHA, bwrap version, setpriv/bash paths, pasta SHA). */
280
+ linuxKeyParts?: (open: boolean) => Promise<Pick<import('./tier-self-test').TierSelfTestKeyInput, 'filterSha256' | 'bwrapVersion' | 'setprivPath' | 'bashPath' | 'pastaSha256'>>;
281
+ };
256
282
  /**
257
283
  * CP-7 W2 Stage-2 HIGH (Finding 2) — STRICT delivery sink for the
258
284
  * `tier:'none'` (moat-OFF) reduced-trust badge. When supplied (production
@@ -319,6 +345,8 @@ export interface LocalExecutorImplDeps {
319
345
  readTrackTips?: (taskGroupId: string) => Promise<TrackTipsView>;
320
346
  /** Git-independent reviewed track bundles (the default team merge inputs). */
321
347
  readTrackBundles?: (taskGroupId: string) => Promise<SnapshotBundlesView>;
348
+ /** §9.4 — the group's content store (bundle post-images and merge results as blobs; see `runSnapshotMergeGate`). */
349
+ snapshotContentStore?: (taskGroupId: string) => BlobStore;
322
350
  /** Durable all-generation/cross-process fence around gate + hosted submission. */
323
351
  withSnapshotMergeGateFence?: <T>(taskGroupId: string, operation: () => Promise<T>, signal?: AbortSignal) => Promise<T>;
324
352
  /** Persist merged pre-images before the one group promotion mutates user files. */
@@ -364,6 +392,16 @@ export interface LocalExecutorImplDeps {
364
392
  conflicts: readonly string[];
365
393
  writeErrors: readonly string[];
366
394
  }) => void;
395
+ /** The tracks' changes could not be merged, before anything was applied. */
396
+ onTeamMergeConflict?: (info: {
397
+ taskGroupId: string;
398
+ paths: readonly string[];
399
+ workLimit: boolean;
400
+ }) => void;
401
+ /** The merge could not be finished and its verdict is withheld: some of its files may already be in the folder. */
402
+ onTeamMergeUnresolved?: (info: {
403
+ taskGroupId: string;
404
+ }) => void;
367
405
  /**
368
406
  * Legacy-named TEST SEAM around the merge-gate body. Production defaults to
369
407
  * the reviewed-bundle filesystem snapshot implementation.
@@ -525,6 +563,11 @@ export declare class LocalExecutorImpl implements LocalExecutor {
525
563
  private readonly logger;
526
564
  private readonly teamModeEnabled;
527
565
  private readonly substrateEngager?;
566
+ private readonly taskFolder?;
567
+ private readonly tierSelfTestConfig?;
568
+ /** One self-test per key at a time; concurrent spawns of the same key share it. */
569
+ private readonly tierSelfTestsInFlight;
570
+ private readonly agentVersions;
528
571
  private readonly strictBadgeSink?;
529
572
  constructor(deps: LocalExecutorImplDeps);
530
573
  /**
@@ -596,6 +639,19 @@ export declare class LocalExecutorImpl implements LocalExecutor {
596
639
  * takes the legacy path.
597
640
  */
598
641
  private engageSubstrateForSpawn;
642
+ /**
643
+ * P63 §6.4 — the reason this agent FAILED its cached execute-mode self-test in this tier, or null (no result yet, or
644
+ * a pass). The pre-start preflight consults it, so a task whose agent failed its self-test is refused before it
645
+ * starts and the user is offered the unconfined run.
646
+ */
647
+ tierSelfTestFailure(agentKind: AgentKind, tier: 'docker' | 'sandbox_exec' | 'bubblewrap'): Promise<string | null>;
648
+ /** P63 §6.4 — the cached tier self-test for (agent, version, tier, mode, argv template, tier binaries). */
649
+ private ensureTierSelfTest;
650
+ /**
651
+ * P63 Slice 2 (§8.2) — create a spawn's capture dir exclusively: `<task folder>/capture/<spawn>/` (0700), or a fresh
652
+ * `os.tmpdir()` folder when no task folder is wired. Returned canonical (Seatbelt grants match canonical paths).
653
+ */
654
+ private createCaptureDir;
599
655
  /**
600
656
  * CP-7 PART F — surface the PROMINENT reduced-trust badge (§6 Q2). The moat
601
657
  * being OFF (or coarse) MUST be visible + auditable, never silent: a
@@ -3,6 +3,8 @@ export type NoLinkOperation = 'replace' | 'create' | 'delete';
3
3
  export type NoLinkState = 'prepared' | 'moved' | 'placing' | 'restoring' | 'restoring-link' | 'pending-restore' | 'create-conflict' | 'done';
4
4
  export interface NoLinkJournalEntry {
5
5
  version: 1;
6
+ /** §17: the versioned no-link journal form. Absent on an entry a released desktop left open; any other value is refused. */
7
+ casMode?: 'nolink';
6
8
  token: string;
7
9
  op: NoLinkOperation;
8
10
  /** The target's path relative to the workspace root, for reporting; the helper uses only its basename. */
@@ -43,6 +45,16 @@ interface NoLinkHelperSpec {
43
45
  name: string;
44
46
  token: string;
45
47
  content?: string;
48
+ /**
49
+ * P63 Slice 2 (§9.4): the intended post-image as a blob in the instance's content store (`store-<uuid>/` in the
50
+ * journal's directory, F-S2-4) — replaces `content`. The parent opens it on apply only (recovery restores or completes
51
+ * renames and never needs it) and passes it as the helper's fd 3; the helper hashes it while writing and writes nothing
52
+ * on a mismatch.
53
+ */
54
+ blob?: {
55
+ name: string;
56
+ size: number;
57
+ };
46
58
  mode?: number;
47
59
  baseHash: string | null;
48
60
  intendedHash: string | null;
@@ -108,8 +120,8 @@ export declare class NoLinkJournalStore {
108
120
  write(entry: NoLinkJournalEntry): Promise<void>;
109
121
  read(token: string): Promise<NoLinkJournalEntry | null>;
110
122
  /**
111
- * Every entry not yet `done`. Slice 1 recovers them at every `promote()` — each retry and the startup reconcile
112
- * (R1-F1); §9.3's 10-second retry while the desktop runs is not implemented (Stage 1 R2-N2, a recorded deviation).
123
+ * Every entry not yet `done`. They are recovered at every `promote()` — each retry and the startup reconcile (R1-F1)
124
+ * — and, for a step left pending, every 10 seconds while the desktop runs (§9.3, `retryPendingNoLinkRestores`).
113
125
  */
114
126
  open(): Promise<NoLinkJournalEntry[]>;
115
127
  }
@@ -117,6 +129,8 @@ export declare class NoLinkJournalStore {
117
129
  export declare function parseNoLinkJournalEntry(text: string, token: string): NoLinkJournalEntry;
118
130
  /** The token of a `<prefix>-<token>.json` journal file name, or null when the name is not one. */
119
131
  export declare function journalTokenOf(name: string, prefix: string): string | null;
132
+ /** §9.3: where a pending step's original is, relative to the workspace root — the name the user is told. */
133
+ export declare function noLinkMovedPath(rel: string, token: string): string;
120
134
  /** The CodeVibe token for one operation: the same 48-hex shape as the link-based helper's `.codevibe-cas-<token>`. */
121
135
  export declare function noLinkToken(rootCanonicalPath: string, rel: string, op: NoLinkOperation, baseHash: string | null, intendedHash: string | null): string;
122
136
  export interface NoLinkRunOptions {
@@ -138,6 +152,12 @@ export interface NoLinkRunOptions {
138
152
  export declare function applyNoLink(opts: NoLinkRunOptions & {
139
153
  op: NoLinkOperation;
140
154
  content?: string;
155
+ /** P63 Slice 2 (§9.4): the post-image as a blob in the journal's directory, instead of `content`. */
156
+ blob?: {
157
+ name: string;
158
+ size: number;
159
+ sha256: string;
160
+ };
141
161
  mode?: number;
142
162
  /** Stage 2 R2-F3: the name's base mode, compared by the helper where modes are tracked. */
143
163
  baseMode?: number | null;
@@ -1,5 +1,6 @@
1
1
  import { RevertManifestStore } from './durable-store';
2
2
  import { type CasHelperCrashStage } from './workspace-shadow';
3
+ import { applyFileNoLink } from './shadow-apply-capabilities';
3
4
  /** The outcome of a group revert (Fork A: clean-where-safe + surface). */
4
5
  export interface RevertResult {
5
6
  /** Paths reverted (created→deleted, modified/deleted→restored) THIS pass. */
@@ -20,6 +21,8 @@ export interface RevertRunnerEnv {
20
21
  testCrashHelperStage?: (path: string) => CasHelperCrashStage | undefined;
21
22
  /** @internal swap/race seam before each manifest/journal filesystem proof. */
22
23
  beforeJournalFileRead?: (path: string) => Promise<void> | void;
24
+ /** @internal F-S2-5: the §9.3 helper's crash/fault controls for one no-link reverse step (interruption tests). */
25
+ noLinkTest?: (path: string) => Parameters<typeof applyFileNoLink>[0]['test'] | undefined;
23
26
  }
24
27
  /**
25
28
  * Revert the whole group's applied tree off its durable manifest + journal.
@@ -1,4 +1,4 @@
1
- import { applyNoLink, type NoLinkResult } from './nolink-apply';
1
+ import { applyNoLink, type NoLinkOperation, type NoLinkResult } from './nolink-apply';
2
2
  import type { WorkspaceRootAuthority } from './workspace-authority';
3
3
  import { type VolumeCapabilities } from './shadow-volume-probe';
4
4
  export interface ApplyArtifactNames {
@@ -11,6 +11,24 @@ export interface ApplyArtifactNames {
11
11
  export declare function applyArtifactNames(stateDir: string, shadowDir: string): ApplyArtifactNames;
12
12
  /** §5.2/§7.4: this instance's flat start-record file prefix (`start-record-<uuid>`). */
13
13
  export declare function startRecordPrefix(shadowDir: string): string;
14
+ /**
15
+ * P63 Slice 2 (§9.4, F-S2-4) — this instance's content store: a directory of its own inside the task state directory
16
+ * (`store-<uuid>/`), holding its blobs and change-manifest shards. Not flat beside the markers: capture writes hundreds
17
+ * of blobs a second, and every task start and recovery sweep on this machine enumerates each task state directory and
18
+ * fails closed if it changes during the read — so a capturing task must leave its state directory itself untouched.
19
+ */
20
+ export declare function instanceStoreDir(stateDir: string, shadowDir: string): string;
21
+ /** P63 Slice 2 (§9.4) — this instance's blob namespace in its content store (`blob-store.ts`). */
22
+ export declare function blobStoreFor(stateDir: string, shadowDir: string): {
23
+ dir: string;
24
+ prefix: string;
25
+ };
26
+ /** P63 Slice 2 (§9.4) — this instance's change-manifest shard namespace in its content store (`change-manifest-store.ts`). */
27
+ export declare function manifestStoreFor(stateDir: string, shadowDir: string): {
28
+ dir: string;
29
+ prefix: string;
30
+ };
31
+ export declare function _setStoreRetireSeamForTests(seam: (() => Promise<void>) | undefined): void;
14
32
  /** Test override: run the no-link path on a volume that has links, as SP-C's "APFS forced copy" did. */
15
33
  export interface ForcedApplyCapabilities {
16
34
  mode: 'nolink';
@@ -22,6 +40,7 @@ export interface ForcedApplyCapabilities {
22
40
  export type ResolvedApplyCapabilities = VolumeCapabilities & {
23
41
  linkVolume: boolean;
24
42
  };
43
+ export declare function writeDurable(file: string, body: string): Promise<void>;
25
44
  /**
26
45
  * The volume's capabilities for this snapshot's apply: read back if this snapshot already probed (a resumed or
27
46
  * recovered promotion never probes twice), otherwise probed now — after approval, never at task start (§9.1).
@@ -31,7 +50,9 @@ export declare class NoLinkPendingError extends Error {
31
50
  readonly rel: string;
32
51
  readonly result: NoLinkResult;
33
52
  readonly code = "ENOLINKPENDING";
34
- constructor(rel: string, result: NoLinkResult);
53
+ /** Where the original is now, relative to the workspace root; absent for a create, which moved nothing. */
54
+ readonly temporarilyAt?: string;
55
+ constructor(rel: string, result: NoLinkResult, op: NoLinkOperation, token: string);
35
56
  }
36
57
  /**
37
58
  * Apply one reviewed file on a no-link volume. Returns true when the name now holds the post-image (applied, or
@@ -47,6 +68,12 @@ export declare function applyFileNoLink(args: {
47
68
  rel: string;
48
69
  /** null = delete. */
49
70
  content: string | null;
71
+ /** P63 Slice 2 (§9.4): the post-image as a blob in `stateDir`; the journal names it instead of carrying `content`. */
72
+ blob?: {
73
+ name: string;
74
+ size: number;
75
+ sha256: string;
76
+ };
50
77
  /**
51
78
  * Stage 1 R1-F4: the mode the placed file must carry where the volume tracks modes (§9.1 `modesTracked`); the
52
79
  * helper sets it on the file it writes. Ignored where modes are untracked (exFAT, FAT, drvfs).
@@ -83,6 +110,8 @@ export declare function recoverOpenNoLinkEntries(args: {
83
110
  onKept?: (rel: string, kept: string[]) => void;
84
111
  onClosed?: (rel: string, result: NoLinkResult) => void;
85
112
  onError?: (rel: string, err: Error) => void;
113
+ /** §9.3: a step still pending whose original sits at `movedPath` (never called for a create, which moved nothing). */
114
+ onMoved?: (rel: string, movedPath: string) => void;
86
115
  }): Promise<string[]>;
87
116
  /** Open (not `done`) §9.3 journal entries of this snapshot — non-empty blocks discard. */
88
117
  export declare function openNoLinkEntries(stateDir: string, shadowDir: string): Promise<import("./nolink-apply").NoLinkJournalEntry[]>;
@@ -25,7 +25,24 @@ export type CopySpaceVerdict = {
25
25
  short: FilesystemSpace;
26
26
  requiredBytes: number;
27
27
  reservedBytes: number;
28
+ /** The bytes the copy needs, and what they are for — the structured fields `reason` is built from. */
29
+ bytesToCopy: number;
30
+ purpose: string;
28
31
  };
32
+ /**
33
+ * §5.5 refusal text, built only from the verdict's structured fields (sizes, the short filesystem's path, the
34
+ * purpose), so a caller that relays it — the implementor-round failure label shown on the desktop and the phone —
35
+ * never forwards free-form error text.
36
+ */
37
+ export declare function copySpaceRefusalText(v: Extract<CopySpaceVerdict, {
38
+ ok: false;
39
+ }>,
40
+ /**
41
+ * How the short filesystem is named (default: its path). A message that crosses the start-error sanitizer names it in
42
+ * words instead: the sanitizer redacts an absolute path and its scan runs on past the path's end (lfree4: "… and
43
+ * [path] MiB free."), which would drop the free space §5.5 requires the message to name.
44
+ */
45
+ location?: string): string;
29
46
  export interface CopySpaceRequest {
30
47
  /** The copy location (§5.1). The space test runs here. */
31
48
  copyLocation: string;
@@ -42,6 +59,8 @@ export interface CopySpaceRequest {
42
59
  */
43
60
  alsoRequireOn?: string[];
44
61
  statfs?: StatfsFn;
62
+ /** What the bytes are for, in the refusal ("copy this folder" by default; capture and review copies say so). */
63
+ purpose?: string;
45
64
  }
46
65
  /** §5.5's pre-copy check. A refusal means the task does not start, with this message. */
47
66
  export declare function checkCopySpace(req: CopySpaceRequest): Promise<CopySpaceVerdict>;
@@ -62,6 +81,12 @@ export type WatchdogVerdict = {
62
81
  * virtual disk can look roomy while the drive that backs it is nearly full.
63
82
  */
64
83
  export declare function watchdogTick(paths: string[], statfs?: StatfsFn): Promise<WatchdogVerdict>;
84
+ /**
85
+ * P63 Slice 2 R1-F10 (§8.1 "counted in the free-space check", §5.5) — the same condition for a copy a running task
86
+ * makes after its start (captured post-images and base versions into its content store, a review bundle), on the
87
+ * filesystem that receives it and, on WSL2, the Windows drive too. Throws {@link InsufficientCopySpaceError}.
88
+ */
89
+ export declare function assertCopySpace(copyLocation: string, bytesToCopy: number, purpose: string, statfs?: StatfsFn): Promise<void>;
65
90
  /** A task that §5.5 refuses to start. The message names the folder size and the free space. */
66
91
  export declare class InsufficientCopySpaceError extends Error {
67
92
  readonly verdict: Extract<CopySpaceVerdict, {
@@ -1,7 +1,10 @@
1
1
  import * as fs from 'node:fs/promises';
2
- import { type AnchoredFsIdentity, type AnchoredPathIdentity, type AnchoredWritePrecondition } from './anchored-fs';
2
+ import type { BlobRef } from './change-manifest';
3
+ import type { ChangeManifestRecord } from './change-manifest-store';
4
+ import { type AnchoredReadSession, type AnchoredFsIdentity, type AnchoredPathIdentity, type AnchoredWritePrecondition } from './anchored-fs';
3
5
  import { type ProcessCleanupOwner } from './process-tree';
4
6
  import { type ProcessIdentity } from './process-identity';
7
+ import { type MetadataBudget } from './bounded-directory';
5
8
  import { type WorkspaceRootAuthority } from './workspace-authority';
6
9
  import { type MaterializationHangStage } from './workspace-materializer';
7
10
  /** Directories never materialized into a new filesystem snapshot. */
@@ -88,13 +91,17 @@ export declare const MAX_TRACKED_WORKSPACE_LOGICAL_BYTES: number;
88
91
  export declare const MAX_TRACKED_FILE_COUNT = 5000000;
89
92
  export declare const MAX_TRACKED_PATH_BYTES: number;
90
93
  /**
91
- * The bound on a CHANGE SET — reviewed bundles, merge plans and diff-sanity budgets (`snapshot-merge.ts`,
92
- * `verification-gates/diff-sanity.ts`). §5.6 lifts these in Slice 2 ("replaced by streaming iteration over the
93
- * sharded change manifest"), so they keep the historical value here, under their own name, while the traversal
94
- * ceiling above rises in Slice 1 (P63 SB-33).
94
+ * The bound on a CHANGE SET — the captured and reviewed diff, the promoted set, recovery authority, declared-test
95
+ * drift, merge plans and diff-sanity budgets. §5.6 lifts the historical 100,000-entry / 16 MiB-path cap in Slice 2:
96
+ * persisted change sets live in the sharded change manifest (§9.4) and markers hold only its shard hashes, so what
97
+ * remains is the in-memory sanity bound. A change set is a subset of the walked folder, so it shares the traversal
98
+ * ceiling above rather than carrying a product limit of its own (P63 Slice 2, E2E-4b: 300,000 changed paths).
99
+ * Persisted durable-store records keep the orchestration default (`createMetadataBudget()`, SB-37).
95
100
  */
96
- export declare const MAX_CHANGE_SET_FILE_COUNT = 100000;
101
+ export declare const MAX_CHANGE_SET_FILE_COUNT = 5000000;
97
102
  export declare const MAX_CHANGE_SET_PATH_BYTES: number;
103
+ /** Budget for validating one in-memory change set (see {@link MAX_CHANGE_SET_FILE_COUNT}). */
104
+ export declare function createChangeSetBudget(): MetadataBudget;
98
105
  /** Reviewed text is intentionally bounded before any whole-file decode/read. */
99
106
  export declare const MAX_REVIEW_FILE_BYTES: number;
100
107
  export declare const MAX_PROMOTION_PREIMAGE_AGGREGATE_BYTES: number;
@@ -117,7 +124,67 @@ export interface ShadowDiffFile {
117
124
  content: string;
118
125
  /** POSIX permission bits for the reviewed post-image (omitted by legacy records). */
119
126
  mode?: number;
127
+ /**
128
+ * P63 Slice 2 (§9.4) — the captured post-image in the task's blob store (`blobStoreFor`), written through the
129
+ * anchored copy. Absent for deletions and for records written before Slice 2.
130
+ */
131
+ blob?: BlobRef;
120
132
  }
133
+ /**
134
+ * P63 Slice 2 (§9.4) — a diff entry's optional blob reference is well formed and agrees with its inline content:
135
+ * never on a deletion, a 64-hex SHA-256 and a byte size equal to the content's UTF-8 length.
136
+ */
137
+ export declare function assertDiffBlobRef(file: {
138
+ change_kind: unknown;
139
+ content: unknown;
140
+ blob?: unknown;
141
+ }, label: string): void;
142
+ /**
143
+ * P63 Slice 2 (§9.4) — text above this many bytes is carried as a blob reference only (`content: ''`): it is never
144
+ * held in memory whole, never 3-way line-merged (whole-file, like a binary — §10.3), and only excerpted for review.
145
+ */
146
+ export declare const LARGE_TEXT_BYTES: number;
147
+ /**
148
+ * P63 Slice 2 (§5.6, §9.4) — no per-file size limit on a captured, deleted or promoted file: §5.6 replaces the 16 MiB cap
149
+ * with the blob store, so a file is bounded by disk (the §5.5 free-space check and watchdog), never by a fixed number.
150
+ * Content above LARGE_TEXT_BYTES is streamed in 1 MiB chunks and never held in memory. The value is the largest byte
151
+ * count the anchored helpers' integer checks accept (S2-C8, Stage 2 R1: a 4 GiB cap here contradicted §5.6).
152
+ */
153
+ export declare const MAX_CAPTURE_FILE_BYTES: number;
154
+ /**
155
+ * §9.4 — a diff entry whose post-image lives only in the blob store: the desktop's records and in-memory structures
156
+ * hold references only. Its text is read from the blob when a consumer needs it — the ≤ LARGE_TEXT_BYTES 3-way merge,
157
+ * a projection excerpt, a revise brief — one file at a time, never kept.
158
+ */
159
+ export declare function isReferenceDiffFile(file: {
160
+ change_kind: string;
161
+ content: string;
162
+ blob?: unknown;
163
+ }): boolean;
164
+ /** A reference entry above LARGE_TEXT_BYTES: never read whole, never line-merged (whole-file, §10.3), only excerpted. */
165
+ export declare function isLargeDiffFile(file: {
166
+ change_kind: string;
167
+ content: string;
168
+ blob?: {
169
+ size: number;
170
+ };
171
+ }): boolean;
172
+ /** The post-image SHA-256 of a diff entry (null for a deletion) — from its blob reference when it has one. */
173
+ export declare function diffPostHash(file: {
174
+ change_kind: string;
175
+ content: string;
176
+ blob?: {
177
+ blob: string;
178
+ };
179
+ }): string | null;
180
+ /** The post-image byte size of a diff entry (0 for a deletion). */
181
+ export declare function diffPostSize(file: {
182
+ change_kind: string;
183
+ content: string;
184
+ blob?: {
185
+ size: number;
186
+ };
187
+ }): number;
121
188
  /** A reviewed change plus the exact real-tree pre-image expected at promotion. */
122
189
  export interface ReviewedShadowDiffFile extends ShadowDiffFile {
123
190
  /** SHA-256 of the pre-task file, or null when the path was absent. */
@@ -146,9 +213,18 @@ export interface CasRecoveryAuthority {
146
213
  postHash: string | null;
147
214
  postMode: number | null;
148
215
  }
216
+ /**
217
+ * P63 Slice 2 (§17 versioned state, R1-F11) — the marker version this desktop writes. Version 3 is the first schema
218
+ * with complete snapshot-incarnation authority (Slice 1); version 4 is Slice 2's — reviewed and promoted sets as
219
+ * shard-backed manifests in the instance's content store (`store-<uuid>/`), post-images as blob references. A Slice 1
220
+ * desktop accepts only 3 and protects a marker it cannot read. With {@link EXPOSURE_RETENTION_MARKER_VERSION}, which
221
+ * keeps it from reclaiming the copy through the exposure record, it leaves a Slice 2 task alone. This desktop reads both;
222
+ * every marker it writes is 4.
223
+ */
224
+ export declare const SHADOW_STATE_MARKER_VERSION = 4;
149
225
  export interface ShadowStateFileV2 {
150
- /** Version 3 is the first schema with complete snapshot-incarnation authority. */
151
- version: 3;
226
+ /** 3: Slice 1 (read only); 4: Slice 2 — see {@link SHADOW_STATE_MARKER_VERSION}. */
227
+ version: 3 | 4;
152
228
  taskId: string;
153
229
  shadowDir: string;
154
230
  workingDir: string;
@@ -199,6 +275,12 @@ export interface ShadowStateFileV2 {
199
275
  }>;
200
276
  /** Names + byte identities needed to resolve any post-COMMIT helper death. */
201
277
  casRecovery?: CasRecoveryAuthority[];
278
+ /**
279
+ * P63 Slice 2 (§9.4, S2-9) — the promoted set as a shard-backed change manifest (shard hashes + root), written
280
+ * before the APPLYING marker. Replaces `promotePostHashes` + `casRecovery` (never both): post-images are the
281
+ * entries' blob references and the CAS authority is recomputed from them, so the marker stays O(shards).
282
+ */
283
+ promoteManifest?: ChangeManifestRecord;
202
284
  /** Full snapshot incarnation claimed by immutable DISPOSING authority. */
203
285
  disposalSnapshotAuthority?: WorkspaceRootAuthority;
204
286
  /** Legacy v3 field: protected fail-closed, never accepted for deletion. */
@@ -209,7 +291,19 @@ export interface ShadowStateFileV2 {
209
291
  * authority from mutable snapshot bytes.
210
292
  */
211
293
  reviewedDiff?: ShadowDiffFile[];
294
+ /**
295
+ * P63 Slice 2 (§9.4) — the reviewed artifact as a shard-backed change manifest (shard hashes + root); its content
296
+ * lives in the instance's blob store. Replaces `reviewedDiff` for new markers (never both), so the marker no longer
297
+ * grows with the size or number of reviewed files.
298
+ */
299
+ reviewedManifest?: ChangeManifestRecord;
212
300
  }
301
+ /**
302
+ * P63 Slice 2 (§16 C4, §17, F-ROLLBACK-EXPOSURE-REAP) — the exposure retention version this desktop writes. A Slice 1
303
+ * desktop accepts 1 and 2, and with the owner dead it removes the copy such a record names; it retains a record of any
304
+ * other version and keeps its name protected. Version 3 has version 2's fields. This desktop reads all three.
305
+ */
306
+ export declare const EXPOSURE_RETENTION_MARKER_VERSION = 3;
213
307
  /**
214
308
  * Non-operational authority published before a private snapshot name becomes
215
309
  * visible. It never authorizes use of `finalShadowDir`; it only prevents that
@@ -217,7 +311,8 @@ export interface ShadowStateFileV2 {
217
311
  * that CodeVibe may reclaim.
218
312
  */
219
313
  export interface ExposureRetentionMarker {
220
- version: 1 | 2;
314
+ /** 1 and 2: Slice 1 (read only); 3: Slice 2 — see {@link EXPOSURE_RETENTION_MARKER_VERSION}. */
315
+ version: 1 | 2 | 3;
221
316
  taskId: string;
222
317
  finalShadowDir: string;
223
318
  originalSnapshotAuthority: WorkspaceRootAuthority;
@@ -321,11 +416,6 @@ export interface ShadowEnv {
321
416
  failReservationCleanup?: number;
322
417
  onSeam?: (name: string, parentDir: string) => Promise<void>;
323
418
  } | undefined;
324
- /** @internal Test injection: `/proc/self/mountinfo` text for the drvfs apply refusal (SB-51), and its platform. */
325
- applyMountinfo?: {
326
- mountinfo: string;
327
- platform: NodeJS.Platform;
328
- };
329
419
  /** @internal Control runs only: clone file by file, as before §5.3 batching (SB-36). */
330
420
  disableDarwinBatchClones?: boolean;
331
421
  /**
@@ -335,6 +425,8 @@ export interface ShadowEnv {
335
425
  materializerLanes?: number;
336
426
  /** @internal Lower the inline-baseline threshold so tests exercise sharded markers (default 8 MiB). */
337
427
  baselineInlineMaxBytes?: number;
428
+ /** @internal R1-F10: a lower check interval for copies into the content store (tests). */
429
+ copySpaceCheckBytes?: number;
338
430
  /** @internal Test injection for §5.5's free-space check (`statfs` of the copy location). */
339
431
  copySpaceStatfs?: (path: string) => Promise<{
340
432
  bavail: number | bigint;
@@ -368,8 +460,6 @@ export interface ShadowEnv {
368
460
  killChildrenInShadow?: (shadowDir: string) => Promise<void>;
369
461
  /** @internal lower deterministic aggregate quota for tests. */
370
462
  maxSessionPhysicalCopyBytes?: number;
371
- /** @internal lower deterministic aggregate pre-image quota for tests. */
372
- maxPromotionPreImageAggregateBytes?: number;
373
463
  /** Test seam after the stable source handle is opened but before physical streaming. */
374
464
  beforePhysicalCopy?: (source: string) => Promise<void>;
375
465
  /** @internal force the production helper's bounded physical-copy branch. */
@@ -402,8 +492,14 @@ export interface ShadowEnv {
402
492
  afterPromotionParentValidation?: (path: string) => Promise<void>;
403
493
  /** @internal lower deterministic CAS-helper deadline for tests. */
404
494
  promotionHelperTimeoutMs?: number;
495
+ /** F-APPLY-RETRY: delays before each retry of a transiently failed placement; absent = 1 s, 5 s, 15 s; [] = no retry. */
496
+ promotionRetryDelaysMs?: readonly number[];
497
+ /** Test-only: retry even after a fault the test injected (hang/crash stage). */
498
+ retryInjectedPromotionFaults?: boolean;
499
+ /** Test-only: inject the hang/crash stage on the first placement attempt only. */
500
+ injectPromotionFaultOnFirstAttemptOnly?: boolean;
405
501
  /** @internal deterministic CAS-helper target-filesystem stall seam. */
406
- testHangPromotionHelperStage?: (path: string) => 'root' | 'parent' | 'target' | undefined;
502
+ testHangPromotionHelperStage?: (path: string) => 'root' | 'parent' | 'target' | 'prepared' | undefined;
407
503
  /** @internal deterministic helper-death seam after COMMIT/mutation. */
408
504
  testCrashPromotionHelperStage?: (path: string) => CasHelperCrashStage | undefined;
409
505
  /** Test seam after the exact target is displaced but before replacement/deletion. */
@@ -488,6 +584,7 @@ export declare function assertSafeRelativePath(rel: string): void;
488
584
  export declare function isInternalShadowPath(rel: string): boolean;
489
585
  export declare function assertNotInternalShadowPath(rel: string): void;
490
586
  export declare function normalizeFileMode(mode: number): number;
587
+ export declare function decodeStrictUtf8(bytes: Buffer, rel: string): string;
491
588
  /**
492
589
  * PHASE-589/469 W3.1 (C1) — read a real-tree file's CONTENT + hash by RELATIVE
493
590
  * path BEFORE the promote mutates it; `{ content: null, hash: null }` if absent
@@ -498,7 +595,9 @@ export declare function readRealFileForManifest(workingDir: string, rel: string,
498
595
  hash: string | null;
499
596
  mode: number | null;
500
597
  }>;
501
- export declare function readRealFileMetadata(workingDir: string, rel: string, expectedRoot: AnchoredFsIdentity | WorkspaceRootAuthority): Promise<{
598
+ export declare function readRealFileMetadata(workingDir: string, rel: string, expectedRoot: AnchoredFsIdentity | WorkspaceRootAuthority,
599
+ /** F-CAS-BATCH: read through this pass's one helper. */
600
+ session?: AnchoredReadSession): Promise<{
502
601
  hash: string | null;
503
602
  mode: number | null;
504
603
  }>;
@@ -7,6 +7,7 @@ import { type WorkspaceRootAuthority } from './workspace-authority';
7
7
  import { type MaterializedFileMetadata } from './workspace-materializer';
8
8
  import { type DurableDirectoryIdentity } from './durable-directory';
9
9
  import { CasRecoveryAuthority, DisplacedShadowMarkerRecord, ExposureRetentionMarker, RetainedStateFileAuthority, ShadowDiffFile, ShadowEnv, ShadowPromoteState, ShadowStateFileV2, SnapshotIncarnationIndex, StructurallyBoundStateSnapshot, UnresolvedBundlePublicationRetention } from './shadow-protocol';
10
+ import { type ChangeManifestRecord } from './change-manifest-store';
10
11
  /** A recovered shadow state (read from the durable per-task marker). */
11
12
  export interface RecoveredShadow {
12
13
  taskId: string;
@@ -43,10 +44,14 @@ export interface RecoveredShadow {
43
44
  mode?: number | null;
44
45
  }>;
45
46
  casRecovery?: CasRecoveryAuthority[];
47
+ /** P63 Slice 2 (S2-9) — shard-backed promotion authority (replaces the two inline lists above). */
48
+ promoteManifest?: ChangeManifestRecord;
46
49
  disposalSnapshotAuthority?: WorkspaceRootAuthority;
47
50
  disposalShadowIdentity?: AnchoredFsIdentity;
48
51
  /** Exact reviewed artifact persisted before authorization could resolve. */
49
52
  reviewedDiff?: ShadowDiffFile[];
53
+ /** P63 Slice 2 (§9.4) — the reviewed artifact as a shard-backed manifest (replaces `reviewedDiff`). */
54
+ reviewedManifest?: ChangeManifestRecord;
50
55
  cleanupBlocked?: boolean;
51
56
  cleanupOwners?: ProcessCleanupOwner[];
52
57
  unresolvedAuthorityRetention?: UnresolvedBundlePublicationRetention;
@@ -175,11 +180,31 @@ export declare function copyStatTuple(stat: {
175
180
  ino: string;
176
181
  mode: string;
177
182
  }): string;
183
+ /** P63 Slice 2 R1-F6 — what a snapshot walk leaves out, for the review INDEX (§5.7). */
184
+ export interface ShadowPrivateEntries {
185
+ folders: Array<{
186
+ path: string;
187
+ group: 'hidden' | 'dependencies' | 'rebuild-output' | 'codevibe';
188
+ }>;
189
+ notApplicable: string[];
190
+ }
191
+ /** The §5.7 group of a skipped folder name. */
192
+ export declare function privateFolderGroup(name: string): ShadowPrivateEntries['folders'][number]['group'];
178
193
  export declare function listShadowRegularFiles(shadowDir: string, shadowRootAuthority: WorkspaceRootAuthority,
179
194
  /** §7.1: when given, receives every listed file's stat tuple (from the same isolated enumeration — no extra I/O). */
180
- statsOut?: Map<string, string>): Promise<string[]>;
181
- type PathBudget = MetadataBudget;
182
- export declare function createPathBudget(): PathBudget;
195
+ statsOut?: Map<string, string>,
196
+ /**
197
+ * P63 Slice 2 R1-F6 (§5.7): when given, receives the private folders the walk skips (with their group) and every
198
+ * entry that is neither a file nor a directory — the review INDEX lists both as never applied.
199
+ */
200
+ privateOut?: ShadowPrivateEntries,
201
+ /**
202
+ * F-EXIT-WAITS-CAPTURE (tick): a caller-owned budget. A listing larger than it stops at the first folder past the
203
+ * limit, and the caller reads `budget.entries > budget.maxEntries` to tell that apart from any other failure.
204
+ */
205
+ callerBudget?: PathBudget): Promise<string[]>;
206
+ export type PathBudget = MetadataBudget;
207
+ export declare function createPathBudget(maxEntries?: number): PathBudget;
183
208
  export declare function assertPathCollectionBounds(paths: readonly string[], label: string): void;
184
209
  export declare function isWithinRoot(root: string, candidate: string): boolean;
185
210
  export declare function assertSafeStateComponent(value: string): void;
@@ -0,0 +1,29 @@
1
+ export declare const TASK_INTENT_VERSION = 1;
2
+ /** How long a stopped task's copy waits for a resume: the session resume window, not the 24 h orphan TTL. */
3
+ export declare const INTERRUPTED_TASK_RETENTION_MS: number;
4
+ export interface TaskIntent {
5
+ version: typeof TASK_INTENT_VERSION;
6
+ sessionId: string;
7
+ brief: string;
8
+ request?: string;
9
+ agent: string;
10
+ decision: unknown;
11
+ /**
12
+ * S2-C3 (Stage 2 R1): the task was started opted in to run UNCONFINED (§6.4). History, not permission: a reopened
13
+ * task keeps the after-run check of the user's folder and the summary's disclosure, and is never itself allowed to
14
+ * run an agent unconfined because of it.
15
+ */
16
+ ranUnconfined?: true;
17
+ }
18
+ export type ReadTaskIntent = {
19
+ kind: 'none';
20
+ }
21
+ /** §17: a version this desktop does not know is listed and never acted on. */
22
+ | {
23
+ kind: 'newer';
24
+ } | {
25
+ kind: 'intent';
26
+ intent: TaskIntent;
27
+ };
28
+ export declare function writeTaskIntent(stateDir: string, shadowDir: string, intent: TaskIntent): Promise<void>;
29
+ export declare function readTaskIntent(stateDir: string, shadowDir: string): Promise<ReadTaskIntent>;
@@ -26,17 +26,3 @@ export declare function probeVolumeCapabilities(projectRoot: string, journalProb
26
26
  export declare function removeJournaledProbeEntry(projectRoot: string, name: string): Promise<void>;
27
27
  /** §9.1: inodes count as identity only with the flag, in the same boot, on the same device. */
28
28
  export declare function inodesTrustedNow(journaled: Pick<VolumeCapabilities, 'inodeStable' | 'bootId' | 'dev'>, projectRoot: string, platform?: NodeJS.Platform): Promise<boolean>;
29
- /**
30
- * P63 SB-51 (Hendry, 2026-10-24: "fail closed on drvfs"): applying into a Windows drive from WSL2 is refused in
31
- * Slice 1 — no WSL2 host was available to qualify §9.3 on a real drvfs volume. Nothing is applied; the copy is kept.
32
- */
33
- export declare const DRVFS_APPLY_REFUSAL = "Applying into a Windows drive from WSL2 (drvfs) isn't supported yet \u2014 nothing was applied; the task's copy is kept.";
34
- /**
35
- * True when `target` lives on a WSL drvfs mount: the longest `/proc/self/mountinfo` mount point containing its
36
- * real path has filesystem type `drvfs` (WSL1, some WSL2 builds) or `9p` with `aname=drvfs` (WSL2). Only Linux
37
- * has drvfs; elsewhere this is false without reading anything.
38
- */
39
- export declare function isDrvfsPath(target: string, opts?: {
40
- mountinfo?: string;
41
- platform?: NodeJS.Platform;
42
- }): Promise<boolean>;