@quantiya/codevibe-codex-plugin 2.0.10 → 2.0.13

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 (61) hide show
  1. package/dist/server.js +31 -24
  2. package/libexec/codex-hook-config.js +190 -0
  3. package/libexec/companion-launcher +9 -158
  4. package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/appsync-client-flag-locator.test.d.ts +1 -0
  5. package/node_modules/@quantiya/codevibe-core/dist/appsync/__tests__/dispose-watcher-socket.test.d.ts +1 -0
  6. package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.d.ts +62 -0
  7. package/node_modules/@quantiya/codevibe-core/dist/auth/__tests__/keychain-cli-nudge.test.d.ts +1 -0
  8. package/node_modules/@quantiya/codevibe-core/dist/auth/keychain-cli.d.ts +8 -0
  9. package/node_modules/@quantiya/codevibe-core/dist/index.js +523 -351
  10. package/node_modules/@quantiya/codevibe-core/dist/keychain/__tests__/migrate-nudge.test.d.ts +1 -0
  11. package/node_modules/@quantiya/codevibe-core/dist/keychain/keychain-migrate.d.ts +17 -0
  12. package/node_modules/@quantiya/codevibe-core/dist/keychain/migrate-nudge.d.ts +38 -0
  13. package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/protocol-backlog-wiring.test.d.ts +1 -0
  14. package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/protocol-backlog.test.d.ts +1 -0
  15. package/node_modules/@quantiya/codevibe-core/dist/local-executor/__tests__/workspace-cleanup-audit.test.d.ts +1 -0
  16. package/node_modules/@quantiya/codevibe-core/dist/local-executor/anchored-fs.d.ts +7 -0
  17. package/node_modules/@quantiya/codevibe-core/dist/local-executor/bounded-directory.d.ts +14 -0
  18. package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-identity.d.ts +30 -0
  19. package/node_modules/@quantiya/codevibe-core/dist/local-executor/process-tree.d.ts +1 -1
  20. package/node_modules/@quantiya/codevibe-core/dist/local-executor/protocol-backlog.d.ts +62 -0
  21. package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-cleanup-audit.d.ts +3 -0
  22. package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-materializer.d.ts +23 -2
  23. package/node_modules/@quantiya/codevibe-core/dist/local-executor/workspace-shadow.d.ts +2 -0
  24. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/direct-file-delete.test.d.ts +1 -0
  25. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/flag-command.test.d.ts +1 -0
  26. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/__tests__/halt-message-wire-content.test.d.ts +1 -0
  27. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/audit-browser.d.ts +54 -1
  28. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.d.ts +10 -0
  29. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/cli.js +2711 -1474
  30. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/context-items.d.ts +3 -0
  31. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/direct-file-delete.d.ts +22 -0
  32. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/flag-command.d.ts +81 -0
  33. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/gate-prompts.d.ts +41 -0
  34. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/index.d.ts +31 -6
  35. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/quorum-loop.d.ts +42 -1
  36. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/task-progress.d.ts +3 -0
  37. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/types.d.ts +58 -0
  38. package/node_modules/@quantiya/codevibe-core/dist/orchestration-shell/workspace-shutdown.d.ts +20 -0
  39. package/node_modules/@quantiya/codevibe-core/dist/reviewer/providers/codex.d.ts +5 -3
  40. package/node_modules/@quantiya/codevibe-core/dist/reviewer/token-usage.d.ts +41 -12
  41. package/node_modules/@quantiya/codevibe-core/dist/reviewer/types.d.ts +3 -0
  42. package/node_modules/@quantiya/codevibe-core/dist/substrate/__tests__/command-runner.test.d.ts +1 -0
  43. package/node_modules/@quantiya/codevibe-core/dist/substrate/types.d.ts +8 -0
  44. package/node_modules/@quantiya/codevibe-core/dist/track-presentation.d.ts +2 -0
  45. package/node_modules/@quantiya/codevibe-core/dist/track-presentation.test.d.ts +1 -0
  46. package/node_modules/@quantiya/codevibe-core/package.json +1 -1
  47. package/node_modules/fs-ext/build/Makefile +2 -2
  48. package/node_modules/fs-ext/build/Release/fs_ext.node +0 -0
  49. package/node_modules/fs-ext/build/Release/obj.target/fs_ext/fs-ext.o +0 -0
  50. package/node_modules/fs-ext/build/config.gypi +1 -1
  51. package/package.json +5 -2
  52. package/node_modules/@iarna/toml/CHANGELOG.md +0 -278
  53. package/node_modules/color-convert/CHANGELOG.md +0 -54
  54. package/node_modules/deep-extend/CHANGELOG.md +0 -46
  55. package/node_modules/es-toolkit/CHANGELOG.md +0 -810
  56. package/node_modules/es-toolkit/src/compat/_internal/Equals.d.ts +0 -1
  57. package/node_modules/es-toolkit/src/compat/_internal/IsWritable.d.ts +0 -3
  58. package/node_modules/es-toolkit/src/compat/_internal/MutableList.d.ts +0 -4
  59. package/node_modules/es-toolkit/src/compat/_internal/RejectReadonly.d.ts +0 -4
  60. package/node_modules/keytar/build/Release/keytar.node +0 -0
  61. package/node_modules/which/CHANGELOG.md +0 -166
