pi-mega-compact 0.21.2 → 0.21.4
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.
- package/dist/extensions/dashboard-server/routes-rag-settings-helpers.js +1 -0
- package/dist/extensions/mega-compact-child.js +126 -0
- package/dist/extensions/mega-config.js +5 -0
- package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +12 -2
- package/dist/extensions/mega-events/context-handler/afterCompact.js +11 -0
- package/dist/extensions/mega-events/context-handler/dbMirrorAppend.js +8 -0
- package/dist/extensions/mega-events/health-handler.js +12 -2
- package/dist/extensions/mega-events/session-handlers.js +1 -0
- package/dist/extensions/mega-pipeline/compact/run.js +11 -0
- package/dist/extensions/mega-runtime/helpers.js +45 -0
- package/dist/extensions/mega-runtime/internal-errors.js +14 -0
- package/dist/extensions/mega-runtime/runtime-instrumentation.js +166 -0
- package/dist/extensions/mega-runtime/runtime.js +27 -193
- package/dist/extensions/mega-runtime/state.js +1 -1
- package/dist/extensions/mega-runtime/widget.js +1 -1
- package/dist/extensions/mega-turn-store.js +5 -0
- package/dist/src/bridge/factory.js +195 -0
- package/dist/src/bridge/types.js +10 -0
- package/dist/src/bridge.js +1 -0
- package/dist/src/contextHealth.js +13 -5
- package/dist/src/metrics/turns.js +6 -1
- package/dist/src/store/sqlite/context-health-cache-poison.js +60 -0
- package/dist/src/store/sqlite/context-health.js +6 -48
- package/dist/src/store/sqlite/embedding-cache.js +4 -0
- package/dist/src/store/sqlite/schema.js +4 -0
- package/dist/src/store/turns/memory-store/admin.js +119 -0
- package/dist/src/store/turns/memory-store/ctx.js +28 -0
- package/dist/src/store/turns/memory-store/read.js +110 -0
- package/dist/src/store/turns/memory-store/write.js +88 -0
- package/dist/src/store/turns/memory-store.js +44 -273
- package/dist/src/store/turns/migrations.js +4 -0
- package/dist/src/store/turns/schema.js +1 -0
- package/dist/src/store/turns/sqlite-store/read.js +13 -0
- package/dist/src/store/turns/sqlite-store/rows.js +4 -0
- package/dist/src/store/turns/sqlite-store/write.js +3 -3
- package/dist/src/store/turns/sqlite-store.js +4 -0
- package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js → AdminTab-C4Y0_UVx.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AdminTab-BsA8-_bX.js.map → AdminTab-C4Y0_UVx.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js → AreaChart-MCWK9rn6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AreaChart-BRHEfWhc.js.map → AreaChart-MCWK9rn6.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js → BarChart-CvSGAiWV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{BarChart-C-M0LaFc.js.map → BarChart-CvSGAiWV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js → CacheTab-DnMEVRro.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{CacheTab-Cj3n59U3.js.map → CacheTab-DnMEVRro.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js → ConfigTab-BExuZYy2.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-C016XTNC.js.map → ConfigTab-BExuZYy2.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js → EventsTab-CspsEslY.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DE29fuqe.js.map → EventsTab-CspsEslY.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/HealthTab-DiWzo2ML.js +2 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-DiWzo2ML.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js → MaintenanceTab-9NJuv3dB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-4fJ7M4S4.js.map → MaintenanceTab-9NJuv3dB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js → MemoryMapTab-YpJK1h17.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MemoryMapTab-CDbcK9e4.js.map → MemoryMapTab-YpJK1h17.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js → MetricsCards-D2Mom1M7.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsCards-VUEcgGzo.js.map → MetricsCards-D2Mom1M7.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-DqWF7wud.js +2 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-CKqJ0Zl-.js.map → MetricsTab-DqWF7wud.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{OverviewTab-9Kxo79mi.js → OverviewTab-YuRxjV9U.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{OverviewTab-9Kxo79mi.js.map → OverviewTab-YuRxjV9U.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js → ReposTab-sXMrR7je.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ReposTab-C4iveqrP.js.map → ReposTab-sXMrR7je.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js → SessionsTab-4YCXeGNR.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DSEwkMQm.js.map → SessionsTab-4YCXeGNR.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js → SetupTab-67pZcM_W.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SetupTab-CCyWgy4O.js.map → SetupTab-67pZcM_W.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js → TimeSavedCard-DInjNETP.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CyD8e_e_.js.map → TimeSavedCard-DInjNETP.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js → TurnMemoryView-C3Bif8ix.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnMemoryView-5tLbWkRv.js.map → TurnMemoryView-C3Bif8ix.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/TurnsTab-BUGfiANp.js +2 -0
- package/extensions/dashboard-client/dist/assets/{TurnsTab-C7pm92Hg.js.map → TurnsTab-BUGfiANp.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js → VcStatusBadge-DKU7qz9f.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VcStatusBadge-ObhcIyfN.js.map → VcStatusBadge-DKU7qz9f.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js → VectorCortexTab-BsVSf4xm.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{VectorCortexTab-BtHZb7o7.js.map → VectorCortexTab-BsVSf4xm.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js → WikiTab-DZw9_Fwd.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{WikiTab-lQ4l3QIK.js.map → WikiTab-DZw9_Fwd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js → button-BLRPN489.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{button-DhQQAJGm.js.map → button-BLRPN489.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js → card-BGkkcM4Q.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{card-Cfhz9X31.js.map → card-BGkkcM4Q.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js → client-extra-0OJClxHD.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{client-extra-CKNOGEz_.js.map → client-extra-0OJClxHD.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js → generateCategoricalChart-Dl5lmrHF.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{generateCategoricalChart-JKjK9_cB.js.map → generateCategoricalChart-Dl5lmrHF.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/health-lSeDFTz7.js.map +1 -1
- package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js → index-CH_AD-S_.js} +3 -3
- package/extensions/dashboard-client/dist/assets/{index-iuxgtzEW.js.map → index-CH_AD-S_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js → switch-BPP2-d12.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{switch-7EHEQMSP.js.map → switch-BPP2-d12.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js → toggle-Dez0JaMV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{toggle-z1lWY4UM.js.map → toggle-Dez0JaMV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js → useSSE-Cx2W7BTo.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-sctOZuRM.js.map → useSSE-Cx2W7BTo.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/api/health.ts +1 -0
- package/extensions/dashboard-client/src/tabs/HealthTab.tsx +1 -0
- package/extensions/dashboard-server/routes-rag-settings-helpers.ts +6 -0
- package/extensions/mega-compact-child.ts +129 -0
- package/extensions/mega-config-types.ts +5 -0
- package/extensions/mega-config.ts +5 -0
- package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +12 -2
- package/extensions/mega-events/context-handler/afterCompact.ts +11 -0
- package/extensions/mega-events/context-handler/dbMirrorAppend.ts +8 -0
- package/extensions/mega-events/health-handler.ts +13 -3
- package/extensions/mega-events/session-handlers.ts +1 -0
- package/extensions/mega-pipeline/compact/run.ts +11 -0
- package/extensions/mega-runtime/helpers.ts +46 -0
- package/extensions/mega-runtime/internal-errors.ts +30 -0
- package/extensions/mega-runtime/runtime-instrumentation.ts +195 -0
- package/extensions/mega-runtime/runtime.ts +29 -209
- package/extensions/mega-runtime/state.ts +1 -1
- package/extensions/mega-runtime/widget.ts +1 -1
- package/extensions/mega-turn-store.ts +9 -0
- package/package.json +2 -2
- package/src/bridge/factory.ts +245 -0
- package/src/bridge/types.ts +140 -0
- package/src/bridge.ts +24 -0
- package/src/contextHealth.ts +26 -6
- package/src/metrics/turns.ts +6 -1
- package/src/store/sqlite/context-health-cache-poison.ts +97 -0
- package/src/store/sqlite/context-health.ts +12 -84
- package/src/store/sqlite/embedding-cache.ts +4 -0
- package/src/store/sqlite/schema.ts +4 -0
- package/src/store/turns/memory-store/admin.ts +171 -0
- package/src/store/turns/memory-store/ctx.ts +62 -0
- package/src/store/turns/memory-store/read.ts +172 -0
- package/src/store/turns/memory-store/write.ts +126 -0
- package/src/store/turns/memory-store.ts +51 -330
- package/src/store/turns/migrations.ts +4 -0
- package/src/store/turns/schema.ts +1 -0
- package/src/store/turns/sqlite-store/read.ts +19 -0
- package/src/store/turns/sqlite-store/rows.ts +5 -0
- package/src/store/turns/sqlite-store/write.ts +3 -2
- package/src/store/turns/sqlite-store.ts +5 -0
- package/src/store/turns/types.ts +15 -0
- package/extensions/dashboard-client/dist/assets/HealthTab-D4eVI8ar.js +0 -2
- package/extensions/dashboard-client/dist/assets/HealthTab-D4eVI8ar.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/MetricsTab-CKqJ0Zl-.js +0 -2
- package/extensions/dashboard-client/dist/assets/TurnsTab-C7pm92Hg.js +0 -2
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge/types.ts — contract types for the bidirectional mega-compact bridge.
|
|
3
|
+
*
|
|
4
|
+
* A pi-agnostic, unit-testable adapter surface that wraps the engine's
|
|
5
|
+
* compaction / recall / memory / fork / vector APIs behind one factory so an
|
|
6
|
+
* external host (ithacus) can drive them without importing pi-runtime types.
|
|
7
|
+
* Every type here mirrors a real engine signature (see factory.ts for the
|
|
8
|
+
* wiring).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { EngineMessage } from "../types.js";
|
|
12
|
+
|
|
13
|
+
/** Same shape as the engine's internal message. Identity re-export. */
|
|
14
|
+
export type BridgeMessage = EngineMessage;
|
|
15
|
+
|
|
16
|
+
/** Bridge construction options. */
|
|
17
|
+
export interface BridgeOptions {
|
|
18
|
+
stateDir: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Input to compact a message slice into a checkpoint. */
|
|
22
|
+
export interface BridgeCompactInput {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
messages: BridgeMessage[];
|
|
25
|
+
keepFrom?: number;
|
|
26
|
+
summary?: string;
|
|
27
|
+
keyDecisions?: string[];
|
|
28
|
+
nextSteps?: string[];
|
|
29
|
+
filesModified?: string[];
|
|
30
|
+
compressionPressure?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Useful subset of CompactResult. */
|
|
34
|
+
export interface BridgeCompactResult {
|
|
35
|
+
skipped: boolean;
|
|
36
|
+
deduped: boolean;
|
|
37
|
+
summary: string;
|
|
38
|
+
checkpointId?: string;
|
|
39
|
+
tokenEstimate: number;
|
|
40
|
+
originalTokenEstimate?: number;
|
|
41
|
+
compactedFrom?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Options for checkpoint recall (per-session). */
|
|
45
|
+
export interface BridgeRecallOptions {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
query: string;
|
|
48
|
+
limit?: number;
|
|
49
|
+
recallMaxTokens?: number;
|
|
50
|
+
skipInjected?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Mapped from RecallInjectResult. */
|
|
54
|
+
export interface BridgeRecallResult {
|
|
55
|
+
block: string;
|
|
56
|
+
report: string[];
|
|
57
|
+
hitCount: number;
|
|
58
|
+
empty: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Options for durable memory recall (stateDir-scoped, no sessionId). */
|
|
62
|
+
export interface BridgeMemoryRecallOptions {
|
|
63
|
+
query: string;
|
|
64
|
+
limit?: number;
|
|
65
|
+
minSimilarity?: number;
|
|
66
|
+
crossRepo?: boolean;
|
|
67
|
+
crossRepoCosine?: number;
|
|
68
|
+
recallMaxTokens?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface BridgeMemoryRecallResult {
|
|
72
|
+
block: string;
|
|
73
|
+
report: string[];
|
|
74
|
+
hitCount: number;
|
|
75
|
+
empty: boolean;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Options to fork a child conversation off a parent turn. */
|
|
79
|
+
export interface BridgeForkOptions {
|
|
80
|
+
parentConversationId: string;
|
|
81
|
+
turnIndex: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Fork result: success variant OR a graceful error variant. */
|
|
85
|
+
export interface BridgeForkSuccess {
|
|
86
|
+
childConversationId: string;
|
|
87
|
+
checkpointIds: string[];
|
|
88
|
+
forkTurnIndex: number;
|
|
89
|
+
}
|
|
90
|
+
export interface BridgeForkError {
|
|
91
|
+
error: "TURN_NOT_FOUND" | "NO_RECALL";
|
|
92
|
+
}
|
|
93
|
+
export type BridgeForkResult = BridgeForkSuccess | BridgeForkError;
|
|
94
|
+
|
|
95
|
+
/** Options for a top-k corpus / vector query. */
|
|
96
|
+
export interface BridgeCortexOptions {
|
|
97
|
+
query: string;
|
|
98
|
+
limit?: number;
|
|
99
|
+
repo?: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface BridgeCortexResult {
|
|
103
|
+
results: Array<{ checkpointId: string; score: number; summary?: string }>;
|
|
104
|
+
hitCount: number;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Input to persist a durable memory. */
|
|
108
|
+
export interface BridgeAddMemoryInput {
|
|
109
|
+
content: string;
|
|
110
|
+
kind?: string;
|
|
111
|
+
tags?: string[];
|
|
112
|
+
category?: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** Input to record a turn fact. */
|
|
116
|
+
export interface BridgeRecordTurnInput {
|
|
117
|
+
conversationId: string;
|
|
118
|
+
sessionId: string;
|
|
119
|
+
turnIndex: number;
|
|
120
|
+
/** S49R: pi's per-session counter, carried for the raw_transcript join. */
|
|
121
|
+
sessionTurnIndex?: number;
|
|
122
|
+
role?: string;
|
|
123
|
+
endedAt?: number;
|
|
124
|
+
ctxTokens?: number;
|
|
125
|
+
ctxPercent?: number;
|
|
126
|
+
model?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The bridge surface exposed to the host. */
|
|
130
|
+
export interface MegaBridge {
|
|
131
|
+
compact(input: BridgeCompactInput): BridgeCompactResult;
|
|
132
|
+
recallCheckpoints(opts: BridgeRecallOptions): BridgeRecallResult;
|
|
133
|
+
recallMemories(opts: BridgeMemoryRecallOptions): Promise<BridgeMemoryRecallResult>;
|
|
134
|
+
recallAndInlineAsync(opts: BridgeRecallOptions): Promise<BridgeRecallResult>;
|
|
135
|
+
fork(opts: BridgeForkOptions): BridgeForkResult;
|
|
136
|
+
cortexQuery(opts: BridgeCortexOptions): BridgeCortexResult;
|
|
137
|
+
addMemory(input: BridgeAddMemoryInput): number | void;
|
|
138
|
+
recordTurn(input: BridgeRecordTurnInput): void;
|
|
139
|
+
close(): void;
|
|
140
|
+
}
|
package/src/bridge.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge.ts — public barrel for the mega-compact bidirectional bridge.
|
|
3
|
+
*
|
|
4
|
+
* Hosts import only this file. The factory and all contracts live under
|
|
5
|
+
* src/bridge/ (kept thin per the delegate-shell pattern).
|
|
6
|
+
*/
|
|
7
|
+
export type {
|
|
8
|
+
MegaBridge,
|
|
9
|
+
BridgeOptions,
|
|
10
|
+
BridgeMessage,
|
|
11
|
+
BridgeCompactInput,
|
|
12
|
+
BridgeCompactResult,
|
|
13
|
+
BridgeRecallOptions,
|
|
14
|
+
BridgeRecallResult,
|
|
15
|
+
BridgeMemoryRecallOptions,
|
|
16
|
+
BridgeMemoryRecallResult,
|
|
17
|
+
BridgeForkOptions,
|
|
18
|
+
BridgeForkResult,
|
|
19
|
+
BridgeCortexOptions,
|
|
20
|
+
BridgeCortexResult,
|
|
21
|
+
BridgeAddMemoryInput,
|
|
22
|
+
BridgeRecordTurnInput,
|
|
23
|
+
} from "./bridge/types.js";
|
|
24
|
+
export { createMegaBridge } from "./bridge/factory.js";
|
package/src/contextHealth.ts
CHANGED
|
@@ -23,23 +23,39 @@ export interface ContextHealthSubScores {
|
|
|
23
23
|
drift: number;
|
|
24
24
|
/** Output quality score (0–1, higher = healthier output). */
|
|
25
25
|
outputQuality: number;
|
|
26
|
-
/** Error-rate score (0–1, higher = fewer errors). */
|
|
26
|
+
/** Error-rate score (0–1, higher = fewer API-retry errors). */
|
|
27
27
|
errorRate: number;
|
|
28
28
|
/** Cache health score (0–1, higher = healthier cache). */
|
|
29
29
|
cacheHealth: number;
|
|
30
30
|
/** Cache poison score (0–1, higher = less poison). */
|
|
31
31
|
cachePoison: number;
|
|
32
|
+
/**
|
|
33
|
+
* Store-error rate score (0–1, higher = fewer internal store-write
|
|
34
|
+
* failures). Sprint H (B2): a SEPARATE 6th axis from `errorRate` — `errorRate`
|
|
35
|
+
* reflects API-retry errors only; `storeErrorRate` reflects internal
|
|
36
|
+
* store/service-write failures (turn rows, recall, dbMirror, ledger, epoch,
|
|
37
|
+
* stats, memory consolidate, compact persist, vector index, embedder,
|
|
38
|
+
* migration). The two are equally loud at 0.09 each.
|
|
39
|
+
*/
|
|
40
|
+
storeErrorRate: number;
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
/**
|
|
35
|
-
* Weighted composite health score from
|
|
44
|
+
* Weighted composite health score from six sub-dimensions (Sprint H — B2).
|
|
36
45
|
*
|
|
37
46
|
* Weights:
|
|
38
47
|
* output quality 22% — primary measure of model output health
|
|
39
48
|
* drift 22% — session-level coherence
|
|
40
49
|
* cache poison 20% — prompt-cache integrity
|
|
41
50
|
* cache health 18% — cache hit rate stability
|
|
42
|
-
* error rate
|
|
51
|
+
* error rate 9% — API-retry error frequency (split from 18%)
|
|
52
|
+
* store error 9% — internal store-write failure frequency (NEW 6th axis)
|
|
53
|
+
*
|
|
54
|
+
* The 0.18 `errorRate` budget was split into `errorRate 0.09` +
|
|
55
|
+
* `storeErrorRate 0.09` so a silent store-write failure is as visible as a
|
|
56
|
+
* retryable API error. Weights sum to 1.0. NOTE (step-change): historical
|
|
57
|
+
* context_health rows keep their stored composite (point-in-time snapshots — no
|
|
58
|
+
* backfill), so the trend line shows a boundary step where weights change.
|
|
43
59
|
*
|
|
44
60
|
* Returns 0–1: 1 = fully healthy, 0 = severely degraded.
|
|
45
61
|
*/
|
|
@@ -49,7 +65,8 @@ export function computeHealthScore(sub: ContextHealthSubScores): number {
|
|
|
49
65
|
sub.drift * 0.22 +
|
|
50
66
|
sub.cachePoison * 0.20 +
|
|
51
67
|
sub.cacheHealth * 0.18 +
|
|
52
|
-
sub.errorRate * 0.
|
|
68
|
+
sub.errorRate * 0.09 +
|
|
69
|
+
sub.storeErrorRate * 0.09;
|
|
53
70
|
return Math.max(0, Math.min(1, raw));
|
|
54
71
|
}
|
|
55
72
|
|
|
@@ -66,6 +83,8 @@ export interface ContextHealthRow {
|
|
|
66
83
|
cacheHealth: number;
|
|
67
84
|
cachePoison: number;
|
|
68
85
|
composite: number;
|
|
86
|
+
/** Sprint H (B2): 6th axis — internal store-write failure score (0–1). */
|
|
87
|
+
storeErrorScore?: number;
|
|
69
88
|
modelId?: string;
|
|
70
89
|
repetitionRatio?: number;
|
|
71
90
|
coherenceScore?: number;
|
|
@@ -89,9 +108,9 @@ export function recordContextHealth(
|
|
|
89
108
|
db.prepare(
|
|
90
109
|
`INSERT INTO context_health
|
|
91
110
|
(ts, session_id, turn_index, drift_score, output_quality,
|
|
92
|
-
error_score, cache_health, cache_poison, composite,
|
|
111
|
+
error_score, cache_health, cache_poison, composite, store_error_score,
|
|
93
112
|
model_id, repetition_ratio, coherence_score, prefix_hash)
|
|
94
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
113
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
95
114
|
).run(
|
|
96
115
|
row.ts,
|
|
97
116
|
row.sessionId,
|
|
@@ -102,6 +121,7 @@ export function recordContextHealth(
|
|
|
102
121
|
row.cacheHealth,
|
|
103
122
|
row.cachePoison,
|
|
104
123
|
row.composite,
|
|
124
|
+
row.storeErrorScore ?? null,
|
|
105
125
|
row.modelId ?? null,
|
|
106
126
|
row.repetitionRatio ?? null,
|
|
107
127
|
row.coherenceScore ?? null,
|
package/src/metrics/turns.ts
CHANGED
|
@@ -113,7 +113,12 @@ export function turnMetrics(
|
|
|
113
113
|
|
|
114
114
|
return turns.map((t) => {
|
|
115
115
|
const recall = store.listRecallByIndex(conversationId, t.turnIndex);
|
|
116
|
-
|
|
116
|
+
// S49R: raw_transcript is keyed by pi's per-session counter. Pre-migration
|
|
117
|
+
// turn rows have NULL sessionTurnIndex and their turnIndex IS the session
|
|
118
|
+
// counter, so fall back to turnIndex (COALESCE). Post-migration rows join
|
|
119
|
+
// on sessionTurnIndex so resume doesn't zero the raw message count.
|
|
120
|
+
const sessionKey = t.sessionTurnIndex ?? t.turnIndex;
|
|
121
|
+
const agg = rawAggBySessionTurn.get(`${t.sessionId}::${sessionKey}`);
|
|
117
122
|
const rawCount = agg?.raw_count ?? 0;
|
|
118
123
|
const uniqueCount = agg?.unique_count ?? 0;
|
|
119
124
|
return {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* context-health-cache-poison.ts — `cache_poison_events` table accessors.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from context-health.ts (Sprint H split — context-health.ts crossed
|
|
5
|
+
* the soft limit when storeErrorScore was added). Records cache-poison advisory
|
|
6
|
+
* events when cache corruption/inconsistency is detected at a given cache layer.
|
|
7
|
+
*
|
|
8
|
+
* PREVENT-PI-004: local SQLite only, zero network.
|
|
9
|
+
* PREVENT-002: all SQL uses ? placeholders, no string-concatenated values.
|
|
10
|
+
* Pi-agnostic: no pi runtime types.
|
|
11
|
+
*/
|
|
12
|
+
import { getStateDir } from "../../store.js";
|
|
13
|
+
import { openStore } from "./utils.js";
|
|
14
|
+
|
|
15
|
+
/** A single cache_poison_events row (as stored + returned). */
|
|
16
|
+
export interface CachePoisonEvent {
|
|
17
|
+
id: number;
|
|
18
|
+
ts: number;
|
|
19
|
+
turnIndex: number;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
layer: number | null;
|
|
22
|
+
detail: string | null;
|
|
23
|
+
severity: string | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Record one cache-poison advisory event. All values are fully parameterized
|
|
28
|
+
* (PREVENT-002). Non-fatal: any write error is logged to stderr and silently
|
|
29
|
+
* swallowed so advisory logging never blocks the agent loop.
|
|
30
|
+
*/
|
|
31
|
+
export function recordCachePoisonEvent(
|
|
32
|
+
stateDir: string = getStateDir(),
|
|
33
|
+
event: {
|
|
34
|
+
ts: number;
|
|
35
|
+
turnIndex: number;
|
|
36
|
+
sessionId: string;
|
|
37
|
+
layer: number;
|
|
38
|
+
detail: string;
|
|
39
|
+
severity: "warn" | "alert";
|
|
40
|
+
},
|
|
41
|
+
): void {
|
|
42
|
+
try {
|
|
43
|
+
const db = openStore(stateDir);
|
|
44
|
+
db.prepare(
|
|
45
|
+
`INSERT INTO cache_poison_events
|
|
46
|
+
(ts, turn_index, session_id, layer, detail, severity)
|
|
47
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
48
|
+
).run(event.ts, event.turnIndex, event.sessionId, event.layer, event.detail, event.severity);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
// Non-fatal: advisory logging must never block the agent loop.
|
|
51
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
52
|
+
process.stderr.write(`[context-health] recordCachePoisonEvent error: ${msg}\n`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Read cache poison events since `sinceTs`, ordered descending by ts (most
|
|
58
|
+
* recent first), capped at 100 rows. Returns an empty array on any error
|
|
59
|
+
* (non-fatal). SQL is fully parameterized (PREVENT-002).
|
|
60
|
+
*/
|
|
61
|
+
export function readCachePoisonEvents(
|
|
62
|
+
stateDir: string = getStateDir(),
|
|
63
|
+
sinceTs: number,
|
|
64
|
+
): CachePoisonEvent[] {
|
|
65
|
+
try {
|
|
66
|
+
const db = openStore(stateDir);
|
|
67
|
+
const rows = db
|
|
68
|
+
.prepare(
|
|
69
|
+
`SELECT id, ts, turn_index, session_id, layer, detail, severity
|
|
70
|
+
FROM cache_poison_events
|
|
71
|
+
WHERE ts >= ?
|
|
72
|
+
ORDER BY ts DESC
|
|
73
|
+
LIMIT 100`,
|
|
74
|
+
)
|
|
75
|
+
.all(sinceTs) as Array<{
|
|
76
|
+
id: number;
|
|
77
|
+
ts: number;
|
|
78
|
+
turn_index: number;
|
|
79
|
+
session_id: string;
|
|
80
|
+
layer: number | null;
|
|
81
|
+
detail: string | null;
|
|
82
|
+
severity: string | null;
|
|
83
|
+
}>;
|
|
84
|
+
|
|
85
|
+
return rows.map((r) => ({
|
|
86
|
+
id: r.id,
|
|
87
|
+
ts: r.ts,
|
|
88
|
+
turnIndex: r.turn_index,
|
|
89
|
+
sessionId: r.session_id,
|
|
90
|
+
layer: r.layer,
|
|
91
|
+
detail: r.detail,
|
|
92
|
+
severity: r.severity,
|
|
93
|
+
}));
|
|
94
|
+
} catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -41,6 +41,8 @@ export interface LatestContextHealth {
|
|
|
41
41
|
driftScore: number;
|
|
42
42
|
outputQuality: number;
|
|
43
43
|
errorScore: number;
|
|
44
|
+
/** Sprint H (B2): internal store-write failure score (NULL→1 on pre-upgrade DBs). */
|
|
45
|
+
storeErrorScore: number;
|
|
44
46
|
cacheHealth: number;
|
|
45
47
|
cachePoison: number;
|
|
46
48
|
ts: number;
|
|
@@ -48,15 +50,13 @@ export interface LatestContextHealth {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/** A single cache_poison_events row (as stored + returned). */
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
severity: string | null;
|
|
59
|
-
}
|
|
53
|
+
// Sprint H split: cache_poison_events accessors live in context-health-cache-poison.ts.
|
|
54
|
+
// Re-exported here so existing importers (barrel `export *`) are unaffected.
|
|
55
|
+
export {
|
|
56
|
+
recordCachePoisonEvent,
|
|
57
|
+
readCachePoisonEvents,
|
|
58
|
+
type CachePoisonEvent,
|
|
59
|
+
} from "./context-health-cache-poison.js";
|
|
60
60
|
|
|
61
61
|
// ─── context_health accessors ────────────────────────────────────────────────
|
|
62
62
|
|
|
@@ -205,7 +205,7 @@ export function readLatestContextHealth(
|
|
|
205
205
|
const db = openStore(stateDir);
|
|
206
206
|
const row = db
|
|
207
207
|
.prepare(
|
|
208
|
-
`SELECT composite, drift_score, output_quality, error_score,
|
|
208
|
+
`SELECT composite, drift_score, output_quality, error_score, store_error_score,
|
|
209
209
|
cache_health, cache_poison, ts, model_id
|
|
210
210
|
FROM context_health
|
|
211
211
|
ORDER BY ts DESC
|
|
@@ -217,6 +217,7 @@ export function readLatestContextHealth(
|
|
|
217
217
|
drift_score: number;
|
|
218
218
|
output_quality: number;
|
|
219
219
|
error_score: number;
|
|
220
|
+
store_error_score: number | null;
|
|
220
221
|
cache_health: number;
|
|
221
222
|
cache_poison: number;
|
|
222
223
|
ts: number;
|
|
@@ -230,6 +231,7 @@ export function readLatestContextHealth(
|
|
|
230
231
|
driftScore: row.drift_score,
|
|
231
232
|
outputQuality: row.output_quality,
|
|
232
233
|
errorScore: row.error_score,
|
|
234
|
+
storeErrorScore: row.store_error_score ?? 1,
|
|
233
235
|
cacheHealth: row.cache_health,
|
|
234
236
|
cachePoison: row.cache_poison,
|
|
235
237
|
ts: row.ts,
|
|
@@ -266,77 +268,3 @@ export function readContextHealthTrend(
|
|
|
266
268
|
return [];
|
|
267
269
|
}
|
|
268
270
|
}
|
|
269
|
-
|
|
270
|
-
// ─── cache_poison_events accessors ───────────────────────────────────────────
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Record one cache poison advisory event. Fully parameterized (PREVENT-002).
|
|
274
|
-
* Non-fatal: write errors are logged to stderr and silently swallowed.
|
|
275
|
-
*/
|
|
276
|
-
export function recordCachePoisonEvent(
|
|
277
|
-
stateDir: string = getStateDir(),
|
|
278
|
-
event: {
|
|
279
|
-
ts: number;
|
|
280
|
-
turnIndex: number;
|
|
281
|
-
sessionId: string;
|
|
282
|
-
layer: number;
|
|
283
|
-
detail: string;
|
|
284
|
-
severity: "warn" | "alert";
|
|
285
|
-
},
|
|
286
|
-
): void {
|
|
287
|
-
try {
|
|
288
|
-
const db = openStore(stateDir);
|
|
289
|
-
db.prepare(
|
|
290
|
-
`INSERT INTO cache_poison_events
|
|
291
|
-
(ts, turn_index, session_id, layer, detail, severity)
|
|
292
|
-
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
293
|
-
).run(event.ts, event.turnIndex, event.sessionId, event.layer, event.detail, event.severity);
|
|
294
|
-
} catch (err) {
|
|
295
|
-
// Non-fatal: advisory logging must never block the agent loop.
|
|
296
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
297
|
-
process.stderr.write(`[context-health] recordCachePoisonEvent error: ${msg}\n`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Read cache poison events since `sinceTs`, ordered descending by ts (most
|
|
303
|
-
* recent first), capped at 100 rows. Returns an empty array on any error
|
|
304
|
-
* (non-fatal). SQL is fully parameterized (PREVENT-002).
|
|
305
|
-
*/
|
|
306
|
-
export function readCachePoisonEvents(
|
|
307
|
-
stateDir: string = getStateDir(),
|
|
308
|
-
sinceTs: number,
|
|
309
|
-
): CachePoisonEvent[] {
|
|
310
|
-
try {
|
|
311
|
-
const db = openStore(stateDir);
|
|
312
|
-
const rows = db
|
|
313
|
-
.prepare(
|
|
314
|
-
`SELECT id, ts, turn_index, session_id, layer, detail, severity
|
|
315
|
-
FROM cache_poison_events
|
|
316
|
-
WHERE ts >= ?
|
|
317
|
-
ORDER BY ts DESC
|
|
318
|
-
LIMIT 100`,
|
|
319
|
-
)
|
|
320
|
-
.all(sinceTs) as Array<{
|
|
321
|
-
id: number;
|
|
322
|
-
ts: number;
|
|
323
|
-
turn_index: number;
|
|
324
|
-
session_id: string;
|
|
325
|
-
layer: number | null;
|
|
326
|
-
detail: string | null;
|
|
327
|
-
severity: string | null;
|
|
328
|
-
}>;
|
|
329
|
-
|
|
330
|
-
return rows.map((r) => ({
|
|
331
|
-
id: r.id,
|
|
332
|
-
ts: r.ts,
|
|
333
|
-
turnIndex: r.turn_index,
|
|
334
|
-
sessionId: r.session_id,
|
|
335
|
-
layer: r.layer,
|
|
336
|
-
detail: r.detail,
|
|
337
|
-
severity: r.severity,
|
|
338
|
-
}));
|
|
339
|
-
} catch {
|
|
340
|
-
return [];
|
|
341
|
-
}
|
|
342
|
-
}
|
|
@@ -76,6 +76,10 @@ export function getOrComputeEmbedding(db: DatabaseSync, content: string): number
|
|
|
76
76
|
process.stderr.write(
|
|
77
77
|
`[embedding-cache] getOrComputeEmbedding error: ${msg}\n`
|
|
78
78
|
);
|
|
79
|
+
// Sprint H (Option A): this leaf src/ cache-persist path has no runtime
|
|
80
|
+
// handle, so the embedder cache-write failure is recorded by callers that
|
|
81
|
+
// own a runtime, not here.
|
|
82
|
+
// guardrails-allow INTERNAL-ERR: src/ leaf cache writer, no runtime handle — callers record embedder cache failures
|
|
79
83
|
return [];
|
|
80
84
|
}
|
|
81
85
|
}
|
|
@@ -71,6 +71,10 @@ export function initSchema(db: DatabaseSync): void {
|
|
|
71
71
|
// S43: conversation_id + last_turn_id on session_state (legacy DBs have NULL).
|
|
72
72
|
ensureColumn(db, "session_state", "conversation_id", "TEXT");
|
|
73
73
|
ensureColumn(db, "session_state", "last_turn_id", "INTEGER");
|
|
74
|
+
// Sprint H (B2): store_error_score column on context_health — the 6th health
|
|
75
|
+
// axis. Additive; DBs created before this version have NULL → the health
|
|
76
|
+
// handler degrades to 1.0 (no data → healthy), matching the empty-ring rule.
|
|
77
|
+
ensureColumn(db, "context_health", "store_error_score", "REAL");
|
|
74
78
|
// S35: idempotent seed of the 9 achievement rows. ON CONFLICT(id) DO
|
|
75
79
|
// NOTHING so a re-open never clobbers an already-unlocked row's
|
|
76
80
|
// unlocked_at. No user input reaches this SQL (PREVENT-002 safe).
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* admin.ts — TurnAdmin method bodies for InMemoryTurnStore (extracted from
|
|
3
|
+
* memory-store.ts). Free functions operating on a MemoryTurnStoreCtx; the
|
|
4
|
+
* shell class delegates here. Mirrors sqlite-store/admin.ts.
|
|
5
|
+
*
|
|
6
|
+
* Arrays (recall, forks) are mutated IN PLACE (push/length=0), never
|
|
7
|
+
* reassigned, so the ctx references captured by the shell stay valid. This
|
|
8
|
+
* differs from the original class methods, which reassigned `this.recall` /
|
|
9
|
+
* `this.forks` — the rewrite is behavior-identical but in-place so the
|
|
10
|
+
* extracted ctx model works.
|
|
11
|
+
*/
|
|
12
|
+
import type {
|
|
13
|
+
PruneReport,
|
|
14
|
+
RetentionPolicy,
|
|
15
|
+
SessionId,
|
|
16
|
+
StoreSnapshot,
|
|
17
|
+
} from "../types.js";
|
|
18
|
+
import {
|
|
19
|
+
allocId,
|
|
20
|
+
normalizeSessionId,
|
|
21
|
+
resetIdCounter,
|
|
22
|
+
type MemoryTurnStoreCtx,
|
|
23
|
+
} from "./ctx.js";
|
|
24
|
+
|
|
25
|
+
export function prune(
|
|
26
|
+
ctx: MemoryTurnStoreCtx,
|
|
27
|
+
policy: RetentionPolicy,
|
|
28
|
+
): PruneReport {
|
|
29
|
+
const cutoff = Date.now() - policy.maxTurnAgeMs;
|
|
30
|
+
let turnsRemoved = 0;
|
|
31
|
+
let recallRemoved = 0;
|
|
32
|
+
|
|
33
|
+
for (const [convId, turnIds] of ctx.convIndex.entries()) {
|
|
34
|
+
const total = turnIds.length;
|
|
35
|
+
const canDelete = Math.max(0, total - policy.keepMinPerConversation);
|
|
36
|
+
if (canDelete === 0) continue;
|
|
37
|
+
|
|
38
|
+
// Find old turns to delete
|
|
39
|
+
const oldIds = turnIds
|
|
40
|
+
.filter((id) => {
|
|
41
|
+
const row = ctx.turns.get(id);
|
|
42
|
+
return row && row.entry.endedAt < cutoff;
|
|
43
|
+
})
|
|
44
|
+
.slice(0, canDelete);
|
|
45
|
+
|
|
46
|
+
for (const id of oldIds) {
|
|
47
|
+
// Remove recall for this turn, in place (preserve ctx.recall reference)
|
|
48
|
+
const before = ctx.recall.length;
|
|
49
|
+
let w = 0;
|
|
50
|
+
for (let i = 0; i < ctx.recall.length; i++) {
|
|
51
|
+
if (ctx.recall[i].turnId !== id) ctx.recall[w++] = ctx.recall[i];
|
|
52
|
+
}
|
|
53
|
+
ctx.recall.length = w;
|
|
54
|
+
recallRemoved += before - ctx.recall.length;
|
|
55
|
+
|
|
56
|
+
ctx.turns.delete(id);
|
|
57
|
+
turnsRemoved++;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Update convIndex
|
|
61
|
+
ctx.convIndex.set(
|
|
62
|
+
convId,
|
|
63
|
+
turnIds.filter((id) => ctx.turns.has(id)),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const branchesPreserved = ctx.forks.length;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
turnsRemoved,
|
|
71
|
+
recallRemoved,
|
|
72
|
+
branchesPreserved,
|
|
73
|
+
freedBytes: 0, // in-memory — no file size to measure
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function vacuum(_ctx: MemoryTurnStoreCtx): void {
|
|
78
|
+
// No-op for in-memory store
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function checkpoint(ctx: MemoryTurnStoreCtx): StoreSnapshot {
|
|
82
|
+
// Emit turns in a stable order (by internal ID / insertion order)
|
|
83
|
+
const sorted = [...ctx.turns.values()].sort((a, b) => a.id - b.id);
|
|
84
|
+
const turns = sorted.map((r) => r.entry);
|
|
85
|
+
|
|
86
|
+
// Build mapping: internal ID → 1-based position
|
|
87
|
+
const idToPos = new Map<number, number>();
|
|
88
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
89
|
+
idToPos.set(sorted[i].id, i + 1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Replace recall's turnId with the stable 1-based position
|
|
93
|
+
const recall = ctx.recall.map((r) => ({
|
|
94
|
+
turnId: String(idToPos.get(r.turnId) ?? r.turnId),
|
|
95
|
+
checkpointId: r.entry.checkpointId,
|
|
96
|
+
score: r.entry.score,
|
|
97
|
+
source: r.entry.source,
|
|
98
|
+
raptorLevel: r.entry.raptorLevel,
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
version: 1,
|
|
103
|
+
exportedAt: Date.now(),
|
|
104
|
+
turns,
|
|
105
|
+
recall,
|
|
106
|
+
forks: [...ctx.forks],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function restore(ctx: MemoryTurnStoreCtx, from: StoreSnapshot): void {
|
|
111
|
+
clear(ctx);
|
|
112
|
+
|
|
113
|
+
// Map: 1-based position → new internal ID
|
|
114
|
+
const posToNewId = new Map<number, number>();
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < from.turns.length; i++) {
|
|
117
|
+
const t = from.turns[i];
|
|
118
|
+
const newId = allocId();
|
|
119
|
+
const sid = normalizeSessionId(t.sessionId);
|
|
120
|
+
const normalized = { ...t, sessionId: sid };
|
|
121
|
+
ctx.turns.set(newId, { id: newId, entry: normalized });
|
|
122
|
+
|
|
123
|
+
// Update indices
|
|
124
|
+
const convIds = ctx.convIndex.get(t.conversationId) ?? [];
|
|
125
|
+
convIds.push(newId);
|
|
126
|
+
ctx.convIndex.set(t.conversationId, convIds);
|
|
127
|
+
ctx.sessionConv.set(sid, t.conversationId);
|
|
128
|
+
|
|
129
|
+
posToNewId.set(i + 1, newId);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Re-attach recall with position-based TurnId → new internal ID mapping
|
|
133
|
+
for (const r of from.recall) {
|
|
134
|
+
const targetInternalId = posToNewId.get(Number(r.turnId));
|
|
135
|
+
if (targetInternalId !== undefined) {
|
|
136
|
+
ctx.recall.push({
|
|
137
|
+
turnId: targetInternalId,
|
|
138
|
+
entry: r,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// In-place replace forks (preserve ctx.forks reference)
|
|
144
|
+
ctx.forks.length = 0;
|
|
145
|
+
ctx.forks.push(...from.forks);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function clear(ctx: MemoryTurnStoreCtx): void {
|
|
149
|
+
ctx.turns.clear();
|
|
150
|
+
ctx.recall.length = 0;
|
|
151
|
+
ctx.forks.length = 0;
|
|
152
|
+
ctx.convIndex.clear();
|
|
153
|
+
ctx.sessionConv.clear();
|
|
154
|
+
resetIdCounter();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function stampTurnsEpoch(
|
|
158
|
+
ctx: MemoryTurnStoreCtx,
|
|
159
|
+
sessionId: SessionId,
|
|
160
|
+
epochId: string,
|
|
161
|
+
): number {
|
|
162
|
+
const sid = normalizeSessionId(sessionId);
|
|
163
|
+
let stamped = 0;
|
|
164
|
+
for (const row of ctx.turns.values()) {
|
|
165
|
+
if (row.epochId === undefined && row.entry.sessionId === sid) {
|
|
166
|
+
row.epochId = epochId;
|
|
167
|
+
stamped++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return stamped;
|
|
171
|
+
}
|