@gajae-code/coding-agent 0.12.19 → 0.12.20

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 (97) hide show
  1. package/CHANGELOG.md +20 -9
  2. package/dist/types/commands/gc.d.ts +4 -0
  3. package/dist/types/commands/sdk.d.ts +66 -0
  4. package/dist/types/config/settings-schema.d.ts +34 -0
  5. package/dist/types/gjc-runtime/gc-runtime.d.ts +97 -1
  6. package/dist/types/modes/acp/acp-agent.d.ts +33 -1
  7. package/dist/types/modes/components/bash-execution.d.ts +7 -0
  8. package/dist/types/modes/components/eval-execution.d.ts +7 -0
  9. package/dist/types/modes/utils/ui-helpers.d.ts +10 -1
  10. package/dist/types/sdk/acp/adapter.d.ts +9 -17
  11. package/dist/types/sdk/acp/index.d.ts +1 -1
  12. package/dist/types/sdk/broker/broker.d.ts +93 -1
  13. package/dist/types/sdk/broker/ensure.d.ts +17 -0
  14. package/dist/types/sdk/broker/index.d.ts +1 -0
  15. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +10 -1
  16. package/dist/types/sdk/broker/lifecycle.d.ts +81 -2
  17. package/dist/types/sdk/broker/startup-budget.d.ts +44 -0
  18. package/dist/types/sdk/bus/chat-daemon-control.d.ts +7 -1
  19. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +11 -1
  20. package/dist/types/sdk/bus/discord-daemon.d.ts +1 -0
  21. package/dist/types/sdk/bus/index.d.ts +20 -9
  22. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +2 -6
  23. package/dist/types/sdk/bus/slack-daemon.d.ts +2 -2
  24. package/dist/types/sdk/host/query/revision-store.d.ts +5 -0
  25. package/dist/types/sdk/host/session-runtime.d.ts +4 -0
  26. package/dist/types/sdk/lifecycle-session.d.ts +13 -2
  27. package/dist/types/sdk/prompt-failure.d.ts +13 -0
  28. package/dist/types/sdk/prompt-watchdog.d.ts +82 -0
  29. package/dist/types/sdk/session-reconnect.d.ts +20 -0
  30. package/dist/types/sdk/startup-capability.d.ts +1 -1
  31. package/dist/types/session/agent-session.d.ts +14 -4
  32. package/dist/types/session/blob-store.d.ts +32 -0
  33. package/dist/types/session/internal/managed-session-storage.d.ts +40 -2
  34. package/dist/types/session/internal/session-memory-sidecar.d.ts +881 -0
  35. package/dist/types/session/session-manager.d.ts +301 -27
  36. package/dist/types/session/session-storage.d.ts +170 -0
  37. package/dist/types/task/executor.d.ts +2 -1
  38. package/dist/types/tools/bash.d.ts +1 -0
  39. package/dist/types/tui/output-block.d.ts +6 -0
  40. package/package.json +8 -8
  41. package/src/cli/args.ts +75 -34
  42. package/src/commands/acp.ts +7 -1
  43. package/src/commands/gc.ts +31 -2
  44. package/src/commands/sdk.ts +141 -7
  45. package/src/config/settings-schema.ts +43 -0
  46. package/src/coordinator-mcp/server.ts +6 -1
  47. package/src/export/html/index.ts +83 -39
  48. package/src/gjc-runtime/gc-runtime.ts +1283 -4
  49. package/src/internal-urls/docs-index.generated.ts +1 -1
  50. package/src/internal-urls/local-protocol.ts +14 -0
  51. package/src/main.ts +40 -9
  52. package/src/modes/acp/acp-agent.ts +564 -37
  53. package/src/modes/acp/terminal-auth.ts +2 -2
  54. package/src/modes/components/bash-execution.ts +14 -0
  55. package/src/modes/components/eval-execution.ts +14 -0
  56. package/src/modes/controllers/command-controller.ts +63 -8
  57. package/src/modes/utils/ui-helpers.ts +81 -11
  58. package/src/sdk/acp/adapter.ts +46 -45
  59. package/src/sdk/acp/index.ts +1 -1
  60. package/src/sdk/broker/broker.ts +392 -11
  61. package/src/sdk/broker/ensure.ts +131 -61
  62. package/src/sdk/broker/index.ts +1 -0
  63. package/src/sdk/broker/lifecycle-ledger.ts +59 -5
  64. package/src/sdk/broker/lifecycle.ts +278 -63
  65. package/src/sdk/broker/startup-budget.ts +103 -0
  66. package/src/sdk/bus/chat-daemon-control.ts +9 -3
  67. package/src/sdk/bus/chat-daemon-runtime.ts +565 -29
  68. package/src/sdk/bus/conversation-store.ts +1 -1
  69. package/src/sdk/bus/discord-daemon.ts +2 -1
  70. package/src/sdk/bus/discord-live-provider.ts +34 -6
  71. package/src/sdk/bus/index.ts +118 -20
  72. package/src/sdk/bus/kind-aware-reconciliation.ts +2 -1
  73. package/src/sdk/bus/prompt-reconciliation.ts +4 -9
  74. package/src/sdk/bus/slack-daemon.ts +91 -18
  75. package/src/sdk/cli/session-cli.ts +8 -1
  76. package/src/sdk/host/query/handlers.ts +1 -1
  77. package/src/sdk/host/query/revision-store.ts +48 -12
  78. package/src/sdk/host/session-runtime.test.ts +50 -0
  79. package/src/sdk/host/session-runtime.ts +12 -2
  80. package/src/sdk/lifecycle-session.ts +26 -3
  81. package/src/sdk/mcp/server.ts +6 -1
  82. package/src/sdk/prompt-failure.ts +42 -0
  83. package/src/sdk/prompt-watchdog.ts +162 -0
  84. package/src/sdk/session-reconnect.ts +43 -0
  85. package/src/sdk/startup-capability.ts +13 -4
  86. package/src/session/agent-session.ts +525 -222
  87. package/src/session/blob-store.ts +89 -0
  88. package/src/session/internal/managed-session-scope.ts +36 -26
  89. package/src/session/internal/managed-session-storage.ts +303 -32
  90. package/src/session/internal/session-memory-sidecar.ts +2160 -0
  91. package/src/session/session-manager.ts +7027 -394
  92. package/src/session/session-storage.ts +1134 -11
  93. package/src/task/executor.ts +17 -4
  94. package/src/tools/bash.ts +109 -7
  95. package/src/tools/find.ts +55 -26
  96. package/src/tools/todo-contract.ts +40 -8
  97. package/src/tui/output-block.ts +11 -1
