pi-mega-compact 0.16.2 → 0.17.0

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 (168) hide show
  1. package/dist/config/vector-cortex.js +81 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry-ext.js +30 -0
  3. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +4 -14
  4. package/dist/extensions/dashboard-server/api-contracts/vector-cortex.js +12 -0
  5. package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +10 -0
  6. package/dist/extensions/dashboard-server/routes-vector-cortex.js +292 -0
  7. package/dist/extensions/dashboard-server/routes.js +1 -0
  8. package/dist/extensions/dashboard-server/server.js +9 -1
  9. package/dist/extensions/mega-events/context-handler.js +11 -0
  10. package/dist/extensions/mega-runtime/vector-cortex-ledger.js +131 -0
  11. package/dist/extensions/mega-runtime/vector-cortex-safety.js +100 -0
  12. package/dist/src/config/vector-cortex.js +81 -0
  13. package/dist/src/config.js +6 -0
  14. package/dist/src/httpEmbedder.js +136 -1
  15. package/dist/src/vector-cortex/eval/annotations.js +41 -0
  16. package/dist/src/vector-cortex/eval/metrics.js +86 -0
  17. package/dist/src/vector-cortex/eval/observer.js +61 -0
  18. package/dist/src/vector-cortex/eval/persist.js +67 -0
  19. package/dist/src/vector-cortex/eval/reader.js +29 -0
  20. package/dist/src/vector-cortex/eval/types.js +33 -0
  21. package/dist/src/vector-cortex/ledger/adapter.js +55 -0
  22. package/dist/src/vector-cortex/ledger/compat-journal.js +206 -0
  23. package/dist/src/vector-cortex/ledger/emit.js +53 -0
  24. package/dist/src/vector-cortex/ledger/event-codec.js +72 -0
  25. package/dist/src/vector-cortex/ledger/event-codecB.js +76 -0
  26. package/dist/src/vector-cortex/ledger/sqlite.js +200 -0
  27. package/dist/src/vector-cortex/ledger/store.js +184 -0
  28. package/dist/src/vector-cortex/ledger/types.js +52 -0
  29. package/dist/src/vector-cortex/ledger/validator.js +117 -0
  30. package/dist/src/vector-cortex/migrations/effective-cut-v2.js +103 -0
  31. package/dist/src/vector-cortex/migrations/occurrence-v2.js +139 -0
  32. package/dist/src/vector-cortex/replay/cut.js +123 -0
  33. package/dist/src/vector-cortex/replay/emit.js +58 -0
  34. package/dist/src/vector-cortex/replay/replay.js +260 -0
  35. package/dist/src/vector-cortex/replay/replayB.js +73 -0
  36. package/dist/src/vector-cortex/replay/types.js +52 -0
  37. package/dist/src/vector-cortex/resilience/breaker-core.js +359 -0
  38. package/dist/src/vector-cortex/resilience/breaker.js +12 -0
  39. package/dist/src/vector-cortex/resilience/emit.js +56 -0
  40. package/dist/src/vector-cortex/resilience/spool-core.js +347 -0
  41. package/dist/src/vector-cortex/resilience/spool.js +12 -0
  42. package/dist/src/vector-cortex/resilience/types.js +32 -0
  43. package/dist/vector-cortex/eval/annotations.js +41 -0
  44. package/dist/vector-cortex/eval/metrics.js +86 -0
  45. package/dist/vector-cortex/eval/observer.js +61 -0
  46. package/dist/vector-cortex/eval/persist.js +67 -0
  47. package/dist/vector-cortex/eval/reader.js +29 -0
  48. package/dist/vector-cortex/eval/types.js +33 -0
  49. package/dist/vector-cortex/ledger/adapter.js +55 -0
  50. package/dist/vector-cortex/ledger/compat-journal.js +206 -0
  51. package/dist/vector-cortex/ledger/emit.js +53 -0
  52. package/dist/vector-cortex/ledger/event-codec.js +72 -0
  53. package/dist/vector-cortex/ledger/event-codecB.js +76 -0
  54. package/dist/vector-cortex/ledger/sqlite.js +200 -0
  55. package/dist/vector-cortex/ledger/store.js +184 -0
  56. package/dist/vector-cortex/ledger/types.js +52 -0
  57. package/dist/vector-cortex/ledger/validator.js +117 -0
  58. package/dist/vector-cortex/migrations/effective-cut-v2.js +103 -0
  59. package/dist/vector-cortex/migrations/occurrence-v2.js +139 -0
  60. package/dist/vector-cortex/replay/cut.js +123 -0
  61. package/dist/vector-cortex/replay/emit.js +58 -0
  62. package/dist/vector-cortex/replay/replay.js +260 -0
  63. package/dist/vector-cortex/replay/replayB.js +73 -0
  64. package/dist/vector-cortex/replay/types.js +52 -0
  65. package/dist/vector-cortex/resilience/breaker-core.js +359 -0
  66. package/dist/vector-cortex/resilience/breaker.js +12 -0
  67. package/dist/vector-cortex/resilience/emit.js +56 -0
  68. package/dist/vector-cortex/resilience/spool-core.js +347 -0
  69. package/dist/vector-cortex/resilience/spool.js +12 -0
  70. package/dist/vector-cortex/resilience/types.js +32 -0
  71. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js → AreaChart-CAnWomhw.js} +2 -2
  72. package/extensions/dashboard-client/dist/assets/{AreaChart-Y_X0Bbjd.js.map → AreaChart-CAnWomhw.js.map} +1 -1
  73. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js → BarChart-lZ2-s71k.js} +2 -2
  74. package/extensions/dashboard-client/dist/assets/{BarChart-B9XDb-Ob.js.map → BarChart-lZ2-s71k.js.map} +1 -1
  75. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js → CacheTab-BL2Thh66.js} +2 -2
  76. package/extensions/dashboard-client/dist/assets/{CacheTab-BUGVb1G1.js.map → CacheTab-BL2Thh66.js.map} +1 -1
  77. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js → EventsTab-DEh2Z9fp.js} +2 -2
  78. package/extensions/dashboard-client/dist/assets/{EventsTab-dOqLYTsy.js.map → EventsTab-DEh2Z9fp.js.map} +1 -1
  79. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js → HealthTab-DTwKclxk.js} +2 -2
  80. package/extensions/dashboard-client/dist/assets/{HealthTab-c2vezKg1.js.map → HealthTab-DTwKclxk.js.map} +1 -1
  81. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js → MaintenanceTab-DDfBEdbX.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{MaintenanceTab-bQYHIcdx.js.map → MaintenanceTab-DDfBEdbX.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js → MemoryMapTab-Dgfmg-0Q.js} +2 -2
  84. package/extensions/dashboard-client/dist/assets/{MemoryMapTab-2L8pH6y2.js.map → MemoryMapTab-Dgfmg-0Q.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js → MetricsTab-DOcMm0Tx.js} +2 -2
  86. package/extensions/dashboard-client/dist/assets/{MetricsTab-CzUx1jpo.js.map → MetricsTab-DOcMm0Tx.js.map} +1 -1
  87. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js → OverviewTab-DHT5aoHg.js} +2 -2
  88. package/extensions/dashboard-client/dist/assets/{OverviewTab-eCNoaclv.js.map → OverviewTab-DHT5aoHg.js.map} +1 -1
  89. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js → ReposTab-CGAmtR_c.js} +2 -2
  90. package/extensions/dashboard-client/dist/assets/{ReposTab-tb0IW3NF.js.map → ReposTab-CGAmtR_c.js.map} +1 -1
  91. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js → SessionsTab-BIgx6iKT.js} +2 -2
  92. package/extensions/dashboard-client/dist/assets/{SessionsTab-C-IoLw5h.js.map → SessionsTab-BIgx6iKT.js.map} +1 -1
  93. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js → SetupTab-ADghKuEI.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{SetupTab-BvmFi8gf.js.map → SetupTab-ADghKuEI.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js → TimeSavedCard-C33UiiOM.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-DBH-cOlA.js.map → TimeSavedCard-C33UiiOM.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js → TurnsTab-DdbL9wPN.js} +2 -2
  98. package/extensions/dashboard-client/dist/assets/{TurnsTab-DBMgmvnX.js.map → TurnsTab-DdbL9wPN.js.map} +1 -1
  99. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js +2 -0
  100. package/extensions/dashboard-client/dist/assets/VectorCortexTab-Cl1pbEdR.js.map +1 -0
  101. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js → WikiTab-Ch00OFKs.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{WikiTab-1LcQrXk3.js.map → WikiTab-Ch00OFKs.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js → button-ByeEdo2g.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{button-DtEsZTUE.js.map → button-ByeEdo2g.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js → card-DesoLXvc.js} +2 -2
  106. package/extensions/dashboard-client/dist/assets/{card-DcfTmw-4.js.map → card-DesoLXvc.js.map} +1 -1
  107. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js → generateCategoricalChart-CCAB7Iz4.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-BsIxUDyw.js.map → generateCategoricalChart-CCAB7Iz4.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/assets/{index-B93cewiX.js → index-B6cgyAPr.js} +34 -29
  110. package/extensions/dashboard-client/dist/assets/index-B6cgyAPr.js.map +1 -0
  111. package/extensions/dashboard-client/dist/assets/index-CfvziyBi.css +1 -0
  112. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js → switch-DIZC-oe-.js} +2 -2
  113. package/extensions/dashboard-client/dist/assets/{switch-Bowd7jCD.js.map → switch-DIZC-oe-.js.map} +1 -1
  114. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js → toggle-CIoWaH2e.js} +2 -2
  115. package/extensions/dashboard-client/dist/assets/{toggle-Bo7kJZ4u.js.map → toggle-CIoWaH2e.js.map} +1 -1
  116. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js → useSSE-Bv4943l1.js} +2 -2
  117. package/extensions/dashboard-client/dist/assets/{useSSE-zNhb0c3u.js.map → useSSE-Bv4943l1.js.map} +1 -1
  118. package/extensions/dashboard-client/dist/index.html +2 -2
  119. package/extensions/dashboard-client/src/App.tsx +2 -0
  120. package/extensions/dashboard-client/src/api/vector-cortex.ts +54 -0
  121. package/extensions/dashboard-client/src/tabs/VectorCortexTab.tsx +272 -0
  122. package/extensions/dashboard-client/src/tabs/registry.ts +4 -1
  123. package/extensions/dashboard-client/src/types/vector-cortex.ts +84 -0
  124. package/extensions/dashboard-server/api-contracts/endpoints/registry-ext.ts +67 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +4 -21
  126. package/extensions/dashboard-server/api-contracts/index.ts +3 -0
  127. package/extensions/dashboard-server/api-contracts/vector-cortex.ts +136 -0
  128. package/extensions/dashboard-server/routes-rag-settings-helpers.ts +35 -0
  129. package/extensions/dashboard-server/routes-vector-cortex.ts +332 -0
  130. package/extensions/dashboard-server/routes.ts +6 -0
  131. package/extensions/dashboard-server/server.ts +8 -0
  132. package/extensions/mega-events/context-handler.ts +16 -0
  133. package/extensions/mega-runtime/vector-cortex-ledger.ts +165 -0
  134. package/extensions/mega-runtime/vector-cortex-safety.ts +181 -0
  135. package/package.json +2 -1
  136. package/src/config/vector-cortex.ts +87 -0
  137. package/src/config.ts +27 -0
  138. package/src/httpEmbedder.ts +134 -1
  139. package/src/vector-cortex/eval/annotations.ts +70 -0
  140. package/src/vector-cortex/eval/metrics.ts +113 -0
  141. package/src/vector-cortex/eval/observer.ts +86 -0
  142. package/src/vector-cortex/eval/persist.ts +73 -0
  143. package/src/vector-cortex/eval/reader.ts +45 -0
  144. package/src/vector-cortex/eval/types.ts +89 -0
  145. package/src/vector-cortex/ledger/adapter.ts +71 -0
  146. package/src/vector-cortex/ledger/compat-journal.ts +234 -0
  147. package/src/vector-cortex/ledger/emit.ts +86 -0
  148. package/src/vector-cortex/ledger/event-codec.ts +79 -0
  149. package/src/vector-cortex/ledger/event-codecB.ts +87 -0
  150. package/src/vector-cortex/ledger/sqlite.ts +265 -0
  151. package/src/vector-cortex/ledger/store.ts +380 -0
  152. package/src/vector-cortex/ledger/types.ts +141 -0
  153. package/src/vector-cortex/ledger/validator.ts +124 -0
  154. package/src/vector-cortex/migrations/effective-cut-v2.ts +149 -0
  155. package/src/vector-cortex/migrations/occurrence-v2.ts +192 -0
  156. package/src/vector-cortex/replay/cut.ts +169 -0
  157. package/src/vector-cortex/replay/emit.ts +89 -0
  158. package/src/vector-cortex/replay/replay.ts +308 -0
  159. package/src/vector-cortex/replay/replayB.ts +97 -0
  160. package/src/vector-cortex/replay/types.ts +145 -0
  161. package/src/vector-cortex/resilience/breaker-core.ts +444 -0
  162. package/src/vector-cortex/resilience/breaker.ts +22 -0
  163. package/src/vector-cortex/resilience/emit.ts +99 -0
  164. package/src/vector-cortex/resilience/spool-core.ts +411 -0
  165. package/src/vector-cortex/resilience/spool.ts +22 -0
  166. package/src/vector-cortex/resilience/types.ts +158 -0
  167. package/extensions/dashboard-client/dist/assets/index-B93cewiX.js.map +0 -1
  168. package/extensions/dashboard-client/dist/assets/index-C_tCbOI0.css +0 -1