@@ -43,4 +43,21 @@ export interface MigrateResult {
43
43
  fileDeviceId?: string | null;
44
44
  keychainDeviceId?: string | null;
45
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;
46
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[];
@@ -22,6 +22,13 @@ export declare function listAnchoredPublicationAuthorityResidueForParents(parent
22
22
  ownerKey: string;
23
23
  label: string;
24
24
  }[]): Promise<AnchoredPublicationAuthorityResidue[]>;
25
+ export declare class AnchoredFileRemovalDurabilityError extends Error {
26
+ readonly code = "EANCHOREDFILEREMOVALDURABILITY";
27
+ readonly removed = true;
28
+ readonly durabilityUncertain = true;
29
+ constructor(message: string);
30
+ }
31
+ export declare function isAnchoredFileRemovalDurabilityError(err: unknown): err is AnchoredFileRemovalDurabilityError;
25
32
  export interface AnchoredFsIdentity {
26
33
  dev: string;
27
34
  ino: string;
@@ -10,6 +10,20 @@ export interface MetadataBudget {
10
10
  readonly maxUtf8PathBytes: number;
11
11
  }
12
12
  export declare function createMetadataBudget(maxEntries?: number, maxUtf8PathBytes?: number): MetadataBudget;
13
+ /**
14
+ * Retry a complete read-only operation when a bounded directory keeps the same
15
+ * authenticated inode but gains or loses a child during one enumeration.
16
+ *
17
+ * The operation must create fresh metadata budgets on every invocation. Only
18
+ * same-inode content churn is retried: root replacement, identity loss,
19
+ * malformed authority, helper timeout, and every other failure remain
20
+ * immediate and fail closed.
21
+ */
22
+ export declare function withTransientDirectoryEnumerationRetry<T>(operation: () => Promise<T>, opts?: {
23
+ maxAttempts?: number;
24
+ retryDelaysMs?: readonly number[];
25
+ sleep?: (ms: number) => Promise<void>;
26
+ }): Promise<T>;
13
27
  /** Charge one path before retaining it or doing work derived from it. */
14
28
  export declare function consumeMetadataPath(budget: MetadataBudget, value: string, label: string): void;
15
29
  /** Validate an existing collection incrementally, without an intermediate map. */
@@ -7,10 +7,40 @@ export interface ProcessMetadata {
7
7
  identity: ProcessIdentity;
8
8
  processGroupId: number | null;
9
9
  }
10
+ /**
11
+ * Share an in-flight Darwin boot-session probe, but persist only a verified
12
+ * kernel value. A transient sysctl failure or malformed reply must fail the
13
+ * current identity attempt closed without poisoning every later operation in
14
+ * the desktop process.
15
+ */
16
+ export declare function createDarwinBootSessionProvider(runProbe: () => Promise<{
17
+ code: number | null;
18
+ stdout: string;
19
+ } | null>): () => Promise<string | null>;
20
+ /**
21
+ * Compare authenticated process incarnations across the Darwin identity
22
+ * migration from mutable `kern.boottime` microseconds to
23
+ * `kern.bootsessionuuid`.
24
+ *
25
+ * Exact equality remains authoritative on every platform. Darwin legacy
26
+ * records may additionally match when the native microsecond-resolution
27
+ * process creation time is identical and the clock-adjustable legacy boot
28
+ * second differs by at most the adjacent-second carry observed in the field.
29
+ * Two UUID-backed identities still require the same kernel boot UUID.
30
+ */
31
+ export declare function processIncarnationIdsMatch(expected: string, actual: string): boolean;
10
32
  /** Same native identity reader embedded into fd-anchored helper processes. */
11
33
  export declare const PROCESS_IDENTITY_SYNC_HELPER_SOURCE: string;
12
34
  /** Read exact process metadata from one platform-owned source. */
13
35
  export declare function readProcessMetadata(pid: number): Promise<ProcessMetadata | null>;
14
36
  export declare function readProcessIdentity(pid: number): Promise<ProcessIdentity | null>;
37
+ /**
38
+ * Memoize one process's authenticated native identity.
39
+ *
40
+ * A native probe may fail transiently under host load. Share concurrent work
41
+ * and retry once, but cache only a verified identity: a rejected probe must
42
+ * not permanently poison every later workspace operation in this process.
43
+ */
44
+ export declare function createCurrentProcessIdentityProvider(resolveIdentity: () => Promise<ProcessIdentity | null>): () => Promise<ProcessIdentity>;
15
45
  export declare function currentProcessIdentity(): Promise<ProcessIdentity>;
16
46
  export declare function isProcessIdentityAlive(identity: ProcessIdentity): Promise<boolean>;
@@ -94,7 +94,7 @@ export declare function captureProcessCleanupOwner(args: {
94
94
  /** @internal deterministic native-identity seam. */
95
95
  readProcessIncarnation?: ReadProcessIncarnation;
96
96
  }): Promise<ProcessCleanupOwner | undefined>;
97
- export declare function processTreeOwnership(substrateOwned: boolean, platform?: NodeJS.Platform): ProcessTreeOwnership;
97
+ export declare function processTreeOwnership(substrateOwned: boolean, platform?: NodeJS.Platform, advertisedHostOwnership?: 'posix-host'): ProcessTreeOwnership;
98
98
  /**
99
99
  * Terminate a process tree CodeVibe owns. POSIX uses a detached process group;
100
100
  * Windows launches close their original Job Object host handle.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Backlog-based flood guard for line-JSON helper protocols.
3
+ *
4
+ * WHY (2026-08-22, investigation of "anchored-fs helper exceeded its
5
+ * protocol-output limit"): the previous guards accumulated a LIFETIME byte
6
+ * counter (`protocolBytes += chunk.length`) that was never reset when lines
7
+ * were consumed, then killed the helper past 1 MiB. That conflates two very
8
+ * different things:
9
+ *
10
+ * - a FLOOD: the helper emitting output nobody is consuming (backlog grows
11
+ * without bound) — the thing the cap exists to stop; and
12
+ * - a long-lived, perfectly healthy session whose request/response traffic
13
+ * legitimately totals more than 1 MiB — e.g. an implementor task
14
+ * materializing a workspace over a large non-git root (the CodeVibe
15
+ * workspace root holds ~14 sibling repositories), where every per-file
16
+ * exchange is consumed promptly but the cumulative counter still trips
17
+ * and the round dies with "Implementor round 0 failed".
18
+ *
19
+ * The correct discriminator is the UNCONSUMED backlog: bytes sitting in the
20
+ * partial-line buffer plus bytes queued in lines nobody has read yet, and the
21
+ * count of queued lines. Consumed traffic must never count against the cap.
22
+ * A single overlong line is still caught because an unterminated line
23
+ * accumulates entirely in the partial buffer.
24
+ */
25
+ /**
26
+ * UTF-8 byte length of a decoded protocol string.
27
+ *
28
+ * Stage-2 R1 (Codex, 2026-08-22): `String.length` counts UTF-16 CODE UNITS,
29
+ * not bytes. The caps below are documented — and were historically enforced —
30
+ * in BYTES (the pre-fix guards summed `chunk.length` on the raw Buffer). Using
31
+ * `.length` on the DECODED string would under-count every multi-byte
32
+ * character: 400k `€` occupy 1.2 MB of real backlog but measure as 400k,
33
+ * letting a non-ASCII flood sail past a cap that ASCII traffic respects. All
34
+ * helper protocol traffic is JSON, which may legitimately carry non-ASCII
35
+ * (paths, agent prose, refusal text), so this is reachable, not theoretical.
36
+ * Every queue/consume/partial measurement must route through this function so
37
+ * the accounting is symmetric AND byte-accurate.
38
+ */
39
+ export declare function utf8Bytes(value: string): number;
40
+ export interface ProtocolBacklogCaps {
41
+ /** Max unconsumed bytes (partial buffer + queued lines). */
42
+ maxBacklogBytes: number;
43
+ /** Max queued (unconsumed) lines. */
44
+ maxQueuedLines: number;
45
+ }
46
+ export declare const DEFAULT_PROTOCOL_BACKLOG_CAPS: ProtocolBacklogCaps;
47
+ export declare class ProtocolBacklog {
48
+ private readonly caps;
49
+ private queuedBytes;
50
+ private queuedLines;
51
+ constructor(caps?: ProtocolBacklogCaps);
52
+ /**
53
+ * Check the caps against the CURRENT unconsumed state. Call after appending
54
+ * a chunk to the partial buffer (pass that buffer's length) and after any
55
+ * queue/consume transitions. Returns true while within caps.
56
+ */
57
+ withinCaps(partialBytes: number): boolean;
58
+ /** A complete line entered the unconsumed queue. */
59
+ onLineQueued(lineLength: number): void;
60
+ /** A previously queued line was consumed by a reader. */
61
+ onLineConsumed(lineLength: number): void;
62
+ }
@@ -0,0 +1,3 @@
1
+ import { type ShadowCleanupResidue, type ShadowEnv } from './workspace-shadow';
2
+ export { withTransientDirectoryEnumerationRetry } from './bounded-directory';
3
+ export declare function listShadowCleanupResidueWithRetry(taskIds: ReadonlySet<string>, env?: ShadowEnv): Promise<ShadowCleanupResidue[]>;
@@ -1,10 +1,25 @@
1
1
  import { type WorkspaceRootAuthority } from './workspace-authority.js';
2
2
  export type MaterializationHangStage = 'realpath' | 'lstat' | 'open' | 'stat' | 'clone' | 'read' | 'close';
3
+ export interface MaterializedFileMetadata {
4
+ sha256: string;
5
+ size: number;
6
+ mode: number;
7
+ }
3
8
  export interface WorkspaceMaterializerSession {
4
- copyFile(source: string, destination: string): Promise<void>;
5
- copyRelativeSymlink(source: string, destination: string): Promise<void>;
9
+ copyFile(source: string, destination: string, maxLogicalBytes?: number): Promise<MaterializedFileMetadata>;
10
+ hashDestinationFile(destination: string, maxBytes: number): Promise<MaterializedFileMetadata>;
11
+ relocateDestinationRoot(authority: WorkspaceRootAuthority): Promise<void>;
12
+ close(): Promise<void>;
13
+ }
14
+ export interface WorkspaceFileHashSession {
15
+ hashFile(file: string): Promise<MaterializedFileMetadata>;
6
16
  close(): Promise<void>;
7
17
  }
18
+ export interface WorkspaceFileHashOptions {
19
+ root: WorkspaceRootAuthority;
20
+ maxFileBytes: number;
21
+ helperTimeoutMs?: number;
22
+ }
8
23
  export interface WorkspaceMaterializerOptions {
9
24
  sourceRoot: WorkspaceRootAuthority;
10
25
  destinationRoot: WorkspaceRootAuthority;
@@ -25,3 +40,9 @@ export interface WorkspaceMaterializerOptions {
25
40
  * physical fallback only after the helper reports the exact stable file size.
26
41
  */
27
42
  export declare function createWorkspaceMaterializerSession(opts: WorkspaceMaterializerOptions): WorkspaceMaterializerSession;
43
+ /**
44
+ * Reuse one identity-bound helper for a full snapshot scan. This retains the
45
+ * same per-file path/inode/hash checks as `anchoredHashFileWithinRoot` without
46
+ * paying one operating-system process launch for every unchanged source file.
47
+ */
48
+ export declare function createWorkspaceFileHashSession(opts: WorkspaceFileHashOptions): WorkspaceFileHashSession;
@@ -130,6 +130,8 @@ export interface ShadowEnv {
130
130
  failMarkerDirectorySync?: (filePath: string) => boolean;
131
131
  /** @internal inject a post-publication directory-sync failure for the quota registry. */
132
132
  failQuotaRegistryDirectorySync?: boolean;
133
+ /** @internal pause after quota-lock acquisition and before strict state enumeration. */
134
+ beforeQuotaFenceScan?: () => Promise<void>;
133
135
  /** @internal fail initial/retry shadow-root durability before acknowledgement. */
134
136
  failShadowRootDirectorySyncStage?: 'target-child' | 'target-parent';
135
137
  /** @internal fail initial/retry state-root durability before acknowledgement. */
@@ -27,6 +27,42 @@ export declare const WIRE_PUBLIC_PLAINTEXT_KINDS: ReadonlySet<string>;
27
27
  export declare const auditKindCopy: Readonly<Record<string, string>>;
28
28
  /** Humanize a wire kind via `auditKindCopy`; unknown/future → raw snake_case. */
29
29
  export declare function auditKindLabel(kind: string): string;
30
+ /**
31
+ * The engine's `gate_resolved` payload stores `outcome` as the serde
32
+ * snake_case form of `GateOutcome` (`orchestration.rs` finalize emit). Pinned
33
+ * to the server's own validation constant
34
+ * (`record_execution_event.rs` `GATE_RESOLVED_OUTCOME_CONSENSUS_APPROVE`) —
35
+ * the `/flag` client-side subset check is defense-in-depth for the
36
+ * server-enforced check (design §3.1 items 1 + 4).
37
+ */
38
+ export declare const GATE_RESOLVED_OUTCOME_CONSENSUS_APPROVE = "consensus_approve";
39
+ /**
40
+ * The engine's `gate_resolved` payload stores `decision` as the
41
+ * internally-tagged serde form of `GateDecision` (`{"kind": "proceed"}` for
42
+ * the approve consensus). Pinned to
43
+ * `record_execution_event.rs` `GATE_RESOLVED_DECISION_PROCEED`.
44
+ */
45
+ export declare const GATE_RESOLVED_DECISION_PROCEED = "proceed";
46
+ /**
47
+ * The decrypted-outcome labels captured off a `gate_resolved` entry (O1 C1
48
+ * #652). Closed enum LABELS only — never free-form payload content. `null`
49
+ * fields fail closed (a missing key is never treated as an approval),
50
+ * mirroring the server's `flag_target_outcome_is_approve`.
51
+ */
52
+ export interface AuditGateResolvedOutcome {
53
+ /** `payload.outcome` — snake_case `GateOutcome` label, or null. */
54
+ readonly outcome: string | null;
55
+ /** `payload.decision.kind` — internally-tagged `GateDecision` tag, or null. */
56
+ readonly decisionKind: string | null;
57
+ }
58
+ /**
59
+ * True iff the entry is a decrypted `gate_resolved` row whose resolution is
60
+ * the quorum's approve consensus — the ONLY flaggable target class (design
61
+ * §3.1 item 1; byte-mirrors the server predicate
62
+ * `flag_target_outcome_is_approve` in `record_execution_event.rs`). Missing
63
+ * keys fail closed.
64
+ */
65
+ export declare function isFlaggableAuditEntry(entry: AuditEntryModel): boolean;
30
66
  /**
31
67
  * The minimal content-decrypt primitive the router needs — structurally
32
68
  * compatible with `cryptoService.decryptContent(ciphertextB64, sessionKeyB64)`.
@@ -63,6 +99,21 @@ export interface AuditEntryModel {
63
99
  readonly kind: string;
64
100
  /** RFC3339 timestamp (header line), or null when absent. */
65
101
  readonly timestamp: string | null;
102
+ /**
103
+ * O1 C1 (#652) — the row's zero-padded 16-digit audit-table sort key from
104
+ * the additive `AuditEntryWireRow.seqNum` projection (core-rs `types.rs`;
105
+ * D5 plaintext row coordinate). `null` for rows served by a backend that
106
+ * predates the projection — `/flag` then fails closed.
107
+ */
108
+ readonly seqNum: string | null;
109
+ /**
110
+ * O1 C1 (#652) — outcome LABELS captured off a DECRYPTED `gate_resolved`
111
+ * payload (`{outcome, decision.kind}` — closed enum labels only), else
112
+ * null. Feeds the `/flag` client-side approve-subset check
113
+ * ({@link isFlaggableAuditEntry}); the decrypted payload itself is never
114
+ * retained beyond the render.
115
+ */
116
+ readonly gateResolved: AuditGateResolvedOutcome | null;
66
117
  /** How the payload resolved. */
67
118
  readonly status: AuditEntryRouteStatus;
68
119
  /**
@@ -106,7 +157,9 @@ export interface AuditBrowserModel {
106
157
  * Build the full newest-first audit browser model from the server-ascending
107
158
  * `rows`. PURE — takes the rows + the session key + the injected decrypt fn.
108
159
  * Ordering (§2): the rows come back server-ascending; we reverse to newest-first
109
- * (using `timestamp`/`entryId` as keys there is NO `seq_num` on the wire).
160
+ * (using `timestamp`/`entryId` as keys; the additive O1 C1 `seqNum` projection
161
+ * is a per-row COORDINATE for `/flag`, deliberately not re-litigating the
162
+ * shipped ordering).
110
163
  */
111
164
  export declare function buildAuditBrowserModel(rows: ReadonlyArray<Record<string, unknown>>, sessionKey: string | null, decrypt: DecryptContentFn): AuditBrowserModel;
112
165
  /** Render a single entry model as readable plaintext lines. */
@@ -395,6 +395,16 @@ export declare function buildQuorumLoopExecutor(args: {
395
395
  */
396
396
  classifyRunner?: LocalGemmaPlannerRunner | null;
397
397
  }): Promise<BuildQuorumLoopExecutorResult>;
398
+ /**
399
+ * Single-task LE emit → wire shape. Exported for tests.
400
+ *
401
+ * 2026-08-22 (mobile halt-message loss): forwards `content` — previously
402
+ * dropped, so surfaceHalt's message ("Implementor round 0 failed…") rode only
403
+ * in encrypted metadata that no renderer shows, and mobile displayed a
404
+ * content-free NOTIFICATION as the bare word "notification". Content is
405
+ * E2E-encrypted on the wire like every other event.
406
+ */
407
+ export declare function mapSingleTaskLeEventToShellEmit(e: Parameters<LEEmitShellEventFn>[0]): ShellEventEmit;
398
408
  export declare function mapTeamLeEventToShellEmit(e: Parameters<LEEmitShellEventFn>[0]): ShellEventEmit;
399
409
  /**
400
410
  * Construct the team-mode LocalExecutorImpl + the store-tap holder. Returns the