@@ -0,0 +1,881 @@
1
+ /**
2
+ * Standalone sidecar primitives for bounded-memory cold-session offloading.
3
+ *
4
+ * This module is intentionally self-contained: it imports only Node built-ins and
5
+ * defines the reusable P2/P3/P4 primitives agreed in the approved memory spec —
6
+ * the retained-byte accountant, reducer disk-ref demotion guardrails, the
7
+ * `isDerivedSessionMemoryFile` lifecycle predicate, the disposable sidecar v2
8
+ * index schemas, the unsorted append-only bounded dictionary builder with
9
+ * duplicate-ID sidecar-ineligibility detection, fixed-size cache accounting,
10
+ * the anchored base digest + rolling tail checksum chain with commit validation
11
+ * and the five-class reopen classifier, the `latestModelChange` / TTSR
12
+ * latest-wins reducer semantics, and bounded parent→children / labels/pins
13
+ * descriptors.
14
+ *
15
+ * Transcript v5 remains authoritative and every sidecar index is disposable /
16
+ * rebuildable. No builder here requires the whole transcript resident in
17
+ * memory; all builders consume records one at a time and enforce their peak
18
+ * budgets as they go.
19
+ */
20
+ /** Provider-context materialization acceptance peak (AC1). */
21
+ export declare const SESSION_MEMORY_ACCEPTANCE_BUDGET_BYTES: number;
22
+ /** Steady-state retained-byte budget: 61.0625 MiB leaves the 64 MiB margin. */
23
+ export declare const SESSION_MEMORY_STEADY_STATE_BUDGET_BYTES: number;
24
+ /** Fixed 24 B resident disk-ref descriptor (ADR / A.1). */
25
+ export declare const DESCRIPTOR_BYTES = 24;
26
+ /** Conservative per-record object overhead (A.1). */
27
+ export declare const RECORD_OBJECT_OVERHEAD_BYTES = 48;
28
+ /** labels+pins steady-state budget (0.0625 MiB). */
29
+ export declare const LABELS_PINS_BUDGET_BYTES: number;
30
+ /** Reducer resident budget (4 MiB) — enforced by disk-ref demotion. */
31
+ export declare const REDUCER_BUDGET_BYTES: number;
32
+ /** Guardrail: values over this are demoted to disk-ref descriptors. */
33
+ export declare const MAX_REDUCER_INLINE_BYTES: number;
34
+ /** Guardrail: max resident string length. */
35
+ export declare const MAX_REDUCER_STRING_BYTES: number;
36
+ /** Guardrail: max distinct roles. */
37
+ export declare const MAX_ROLES = 64;
38
+ /** Guardrail: max chain entries per role. */
39
+ export declare const MAX_CHAIN_ENTRIES = 32;
40
+ /** Guardrail: max TTSR rules. */
41
+ export declare const MAX_TTSR_RULES = 256;
42
+ /** Guardrail: max MCP names. */
43
+ export declare const MAX_MCP_NAMES = 4096;
44
+ /** Guardrail: max chars per MCP name. */
45
+ export declare const MAX_MCP_NAME_CHARS = 256;
46
+ /** Guardrail: modeData ≤ 64 KiB. */
47
+ export declare const MODE_DATA_MAX_BYTES: number;
48
+ /** Guardrail: compaction summary ≤ 64 KiB. */
49
+ export declare const COMPACTION_SUMMARY_MAX_BYTES: number;
50
+ /** preserveData.openaiRemoteCompaction ≤ 8 MiB (always a disk ref). */
51
+ export declare const OPENAI_REMOTE_COMPACTION_MAX_BYTES: number;
52
+ /** Fixed-size index block cache (steady-state). */
53
+ export declare const BLOCK_CACHE_BUDGET_BYTES: number;
54
+ /** Fixed-size resolved-entry cache (steady-state). */
55
+ export declare const ENTRY_CACHE_BUDGET_BYTES: number;
56
+ /** Tail journal buffer/index (steady-state). */
57
+ export declare const TAIL_BUFFER_BUDGET_BYTES: number;
58
+ /** Bounded forward-scan window for transcript-ahead recovery (R2.4). */
59
+ export declare const INDEX_TAIL_SCAN_MAX_BYTES: number;
60
+ /** Maximum ordinal-index records retained transiently for one cold branch activation. */
61
+ export declare const COLD_BRANCH_PREFETCH_MAX_INDEX_ENTRIES: number;
62
+ /**
63
+ * Decide the cold-branch ordinal-run fast path before allocating its Map or
64
+ * transcript window. Large sparse/interleaved intervals fail closed to bounded
65
+ * dictionary/index fallback; the transcript remains authoritative.
66
+ */
67
+ export declare function coldBranchOrdinalRunWithinPrefetchBounds(input: {
68
+ boundaryOrdinal: number;
69
+ leafOrdinal: number;
70
+ transcriptStart: number;
71
+ transcriptEnd: number;
72
+ maxTranscriptBytes: number;
73
+ }): boolean;
74
+ /** Dictionary build acceptance cap; default buffers stay well below this 20 MiB maximum. */
75
+ export declare const DICTIONARY_BUILD_PEAK_BYTES: number;
76
+ /** One dictionary partition buffer. */
77
+ export declare const DICTIONARY_PARTITION_BUFFER_BYTES: number;
78
+ /** Dictionary bucket journal flush buffer. */
79
+ export declare const DICTIONARY_BUCKET_JOURNAL_BYTES: number;
80
+ /** Bounded parent→children index bounds. */
81
+ export declare const PARENT_CHILDREN_MAX_PARENTS = 4096;
82
+ export declare const PARENT_CHILDREN_MAX_CHILDREN_PER_PARENT = 256;
83
+ export declare const PARENT_CHILDREN_BUDGET_BYTES: number;
84
+ /** Deterministic hash-bucket count for the disposable on-disk parent artifact. */
85
+ export declare const PARENT_CHILDREN_BUCKET_COUNT = 64;
86
+ /** Resident string = `2 × charCount + 16 B`. */
87
+ export declare function residentStringBytes(value: string): number;
88
+ /** Resident array = `8 B/slot + Σ elements`. */
89
+ export declare function residentArrayBytes(slotCount: number, elementBytes: number): number;
90
+ /** Per-record object with `childBytes` of payload = 48 B overhead + payload. */
91
+ export declare function residentRecordBytes(childBytes: number): number;
92
+ /**
93
+ * Authoritative measured retained-byte counter. Enforcement acts on the total,
94
+ * never on field counts alone. `tryCharge`/`wouldExceed` let callers enforce a
95
+ * budget without mutating state (used by the provider-context preflight which
96
+ * must never retain an over-budget graph).
97
+ */
98
+ export declare class SessionMemoryAccountant {
99
+ private total;
100
+ private readonly budget;
101
+ constructor(budgetBytes?: number);
102
+ get totalBytes(): number;
103
+ get budgetBytes(): number;
104
+ get remainingBytes(): number;
105
+ /** Adds `bytes` to the running total and returns the new total. */
106
+ charge(bytes: number): number;
107
+ /** Charges one resident string via the `2 × charCount + 16` formula. */
108
+ chargeString(value: string): number;
109
+ /** Charges one resident array via the `8 B/slot + Σ elements` formula. */
110
+ chargeArray(slotCount: number, elementBytes: number): number;
111
+ /** Charges one fixed 24 B disk-ref descriptor. */
112
+ chargeDescriptor(): number;
113
+ /** Charges one per-record object (48 B overhead + payload). */
114
+ chargeRecord(childBytes: number): number;
115
+ /**
116
+ * Adds `bytes` only if the running total stays within the budget.
117
+ * Returns `false` (and does not mutate) when the addition would exceed it.
118
+ */
119
+ tryCharge(bytes: number): boolean;
120
+ /** Returns `true` when charging `bytes` would exceed the budget. */
121
+ wouldExceed(bytes: number): boolean;
122
+ isWithinBudget(): boolean;
123
+ release(bytes: number): void;
124
+ snapshot(): {
125
+ totalBytes: number;
126
+ budgetBytes: number;
127
+ remainingBytes: number;
128
+ };
129
+ private assertBytes;
130
+ }
131
+ /**
132
+ * A 24 B resident descriptor that points at a value stored in the sidecar
133
+ * metadata-delta section. Demoted values are never resident as full strings.
134
+ */
135
+ export interface ReducerDemotionDescriptor {
136
+ section: "metadata-delta";
137
+ key: string;
138
+ offset: number;
139
+ length: number;
140
+ sha256: string;
141
+ }
142
+ export interface ReducerValueSnapshot {
143
+ key: string;
144
+ kind: "inline" | "disk_ref";
145
+ residentBytes: number;
146
+ storedBytes: number;
147
+ }
148
+ export type ReducerBudgetResult = {
149
+ kind: "ok";
150
+ totalBytes: number;
151
+ } | {
152
+ kind: "over_budget_irreducible";
153
+ totalBytes: number;
154
+ };
155
+ /**
156
+ * Tracks the reducer's resident bytes against the 4 MiB budget. Values over
157
+ * `MAX_REDUCER_INLINE_BYTES` are immediately stored as 24 B disk-ref
158
+ * descriptors. When the total would exceed the budget, the largest resident
159
+ * inline value is demoted, repeating until the total fits or only
160
+ * descriptors/scalars remain (impossible by arithmetic — checked anyway).
161
+ */
162
+ export declare class ReducerBudget {
163
+ private readonly values;
164
+ private total;
165
+ private nextOffset;
166
+ private readonly budgetBytesValue;
167
+ private readonly maxInlineBytes;
168
+ constructor(budgetBytes?: number, maxInlineBytes?: number);
169
+ get totalBytes(): number;
170
+ get budgetBytes(): number;
171
+ get(key: string): ReducerValueSnapshot | undefined;
172
+ /** Returns the resident 24 B descriptor for a demoted key, if any. */
173
+ getDescriptor(key: string): ReducerDemotionDescriptor | undefined;
174
+ /**
175
+ * Records a reducer value of `storedBytes` bytes (optionally with its content
176
+ * digest) and enforces the budget by demoting the largest resident inline
177
+ * value until the total fits.
178
+ */
179
+ setInline(key: string, storedBytes: number, sha256?: string): ReducerBudgetResult;
180
+ /** Demotes every currently resident inline value to a disk-ref descriptor. */
181
+ demoteAll(): void;
182
+ private enforceBudget;
183
+ private largestDemotableInline;
184
+ private demote;
185
+ }
186
+ /**
187
+ * Single source of truth for every sidecar artifact and temp: `*.spill.idx`,
188
+ * `*.spill.tail`, `*.spill.commit`, `*.spill.buckets`, `*.spill.dict-*`,
189
+ * `*.spill.capture-*`, `*.spill.fork-*`, `*.spill.overlay-*`,
190
+ * `*.spill.parent-*` (persistent parent→children buckets),
191
+ * `*.spill.metadata-*` (persistent metadata-delta section), and any
192
+ * `*.spill.*.tmp`. Used at every fork/delete/sweep/Memory-parity callsite so
193
+ * ad-hoc filters cannot drift.
194
+ */
195
+ export declare function isDerivedSessionMemoryFile(pathname: string): boolean;
196
+ /** Common header for every sidecar v2 index file. */
197
+ export interface SidecarIndexHeaderV2 {
198
+ version: 2;
199
+ sessionId: string;
200
+ /** Set when duplicate record IDs were detected; session is eager-ineligible. */
201
+ sidecarIneligible: boolean;
202
+ }
203
+ /** Unsorted append-only variable-width dictionary (term → id). */
204
+ export interface SidecarDictionaryV2 {
205
+ header: SidecarIndexHeaderV2;
206
+ /** Insertion-ordered unique terms (append-only; deterministic). */
207
+ terms: readonly string[];
208
+ /** term → dictionary id, assigned by first-insertion order. */
209
+ idByTerm: ReadonlyMap<string, number>;
210
+ /** Combined serialized term bytes. */
211
+ totalBytes: number;
212
+ }
213
+ /** Rolling tail checksum index (anchored base + terminal chain). */
214
+ export interface SidecarTailIndexV2 {
215
+ header: SidecarIndexHeaderV2;
216
+ base: BaseAnchor;
217
+ records: readonly TailRecord[];
218
+ terminalChecksum: string;
219
+ terminalSeq: number;
220
+ transcriptSize: number;
221
+ }
222
+ /** Metadata-delta section: demoted reducer values + deltas. */
223
+ export interface SidecarMetadataIndexV2 {
224
+ header: SidecarIndexHeaderV2;
225
+ /** key → { offset, length, sha256 } for demoted values. */
226
+ entries: Record<string, {
227
+ offset: number;
228
+ length: number;
229
+ sha256: string;
230
+ }>;
231
+ }
232
+ /** Bounded parent→children index. */
233
+ export interface SidecarParentIndexV2 {
234
+ header: SidecarIndexHeaderV2;
235
+ entries: readonly ParentChildrenIndexEntry[];
236
+ }
237
+ /** Bounded labels/pins descriptor. */
238
+ export interface SidecarLabelsPinsIndexV2 {
239
+ header: SidecarIndexHeaderV2;
240
+ labels: ReadonlyMap<string, string>;
241
+ pins: ReadonlyMap<string, string>;
242
+ totalBytes: number;
243
+ }
244
+ export interface DictionaryRecordInput {
245
+ ordinal: number;
246
+ id: string;
247
+ bytes: Uint8Array;
248
+ }
249
+ export interface DictionaryBuildOptions {
250
+ peakBudgetBytes?: number;
251
+ partitionBufferBytes?: number;
252
+ bucketJournalBytes?: number;
253
+ }
254
+ export type DictionaryAddResult = {
255
+ kind: "ok";
256
+ } | {
257
+ kind: "budget_exceeded";
258
+ peakBytes: number;
259
+ budgetBytes: number;
260
+ };
261
+ export type DictionaryBuildResult = {
262
+ kind: "ok";
263
+ dictionary: SidecarDictionaryV2;
264
+ stats: DictionaryBuildStats;
265
+ } | {
266
+ kind: "budget_exceeded";
267
+ peakBytes: number;
268
+ budgetBytes: number;
269
+ };
270
+ export interface DictionaryBuildStats {
271
+ totalRecords: number;
272
+ uniqueTerms: number;
273
+ totalBytes: number;
274
+ peakBytes: number;
275
+ duplicateIds: readonly string[];
276
+ sidecarIneligible: boolean;
277
+ }
278
+ /**
279
+ * Unsorted append-only variable-width dictionary builder. Consumes records one
280
+ * at a time (no whole transcript resident) and tracks the 20 MiB build peak as
281
+ * `Σ term bytes + 4 × partition buffer + bucket journal`. Duplicate record IDs
282
+ * mark the session sidecar-ineligible (eager fallback) while the build still
283
+ * completes deterministically.
284
+ */
285
+ export declare class BoundedDictionaryBuilder {
286
+ private readonly terms;
287
+ private readonly idByTerm;
288
+ private readonly seenRecordIds;
289
+ private readonly duplicateIds;
290
+ private totalTermBytes;
291
+ private recordIdBytes;
292
+ private duplicateIdBytes;
293
+ private peakBytes;
294
+ private sidecarIneligible;
295
+ private recordCount;
296
+ private finished;
297
+ private readonly peakBudget;
298
+ private readonly partitionBase;
299
+ constructor(options?: DictionaryBuildOptions);
300
+ add(record: DictionaryRecordInput): DictionaryAddResult;
301
+ finish(): DictionaryBuildResult;
302
+ private updatePeak;
303
+ }
304
+ /**
305
+ * Tracks a fixed-size cache budget (8 MiB block, 28 MiB entry, 4 MiB tail).
306
+ * `tryAllocate` rejects allocations that would exceed the budget without
307
+ * mutating state.
308
+ */
309
+ export declare class FixedCacheAccount {
310
+ private allocated;
311
+ private readonly budget;
312
+ constructor(budgetBytes: number);
313
+ get allocatedBytes(): number;
314
+ get budgetBytes(): number;
315
+ get remainingBytes(): number;
316
+ tryAllocate(bytes: number): boolean;
317
+ release(bytes: number): void;
318
+ }
319
+ /** Anchors the base range `[0, baseEndOffset)` with a content digest. */
320
+ export interface BaseAnchor {
321
+ baseDigest: string;
322
+ baseEndOffset: number;
323
+ }
324
+ export type TailRecordKind = "user" | "assistant" | "tool" | "model_change" | "ttsr_injection" | "internal" | "other";
325
+ /** One committed rolling tail record. */
326
+ export interface TailRecord {
327
+ gen: number;
328
+ seq: number;
329
+ kind: TailRecordKind;
330
+ ordinal: number;
331
+ id: string;
332
+ parentId: string | null;
333
+ type: string;
334
+ byteOffset: number;
335
+ byteLength: number;
336
+ recordDigest: string;
337
+ checksum: string;
338
+ }
339
+ /** Input for appending to a rolling tail chain (checksum is computed). */
340
+ export interface TailRecordInput {
341
+ gen?: number;
342
+ seq: number;
343
+ kind: TailRecordKind;
344
+ ordinal: number;
345
+ id: string;
346
+ parentId: string | null;
347
+ type: string;
348
+ byteOffset: number;
349
+ byteLength: number;
350
+ recordDigest: string;
351
+ }
352
+ /** Effective base+tail identity (no per-append whole-file hash). */
353
+ export interface CommittedTail {
354
+ base: BaseAnchor;
355
+ records: readonly TailRecord[];
356
+ terminalChecksum: string;
357
+ terminalSeq: number;
358
+ transcriptSize: number;
359
+ }
360
+ /** SHA-256 of one record line's bytes. */
361
+ export declare function computeLineDigest(bytes: Uint8Array): string;
362
+ /**
363
+ * Canonical length-prefixed chain hash. Each part is hashed as its UTF-8 /
364
+ * raw bytes prefixed by a 4-byte big-endian length, so concatenations are
365
+ * unambiguous.
366
+ */
367
+ export declare function hashChain(parts: ReadonlyArray<bigint | number | string | Uint8Array>): string;
368
+ /** First tail checksum: `C0 = H(baseDigest ‖ baseEndOffset ‖ seq0 ‖ lineDigest0)`. */
369
+ export declare function computeC0(base: BaseAnchor, seq0: number, lineDigest0: string): string;
370
+ /** Checksum one complete tail record, including every field used for ordering or lookup. */
371
+ export declare function computeTailRecordChecksum(base: BaseAnchor, previousChecksum: string | undefined, record: TailRecordInput & {
372
+ gen?: number;
373
+ }): string;
374
+ /** Resident bytes for every retained tail string plus the record object. */
375
+ export declare function tailRecordResidentBytes(record: TailRecordInput, checksumChars?: number): number;
376
+ /** Terminal checksum for a committed tail (empty chain → base only). */
377
+ export declare function computeTerminalChecksum(base: BaseAnchor, records: readonly TailRecord[]): string;
378
+ export interface TailValidationResult {
379
+ valid: boolean;
380
+ firstInvalidSeq?: number;
381
+ reason?: string;
382
+ }
383
+ /**
384
+ * Validates a committed tail chain: contiguous seq from 0, contiguous absolute
385
+ * offsets from `baseEndOffset`, and checksum continuity
386
+ * `checksum_i = H(checksum_{i-1} ‖ seq_i ‖ offset_i ‖ length_i ‖ lineDigest_i)`.
387
+ */
388
+ export declare function validateTailChain(base: BaseAnchor, records: readonly TailRecord[]): TailValidationResult;
389
+ /**
390
+ * Streams tail records, computing the rolling checksum chain incrementally and
391
+ * enforcing the 4 MiB tail buffer budget. Never buffers the whole transcript.
392
+ */
393
+ export declare class RollingTailChainBuilder {
394
+ private readonly records;
395
+ private readonly base;
396
+ private lastChecksum;
397
+ private lastSeq;
398
+ private lastEndOffset;
399
+ private remainingTailBytes;
400
+ constructor(base: BaseAnchor, options?: {
401
+ tailBufferBytes?: number;
402
+ });
403
+ get recordCount(): number;
404
+ /** Appends one tail record; returns `undefined` when the buffer budget is hit. */
405
+ append(input: TailRecordInput): TailRecord | undefined;
406
+ build(): CommittedTail;
407
+ }
408
+ /** Descriptor snapshot captured post-fsync from the authoritative open handle. */
409
+ export interface DescriptorSnapshot {
410
+ dev: bigint;
411
+ ino: bigint;
412
+ nlink?: bigint;
413
+ size: number;
414
+ mtimeNs: bigint;
415
+ ctimeNs: bigint;
416
+ }
417
+ /** Exact enum-order descriptor comparison (dev/ino/nlink/size/mtimeNs/ctimeNs). */
418
+ export declare function sameDescriptor(left: DescriptorSnapshot, right: DescriptorSnapshot): boolean;
419
+ /** Contents of the mutable `.spill.commit` marker (R2). */
420
+ export interface CommitMarkerContents {
421
+ gen: number;
422
+ descriptor: DescriptorSnapshot;
423
+ base: BaseAnchor;
424
+ terminalChecksum: string;
425
+ terminalSeq: number;
426
+ transcriptSize: number;
427
+ }
428
+ export type CommitInvalidReason = "missing_fields" | "base_invalid" | "chain_invalid" | "terminal_checksum_mismatch" | "terminal_seq_mismatch" | "transcript_size_mismatch" | "descriptor_mismatch" | "terminal_marker_mismatch";
429
+ export type CommitValidationResult = {
430
+ kind: "valid";
431
+ } | {
432
+ kind: "invalid";
433
+ reason: CommitInvalidReason;
434
+ };
435
+ /**
436
+ * Validates a commit marker's internal consistency plus its descriptor against
437
+ * the currently opened transcript. `baseValid`/`tailValid`/`terminalMarkerValid`
438
+ * are the externally-observed proof results.
439
+ */
440
+ export declare function validateCommit(commit: Partial<CommitMarkerContents> | CommitMarkerContents, records: readonly TailRecord[], observed: {
441
+ descriptor: DescriptorSnapshot;
442
+ baseValid: boolean;
443
+ tailValid: boolean;
444
+ terminalMarkerValid: boolean;
445
+ }): CommitValidationResult;
446
+ export type ReopenClassKind = "exact" | "transcript_ahead" | "tail_ahead" | "rebuild" | "stale_commit";
447
+ /** Physical evidence the reopen classifier consumes. */
448
+ export interface ReopenEvidence {
449
+ markerPresent: boolean;
450
+ descriptorExact: boolean;
451
+ sameObject: boolean;
452
+ sameSize: boolean;
453
+ sizeGrew: boolean;
454
+ sizeShrank: boolean;
455
+ withinScanWindow: boolean;
456
+ timesAdvanced: boolean;
457
+ timesChanged: boolean;
458
+ baseValid: boolean;
459
+ tailValid: boolean;
460
+ terminalMarkerValid: boolean;
461
+ }
462
+ export interface ReopenClassification {
463
+ kind: ReopenClassKind;
464
+ reason: string;
465
+ }
466
+ /**
467
+ * Five-class reopen classification (R2.4). Deterministic over the supplied
468
+ * evidence; a stale commit is never accepted as current on any path.
469
+ */
470
+ export declare function classifyReopen(evidence: ReopenEvidence): ReopenClassification;
471
+ export interface LatestModelChangeDelta {
472
+ kind: "latest_model_change";
473
+ ordinal: number;
474
+ role?: string;
475
+ }
476
+ export interface TtsrDelta {
477
+ kind: "ttsr_injection";
478
+ ordinal: number;
479
+ rulesCount: number;
480
+ recordsCount: number;
481
+ count: number;
482
+ }
483
+ export type ReducerDelta = LatestModelChangeDelta | TtsrDelta;
484
+ export interface ModelChangeReducerState {
485
+ latest: {
486
+ ordinal: number;
487
+ role: string | undefined;
488
+ } | undefined;
489
+ }
490
+ export interface TtsrReducerState {
491
+ count: number;
492
+ rulesCount: number;
493
+ recordsCount: number;
494
+ largestOrdinal: number;
495
+ }
496
+ export interface ReducerState {
497
+ modelChange: ModelChangeReducerState;
498
+ ttsr: TtsrReducerState;
499
+ }
500
+ export declare function createReducerState(): ReducerState;
501
+ /**
502
+ * Applies one reducer delta. `latest_model_change` overwrites the nearest
503
+ * event's `{ordinal, role}`; `ttsr_injection.count` replaces the prior value
504
+ * (authoritative `buildSessionContext`, latest-wins).
505
+ */
506
+ export declare function applyReducerDelta(state: ReducerState, delta: ReducerDelta): ReducerState;
507
+ /**
508
+ * R1 consumer: returns `latestModelChange.role ?? "default"` when a
509
+ * `model_change` exists on the path, else `undefined`. `hasExplicitDefaultModel`
510
+ * only gates legacy assistant inference into `models.default`; it never feeds
511
+ * this getter.
512
+ */
513
+ export declare function getLastModelChangeRole(state: ReducerState): string | undefined;
514
+ /**
515
+ * Compaction fold: keeps the max-ordinal `model_change` (nearest wins) and the
516
+ * latest-ordinal TTSR state, matching the eager leaf→root first-hit semantics.
517
+ */
518
+ export declare function foldReducerStates(left: ReducerState, right: ReducerState): ReducerState;
519
+ export interface ParentChildrenIndexEntry {
520
+ parentId: string;
521
+ children: readonly string[];
522
+ }
523
+ /** Bounded parent→children index (rejects when either bound is exceeded). */
524
+ export declare class BoundedParentChildrenIndex {
525
+ private readonly childrenByParent;
526
+ private readonly maxParents;
527
+ private readonly maxChildrenPerParent;
528
+ private readonly budgetBytesValue;
529
+ private total;
530
+ constructor(options?: {
531
+ maxParents?: number;
532
+ maxChildrenPerParent?: number;
533
+ budgetBytes?: number;
534
+ });
535
+ get size(): number;
536
+ get totalBytes(): number;
537
+ get budgetBytes(): number;
538
+ add(parentId: string, childId: string): boolean;
539
+ get(parentId: string): readonly string[] | undefined;
540
+ entries(): ParentChildrenIndexEntry[];
541
+ }
542
+ /**
543
+ * Bounded labels/pins store. Accounting uses `stored bytes + map overhead`
544
+ * (resident string formulas + per-entry overhead), rejected when the labels+pins
545
+ * budget would be exceeded.
546
+ */
547
+ export declare class BoundedLabelsPinsStore {
548
+ private readonly labels;
549
+ private readonly pins;
550
+ private total;
551
+ private readonly budget;
552
+ constructor(budgetBytes?: number);
553
+ get totalBytes(): number;
554
+ get budgetBytes(): number;
555
+ setLabel(key: string, value: string): boolean;
556
+ deleteLabel(key: string): void;
557
+ setPin(key: string, value: string): boolean;
558
+ getLabel(key: string): string | undefined;
559
+ getPin(key: string): string | undefined;
560
+ labelsEntries(): ReadonlyMap<string, string>;
561
+ pinsEntries(): ReadonlyMap<string, string>;
562
+ clear(): void;
563
+ private setIn;
564
+ }
565
+ /**
566
+ * One parent→child edge plus the child's authoritative cold index fields. The
567
+ * artifact is disposable derived proof; the `.spill.idx` remains authoritative
568
+ * for entry offsets and the transcript remains authoritative for entry bytes.
569
+ */
570
+ export interface ParentBucketRecordInput {
571
+ parentId: string;
572
+ childId: string;
573
+ ordinal: number;
574
+ seq: number;
575
+ byteOffset: number;
576
+ byteLength: number;
577
+ recordDigest: string;
578
+ entryType?: string;
579
+ }
580
+ /** Committed on-disk state of one parent bucket (exact bytes). */
581
+ export interface ParentBucketCommit {
582
+ size: number;
583
+ digest: string;
584
+ complete: boolean;
585
+ }
586
+ /**
587
+ * Commit-marker binding for the disposable parent artifact. The artifact
588
+ * covers exactly the entry set of the `.spill.idx` whose digest is bound here;
589
+ * a binding mismatch fails closed to the authoritative cold scan.
590
+ */
591
+ export interface ParentArtifactCommit {
592
+ bucketCount: number;
593
+ indexDigest: string;
594
+ buckets: ParentBucketCommit[];
595
+ }
596
+ /**
597
+ * Deterministic hash-partition assignment for a parent id. Bucket files are
598
+ * append-only journals; a lookup reads exactly one bucket (bounded range read)
599
+ * and verifies each record's `parentId` to filter hash collisions.
600
+ */
601
+ export declare function parentBucketForId(parentId: string, bucketCount?: number): number;
602
+ /** Serialize one parent→child edge as a compact JSON journal line (trailing newline). */
603
+ export declare function serializeParentBucketRecord(input: ParentBucketRecordInput): string;
604
+ /** Strictly parse one parent bucket journal line; `undefined` marks the bucket corrupt. */
605
+ export declare function parseParentBucketRecord(line: string): ParentBucketRecordInput | undefined;
606
+ export interface ParentArtifactBuildResult {
607
+ metadata: ParentArtifactCommit;
608
+ /** Per-bucket records in physical (transcript) order. */
609
+ buckets: string[][];
610
+ /** Parents excluded because a bound (children/budget/parents) was exceeded. */
611
+ excludedParents: readonly string[];
612
+ }
613
+ /**
614
+ * Bounded builder for the persistent parent artifact. Parents are indexed
615
+ * fully or not at all: a parent whose children would exceed
616
+ * `PARENT_CHILDREN_MAX_CHILDREN_PER_PARENT`, whose first record would exceed
617
+ * `PARENT_CHILDREN_BUDGET_BYTES`, or that would exceed
618
+ * `PARENT_CHILDREN_MAX_PARENTS` is excluded (lookups for it fail closed), and
619
+ * its buffered records are dropped so a truncated "complete" result can never
620
+ * be published. Per-parent child order is preserved (insertion order).
621
+ */
622
+ export declare class BoundedParentArtifactBuilder {
623
+ private readonly parents;
624
+ private readonly excluded;
625
+ private readonly incompleteBuckets;
626
+ private totalBytesValue;
627
+ private readonly maxParents;
628
+ private readonly maxChildrenPerParent;
629
+ private readonly budgetBytesValue;
630
+ private readonly bucketCount;
631
+ private finished;
632
+ private capacityExhausted;
633
+ constructor(options?: {
634
+ maxParents?: number;
635
+ maxChildrenPerParent?: number;
636
+ budgetBytes?: number;
637
+ bucketCount?: number;
638
+ });
639
+ get distinctParents(): number;
640
+ get totalBytes(): number;
641
+ add(record: ParentBucketRecordInput): void;
642
+ private exclude;
643
+ finish(indexDigest: string): ParentArtifactBuildResult;
644
+ }
645
+ /** Fixed in-memory cost of the retained parent-artifact runtime state (block-cache charged). */
646
+ export declare function parentArtifactRuntimeBytes(bucketCount?: number): number;
647
+ /** Fixed partition count for the on-disk dictionary artifact (4 × 4 MiB buffers). */
648
+ export declare const DICTIONARY_PARTITION_COUNT = 4;
649
+ /** Maximum unique duplicate record ids retained as diagnostics in the meta. */
650
+ export declare const DICTIONARY_DUPLICATE_DIAGNOSTIC_MAX = 64;
651
+ /** Fixed capacity of the bounded duplicate-id detector (open addressing). */
652
+ export declare const DICTIONARY_ID_SET_CAPACITY = 1125001;
653
+ /** Maximum records the bounded duplicate-id detector can prove unique. */
654
+ export declare const DICTIONARY_ID_SET_MAX_RECORDS = 1000000;
655
+ /**
656
+ * One partition journal record. `term` is the lossless record id (never
657
+ * truncated or hash-substituted); the remaining fields are the same exact
658
+ * index fields the covered `.spill.idx` records, so a partition is a
659
+ * self-sufficient hash-partitioned flat index for one id.
660
+ */
661
+ export interface DictionaryPartitionRecord {
662
+ term: string;
663
+ dictId: number;
664
+ ordinal: number;
665
+ seq: number;
666
+ byteOffset: number;
667
+ byteLength: number;
668
+ recordDigest: string;
669
+ parentId: string | null;
670
+ entryType: string;
671
+ }
672
+ /** Serialize one dictionary partition record as a compact JSON journal line (trailing newline). */
673
+ export declare function serializeDictionaryPartitionRecord(input: DictionaryPartitionRecord): string;
674
+ /** Strictly parse one dictionary partition journal line; `undefined` marks the partition corrupt. */
675
+ export declare function parseDictionaryPartitionRecord(line: string): DictionaryPartitionRecord | undefined;
676
+ /**
677
+ * Deterministic hash-partition assignment for a record id (same FNV-1a family
678
+ * as `parentBucketForId`). The same id always lands in the same partition, so
679
+ * every occurrence of a duplicate id is confined to one journal.
680
+ */
681
+ export declare function dictionaryPartitionForId(id: string, partitionCount?: number): number;
682
+ /** Committed exact bytes of one on-disk dictionary partition. */
683
+ export interface DictionaryPartitionCommit {
684
+ size: number;
685
+ digest: string;
686
+ records: number;
687
+ complete: boolean;
688
+ }
689
+ /**
690
+ * Commit-marker binding for the disposable dictionary artifact. Every partition
691
+ * and the meta file are bound by exact size + SHA-256, and the artifact covers
692
+ * exactly the entry set of the `.spill.idx` whose digest is bound here; a
693
+ * binding mismatch fails closed to the authoritative cold scan.
694
+ */
695
+ export interface DictionaryArtifactCommit {
696
+ header: SidecarIndexHeaderV2;
697
+ /** Exact `.spill.idx` digest the artifact covers. */
698
+ indexDigest: string;
699
+ /** Per-partition exact-byte commits, one per partition file. */
700
+ partitions: DictionaryPartitionCommit[];
701
+ /** Exact bytes of the `.spill.dict-meta` file (self-referential fields omitted from the payload). */
702
+ metaSize: number;
703
+ metaDigest: string;
704
+ recordCount: number;
705
+ uniqueTerms: number;
706
+ /** Combined serialized term bytes (lossless ids). */
707
+ totalBytes: number;
708
+ /** Bounded duplicate-id diagnostics; non-empty ⇒ the artifact must never be adopted. */
709
+ duplicateIds: readonly string[];
710
+ sidecarIneligible: boolean;
711
+ }
712
+ /**
713
+ * Exact serialized bytes of the meta file. `metaSize`/`metaDigest` are omitted
714
+ * from the payload (they authenticate the bytes that omit them), so
715
+ * serialization is deterministic and self-referentiality is impossible.
716
+ */
717
+ export declare function dictionaryArtifactMetaBytes(commit: Omit<DictionaryArtifactCommit, "metaSize" | "metaDigest">): string;
718
+ /** Compute the exact meta bytes and the resulting self-authenticating commit. */
719
+ export declare function finalizeDictionaryArtifactCommit(commit: Omit<DictionaryArtifactCommit, "metaSize" | "metaDigest">): {
720
+ bytes: string;
721
+ commit: DictionaryArtifactCommit;
722
+ };
723
+ /**
724
+ * Strictly parse the exact meta bytes. The digest and size are recomputed over
725
+ * the supplied exact bytes; any structural, bound, or digest mismatch returns
726
+ * `undefined` (the artifact is then never trusted).
727
+ */
728
+ export declare function parseDictionaryArtifactCommit(bytes: string | Uint8Array): DictionaryArtifactCommit | undefined;
729
+ /** Bounded duplicate-id oracle for the streaming dictionary build. */
730
+ export interface DictionaryIdDetector {
731
+ add(id: string): "added" | "duplicate" | "full";
732
+ }
733
+ /**
734
+ * Bounded duplicate-id oracle. Small transcripts stay in a tiny exact set;
735
+ * larger transcripts promote once to the fixed-capacity 48-bit hash table.
736
+ * A hash collision can only produce a spurious duplicate (fail closed), never
737
+ * miss a true duplicate.
738
+ */
739
+ export declare class BoundedDictionaryIdSet implements DictionaryIdDetector {
740
+ #private;
741
+ has(value: string): boolean;
742
+ add(value: string): "added" | "duplicate" | "full";
743
+ }
744
+ /** One record consumed by the streaming dictionary artifact builder. */
745
+ export interface DictionaryArtifactRecordInput {
746
+ id: string;
747
+ ordinal: number;
748
+ seq: number;
749
+ byteOffset: number;
750
+ byteLength: number;
751
+ recordDigest: string;
752
+ parentId: string | null;
753
+ entryType: string;
754
+ }
755
+ /**
756
+ * Persistence target for flushed partition lines. The target owns the exact
757
+ * bytes on disk and their running digests (it must keep per-partition hashes
758
+ * for append-time rebinding); the builder never retains flushed lines.
759
+ */
760
+ export interface DictionaryArtifactFlushTarget {
761
+ /** Append `lines` to `partition`'s journal, fsyncing before returning false on failure. */
762
+ writePartitionLines(partition: number, lines: readonly string[]): boolean;
763
+ /** Exact-byte commit of one partition as currently persisted. */
764
+ getPartitionCommit(partition: number): DictionaryPartitionCommit;
765
+ }
766
+ export type DictionaryArtifactAddResult = {
767
+ kind: "ok";
768
+ } | {
769
+ kind: "budget_exceeded";
770
+ peakBytes: number;
771
+ budgetBytes: number;
772
+ } | {
773
+ kind: "flush_failed";
774
+ };
775
+ export type DictionaryArtifactBuildResult = {
776
+ kind: "ok";
777
+ commit: DictionaryArtifactCommit;
778
+ stats: DictionaryBuildStats;
779
+ } | {
780
+ kind: "budget_exceeded";
781
+ peakBytes: number;
782
+ budgetBytes: number;
783
+ } | {
784
+ kind: "flush_failed";
785
+ };
786
+ export interface DictionaryArtifactBuildOptions {
787
+ peakBudgetBytes?: number;
788
+ partitionBufferBytes?: number;
789
+ journalBytes?: number;
790
+ partitionCount?: number;
791
+ detector?: DictionaryIdDetector;
792
+ /** Persistence target for flushed partition lines; required for `add`/`finish`. */
793
+ target?: DictionaryArtifactFlushTarget;
794
+ }
795
+ /**
796
+ * Streaming dictionary artifact builder with a deterministic 20 MiB build peak
797
+ * (4 × `partitionBufferBytes` partition buffers + one `journalBytes` flush
798
+ * buffer). Consumes records one at a time, flushes full partition buffers
799
+ * through the journal to the flush target, and never retains the complete
800
+ * dictionary object/JSON graph. Record ids are stored losslessly as terms.
801
+ * Duplicate record ids mark the artifact `sidecarIneligible` (never adopted;
802
+ * the session falls back to eager) while the build still completes
803
+ * deterministically.
804
+ */
805
+ export declare class BoundedDictionaryArtifactBuilder {
806
+ private readonly buffers;
807
+ private readonly bufferBytes;
808
+ private journal;
809
+ private journalBytes;
810
+ private journalPartition;
811
+ private readonly partitionCountValue;
812
+ private readonly partitionBufferBytesValue;
813
+ private readonly journalBytesValue;
814
+ private readonly peakBudgetValue;
815
+ private readonly detector;
816
+ private readonly target;
817
+ private readonly duplicateIds;
818
+ private duplicateIdBytes;
819
+ private uniqueTerms;
820
+ private totalTermBytes;
821
+ private recordCount;
822
+ private peakBytesValue;
823
+ private sidecarIneligible;
824
+ private flushFailed;
825
+ private finished;
826
+ constructor(options?: DictionaryArtifactBuildOptions);
827
+ get peakBytes(): number;
828
+ /** Consumes one record. The build peak stays within the 20 MiB buffer model. */
829
+ add(record: DictionaryArtifactRecordInput): DictionaryArtifactAddResult;
830
+ /** Flush every buffer, read the exact partition commits from the target, and finalize. */
831
+ finish(sessionId: string, indexDigest: string): DictionaryArtifactBuildResult;
832
+ /** Moves one partition's buffer into the journal (≤ 4 MiB) and flushes it to the target. */
833
+ private flushPartition;
834
+ private flushJournal;
835
+ private updatePeak;
836
+ }
837
+ /** Fixed in-memory cost of the retained dictionary-artifact runtime state (block-cache charged). */
838
+ export declare function dictionaryArtifactRuntimeBytes(partitionCount?: number): number;
839
+ /**
840
+ * One demoted provider-state value descriptor: exact ordinal, kind, byte
841
+ * location, and digest inside the `.spill.metadata-delta` section. The value
842
+ * bytes are persisted before demotion, so a descriptor always points at
843
+ * durable authenticated bytes.
844
+ */
845
+ export interface MetadataDeltaValue {
846
+ /** Provider-state key (`providerStateEntryKey` semantics: type or `type:role`). */
847
+ key: string;
848
+ /** Entry type of the demoted value. */
849
+ kind: string;
850
+ /** Exact transcript ordinal of the demoted entry. */
851
+ ordinal: number;
852
+ /** Position in the build's ordered provider list (reopen merge slot). */
853
+ position: number;
854
+ offset: number;
855
+ length: number;
856
+ sha256: string;
857
+ }
858
+ /** Commit-marker binding for the disposable metadata-delta section. */
859
+ export interface MetadataDeltaArtifactCommit {
860
+ /** Exact `.spill.idx` digest the sidecar set was built with. */
861
+ indexDigest: string;
862
+ /** Exact bytes of the `.spill.metadata-delta` file. */
863
+ size: number;
864
+ sha256: string;
865
+ values: readonly MetadataDeltaValue[];
866
+ }
867
+ /** Strict descriptor validation (any ambiguity fails closed). */
868
+ export declare function isValidMetadataDeltaValue(value: unknown): value is MetadataDeltaValue;
869
+ /** Strict commit-binding validation: bounded values, unique keys/positions, in-range offsets. */
870
+ export declare function isValidMetadataDeltaCommit(value: unknown): value is MetadataDeltaArtifactCommit;
871
+ /** Compute the descriptor for one value whose exact line bytes are about to be persisted. */
872
+ export declare function metadataDeltaValueDescriptor(input: {
873
+ key: string;
874
+ kind: string;
875
+ ordinal: number;
876
+ position: number;
877
+ offset: number;
878
+ lineBytes: Uint8Array;
879
+ }): MetadataDeltaValue;
880
+ /** Resident bytes of one retained metadata-delta descriptor (key + 24 B descriptor + object). */
881
+ export declare function metadataDeltaDescriptorResidentBytes(key: string): number;