@@ -0,0 +1,308 @@
1
+ /**
2
+ * vector-cortex/replay/replay.ts — ReplayCutV2 replay scan (VC0B).
3
+ *
4
+ * Scans EventV2 occurrences in ascending `(seq, eventId bytewise UTF-8)`,
5
+ * computes the v2 effective cut (M3 min-of-three + pair retreat + anchor floor),
6
+ * and returns the replayed prefix bytes joined in strict source order. Before
7
+ * returning bytes it reports any CUT_TOOL_PAIR_SPLIT / CUT_ANCHOR_FLOOR retreat
8
+ * via the injected emit callback. Hard invariants are enforced zero-tolerance:
9
+ * the output is a source-order prefix with no orphan tool event and no split
10
+ * pair (reordered === 0, orphanToolEvents === 0, splitPairs-after-cut === 0).
11
+ *
12
+ * Mode A uses the v2 effective-cut calculator; mode B is a plain sequential
13
+ * boundary scan with no precomputed index; mode C leaves the host transcript
14
+ * unchanged and returns a zero-byte report with the derived high-water frozen.
15
+ *
16
+ * Pure wrt inputs, emits structured events via the replay emit seam
17
+ * (`src/vector-cortex/replay/emit.ts`) — no console, no network (PREVENT-PI-004).
18
+ */
19
+
20
+ import { computeEffectiveCutV2 } from "./cut.js";
21
+ import { computeEffectiveCutV2B } from "./replayB.js";
22
+ import { createReplayReporter, type ReplayReporter } from "./emit.js";
23
+ import type {
24
+ ReplayCutV2,
25
+ ReplayOccurrenceV2,
26
+ ReplayReportV2,
27
+ ReplayRetreatRecord,
28
+ ReplayToolPair,
29
+ } from "./types.js";
30
+
31
+ export interface ReplayV2Options {
32
+ readonly sessionId: string;
33
+ /** Occurrences in ascending (seq, eventId bytewise UTF-8). */
34
+ readonly occurrences: readonly ReplayOccurrenceV2[];
35
+ readonly requestedSeq: bigint;
36
+ readonly committedSeq: bigint;
37
+ readonly capturedHighWater: bigint;
38
+ readonly anchorFloor: bigint;
39
+ readonly mode?: "A" | "B" | "C";
40
+ /** Structured event reporter (ts+event); optional, best-effort. */
41
+ readonly reporter?: ReplayReporter;
42
+ }
43
+
44
+ /** The pair map is per-session; a tool result references one earlier call by id. */
45
+ export function extractToolPairs(
46
+ occurrences: readonly ReplayOccurrenceV2[],
47
+ ): ReplayToolPair[] {
48
+ const calls = new Map<string, bigint>();
49
+ const pairs: ReplayToolPair[] = [];
50
+ let maxSeq = -1n;
51
+ for (const o of occurrences) {
52
+ if (o.seq > maxSeq) maxSeq = o.seq;
53
+ if (o.role === "tool" && o.toolCallId !== undefined) {
54
+ const callSeq = calls.get(o.toolCallId);
55
+ if (callSeq !== undefined && callSeq < o.seq) {
56
+ pairs.push({ callSeq, resultSeq: o.seq });
57
+ // Matched: remove so only genuinely-unclosed calls remain for the
58
+ // synthetic dangling-call pairs below (otherwise a matched call would
59
+ // also get a synthetic pair and force an over-retreat).
60
+ calls.delete(o.toolCallId);
61
+ }
62
+ continue;
63
+ }
64
+ // A call is an assistant event carrying a tool call id in EventV2; here we
65
+ // treat an assistant occurrence with a toolCallId as the CALL side.
66
+ if (o.toolCallId !== undefined) {
67
+ calls.set(o.toolCallId, o.seq);
68
+ }
69
+ }
70
+ // VC0B-I11: a call with NO observed result (dangling / unclosed) must still
71
+ // bound the cut. Mode B's open-call tracker retreats below such a call; to
72
+ // keep A and B in lockstep we surface each dangling call as a synthetic pair
73
+ // whose "result" lies one past the last observed seq. Any cut at/after the
74
+ // call then splits it, so A retreats to the call boundary exactly like B —
75
+ // and the synthetic unmatched marker also makes finalizeReplay count the
76
+ // dangling call as an orphan instead of reporting a false 0.
77
+ const pastEnd = maxSeq + 1n;
78
+ for (const callSeq of calls.values()) {
79
+ pairs.push({ callSeq, resultSeq: pastEnd });
80
+ }
81
+ return pairs;
82
+ }
83
+
84
+ /**
85
+ * Largest pair-safe seq <= `ceiling` (a cut at e keeps seq <= e and must not
86
+ * split any pair where callSeq <= e < resultSeq). Sequential boundary scan
87
+ * (mode B); no aggregate index required.
88
+ */
89
+ export function largestPairSafeSeq(
90
+ pairs: readonly ReplayToolPair[],
91
+ ceiling: bigint,
92
+ ): bigint {
93
+ let e = ceiling;
94
+ for (;;) {
95
+ let blocked = false;
96
+ for (const p of pairs) {
97
+ if (p.callSeq <= e && e < p.resultSeq) {
98
+ e = p.callSeq - 1n;
99
+ blocked = true;
100
+ break;
101
+ }
102
+ }
103
+ if (!blocked) return e;
104
+ }
105
+ }
106
+
107
+ /** Ascending (seq, eventId) comparator over two occurrences. */
108
+ export function compareOccurrences(a: ReplayOccurrenceV2, b: ReplayOccurrenceV2): number {
109
+ if (a.seq !== b.seq) return a.seq < b.seq ? -1 : 1;
110
+ const ea = a.eventId;
111
+ const eb = b.eventId;
112
+ if (ea < eb) return -1;
113
+ if (ea > eb) return 1;
114
+ return 0;
115
+ }
116
+
117
+ /**
118
+ * Run the v2 replay scan and produce a ReplayReportV2 plus the replayed bytes.
119
+ */
120
+ export function runReplayV2(
121
+ options: ReplayV2Options,
122
+ ): { report: ReplayReportV2; bytes: Uint8Array } {
123
+ const { sessionId, occurrences, requestedSeq, committedSeq, capturedHighWater, anchorFloor } = options;
124
+ const mode = options.mode ?? "A";
125
+ const reporter = options.reporter ?? createReplayReporter();
126
+
127
+ // Mode C: unchanged host transcript — derived high-water frozen, zero bytes.
128
+ if (mode === "C") {
129
+ const cut = {
130
+ requestedSeq,
131
+ boundarySafeSeq: requestedSeq,
132
+ committedSeq,
133
+ capturedHighWater,
134
+ effectiveSeq: anchorFloor,
135
+ };
136
+ reporter.highWaterFrozen({
137
+ session: sessionId,
138
+ committedSeq: committedSeq.toString(),
139
+ frozenHighWater: capturedHighWater.toString(),
140
+ });
141
+ return {
142
+ report: zeroReport(cut, "C", []),
143
+ bytes: new Uint8Array(0),
144
+ };
145
+ }
146
+
147
+ const pairs = extractToolPairs(occurrences);
148
+
149
+ // Mode B: genuinely independent deterministic local cut (streams the bytes,
150
+ // shares no subroutine with A). Mode A: optimized pair-list + min-of-three.
151
+ if (mode === "B") {
152
+ const b = computeEffectiveCutV2B({
153
+ occurrences,
154
+ requestedSeq,
155
+ committedSeq,
156
+ capturedHighWater,
157
+ anchorFloor,
158
+ });
159
+ const bCut: ReplayCutV2 = {
160
+ requestedSeq,
161
+ boundarySafeSeq: b.boundarySafeSeq,
162
+ committedSeq,
163
+ capturedHighWater,
164
+ effectiveSeq: b.effectiveSeq,
165
+ };
166
+ return finalizeReplay({
167
+ occurrences,
168
+ cut: bCut,
169
+ mode: "B",
170
+ retreats: [],
171
+ pairs,
172
+ });
173
+ }
174
+
175
+ // Boundary-safe cap: largest pair-safe seq (sequential scan; mode A).
176
+ const boundarySafeSeq = largestPairSafeSeq(pairs, requestedSeq);
177
+
178
+ // effective cut: min-of-three + pair retreat + anchor floor.
179
+ const effectiveResult = computeEffectiveCutV2({
180
+ requestedSeq,
181
+ boundarySafeSeq,
182
+ committedSeq,
183
+ capturedHighWater,
184
+ anchorFloor,
185
+ pairs,
186
+ });
187
+ const { cut, retreats } = effectiveResult;
188
+
189
+ for (const r of retreats) {
190
+ reporter.cutRetreat({
191
+ session: sessionId,
192
+ code: r.code,
193
+ fromSeq: r.fromSeq.toString(),
194
+ toSeq: r.toSeq.toString(),
195
+ });
196
+ }
197
+
198
+ return finalizeReplay({
199
+ occurrences,
200
+ cut,
201
+ mode: "A",
202
+ retreats,
203
+ pairs,
204
+ });
205
+ }
206
+
207
+ /** Shared post-cut scan: strict (seq,eventId) prefix + invariant counting. */
208
+ function finalizeReplay(opts: {
209
+ occurrences: readonly ReplayOccurrenceV2[];
210
+ cut: ReplayCutV2;
211
+ mode: "A" | "B";
212
+ retreats: readonly ReplayRetreatRecord[];
213
+ pairs: readonly ReplayToolPair[];
214
+ }): { report: ReplayReportV2; bytes: Uint8Array } {
215
+ const { occurrences, cut, mode, retreats, pairs } = opts;
216
+ const effective = cut.effectiveSeq;
217
+
218
+ // Strict ascending source order scan up to the effective cut.
219
+ const kept: ReplayOccurrenceV2[] = [];
220
+ let reordered = 0;
221
+ let scanned = 0;
222
+ let bytesTotal = 0;
223
+ const keptBytes: Uint8Array[] = [];
224
+
225
+ let prev: ReplayOccurrenceV2 | null = null;
226
+ const effectiveBig = effective;
227
+ for (const o of occurrences) {
228
+ scanned += 1;
229
+ if (compareOccurrences(prev ?? o, o) > 0) reordered += 1;
230
+ prev = o;
231
+ if (o.seq > effectiveBig) break;
232
+ kept.push(o);
233
+ if (o.originalBytes) {
234
+ keptBytes.push(o.originalBytes);
235
+ bytesTotal += o.originalBytes.length;
236
+ }
237
+ }
238
+
239
+ // No orphan tool event: every kept tool RESULT must have its CALL kept too.
240
+ const keptCallIds = new Set<string>();
241
+ for (const o of kept) {
242
+ if (o.toolCallId !== undefined && o.role !== "tool") {
243
+ keptCallIds.add(o.toolCallId);
244
+ }
245
+ }
246
+ let orphanToolEvents = 0;
247
+ for (const o of kept) {
248
+ if (o.role === "tool" && o.toolCallId !== undefined && !keptCallIds.has(o.toolCallId)) {
249
+ orphanToolEvents += 1;
250
+ }
251
+ }
252
+ // Pair-splits that survived retreat (must be 0 for a correct boundary-safe cut).
253
+ let splitPairs = 0;
254
+ for (const p of pairs) {
255
+ if (p.callSeq <= effectiveBig && effectiveBig < p.resultSeq) splitPairs += 1;
256
+ }
257
+
258
+ const bytes = concatBytes(keptBytes);
259
+ const report: ReplayReportV2 = {
260
+ cut,
261
+ mode,
262
+ counts: {
263
+ scanned,
264
+ bytes: bytesTotal,
265
+ replayed: kept.length,
266
+ splitPairs,
267
+ anchorFloorCuts: retreats.filter((r) => r.code === "CUT_ANCHOR_FLOOR").length,
268
+ lowestSourceCuts: retreats.filter((r) => r.code === "CUT_LOWEST_SOURCE_ORDER").length,
269
+ reordered,
270
+ orphanToolEvents,
271
+ },
272
+ retreats,
273
+ };
274
+ return { report, bytes };
275
+ }
276
+
277
+ function concatBytes(chunks: readonly Uint8Array[]): Uint8Array {
278
+ const total = chunks.reduce((a, c) => a + c.length, 0);
279
+ const out = new Uint8Array(total);
280
+ let off = 0;
281
+ for (const c of chunks) {
282
+ out.set(c, off);
283
+ off += c.length;
284
+ }
285
+ return out;
286
+ }
287
+
288
+ function zeroReport(
289
+ cut: ReplayReportV2["cut"],
290
+ mode: "A" | "B" | "C",
291
+ retreats: ReplayReportV2["retreats"],
292
+ ): ReplayReportV2 {
293
+ return {
294
+ cut,
295
+ mode,
296
+ counts: {
297
+ scanned: 0,
298
+ bytes: 0,
299
+ replayed: 0,
300
+ splitPairs: 0,
301
+ anchorFloorCuts: 0,
302
+ lowestSourceCuts: 0,
303
+ reordered: 0,
304
+ orphanToolEvents: 0,
305
+ },
306
+ retreats,
307
+ };
308
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * vector-cortex/replay/replayB.ts — Mode B: independent deterministic replay cut
3
+ * (VC0B, TRIAD_RESILIENCE).
4
+ *
5
+ * Mode B is the deliberately SLOW-but-simple local algorithm. It is a genuine
6
+ * counterpart to mode A (`./cut.ts`): it shares NO subroutine with A — no
7
+ * `computeEffectiveCutV2`, no `largestPairSafeSeq`, no `cutIsPairSafe`, no
8
+ * min-of-three shortcut. Instead it walks the authoritative observed occurrence
9
+ * bytes in ascending `(seq, eventId)` order from seq 0 and, at EVERY step,
10
+ * checks tool-pair completeness (via its own open-call tracker built from the
11
+ * bytes) plus the authority high-water caps and the anchor floor.
12
+ *
13
+ * Contract parity: the v2 effective cut is a well-defined pure function of the
14
+ * stream and the authority inputs, so two independent implementations of the
15
+ * same rule MUST agree. A optimizes via a precomputed pair list + min-of-three +
16
+ * retreat; B brute-forces the LARGEST seq that satisfies every constraint by
17
+ * scanning — slower (O(n·open)), which is exactly its purpose: when the breaker
18
+ * flips to B it catches bugs in A because the two share no code.
19
+ *
20
+ * Pure/deterministic, no network, no side effects (PREVENT-PI-004).
21
+ */
22
+
23
+ import type { ReplayOccurrenceV2 } from "./types.js";
24
+
25
+ export interface ReplayBCut {
26
+ /** Largest pair-complete seq <= requestedSeq (comparable to A's boundary). */
27
+ readonly boundarySafeSeq: bigint;
28
+ /** The cut actually applied: largest pair-complete seq <= the authority cap,
29
+ * never below the anchor floor. */
30
+ readonly effectiveSeq: bigint;
31
+ }
32
+
33
+ export interface ReplayBInput {
34
+ /** Occurrences in ascending (seq, eventId bytewise UTF-8) source order. */
35
+ readonly occurrences: readonly ReplayOccurrenceV2[];
36
+ readonly requestedSeq: bigint;
37
+ /** Contiguous durable authority high-water. */
38
+ readonly committedSeq: bigint;
39
+ /** Derived high-water captured/frozen at replay start. */
40
+ readonly capturedHighWater: bigint;
41
+ /** Recent-anchor floor (inclusive lower bound the cut never crosses). */
42
+ readonly anchorFloor: bigint;
43
+ }
44
+
45
+ /**
46
+ * Independent mode-B effective cut. Walks the occurrence bytes once, tracking
47
+ * open tool calls from the bytes themselves (no extracted pair list). The
48
+ * boundary-safe seq is the largest pair-complete seq <= requestedSeq; the
49
+ * effective seq is the largest pair-complete seq that also lies within the
50
+ * authority ceiling (requested/committed/captured, applied as independent
51
+ * sequential caps — no shared min-of-three) and at/above the anchor floor.
52
+ */
53
+ export function computeEffectiveCutV2B(input: ReplayBInput): ReplayBCut {
54
+ const { occurrences, requestedSeq, committedSeq, capturedHighWater, anchorFloor } = input;
55
+
56
+ // Independent sequential authority ceiling (NOT A's minOfCapped).
57
+ let ceiling = requestedSeq;
58
+ if (ceiling > committedSeq) ceiling = committedSeq;
59
+ if (ceiling > capturedHighWater) ceiling = capturedHighWater;
60
+
61
+ // Open tool calls awaiting a result (toolCallId -> callSeq), built from bytes.
62
+ const open = new Map<string, bigint>();
63
+ let boundarySafeSeq = 0n; // largest pair-complete seq <= requestedSeq
64
+ let effectiveSeq = 0n; // largest pair-complete seq <= ceiling (>= floor)
65
+
66
+ for (const o of occurrences) {
67
+ if (o.seq > requestedSeq) break;
68
+ const s = o.seq;
69
+
70
+ if (o.toolCallId !== undefined && o.role !== "tool") {
71
+ // A CALL: it stays open until its result is observed.
72
+ open.set(o.toolCallId, s);
73
+ } else if (o.toolCallId !== undefined && o.role === "tool") {
74
+ // Its RESULT: the pair closes.
75
+ open.delete(o.toolCallId);
76
+ }
77
+
78
+ // A cut at `s` is pair-complete iff no still-open call lies at or below `s`
79
+ // (such a call's result is beyond the cut, so keeping the call orphans it).
80
+ let complete = true;
81
+ for (const cseq of open.values()) {
82
+ if (cseq <= s) {
83
+ complete = false;
84
+ break;
85
+ }
86
+ }
87
+ if (!complete) continue;
88
+
89
+ boundarySafeSeq = s;
90
+ if (s <= ceiling && s >= anchorFloor) effectiveSeq = s;
91
+ }
92
+
93
+ // Anchor floor is an absolute lower bound (parity with A's final clamp).
94
+ if (effectiveSeq < anchorFloor) effectiveSeq = anchorFloor;
95
+
96
+ return { boundarySafeSeq, effectiveSeq };
97
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * vector-cortex/replay/types.ts — ReplayCutV2 / ReplayReportV2 contract types
3
+ * and the registered CUT / M3 conformance ID ranges.
4
+ *
5
+ * Owned by VC0B (replay correctness). Consumes reviewer-accepted predecessor
6
+ * contracts and common contracts only (EventV2 minimal shape). Pure type/schema
7
+ * definitions — no runtime network, no side effects (PREVENT-PI-004 / PREVENT-011).
8
+ */
9
+
10
+ /**
11
+ * Minimal EventV2 occurrence shape sufficient for the replay scan, consumed
12
+ * contractually from EventV2 (VC1A owns the full codec). Ordering is
13
+ * `(sessionId, seq, eventId bytewise UTF-8)`. A tool result references exactly
14
+ * one earlier CALL in the same session via `toolCallId`.
15
+ */
16
+ export interface ReplayOccurrenceV2 {
17
+ readonly sessionId: string;
18
+ /** Monotonic per-session sequence (bigint). */
19
+ readonly seq: bigint;
20
+ /** Occurrence id; bytewise UTF-8 tiebreak within equal seq. */
21
+ readonly eventId: string;
22
+ readonly role: "policy" | "user" | "assistant" | "tool";
23
+ readonly kind: string;
24
+ /** Present on a tool RESULT; names exactly one earlier CALL in this session. */
25
+ readonly toolCallId?: string;
26
+ /** Original event bytes (authoritative per EventV2); used for the byte count. */
27
+ readonly originalBytes?: Uint8Array;
28
+ }
29
+
30
+ /** A tool call/result pair in a session (call seq < result seq). */
31
+ export interface ReplayToolPair {
32
+ readonly callSeq: bigint;
33
+ readonly resultSeq: bigint;
34
+ }
35
+
36
+ /**
37
+ * ReplayCutV2 — the five authoritative values of the effective cut.
38
+ *
39
+ * `effectiveSeq` is `min(boundarySafeSeq, committedSeq, capturedHighWater)`
40
+ * (capped by `requestedSeq`), then retreated to the call boundary whenever the
41
+ * candidate intersects a tool call/result pair, then clamped to the anchor
42
+ * floor. Flags: `MEGACOMPACT_VC0B=0` routes to the legacy capped replay.
43
+ */
44
+ export interface ReplayCutV2 {
45
+ /** Caller's desired cap (the largest seq the caller asked to replay to). */
46
+ readonly requestedSeq: bigint;
47
+ /** Largest pair-safe cap that never splits a tool call/result pair. */
48
+ readonly boundarySafeSeq: bigint;
49
+ /** Contiguous durable authority high-water (min source). */
50
+ readonly committedSeq: bigint;
51
+ /** Derived high-water captured/frozen at the start of replay (min source). */
52
+ readonly capturedHighWater: bigint;
53
+ /** The resulting cut actually applied to the replay scan. */
54
+ readonly effectiveSeq: bigint;
55
+ }
56
+
57
+ /** Retreat/retraction reasons recorded on a ReplayReportV2. */
58
+ export type ReplayRetreatCode =
59
+ | "CUT_TOOL_PAIR_SPLIT"
60
+ | "CUT_ANCHOR_FLOOR"
61
+ | "CUT_LOWEST_SOURCE_ORDER";
62
+
63
+ /** A single recorded retreat (fromSeq -> toSeq with its reason). */
64
+ export interface ReplayRetreatRecord {
65
+ readonly code: ReplayRetreatCode;
66
+ readonly fromSeq: bigint;
67
+ readonly toSeq: bigint;
68
+ }
69
+
70
+ /**
71
+ * ReplayReportV2 — outcome of a replay scan. Counts are exact; the hard
72
+ * invariants (zero reordered pairs, zero orphan tool events, zero split pairs
73
+ * after the effective cut) are enforced as zero-tolerance.
74
+ */
75
+ export interface ReplayReportV2 {
76
+ readonly cut: ReplayCutV2;
77
+ /** Triad mode that produced this report: "A" | "B" | "C". */
78
+ readonly mode: "A" | "B" | "C";
79
+ readonly counts: {
80
+ /** Total occurrences scanned (ascending (seq,eventId)). */
81
+ readonly scanned: number;
82
+ /** Bytes returned (sum of replayed originalBytes lengths). */
83
+ readonly bytes: number;
84
+ /** Occurrences replayed (seq <= effectiveSeq). */
85
+ readonly replayed: number;
86
+ /** Retreats of kind CUT_TOOL_PAIR_SPLIT. */
87
+ readonly splitPairs: number;
88
+ /** Retreats of kind CUT_ANCHOR_FLOOR. */
89
+ readonly anchorFloorCuts: number;
90
+ /** Tie-break retreats of kind CUT_LOWEST_SOURCE_ORDER. */
91
+ readonly lowestSourceCuts: number;
92
+ /** Reordered (out-of-source-order) pairs — MUST be 0. */
93
+ readonly reordered: number;
94
+ /** Orphan tool events after the cut — MUST be 0. */
95
+ readonly orphanToolEvents: number;
96
+ };
97
+ /** Every retreat applied, in apply order. */
98
+ readonly retreats: readonly ReplayRetreatRecord[];
99
+ }
100
+
101
+ /**
102
+ * Registered CUT conformance ID range (CUT-001..020). The acceptance test
103
+ * reads these rows from the v2 manifest and asserts their expected bytes/results.
104
+ */
105
+ export const CUT_IDS = [
106
+ "CUT-001",
107
+ "CUT-002",
108
+ "CUT-003",
109
+ "CUT-004",
110
+ "CUT-005",
111
+ "CUT-006",
112
+ "CUT-007",
113
+ "CUT-008",
114
+ "CUT-009",
115
+ "CUT-010",
116
+ "CUT-011",
117
+ "CUT-012",
118
+ "CUT-013",
119
+ "CUT-014",
120
+ "CUT-015",
121
+ "CUT-016",
122
+ "CUT-017",
123
+ "CUT-018",
124
+ "CUT-019",
125
+ "CUT-020",
126
+ ] as const;
127
+
128
+ /**
129
+ * Registered M3 conformance ID range (M3-001..010) for the effective-cut-v2
130
+ * migration (copy/validate/switch). The migration fixtures assert that crash
131
+ * after M3 copy validation but before pointer switch retains the old cut pointer
132
+ * and resumes idempotently.
133
+ */
134
+ export const M3_IDS = [
135
+ "M3-001",
136
+ "M3-002",
137
+ "M3-003",
138
+ "M3-004",
139
+ "M3-005",
140
+ "M3-006",
141
+ "M3-007",
142
+ "M3-008",
143
+ "M3-009",
144
+ "M3-010",
145
+ ] as const;