@quantiya/codevibe-core 2.0.7 → 2.0.9

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 (128) hide show
  1. package/dist/appsync/__tests__/dispose-watcher-socket.test.d.ts +1 -0
  2. package/dist/appsync/__tests__/refresh-open-prompt-ttl-chunk.test.d.ts +1 -0
  3. package/dist/appsync/appsync-client.d.ts +232 -30
  4. package/dist/appsync/queries.d.ts +2 -0
  5. package/dist/auth/__tests__/keychain-cli-nudge.test.d.ts +1 -0
  6. package/dist/auth/__tests__/keychain-cli.test.d.ts +1 -0
  7. package/dist/auth/keychain-cli.d.ts +14 -0
  8. package/dist/companion-mode/index.d.ts +14 -0
  9. package/dist/credential-broker/oauth-account-lock.d.ts +47 -1
  10. package/dist/crypto/__tests__/open-prompt-id.test.d.ts +1 -0
  11. package/dist/crypto/index.d.ts +1 -0
  12. package/dist/crypto/open-prompt-id.d.ts +36 -0
  13. package/dist/e1/__tests__/prompt-raise.test.d.ts +1 -0
  14. package/dist/e1/index.d.ts +2 -0
  15. package/dist/e1/prompt-raise.d.ts +65 -0
  16. package/dist/index.d.ts +4 -1
  17. package/dist/index.js +4652 -252
  18. package/dist/keychain/__tests__/keychain-manager.test.d.ts +1 -0
  19. package/dist/keychain/__tests__/keychain-migrate.test.d.ts +1 -0
  20. package/dist/keychain/__tests__/migrate-nudge.test.d.ts +1 -0
  21. package/dist/keychain/keychain-backend.d.ts +105 -0
  22. package/dist/keychain/keychain-manager.d.ts +17 -3
  23. package/dist/keychain/keychain-migrate.d.ts +63 -0
  24. package/dist/keychain/migrate-nudge.d.ts +38 -0
  25. package/dist/local-executor/__tests__/anchored-fs.test.d.ts +1 -0
  26. package/dist/local-executor/__tests__/bounded-directory.test.d.ts +1 -0
  27. package/dist/local-executor/__tests__/bounded-output.test.d.ts +1 -0
  28. package/dist/local-executor/__tests__/durable-directory.test.d.ts +1 -0
  29. package/dist/local-executor/__tests__/hosted-class-a-sink.test.d.ts +1 -0
  30. package/dist/local-executor/__tests__/process-identity.test.d.ts +1 -0
  31. package/dist/local-executor/__tests__/process-tree.test.d.ts +1 -0
  32. package/dist/local-executor/__tests__/protocol-backlog-wiring.test.d.ts +1 -0
  33. package/dist/local-executor/__tests__/protocol-backlog.test.d.ts +1 -0
  34. package/dist/local-executor/__tests__/publication-authority-capability.test.d.ts +1 -0
  35. package/dist/local-executor/__tests__/snapshot-merge.test.d.ts +1 -0
  36. package/dist/local-executor/__tests__/workspace-authority-fixture.d.ts +3 -0
  37. package/dist/local-executor/__tests__/workspace-authority.test.d.ts +1 -0
  38. package/dist/local-executor/__tests__/workspace-cleanup-audit.test.d.ts +1 -0
  39. package/dist/local-executor/__tests__/workspace-materializer.test.d.ts +1 -0
  40. package/dist/local-executor/agentic-disposable-repo.d.ts +23 -6
  41. package/dist/local-executor/agentic-resolve.d.ts +16 -0
  42. package/dist/local-executor/anchored-fs.d.ts +250 -0
  43. package/dist/local-executor/bounded-directory.d.ts +78 -0
  44. package/dist/local-executor/bounded-helper.d.ts +22 -0
  45. package/dist/local-executor/bounded-output.d.ts +11 -0
  46. package/dist/local-executor/class-a-emit.d.ts +13 -1
  47. package/dist/local-executor/durable-directory.d.ts +25 -0
  48. package/dist/local-executor/durable-store.d.ts +217 -25
  49. package/dist/local-executor/hosted-class-a-sink.d.ts +27 -0
  50. package/dist/local-executor/index.d.ts +3 -2
  51. package/dist/local-executor/local-executor-impl.d.ts +159 -69
  52. package/dist/local-executor/process-identity.d.ts +46 -0
  53. package/dist/local-executor/process-tree.d.ts +123 -0
  54. package/dist/local-executor/protocol-backlog.d.ts +62 -0
  55. package/dist/local-executor/revert-reconcile.d.ts +4 -0
  56. package/dist/local-executor/revert-runner.d.ts +14 -1
  57. package/dist/local-executor/snapshot-merge.d.ts +124 -0
  58. package/dist/local-executor/task-group-types.d.ts +7 -6
  59. package/dist/local-executor/team-execution-flag.d.ts +2 -2
  60. package/dist/local-executor/track-registry.d.ts +2 -0
  61. package/dist/local-executor/types.d.ts +33 -2
  62. package/dist/local-executor/verification-gates/build.d.ts +5 -0
  63. package/dist/local-executor/verification-gates/deploy-preflight.d.ts +2 -0
  64. package/dist/local-executor/verification-gates/diff-sanity.d.ts +12 -0
  65. package/dist/local-executor/verification-gates/hostile-grep.d.ts +2 -0
  66. package/dist/local-executor/verification-gates/lint.d.ts +2 -0
  67. package/dist/local-executor/verification-gates/shell-runner.d.ts +5 -0
  68. package/dist/local-executor/verification-gates/source-traceability.d.ts +2 -0
  69. package/dist/local-executor/verification-gates/tests.d.ts +3 -0
  70. package/dist/local-executor/verification-gates/typecheck.d.ts +3 -0
  71. package/dist/local-executor/verification-runner.d.ts +6 -0
  72. package/dist/local-executor/workspace-authority.d.ts +45 -0
  73. package/dist/local-executor/workspace-cleanup-audit.d.ts +3 -0
  74. package/dist/local-executor/workspace-materializer.d.ts +48 -0
  75. package/dist/local-executor/workspace-shadow.d.ts +380 -85
  76. package/dist/orchestration-shell/__tests__/advisory-attachment-journal.test.d.ts +1 -0
  77. package/dist/orchestration-shell/__tests__/direct-file-delete.test.d.ts +1 -0
  78. package/dist/orchestration-shell/__tests__/flag-command.test.d.ts +1 -0
  79. package/dist/orchestration-shell/__tests__/halt-message-wire-content.test.d.ts +1 -0
  80. package/dist/orchestration-shell/__tests__/planner-offer-e1.test.d.ts +1 -0
  81. package/dist/orchestration-shell/__tests__/workspace-shutdown.test.d.ts +1 -0
  82. package/dist/orchestration-shell/advisory-attachment-journal.d.ts +165 -0
  83. package/dist/orchestration-shell/attachments.d.ts +47 -58
  84. package/dist/orchestration-shell/audit-browser.d.ts +54 -1
  85. package/dist/orchestration-shell/bounded-shutdown.d.ts +6 -0
  86. package/dist/orchestration-shell/cli.d.ts +10 -0
  87. package/dist/orchestration-shell/cli.js +38355 -27300
  88. package/dist/orchestration-shell/context-items.d.ts +3 -0
  89. package/dist/orchestration-shell/declared-test-runner.d.ts +21 -2
  90. package/dist/orchestration-shell/direct-file-delete.d.ts +22 -0
  91. package/dist/orchestration-shell/emit-shell-event.d.ts +7 -0
  92. package/dist/orchestration-shell/explicit-tty-exit.d.ts +1 -2
  93. package/dist/orchestration-shell/flag-command.d.ts +81 -0
  94. package/dist/orchestration-shell/gate-decision-submit.d.ts +11 -0
  95. package/dist/orchestration-shell/gate-prompts.d.ts +116 -0
  96. package/dist/orchestration-shell/index.d.ts +34 -6
  97. package/dist/orchestration-shell/planner-offer-e1.d.ts +97 -0
  98. package/dist/orchestration-shell/quorum-loop.d.ts +312 -61
  99. package/dist/orchestration-shell/reducer.d.ts +9 -0
  100. package/dist/orchestration-shell/task-progress.d.ts +18 -4
  101. package/dist/orchestration-shell/team-decompose.d.ts +10 -10
  102. package/dist/orchestration-shell/types.d.ts +90 -2
  103. package/dist/orchestration-shell/workspace-shutdown.d.ts +47 -0
  104. package/dist/reviewer/output-parser.d.ts +19 -0
  105. package/dist/reviewer/providers/codex.d.ts +5 -3
  106. package/dist/reviewer/token-usage.d.ts +41 -12
  107. package/dist/reviewer/types.d.ts +6 -0
  108. package/dist/reviewer/verdict-classifier.d.ts +1 -0
  109. package/dist/session/session-rekey.d.ts +4 -3
  110. package/dist/substrate/__tests__/command-runner.test.d.ts +1 -0
  111. package/dist/substrate/command-runner.d.ts +2 -0
  112. package/dist/substrate/docker.d.ts +7 -32
  113. package/dist/substrate/index.d.ts +0 -1
  114. package/dist/substrate/sandbox-exec.d.ts +0 -13
  115. package/dist/substrate/types.d.ts +15 -2
  116. package/dist/substrate-launch/__tests__/reviewer-credential-preflight.test.d.ts +1 -0
  117. package/dist/substrate-launch/engage-substrate.d.ts +3 -0
  118. package/dist/substrate-launch/index.d.ts +2 -0
  119. package/dist/substrate-launch/reviewer-credential-preflight.d.ts +50 -0
  120. package/dist/test-publication-authority-setup.d.ts +1 -0
  121. package/dist/tool-activity/replay-cursor.d.ts +42 -8
  122. package/dist/track-presentation.d.ts +2 -0
  123. package/dist/track-presentation.test.d.ts +1 -0
  124. package/dist/types/encryption.d.ts +5 -0
  125. package/dist/types/events.d.ts +5 -0
  126. package/package.json +3 -3
  127. package/dist/substrate/git-proxy.d.ts +0 -35
  128. /package/dist/{substrate/__tests__/git-proxy.test.d.ts → appsync/__tests__/appsync-client-flag-locator.test.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,8 @@
1
+ type KeytarApi = {
2
+ getPassword(service: string, account: string): Promise<string | null>;
3
+ setPassword(service: string, account: string, password: string): Promise<void>;
4
+ deletePassword(service: string, account: string): Promise<boolean>;
5
+ };
1
6
  type BackendKind = 'keytar' | 'file';
2
7
  /**
3
8
  * Is the OS-native keyring service STRUCTURALLY UNREACHABLE in THIS process?
@@ -28,6 +33,10 @@ type BackendKind = 'keytar' | 'file';
28
33
  * fallback at all.
29
34
  */
30
35
  export declare function osKeyringStructurallyAbsent(): boolean;
36
+ /** Best-effort: remember that the OS keyring worked here (Linux only — only the
37
+ * Linux headless path ever reads it). Never throws. Exported so #642's
38
+ * migration can record it after moving credentials onto the keyring. */
39
+ export declare function markKeytarUsed(): void;
31
40
  /**
32
41
  * Do durable file-backend credentials already exist on this machine? True when
33
42
  * ~/.codevibe holds any `<service>.json` credential file (written by a prior
@@ -65,6 +74,23 @@ export declare function wasAutoFileFallback(): boolean;
65
74
  * outcome) for tests and diagnostics without triggering initialization.
66
75
  */
67
76
  export declare function peekActiveBackendKind(): BackendKind | null;
77
+ /**
78
+ * Run `fn` while holding the per-service FILE-MUTATION lock — the single
79
+ * cross-process gate that serializes EVERY mutation of `~/.codevibe/<service>.json`
80
+ * (setPassword / deletePassword) against a concurrent `codevibe keychain migrate`.
81
+ * The migration CLI holds this SAME lock across the whole migration, so the file
82
+ * store cannot change mid-migration (the lock IS the freeze — it lets the migration
83
+ * do a single stable read instead of freeze-then-re-migrate).
84
+ *
85
+ * FAIL CLOSED: if the lock cannot be acquired (contention timeout / unwritable lock
86
+ * root) we THROW `KeychainBackendUnavailableError` and NEVER run `fn` unlocked — an
87
+ * unserialized read-modify-write could clobber a concurrent migration's
88
+ * tombstone/retire and re-fork the identity (the fork-incident harm). Reuses the
89
+ * portable, native-addon-free mkdir-lock from the credential broker (§626 §5 /
90
+ * AMEND-1) under a DISTINCT lockdir (`keychain-file-<service>`) from the
91
+ * per-provider vendor-key OAuth locks.
92
+ */
93
+ export declare function withFileStoreLock<T>(service: string, fn: () => Promise<T> | T): Promise<T>;
68
94
  /**
69
95
  * The active backend kind (`'keytar'` = OS-native keyring, `'file'` = the
70
96
  * plaintext `~/.codevibe/<service>.json` fallback). Initializes the backend if
@@ -79,7 +105,86 @@ export declare function peekActiveBackendKind(): BackendKind | null;
79
105
  * backend could be initialized (keytar failed to load AND no file opt-in).
80
106
  */
81
107
  export declare function getActiveBackendKind(): BackendKind;
108
+ /**
109
+ * LOCK-FREE read (F5). Identical to `getPassword` — a read never needs the file lock
110
+ * (atomic writes make a torn read impossible) — but named explicitly so a transaction
111
+ * already holding `withFileStoreLock` (device get-or-create) can read without any risk
112
+ * of re-entering the lock.
113
+ */
114
+ export declare function getPasswordUnlocked(service: string, account: string): Promise<string | null>;
82
115
  export declare function getPassword(service: string, account: string): Promise<string | null>;
116
+ /**
117
+ * LOCK-FREE write (F5) — the file-mutation body of `setPassword` WITHOUT acquiring
118
+ * `withFileStoreLock`. It STILL runs the fail-closed tombstone guard + unreadable-store
119
+ * guard + atomic write. For use ONLY by a caller that already holds the file lock (the
120
+ * device get-or-create transaction); acquiring the lock again would self-deadlock.
121
+ */
122
+ export declare function setPasswordUnlocked(service: string, account: string, password: string): Promise<void>;
83
123
  export declare function setPassword(service: string, account: string, password: string): Promise<void>;
84
124
  export declare function deletePassword(service: string, account: string): Promise<boolean>;
125
+ /** Absolute path of the file-backend credential store for `service`. */
126
+ export declare function fileStorePathForService(service: string): string;
127
+ /**
128
+ * A DISCRIMINATED read of the file store — migration must distinguish "no file"
129
+ * (nothing to do) from "exists but I can't read it right now" (EACCES / transient
130
+ * I/O / mid-write) from "readable but corrupt JSON" from "readable credential map".
131
+ * Retiring a file we merely FAILED to read would lose a valid identity, so the
132
+ * migration never retires on `unreadable`. `raw` is the exact bytes, for a
133
+ * byte-exact concurrency compare (parsed-object equality would miss whitespace /
134
+ * duplicate-key rewrites).
135
+ */
136
+ export type FileStoreRead = {
137
+ kind: 'absent';
138
+ } | {
139
+ kind: 'unreadable';
140
+ error: string;
141
+ } | {
142
+ kind: 'corrupt';
143
+ raw: string;
144
+ } | {
145
+ kind: 'store';
146
+ data: Record<string, string>;
147
+ raw: string;
148
+ };
149
+ export declare function readFileStoreState(service: string): FileStoreRead;
150
+ /**
151
+ * Rename the file store out of the way (`…/<service>.json.migrated-backup-<ts>`)
152
+ * so the NEXT process selects the OS keyring (fileStoreHasCredentials() → false).
153
+ * Reversible — the backup is kept, 0600. Returns the backup path, or null if
154
+ * there was no file store to retire. Only ever called AFTER the migration has
155
+ * written + verified every credential into the keyring (never loses the identity).
156
+ *
157
+ * R4-4: NO fail-open `existsSync` pre-probe (an EACCES there returned null → the CLI
158
+ * would exit 0 while the plaintext file remained). Attempt the atomic rename directly:
159
+ * an ENOENT/ENOTDIR means there was genuinely nothing to retire → null; ANY other
160
+ * error (EACCES/EIO/…) PROPAGATES so the caller surfaces the failure (non-zero exit /
161
+ * verify_failed) instead of silently leaving the file behind.
162
+ */
163
+ export declare function retireFileStore(service: string): string | null;
164
+ /**
165
+ * Load the keytar native module directly (bypassing the pinned backend), or null
166
+ * if it cannot load. Used ONLY by the migration command to write into the OS
167
+ * keyring from a process otherwise committed to the file backend. Same indirect
168
+ * `eval('require')` as initBackends so esbuild does not resolve keytar statically.
169
+ */
170
+ export declare function loadKeytarModule(): KeytarApi | null;
171
+ /** True iff this machine has migrated `service`'s credentials onto the OS keyring.
172
+ * Routed through `pathPresence` (F1); a probe 'error' reads as false here (the
173
+ * fail-closed callers use `migratedTombstoneStatus`, not this boolean). */
174
+ export declare function migratedTombstoneExists(service: string): boolean;
175
+ /**
176
+ * Tri-state probe of the per-service migration tombstone via `pathPresence` (F1) —
177
+ * `fs.statSync`, NOT `fs.existsSync` (which fails OPEN on EACCES/EIO). DISTINGUISHES a
178
+ * genuine probe error from a clean absence so `initBackends` / the in-lock mutation
179
+ * guard / the CLI resume gate all fail CLOSED on 'error' rather than treating an
180
+ * unreadable ~/.codevibe as "not migrated" (which could re-fork the device identity).
181
+ */
182
+ export declare function migratedTombstoneStatus(service: string): 'present' | 'absent' | 'error';
183
+ /**
184
+ * Atomically create the per-service migration tombstone (temp-write + fsync +
185
+ * rename + best-effort dir fsync, 0600). Idempotent. Throws on failure — the
186
+ * migration MUST know the tombstone is durable before it retires the file (the
187
+ * tombstone is what makes stragglers fail closed).
188
+ */
189
+ export declare function writeMigratedTombstone(service: string): void;
85
190
  export {};
@@ -5,6 +5,7 @@ import { DeviceIdentity, TokenData, EncryptedSessionKey } from '../types';
5
5
  export declare class KeychainError extends Error {
6
6
  constructor(message: string);
7
7
  }
8
+ export declare const DEVICE_IDENTITY_ACCOUNT = "device-identity";
8
9
  /**
9
10
  * Manages device identity and OAuth tokens using native keychain
10
11
  */
@@ -36,7 +37,18 @@ export declare class KeychainManager {
36
37
  */
37
38
  setDeviceIdentity(identity: DeviceIdentity): Promise<void>;
38
39
  /**
39
- * Get or create device identity
40
+ * Get or create device identity — ATOMIC across processes (#642 F5).
41
+ *
42
+ * A plain read-then-create is a cross-process race: two fresh processes both read
43
+ * "no identity" outside any lock, generate distinct identities A and B, then each
44
+ * writes under its own lock — last-writer-wins, and the loser keeps using an
45
+ * identity that is NOT the one persisted (forking E2E state). The WHOLE
46
+ * read→validate→generate→write transaction is therefore serialized under the
47
+ * per-service file lock (`withFileStoreLock`), using the LOCK-FREE backend helpers
48
+ * (`getPasswordUnlocked`/`setPasswordUnlocked`) so the transaction never re-enters
49
+ * the lock and self-deadlocks. The lock serializes regardless of backend (keytar or
50
+ * file). The lock is taken ONLY on the slow path (no cached/persisted identity yet),
51
+ * so steady-state reads stay lock-free.
40
52
  */
41
53
  getOrCreateDeviceIdentity(): Promise<DeviceIdentity>;
42
54
  /**
@@ -103,8 +115,10 @@ export declare class KeychainManager {
103
115
  *
104
116
  * When `encryptedKeys` are NOT provided (cache-only callers — e.g. the
105
117
  * orchestration QuorumLoop's `getSessionKey(sessionId)`), the cached key is
106
- * returned regardless of generation. This preserves the shipped cache-only
107
- * behavior; those callers do not participate in the generation-binding.
118
+ * returned as before. An explicitly supplied empty authoritative envelope is
119
+ * different: a generation-bearing row is non-decryptable and may never fall
120
+ * through to a stale cache. Empty-envelope fallback is permitted only for a
121
+ * genuinely generation-absent legacy row/cache.
108
122
  */
109
123
  getSessionKey(sessionId: string, encryptedKeys?: EncryptedSessionKey[], sessionKeyGen?: string | null): Promise<string | null>;
110
124
  /**
@@ -0,0 +1,63 @@
1
+ import { DEVICE_IDENTITY_ACCOUNT } from './keychain-manager';
2
+ import type { FileStoreRead } from './keychain-backend';
3
+ export { DEVICE_IDENTITY_ACCOUNT };
4
+ export type { FileStoreRead };
5
+ export interface MigrateIO {
6
+ print(line: string): void;
7
+ printErr(line: string): void;
8
+ }
9
+ export interface MigrateDeps {
10
+ /** Discriminated read of the file store (absent / unreadable / corrupt / store+raw). */
11
+ readFileState(): FileStoreRead;
12
+ /** Read an account from the OS keyring directly. MAY THROW (locked/denied keyring). */
13
+ keytarGet(account: string): Promise<string | null>;
14
+ /** Write an account to the OS keyring directly. */
15
+ keytarSet(account: string, password: string): Promise<void>;
16
+ /** Atomically create the durable "migrated to keyring" tombstone (throws on failure). */
17
+ writeTombstone(): void;
18
+ /** Retire the file store → backup path (or null if nothing to retire). */
19
+ retireFileStore(): string | null;
20
+ /** Record the Linux `.keyring-used` anti-fork marker (no-op off Linux). */
21
+ markKeyringChosen(): void;
22
+ io: MigrateIO;
23
+ }
24
+ export interface MigrateOptions {
25
+ discardFile: boolean;
26
+ force: boolean;
27
+ /**
28
+ * Crash-recovery RESUME (the CLI sets this when the migration tombstone already
29
+ * existed at entry). Post-tombstone the KEYRING is authoritative and a keyring-ABSENT
30
+ * account is an authoritative DELETION, NOT an incomplete copy — so a resume must
31
+ * copy ZERO accounts from the stale file (F3). It only verifies the keyring still
32
+ * holds a VALID identity and, if so, retires the leftover file (copying nothing); if
33
+ * the keyring lost its identity it REFUSES (never re-runs the copy/replace fork path,
34
+ * never strands the file). First-run (false) keeps the file-authoritative copy.
35
+ */
36
+ resume: boolean;
37
+ }
38
+ export type MigrateOutcome = 'nothing_to_do' | 'file_unreadable' | 'keyring_unreachable' | 'retired_empty' | 'discarded' | 'discard_refused' | 'already_present' | 'migrated' | 'replaced' | 'fork_refused' | 'invalid_source_identity' | 'resume_keyring_lost' | 'verify_failed';
39
+ export interface MigrateResult {
40
+ outcome: MigrateOutcome;
41
+ backupPath?: string | null;
42
+ accountsMigrated?: string[];
43
+ fileDeviceId?: string | null;
44
+ keychainDeviceId?: string | null;
45
+ }
46
+ interface ParsedIdentity {
47
+ deviceId: string;
48
+ publicKey: string;
49
+ }
50
+ /**
51
+ * Parse a stored device-identity blob into a VALIDATED identity: a canonical-UUID
52
+ * `deviceId`, a non-empty `publicKey`, AND a non-empty `privateKey`. Anything else
53
+ * (missing fields, non-UUID deviceId, keyless, unparseable) → null = "not a
54
+ * recognized identity". Returning null here makes the fork guard treat a
55
+ * parseable-but-invalid blob as a CONFLICT, not a match — so a file identity missing
56
+ * its private key can never `--force`-replace a good keyring identity with a keyless
57
+ * one. (Full ECDH derive-validation is deliberately NOT done: identities come from
58
+ * our own P-256 keygen, and the NO-CLOBBER invariant already protects the keyring's
59
+ * copy in the sameIdentity path — a non-empty privateKey field is a sufficient
60
+ * structural check.)
61
+ */
62
+ export declare function parseIdentity(raw: string | null | undefined): ParsedIdentity | null;
63
+ export declare function migrateKeychain(deps: MigrateDeps, opts: MigrateOptions): Promise<MigrateResult>;
@@ -0,0 +1,38 @@
1
+ export interface FileStoreSnapshot {
2
+ kind: 'absent' | 'unreadable' | 'corrupt' | 'store';
3
+ /** Account count when kind === 'store'; 0 otherwise. */
4
+ accountCount: number;
5
+ /** True when kind === 'store' and the device-identity account parses as a valid identity. */
6
+ identityValid: boolean;
7
+ /**
8
+ * True when any account NAME or VALUE is not a non-empty string (Stage-2
9
+ * Codex r1+r2): migrate copies EVERY account via keytar.setPassword, whose
10
+ * checkRequired rejects empty accounts and empty passwords → verify_failed
11
+ * refusal (possibly after earlier accounts were written). Non-secret check —
12
+ * length/type only, never content.
13
+ */
14
+ hasEmptyAccountEntry: boolean;
15
+ }
16
+ export interface NudgeDeps {
17
+ /** CODEVIBE_ALLOW_FILE_KEYCHAIN === '1' — the user explicitly chose file storage. */
18
+ fileKeychainOptIn(): boolean;
19
+ /** Tri-state migration-tombstone probe (statSync-based). */
20
+ tombstoneStatus(): 'present' | 'absent' | 'error';
21
+ /** ONE discriminated read of the file store for the active service. */
22
+ fileStoreSnapshot(): FileStoreSnapshot;
23
+ /** Platform-structural "no keyring here" (e.g. headless Linux, no keytar module). */
24
+ keyringStructurallyAbsent(): boolean;
25
+ /**
26
+ * One live keytar read of the keyring's device-identity account. Resolves to
27
+ * the stored value or null; MAY THROW / reject (locked or denied keyring).
28
+ * The caller compares the result against null and nothing else (invariant 4).
29
+ */
30
+ keytarProbe(): Promise<unknown>;
31
+ }
32
+ /**
33
+ * True when `login`/`status` should print the keychain-migration suggestion.
34
+ * Never throws.
35
+ */
36
+ export declare function shouldSuggestKeychainMigration(deps: NudgeDeps): Promise<boolean>;
37
+ /** The advisory text, single-sourced so login/status/tests render identically. */
38
+ export declare const MIGRATION_NUDGE_LINES: readonly string[];
@@ -0,0 +1,3 @@
1
+ import type { WorkspaceRootAuthority } from '../workspace-authority';
2
+ /** Schema-valid authority for storage-only tests that never touch a user tree. */
3
+ export declare const TEST_WORKSPACE_ROOT_AUTHORITY: WorkspaceRootAuthority;
@@ -1,3 +1,4 @@
1
+ import { type WorkspaceRootAuthority } from './workspace-authority';
1
2
  /** A structured failure in disposable-repo materialization or fd-safe import. */
2
3
  export declare class DisposableRepoError extends Error {
3
4
  readonly reason: 'init' | 'ls-tree' | 'pack-objects' | 'dangerous-attributes' | 'read-tree' | 'checkout';
@@ -5,13 +6,27 @@ export declare class DisposableRepoError extends Error {
5
6
  }
6
7
  /** A structured rejection from {@link fdSafeReadFile} (fail-closed → human-gate). */
7
8
  export declare class FdSafeImportError extends Error {
8
- readonly reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink';
9
- constructor(reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink', message: string);
9
+ readonly reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink' | 'root-incarnation';
10
+ constructor(reason: 'empty' | 'nul' | 'absolute' | 'traversal' | 'missing' | 'symlink-component' | 'not-regular' | 'hardlink' | 'root-incarnation', message: string);
11
+ }
12
+ /** Exact post-rename authority returned by {@link stageDirForRead}. */
13
+ export interface StagedDirectory {
14
+ path: string;
15
+ identity: WorkspaceRootAuthority;
16
+ }
17
+ /** Staging failed after rename; retains the exact authority needed for cleanup. */
18
+ export declare class StageDirForReadError extends Error {
19
+ readonly retained?: StagedDirectory | undefined;
20
+ constructor(message: string, retained?: StagedDirectory | undefined);
21
+ get retainedPath(): string | undefined;
22
+ get retainedIdentity(): WorkspaceRootAuthority | undefined;
10
23
  }
11
24
  /** A materialized disposable repo + its cleanup. */
12
25
  export interface DisposableRepo {
13
26
  /** The working-tree root the agent resolves in (its own `.git` lives under it). */
14
27
  repoDir: string;
28
+ /** Exact directory created by CodeVibe; cleanup never recaptures a replacement. */
29
+ repoAuthority: WorkspaceRootAuthority;
15
30
  /** Remove the disposable repo (try/finally — even on timeout/crash). */
16
31
  cleanup(): Promise<void>;
17
32
  }
@@ -38,10 +53,12 @@ export declare function materializeDisposableRepo(opts: MaterializeOpts): Promis
38
53
  * Atomically rename `dir` to a fresh RANDOM sibling path before reading it back —
39
54
  * the exited agent / any disowned background process does NOT know the new path,
40
55
  * so it cannot pre-place symlinks to win a TOCTOU race. The caller reads from the
41
- * returned staged path (and is responsible for removing it). Same-filesystem
42
- * rename → atomic.
56
+ * returned staged path plus relocated cleanup identity (and is responsible for
57
+ * removing that exact authority). Same-filesystem rename → atomic.
43
58
  */
44
- export declare function stageDirForRead(dir: string): Promise<string>;
59
+ export declare function stageDirForRead(dir: string, expectedAuthority: WorkspaceRootAuthority, seams?: {
60
+ afterRename?: (staged: string) => Promise<void>;
61
+ }): Promise<StagedDirectory>;
45
62
  /**
46
63
  * fd-safe read of ONE conflicted file at `relPath` under `rootDir` (CP-14 WB2 §2.1).
47
64
  * `rootDir` SHOULD be a {@link stageDirForRead} staging path. Closes leaf+parent
@@ -49,4 +66,4 @@ export declare function stageDirForRead(dir: string): Promise<string>;
49
66
  * dir/FIFO/device (`isFile`), and the read race (read-from-the-verified-fd). Any
50
67
  * violation throws {@link FdSafeImportError} → the caller fails closed (human-gate).
51
68
  */
52
- export declare function fdSafeReadFile(rootDir: string, relPath: string): Promise<Buffer>;
69
+ export declare function fdSafeReadFile(staged: StagedDirectory, relPath: string): Promise<Buffer>;
@@ -1,3 +1,5 @@
1
+ import { stageDirForRead, type StagedDirectory } from './agentic-disposable-repo';
2
+ import { type WorkspaceRootAuthority } from './workspace-authority';
1
3
  /**
2
4
  * CP-14 WB2 §2.1 path C (dispatch-time fail-closed, D3-A) — the live
3
5
  * {@link DispatchImplementor} closure THROWS this when the per-resolve confinement
@@ -14,6 +16,8 @@ export declare class AgenticDispatchUnconfinedError extends Error {
14
16
  export type DispatchImplementor = (args: {
15
17
  /** The disposable repo working-tree root (its own `.git`). */
16
18
  repoDir: string;
19
+ /** Original full incarnation authority; dispatch must bind launch to it. */
20
+ repoAuthority: WorkspaceRootAuthority;
17
21
  /** The class-2 conflicted files the agent must resolve (markers removed). */
18
22
  conflictedPaths: readonly string[];
19
23
  /** The implementor agent kind + its per-spawn task identity. */
@@ -30,10 +34,22 @@ export interface AgenticResolveArgs {
30
34
  implementorTaskId: string;
31
35
  /** Under the task's writable workspace area. */
32
36
  parentDir: string;
37
+ /**
38
+ * The caller created `parentDir` solely for this resolve and transfers
39
+ * cleanup ownership here. Direct/library callers may omit this when the
40
+ * parent contains other state.
41
+ */
42
+ cleanupParentDir?: boolean;
33
43
  /** scan-before-checkout (the live caller passes `scanIntegrationTreeAttributes`). */
34
44
  scanTree: (repoDir: string, tree: string) => Promise<string[]>;
35
45
  /** Real implementor spawn (LE seam); tests stub it. */
36
46
  dispatchImplementor: DispatchImplementor;
47
+ /** @internal deterministic post-stage cleanup regression seam. */
48
+ stageDirectory?: typeof stageDirForRead;
49
+ /** @internal deterministic pathname-replacement seam before dispatch. */
50
+ beforeDispatch?: (repoDir: string, authority: WorkspaceRootAuthority) => Promise<void>;
51
+ /** @internal deterministic pathname-replacement seam after staging. */
52
+ beforeStagedRead?: (staged: StagedDirectory) => Promise<void>;
37
53
  }
38
54
  export interface AgenticResolveResolved {
39
55
  status: 'resolved';