praana 0.5.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.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/bin/praana.js +17 -0
- package/bin/pran.js +17 -0
- package/dist/app-banner.d.ts +11 -0
- package/dist/app-banner.js +161 -0
- package/dist/app-controller.d.ts +44 -0
- package/dist/app-controller.js +143 -0
- package/dist/app-identity.d.ts +18 -0
- package/dist/app-identity.js +52 -0
- package/dist/auto-compact.d.ts +16 -0
- package/dist/auto-compact.js +101 -0
- package/dist/cli-args.d.ts +14 -0
- package/dist/cli-args.js +69 -0
- package/dist/compile-classic.d.ts +21 -0
- package/dist/compile-classic.js +106 -0
- package/dist/compiler.d.ts +75 -0
- package/dist/compiler.js +406 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +433 -0
- package/dist/context-engine/activity-log.d.ts +9 -0
- package/dist/context-engine/activity-log.js +109 -0
- package/dist/context-engine/artifact-store.d.ts +32 -0
- package/dist/context-engine/artifact-store.js +272 -0
- package/dist/context-engine/bm25.d.ts +3 -0
- package/dist/context-engine/bm25.js +32 -0
- package/dist/context-engine/checkpoint.d.ts +34 -0
- package/dist/context-engine/checkpoint.js +430 -0
- package/dist/context-engine/classify.d.ts +3 -0
- package/dist/context-engine/classify.js +60 -0
- package/dist/context-engine/db.d.ts +73 -0
- package/dist/context-engine/db.js +505 -0
- package/dist/context-engine/distiller.d.ts +30 -0
- package/dist/context-engine/distiller.js +67 -0
- package/dist/context-engine/engine-compiler.d.ts +23 -0
- package/dist/context-engine/engine-compiler.js +297 -0
- package/dist/context-engine/error-tracker.d.ts +21 -0
- package/dist/context-engine/error-tracker.js +74 -0
- package/dist/context-engine/event-lineage.d.ts +26 -0
- package/dist/context-engine/event-lineage.js +120 -0
- package/dist/context-engine/extraction.d.ts +26 -0
- package/dist/context-engine/extraction.js +83 -0
- package/dist/context-engine/index.d.ts +82 -0
- package/dist/context-engine/index.js +238 -0
- package/dist/context-engine/scoring.d.ts +13 -0
- package/dist/context-engine/scoring.js +47 -0
- package/dist/context-engine/state-snapshot.d.ts +8 -0
- package/dist/context-engine/state-snapshot.js +50 -0
- package/dist/context-engine/summarize.d.ts +6 -0
- package/dist/context-engine/summarize.js +32 -0
- package/dist/context-engine/telemetry.d.ts +25 -0
- package/dist/context-engine/telemetry.js +64 -0
- package/dist/context-engine/turn-digest.d.ts +50 -0
- package/dist/context-engine/turn-digest.js +250 -0
- package/dist/context-engine/turn-ledger.d.ts +18 -0
- package/dist/context-engine/turn-ledger.js +184 -0
- package/dist/context-engine/turn-recorder.d.ts +24 -0
- package/dist/context-engine/turn-recorder.js +88 -0
- package/dist/context-engine/types.d.ts +201 -0
- package/dist/context-engine/types.js +4 -0
- package/dist/context-pressure.d.ts +19 -0
- package/dist/context-pressure.js +36 -0
- package/dist/distillers/generic.d.ts +14 -0
- package/dist/distillers/generic.js +93 -0
- package/dist/distillers/git-diff.d.ts +8 -0
- package/dist/distillers/git-diff.js +119 -0
- package/dist/distillers/index.d.ts +2 -0
- package/dist/distillers/index.js +16 -0
- package/dist/distillers/npm-test.d.ts +8 -0
- package/dist/distillers/npm-test.js +50 -0
- package/dist/distillers/rg-results.d.ts +8 -0
- package/dist/distillers/rg-results.js +28 -0
- package/dist/distillers/tsc-errors.d.ts +8 -0
- package/dist/distillers/tsc-errors.js +52 -0
- package/dist/event-log.d.ts +56 -0
- package/dist/event-log.js +214 -0
- package/dist/llm.d.ts +29 -0
- package/dist/llm.js +155 -0
- package/dist/logger.d.ts +94 -0
- package/dist/logger.js +287 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +54 -0
- package/dist/memory/confidence.d.ts +7 -0
- package/dist/memory/confidence.js +37 -0
- package/dist/memory/consolidation.d.ts +26 -0
- package/dist/memory/consolidation.js +166 -0
- package/dist/memory/db.d.ts +40 -0
- package/dist/memory/db.js +283 -0
- package/dist/memory/dedup.d.ts +6 -0
- package/dist/memory/dedup.js +50 -0
- package/dist/memory/embedder-factory.d.ts +3 -0
- package/dist/memory/embedder-factory.js +81 -0
- package/dist/memory/embeddings.d.ts +15 -0
- package/dist/memory/embeddings.js +67 -0
- package/dist/memory/index.d.ts +9 -0
- package/dist/memory/index.js +11 -0
- package/dist/memory/ollama-summarizer.d.ts +19 -0
- package/dist/memory/ollama-summarizer.js +72 -0
- package/dist/memory/openai-summarizer.d.ts +21 -0
- package/dist/memory/openai-summarizer.js +51 -0
- package/dist/memory/store.d.ts +61 -0
- package/dist/memory/store.js +502 -0
- package/dist/memory/summarizer-factory.d.ts +3 -0
- package/dist/memory/summarizer-factory.js +69 -0
- package/dist/memory/summarizer.d.ts +4 -0
- package/dist/memory/summarizer.js +112 -0
- package/dist/memory/types.d.ts +87 -0
- package/dist/memory/types.js +17 -0
- package/dist/model-context.d.ts +15 -0
- package/dist/model-context.js +212 -0
- package/dist/project-detector.d.ts +37 -0
- package/dist/project-detector.js +604 -0
- package/dist/render.d.ts +15 -0
- package/dist/render.js +46 -0
- package/dist/session.d.ts +118 -0
- package/dist/session.js +809 -0
- package/dist/skills/index.d.ts +69 -0
- package/dist/skills/index.js +885 -0
- package/dist/skills/types.d.ts +93 -0
- package/dist/skills/types.js +8 -0
- package/dist/slash-commands.d.ts +14 -0
- package/dist/slash-commands.js +301 -0
- package/dist/state-graph.d.ts +38 -0
- package/dist/state-graph.js +255 -0
- package/dist/status-bar.d.ts +54 -0
- package/dist/status-bar.js +184 -0
- package/dist/thinking-display.d.ts +21 -0
- package/dist/thinking-display.js +37 -0
- package/dist/tool-summary.d.ts +4 -0
- package/dist/tool-summary.js +67 -0
- package/dist/tools/index.d.ts +925 -0
- package/dist/tools/index.js +86 -0
- package/dist/tools/knowledge.d.ts +140 -0
- package/dist/tools/knowledge.js +260 -0
- package/dist/tools/memory.d.ts +39 -0
- package/dist/tools/memory.js +300 -0
- package/dist/tools/search-code.d.ts +134 -0
- package/dist/tools/search-code.js +390 -0
- package/dist/tools/system.d.ts +16 -0
- package/dist/tools/system.js +499 -0
- package/dist/tools/tool-def.d.ts +6 -0
- package/dist/tools/tool-def.js +3 -0
- package/dist/turn-control.d.ts +51 -0
- package/dist/turn-control.js +210 -0
- package/dist/turn.d.ts +20 -0
- package/dist/turn.js +624 -0
- package/dist/types.d.ts +233 -0
- package/dist/types.js +4 -0
- package/dist/ui/readline-ui.d.ts +2 -0
- package/dist/ui/readline-ui.js +176 -0
- package/dist/ui/tui/app.d.ts +13 -0
- package/dist/ui/tui/app.js +270 -0
- package/dist/ui/tui/busy-indicator.d.ts +2 -0
- package/dist/ui/tui/busy-indicator.js +13 -0
- package/dist/ui/tui/components/gutter-rule.d.ts +5 -0
- package/dist/ui/tui/components/gutter-rule.js +9 -0
- package/dist/ui/tui/components/inline-tool-row.d.ts +10 -0
- package/dist/ui/tui/components/inline-tool-row.js +8 -0
- package/dist/ui/tui/components/prompt-input.d.ts +20 -0
- package/dist/ui/tui/components/prompt-input.js +120 -0
- package/dist/ui/tui/components/system-line.d.ts +5 -0
- package/dist/ui/tui/components/system-line.js +6 -0
- package/dist/ui/tui/components/thinking-block.d.ts +11 -0
- package/dist/ui/tui/components/thinking-block.js +31 -0
- package/dist/ui/tui/components/toast-line.d.ts +4 -0
- package/dist/ui/tui/components/toast-line.js +8 -0
- package/dist/ui/tui/components/tool-result-line.d.ts +5 -0
- package/dist/ui/tui/components/tool-result-line.js +6 -0
- package/dist/ui/tui/components/turn-footer.d.ts +5 -0
- package/dist/ui/tui/components/turn-footer.js +7 -0
- package/dist/ui/tui/components/user-block.d.ts +6 -0
- package/dist/ui/tui/components/user-block.js +6 -0
- package/dist/ui/tui/logo-banner.d.ts +5 -0
- package/dist/ui/tui/logo-banner.js +8 -0
- package/dist/ui/tui/markdown-render.d.ts +16 -0
- package/dist/ui/tui/markdown-render.js +218 -0
- package/dist/ui/tui/palette.d.ts +12 -0
- package/dist/ui/tui/palette.js +13 -0
- package/dist/ui/tui/reasoning-summary.d.ts +12 -0
- package/dist/ui/tui/reasoning-summary.js +27 -0
- package/dist/ui/tui/reducer.d.ts +92 -0
- package/dist/ui/tui/reducer.js +260 -0
- package/dist/ui/tui/run.d.ts +3 -0
- package/dist/ui/tui/run.js +40 -0
- package/dist/ui/tui/sink.d.ts +4 -0
- package/dist/ui/tui/sink.js +89 -0
- package/dist/ui/tui/status-bar-view.d.ts +5 -0
- package/dist/ui/tui/status-bar-view.js +44 -0
- package/dist/ui/tui/terminal-height.d.ts +12 -0
- package/dist/ui/tui/terminal-height.js +20 -0
- package/dist/ui/tui/terminal-width.d.ts +2 -0
- package/dist/ui/tui/terminal-width.js +5 -0
- package/dist/ui/tui/tool-display.d.ts +23 -0
- package/dist/ui/tui/tool-display.js +217 -0
- package/dist/ui/tui/transcript-line.d.ts +12 -0
- package/dist/ui/tui/transcript-line.js +43 -0
- package/dist/ui/tui/transcript-replay.d.ts +12 -0
- package/dist/ui/tui/transcript-replay.js +117 -0
- package/dist/ui-events.d.ts +39 -0
- package/dist/ui-events.js +33 -0
- package/dist/ui.d.ts +77 -0
- package/dist/ui.js +179 -0
- package/package.json +73 -0
- package/praana.config.example.toml +231 -0
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
const ARTIFACT_SCHEMA = `
|
|
3
|
+
CREATE TABLE IF NOT EXISTS context_artifacts (
|
|
4
|
+
id TEXT PRIMARY KEY,
|
|
5
|
+
sha256 TEXT NOT NULL,
|
|
6
|
+
session_id TEXT NOT NULL,
|
|
7
|
+
source_tool TEXT NOT NULL,
|
|
8
|
+
command TEXT,
|
|
9
|
+
created_turn INTEGER NOT NULL,
|
|
10
|
+
raw_tokens INTEGER NOT NULL,
|
|
11
|
+
raw_text TEXT NOT NULL,
|
|
12
|
+
summary TEXT NOT NULL,
|
|
13
|
+
content_type TEXT NOT NULL,
|
|
14
|
+
last_accessed_turn INTEGER NOT NULL,
|
|
15
|
+
access_count INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
created_at INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS idx_context_artifacts_session
|
|
20
|
+
ON context_artifacts(session_id);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_context_artifacts_sha256
|
|
22
|
+
ON context_artifacts(sha256);
|
|
23
|
+
|
|
24
|
+
CREATE TABLE IF NOT EXISTS distiller_stats (
|
|
25
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
26
|
+
session_id TEXT NOT NULL,
|
|
27
|
+
tool TEXT NOT NULL,
|
|
28
|
+
content_type TEXT NOT NULL,
|
|
29
|
+
distiller TEXT NOT NULL,
|
|
30
|
+
input_tokens INTEGER NOT NULL,
|
|
31
|
+
output_tokens INTEGER NOT NULL,
|
|
32
|
+
savings_pct REAL NOT NULL,
|
|
33
|
+
exec_time_ms INTEGER NOT NULL,
|
|
34
|
+
turn INTEGER NOT NULL,
|
|
35
|
+
created_at INTEGER NOT NULL
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS idx_distiller_stats_session
|
|
39
|
+
ON distiller_stats(session_id);
|
|
40
|
+
|
|
41
|
+
CREATE TABLE IF NOT EXISTS turn_ledger (
|
|
42
|
+
session_id TEXT NOT NULL,
|
|
43
|
+
turn INTEGER NOT NULL,
|
|
44
|
+
user_message TEXT NOT NULL,
|
|
45
|
+
assistant_message TEXT NOT NULL,
|
|
46
|
+
tool_calls_json TEXT NOT NULL,
|
|
47
|
+
artifact_ids_json TEXT NOT NULL,
|
|
48
|
+
files_read_json TEXT NOT NULL,
|
|
49
|
+
files_written_json TEXT NOT NULL,
|
|
50
|
+
errors_json TEXT NOT NULL,
|
|
51
|
+
token_count INTEGER NOT NULL,
|
|
52
|
+
search_text TEXT NOT NULL,
|
|
53
|
+
timestamp INTEGER NOT NULL,
|
|
54
|
+
PRIMARY KEY (session_id, turn)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
CREATE INDEX IF NOT EXISTS idx_turn_ledger_session
|
|
58
|
+
ON turn_ledger(session_id);
|
|
59
|
+
|
|
60
|
+
CREATE TABLE IF NOT EXISTS turn_digests (
|
|
61
|
+
session_id TEXT NOT NULL,
|
|
62
|
+
turn INTEGER NOT NULL,
|
|
63
|
+
digest_json TEXT NOT NULL,
|
|
64
|
+
created_at INTEGER NOT NULL,
|
|
65
|
+
PRIMARY KEY (session_id, turn)
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS activity_log (
|
|
69
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
70
|
+
session_id TEXT NOT NULL,
|
|
71
|
+
turn INTEGER NOT NULL,
|
|
72
|
+
type TEXT NOT NULL,
|
|
73
|
+
summary TEXT NOT NULL,
|
|
74
|
+
artifact_ref TEXT,
|
|
75
|
+
created_at INTEGER NOT NULL
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
CREATE INDEX IF NOT EXISTS idx_activity_log_session
|
|
79
|
+
ON activity_log(session_id, id DESC);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS extraction_state (
|
|
82
|
+
session_id TEXT PRIMARY KEY,
|
|
83
|
+
state_json TEXT NOT NULL,
|
|
84
|
+
updated_at INTEGER NOT NULL
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
CREATE TABLE IF NOT EXISTS session_checkpoints (
|
|
88
|
+
session_id TEXT PRIMARY KEY,
|
|
89
|
+
checkpoint_json TEXT NOT NULL,
|
|
90
|
+
updated_at INTEGER NOT NULL
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
CREATE TABLE IF NOT EXISTS artifact_access (
|
|
94
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
95
|
+
artifact_id TEXT NOT NULL,
|
|
96
|
+
session_id TEXT NOT NULL,
|
|
97
|
+
access_type TEXT NOT NULL,
|
|
98
|
+
turn INTEGER NOT NULL,
|
|
99
|
+
created_at INTEGER NOT NULL
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
CREATE INDEX IF NOT EXISTS idx_artifact_access_session
|
|
103
|
+
ON artifact_access(session_id, artifact_id);
|
|
104
|
+
|
|
105
|
+
CREATE TABLE IF NOT EXISTS session_stats (
|
|
106
|
+
session_id TEXT PRIMARY KEY,
|
|
107
|
+
context_engine_enabled INTEGER NOT NULL DEFAULT 0,
|
|
108
|
+
pressure_events INTEGER NOT NULL DEFAULT 0,
|
|
109
|
+
compaction_triggers INTEGER NOT NULL DEFAULT 0,
|
|
110
|
+
artifact_retrievals INTEGER NOT NULL DEFAULT 0,
|
|
111
|
+
total_distiller_savings REAL NOT NULL DEFAULT 0,
|
|
112
|
+
total_turns INTEGER NOT NULL DEFAULT 0,
|
|
113
|
+
updated_at INTEGER NOT NULL
|
|
114
|
+
);
|
|
115
|
+
`;
|
|
116
|
+
export function openContextEngineDb(dbPath) {
|
|
117
|
+
const db = new Database(dbPath);
|
|
118
|
+
db.pragma("journal_mode = WAL");
|
|
119
|
+
db.exec(ARTIFACT_SCHEMA);
|
|
120
|
+
return db;
|
|
121
|
+
}
|
|
122
|
+
function rowToArtifact(row) {
|
|
123
|
+
return {
|
|
124
|
+
id: row.id,
|
|
125
|
+
sha256: row.sha256,
|
|
126
|
+
sessionId: row.session_id,
|
|
127
|
+
sourceTool: row.source_tool,
|
|
128
|
+
command: row.command ?? undefined,
|
|
129
|
+
createdTurn: row.created_turn,
|
|
130
|
+
rawTokens: row.raw_tokens,
|
|
131
|
+
rawText: row.raw_text,
|
|
132
|
+
summary: row.summary,
|
|
133
|
+
contentType: row.content_type,
|
|
134
|
+
lastAccessedTurn: row.last_accessed_turn,
|
|
135
|
+
accessCount: row.access_count,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export function findArtifactByHash(db, sha256) {
|
|
139
|
+
const row = db
|
|
140
|
+
.prepare("SELECT * FROM context_artifacts WHERE sha256 = ? LIMIT 1")
|
|
141
|
+
.get(sha256);
|
|
142
|
+
return row ? rowToArtifact(row) : null;
|
|
143
|
+
}
|
|
144
|
+
export function getArtifactById(db, id) {
|
|
145
|
+
const row = db
|
|
146
|
+
.prepare("SELECT * FROM context_artifacts WHERE id = ?")
|
|
147
|
+
.get(id);
|
|
148
|
+
return row ? rowToArtifact(row) : null;
|
|
149
|
+
}
|
|
150
|
+
export function insertArtifact(db, artifact) {
|
|
151
|
+
db.prepare(`INSERT INTO context_artifacts (
|
|
152
|
+
id, sha256, session_id, source_tool, command, created_turn,
|
|
153
|
+
raw_tokens, raw_text, summary, content_type,
|
|
154
|
+
last_accessed_turn, access_count, created_at
|
|
155
|
+
) VALUES (
|
|
156
|
+
@id, @sha256, @sessionId, @sourceTool, @command, @createdTurn,
|
|
157
|
+
@rawTokens, @rawText, @summary, @contentType,
|
|
158
|
+
@lastAccessedTurn, @accessCount, @createdAt
|
|
159
|
+
)`).run({
|
|
160
|
+
id: artifact.id,
|
|
161
|
+
sha256: artifact.sha256,
|
|
162
|
+
sessionId: artifact.sessionId,
|
|
163
|
+
sourceTool: artifact.sourceTool,
|
|
164
|
+
command: artifact.command ?? null,
|
|
165
|
+
createdTurn: artifact.createdTurn,
|
|
166
|
+
rawTokens: artifact.rawTokens,
|
|
167
|
+
rawText: artifact.rawText,
|
|
168
|
+
summary: artifact.summary,
|
|
169
|
+
contentType: artifact.contentType,
|
|
170
|
+
lastAccessedTurn: artifact.lastAccessedTurn,
|
|
171
|
+
accessCount: artifact.accessCount,
|
|
172
|
+
createdAt: Date.now(),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
export function touchArtifactAccess(db, id, turn) {
|
|
176
|
+
db.prepare(`UPDATE context_artifacts
|
|
177
|
+
SET last_accessed_turn = @turn, access_count = access_count + 1
|
|
178
|
+
WHERE id = @id`).run({ id, turn });
|
|
179
|
+
}
|
|
180
|
+
export function updateArtifactSummary(db, id, summary) {
|
|
181
|
+
db.prepare("UPDATE context_artifacts SET summary = @summary WHERE id = @id").run({
|
|
182
|
+
id,
|
|
183
|
+
summary,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
export function insertDistillerStat(db, row) {
|
|
187
|
+
db.prepare(`INSERT INTO distiller_stats (
|
|
188
|
+
session_id, tool, content_type, distiller,
|
|
189
|
+
input_tokens, output_tokens, savings_pct, exec_time_ms, turn, created_at
|
|
190
|
+
) VALUES (
|
|
191
|
+
@sessionId, @tool, @contentType, @distiller,
|
|
192
|
+
@inputTokens, @outputTokens, @savingsPct, @execTimeMs, @turn, @createdAt
|
|
193
|
+
)`).run({
|
|
194
|
+
sessionId: row.sessionId,
|
|
195
|
+
tool: row.tool,
|
|
196
|
+
contentType: row.contentType,
|
|
197
|
+
distiller: row.distiller,
|
|
198
|
+
inputTokens: row.inputTokens,
|
|
199
|
+
outputTokens: row.outputTokens,
|
|
200
|
+
savingsPct: row.savingsPct,
|
|
201
|
+
execTimeMs: row.execTimeMs,
|
|
202
|
+
turn: row.turn,
|
|
203
|
+
createdAt: Date.now(),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
export function evictStaleArtifacts(db, currentTurn, ttlTurns) {
|
|
207
|
+
const cutoff = currentTurn - ttlTurns;
|
|
208
|
+
const extendedCutoff = currentTurn - ttlTurns * 2;
|
|
209
|
+
const result = db
|
|
210
|
+
.prepare(`DELETE FROM context_artifacts
|
|
211
|
+
WHERE (access_count < 4 AND last_accessed_turn < @cutoff)
|
|
212
|
+
OR (access_count >= 4 AND last_accessed_turn < @extendedCutoff)`)
|
|
213
|
+
.run({ cutoff, extendedCutoff });
|
|
214
|
+
return result.changes;
|
|
215
|
+
}
|
|
216
|
+
function rowToTurnRecord(row) {
|
|
217
|
+
return {
|
|
218
|
+
turn: row.turn,
|
|
219
|
+
userMessage: row.user_message,
|
|
220
|
+
assistantMessage: row.assistant_message,
|
|
221
|
+
toolCalls: JSON.parse(row.tool_calls_json),
|
|
222
|
+
artifactIds: JSON.parse(row.artifact_ids_json),
|
|
223
|
+
filesRead: JSON.parse(row.files_read_json),
|
|
224
|
+
filesWritten: JSON.parse(row.files_written_json),
|
|
225
|
+
errors: JSON.parse(row.errors_json),
|
|
226
|
+
tokenCount: row.token_count,
|
|
227
|
+
timestamp: row.timestamp,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
export function getMaxLedgerTurn(db, sessionId) {
|
|
231
|
+
const row = db
|
|
232
|
+
.prepare("SELECT MAX(turn) AS max_turn FROM turn_ledger WHERE session_id = ?")
|
|
233
|
+
.get(sessionId);
|
|
234
|
+
return row?.max_turn ?? null;
|
|
235
|
+
}
|
|
236
|
+
export function hasLedgerTurn(db, sessionId, turn) {
|
|
237
|
+
const row = db
|
|
238
|
+
.prepare("SELECT 1 FROM turn_ledger WHERE session_id = ? AND turn = ? LIMIT 1")
|
|
239
|
+
.get(sessionId, turn);
|
|
240
|
+
return !!row;
|
|
241
|
+
}
|
|
242
|
+
export function insertTurnRecord(db, sessionId, record, searchText) {
|
|
243
|
+
db.prepare(`INSERT OR IGNORE INTO turn_ledger (
|
|
244
|
+
session_id, turn, user_message, assistant_message,
|
|
245
|
+
tool_calls_json, artifact_ids_json, files_read_json, files_written_json,
|
|
246
|
+
errors_json, token_count, search_text, timestamp
|
|
247
|
+
) VALUES (
|
|
248
|
+
@sessionId, @turn, @userMessage, @assistantMessage,
|
|
249
|
+
@toolCallsJson, @artifactIdsJson, @filesReadJson, @filesWrittenJson,
|
|
250
|
+
@errorsJson, @tokenCount, @searchText, @timestamp
|
|
251
|
+
)`).run({
|
|
252
|
+
sessionId,
|
|
253
|
+
turn: record.turn,
|
|
254
|
+
userMessage: record.userMessage,
|
|
255
|
+
assistantMessage: record.assistantMessage,
|
|
256
|
+
toolCallsJson: JSON.stringify(record.toolCalls),
|
|
257
|
+
artifactIdsJson: JSON.stringify(record.artifactIds),
|
|
258
|
+
filesReadJson: JSON.stringify(record.filesRead),
|
|
259
|
+
filesWrittenJson: JSON.stringify(record.filesWritten),
|
|
260
|
+
errorsJson: JSON.stringify(record.errors),
|
|
261
|
+
tokenCount: record.tokenCount,
|
|
262
|
+
searchText,
|
|
263
|
+
timestamp: record.timestamp,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
export function listTurnRecords(db, sessionId) {
|
|
267
|
+
const rows = db
|
|
268
|
+
.prepare(`SELECT * FROM turn_ledger
|
|
269
|
+
WHERE session_id = ?
|
|
270
|
+
ORDER BY turn ASC`)
|
|
271
|
+
.all(sessionId);
|
|
272
|
+
return rows.map(rowToTurnRecord);
|
|
273
|
+
}
|
|
274
|
+
export function getTurnRecord(db, sessionId, turn) {
|
|
275
|
+
const row = db
|
|
276
|
+
.prepare("SELECT * FROM turn_ledger WHERE session_id = ? AND turn = ?")
|
|
277
|
+
.get(sessionId, turn);
|
|
278
|
+
return row ? rowToTurnRecord(row) : null;
|
|
279
|
+
}
|
|
280
|
+
export function listArtifactIdsForTurn(db, sessionId, turn) {
|
|
281
|
+
const rows = db
|
|
282
|
+
.prepare(`SELECT id FROM context_artifacts
|
|
283
|
+
WHERE session_id = ? AND created_turn = ?`)
|
|
284
|
+
.all(sessionId, turn);
|
|
285
|
+
return rows.map((r) => r.id);
|
|
286
|
+
}
|
|
287
|
+
export function insertTurnDigest(db, sessionId, digest) {
|
|
288
|
+
db.prepare(`INSERT OR REPLACE INTO turn_digests (session_id, turn, digest_json, created_at)
|
|
289
|
+
VALUES (@sessionId, @turn, @digestJson, @createdAt)`).run({
|
|
290
|
+
sessionId,
|
|
291
|
+
turn: digest.turnId,
|
|
292
|
+
digestJson: JSON.stringify(digest),
|
|
293
|
+
createdAt: Date.now(),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
export function insertActivityEntries(db, sessionId, entries) {
|
|
297
|
+
if (entries.length === 0)
|
|
298
|
+
return;
|
|
299
|
+
const stmt = db.prepare(`INSERT INTO activity_log (session_id, turn, type, summary, artifact_ref, created_at)
|
|
300
|
+
VALUES (@sessionId, @turn, @type, @summary, @artifactRef, @createdAt)`);
|
|
301
|
+
const createdAt = Date.now();
|
|
302
|
+
for (const entry of entries) {
|
|
303
|
+
stmt.run({
|
|
304
|
+
sessionId,
|
|
305
|
+
turn: entry.turn,
|
|
306
|
+
type: entry.type,
|
|
307
|
+
summary: entry.summary,
|
|
308
|
+
artifactRef: entry.artifactRef ?? null,
|
|
309
|
+
createdAt,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
export function listActivityEntries(db, sessionId, limit) {
|
|
314
|
+
const rows = db
|
|
315
|
+
.prepare(`SELECT turn, type, summary, artifact_ref
|
|
316
|
+
FROM activity_log
|
|
317
|
+
WHERE session_id = ?
|
|
318
|
+
ORDER BY id DESC
|
|
319
|
+
LIMIT ?`)
|
|
320
|
+
.all(sessionId, limit);
|
|
321
|
+
return rows.reverse().map((row) => ({
|
|
322
|
+
turn: row.turn,
|
|
323
|
+
type: row.type,
|
|
324
|
+
summary: row.summary,
|
|
325
|
+
artifactRef: row.artifact_ref ?? undefined,
|
|
326
|
+
}));
|
|
327
|
+
}
|
|
328
|
+
export function getExtractionState(db, sessionId) {
|
|
329
|
+
const row = db
|
|
330
|
+
.prepare("SELECT state_json FROM extraction_state WHERE session_id = ?")
|
|
331
|
+
.get(sessionId);
|
|
332
|
+
if (!row)
|
|
333
|
+
return null;
|
|
334
|
+
return JSON.parse(row.state_json);
|
|
335
|
+
}
|
|
336
|
+
export function upsertExtractionState(db, sessionId, state) {
|
|
337
|
+
db.prepare(`INSERT INTO extraction_state (session_id, state_json, updated_at)
|
|
338
|
+
VALUES (@sessionId, @stateJson, @updatedAt)
|
|
339
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
340
|
+
state_json = excluded.state_json,
|
|
341
|
+
updated_at = excluded.updated_at`).run({
|
|
342
|
+
sessionId,
|
|
343
|
+
stateJson: JSON.stringify(state),
|
|
344
|
+
updatedAt: Date.now(),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
export function getSessionCheckpoint(db, sessionId) {
|
|
348
|
+
const row = db
|
|
349
|
+
.prepare("SELECT checkpoint_json FROM session_checkpoints WHERE session_id = ?")
|
|
350
|
+
.get(sessionId);
|
|
351
|
+
if (!row)
|
|
352
|
+
return null;
|
|
353
|
+
return JSON.parse(row.checkpoint_json);
|
|
354
|
+
}
|
|
355
|
+
export function upsertSessionCheckpoint(db, sessionId, checkpoint) {
|
|
356
|
+
db.prepare(`INSERT INTO session_checkpoints (session_id, checkpoint_json, updated_at)
|
|
357
|
+
VALUES (@sessionId, @checkpointJson, @updatedAt)
|
|
358
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
359
|
+
checkpoint_json = excluded.checkpoint_json,
|
|
360
|
+
updated_at = excluded.updated_at`).run({
|
|
361
|
+
sessionId,
|
|
362
|
+
checkpointJson: JSON.stringify(checkpoint),
|
|
363
|
+
updatedAt: Date.now(),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
export function getTurnDigest(db, sessionId, turn) {
|
|
367
|
+
const row = db
|
|
368
|
+
.prepare("SELECT digest_json FROM turn_digests WHERE session_id = ? AND turn = ?")
|
|
369
|
+
.get(sessionId, turn);
|
|
370
|
+
if (!row)
|
|
371
|
+
return null;
|
|
372
|
+
return normalizeStoredTurnDigest(JSON.parse(row.digest_json));
|
|
373
|
+
}
|
|
374
|
+
export function listTurnDigests(db, sessionId) {
|
|
375
|
+
const rows = db
|
|
376
|
+
.prepare(`SELECT digest_json FROM turn_digests
|
|
377
|
+
WHERE session_id = ?
|
|
378
|
+
ORDER BY turn ASC`)
|
|
379
|
+
.all(sessionId);
|
|
380
|
+
return rows.map((row) => normalizeStoredTurnDigest(JSON.parse(row.digest_json)));
|
|
381
|
+
}
|
|
382
|
+
function normalizeStoredTurnDigest(raw) {
|
|
383
|
+
const decisions = (raw.decisions ?? []).map((d) => typeof d === "string" ? { summary: d } : d);
|
|
384
|
+
return {
|
|
385
|
+
...raw,
|
|
386
|
+
filesWritten: raw.filesWritten ?? [],
|
|
387
|
+
decisions,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
export function listSessionArtifacts(db, sessionId) {
|
|
391
|
+
const rows = db
|
|
392
|
+
.prepare(`SELECT * FROM context_artifacts
|
|
393
|
+
WHERE session_id = ?
|
|
394
|
+
ORDER BY created_turn ASC, id ASC`)
|
|
395
|
+
.all(sessionId);
|
|
396
|
+
return rows.map(rowToArtifact);
|
|
397
|
+
}
|
|
398
|
+
export function countSessionArtifacts(db, sessionId) {
|
|
399
|
+
const row = db
|
|
400
|
+
.prepare("SELECT COUNT(*) AS count FROM context_artifacts WHERE session_id = ?")
|
|
401
|
+
.get(sessionId);
|
|
402
|
+
return row.count;
|
|
403
|
+
}
|
|
404
|
+
export function listAllActivityEntries(db, sessionId) {
|
|
405
|
+
const rows = db
|
|
406
|
+
.prepare(`SELECT turn, type, summary, artifact_ref
|
|
407
|
+
FROM activity_log
|
|
408
|
+
WHERE session_id = ?
|
|
409
|
+
ORDER BY id ASC`)
|
|
410
|
+
.all(sessionId);
|
|
411
|
+
return rows.map((row) => ({
|
|
412
|
+
turn: row.turn,
|
|
413
|
+
type: row.type,
|
|
414
|
+
summary: row.summary,
|
|
415
|
+
artifactRef: row.artifact_ref ?? undefined,
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
export function insertArtifactAccess(db, sessionId, artifactId, accessType, turn) {
|
|
419
|
+
db.prepare(`INSERT INTO artifact_access (artifact_id, session_id, access_type, turn, created_at)
|
|
420
|
+
VALUES (@artifactId, @sessionId, @accessType, @turn, @createdAt)`).run({
|
|
421
|
+
artifactId,
|
|
422
|
+
sessionId,
|
|
423
|
+
accessType,
|
|
424
|
+
turn,
|
|
425
|
+
createdAt: Date.now(),
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
export function getSessionStatsRow(db, sessionId) {
|
|
429
|
+
const row = db
|
|
430
|
+
.prepare(`SELECT session_id, context_engine_enabled, pressure_events, compaction_triggers, artifact_retrievals,
|
|
431
|
+
total_distiller_savings, total_turns
|
|
432
|
+
FROM session_stats WHERE session_id = ?`)
|
|
433
|
+
.get(sessionId);
|
|
434
|
+
if (!row)
|
|
435
|
+
return null;
|
|
436
|
+
return {
|
|
437
|
+
sessionId: row.session_id,
|
|
438
|
+
contextEngineEnabled: row.context_engine_enabled === 1,
|
|
439
|
+
pressureEvents: row.pressure_events,
|
|
440
|
+
compactionTriggers: row.compaction_triggers,
|
|
441
|
+
artifactRetrievals: row.artifact_retrievals,
|
|
442
|
+
totalDistillerSavings: row.total_distiller_savings,
|
|
443
|
+
totalTurns: row.total_turns,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function ensureSessionStatsRow(db, sessionId, contextEngineEnabled = false) {
|
|
447
|
+
db.prepare(`INSERT OR IGNORE INTO session_stats (
|
|
448
|
+
session_id, context_engine_enabled, pressure_events, compaction_triggers, artifact_retrievals,
|
|
449
|
+
total_distiller_savings, total_turns, updated_at
|
|
450
|
+
) VALUES (@sessionId, @contextEngineEnabled, 0, 0, 0, 0, 0, @updatedAt)`).run({ sessionId, contextEngineEnabled: contextEngineEnabled ? 1 : 0, updatedAt: Date.now() });
|
|
451
|
+
}
|
|
452
|
+
export function incrementSessionStat(db, sessionId, field, amount = 1, contextEngineEnabled = false) {
|
|
453
|
+
ensureSessionStatsRow(db, sessionId, contextEngineEnabled);
|
|
454
|
+
db.prepare(`UPDATE session_stats
|
|
455
|
+
SET ${field} = ${field} + @amount, updated_at = @updatedAt
|
|
456
|
+
WHERE session_id = @sessionId`).run({ sessionId, amount, updatedAt: Date.now() });
|
|
457
|
+
}
|
|
458
|
+
export function finalizeSessionStats(db, sessionId, totalTurns, contextEngineEnabled = false) {
|
|
459
|
+
ensureSessionStatsRow(db, sessionId, contextEngineEnabled);
|
|
460
|
+
const savingsRow = db
|
|
461
|
+
.prepare(`SELECT COALESCE(SUM(input_tokens - output_tokens), 0) AS savings
|
|
462
|
+
FROM distiller_stats WHERE session_id = ?`)
|
|
463
|
+
.get(sessionId);
|
|
464
|
+
db.prepare(`UPDATE session_stats
|
|
465
|
+
SET total_turns = @totalTurns,
|
|
466
|
+
total_distiller_savings = @savings,
|
|
467
|
+
context_engine_enabled = @contextEngineEnabled,
|
|
468
|
+
updated_at = @updatedAt
|
|
469
|
+
WHERE session_id = @sessionId`).run({
|
|
470
|
+
sessionId,
|
|
471
|
+
totalTurns,
|
|
472
|
+
savings: savingsRow.savings,
|
|
473
|
+
contextEngineEnabled: contextEngineEnabled ? 1 : 0,
|
|
474
|
+
updatedAt: Date.now(),
|
|
475
|
+
});
|
|
476
|
+
return getSessionStatsRow(db, sessionId);
|
|
477
|
+
}
|
|
478
|
+
export function listDistillerCostRanking(db, sessionId) {
|
|
479
|
+
const rows = db
|
|
480
|
+
.prepare(`SELECT distiller, tool,
|
|
481
|
+
COUNT(*) AS runs,
|
|
482
|
+
AVG(input_tokens) AS avg_input,
|
|
483
|
+
AVG(savings_pct) AS avg_savings_pct,
|
|
484
|
+
SUM(input_tokens * (1.0 - (savings_pct / 100.0))) AS estimated_cost
|
|
485
|
+
FROM distiller_stats
|
|
486
|
+
WHERE session_id = ?
|
|
487
|
+
GROUP BY distiller, tool
|
|
488
|
+
ORDER BY estimated_cost DESC`)
|
|
489
|
+
.all(sessionId);
|
|
490
|
+
return rows.map((row) => ({
|
|
491
|
+
distiller: row.distiller,
|
|
492
|
+
tool: row.tool,
|
|
493
|
+
runs: row.runs,
|
|
494
|
+
avgInputTokens: Math.round(row.avg_input),
|
|
495
|
+
avgSavingsPct: Number(row.avg_savings_pct.toFixed(1)),
|
|
496
|
+
estimatedCost: Math.round(row.estimated_cost),
|
|
497
|
+
}));
|
|
498
|
+
}
|
|
499
|
+
export function countArtifactAccessByType(db, sessionId, accessType) {
|
|
500
|
+
const row = db
|
|
501
|
+
.prepare(`SELECT COUNT(*) AS count FROM artifact_access
|
|
502
|
+
WHERE session_id = ? AND access_type = ?`)
|
|
503
|
+
.get(sessionId, accessType);
|
|
504
|
+
return row.count;
|
|
505
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ContentType } from "./types.js";
|
|
2
|
+
export type DistillerIntensity = "lite" | "full";
|
|
3
|
+
export type DistillerMode = "sync" | "deferred";
|
|
4
|
+
export interface Distiller {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly contentTypes: ContentType[];
|
|
7
|
+
readonly mode: DistillerMode;
|
|
8
|
+
distill(input: string, intensity: DistillerIntensity, contentType?: ContentType): string;
|
|
9
|
+
}
|
|
10
|
+
export interface DistillResult {
|
|
11
|
+
summary: string;
|
|
12
|
+
distillerName: string;
|
|
13
|
+
execTimeMs: number;
|
|
14
|
+
deferred: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface DistillDeferredResult {
|
|
17
|
+
pendingSummary: string;
|
|
18
|
+
distillerName: string;
|
|
19
|
+
backfill: () => Promise<DistillResult>;
|
|
20
|
+
}
|
|
21
|
+
export declare function buildPendingSummary(): string;
|
|
22
|
+
export declare function isPendingSummary(summary: string): boolean;
|
|
23
|
+
export declare class DistillerRegistry {
|
|
24
|
+
private readonly distillers;
|
|
25
|
+
register(distiller: Distiller): void;
|
|
26
|
+
find(contentType: ContentType): Distiller | null;
|
|
27
|
+
selectIntensity(rawTokens: number, defaultIntensity: DistillerIntensity): DistillerIntensity;
|
|
28
|
+
distillSync(input: string, contentType: ContentType, intensity: DistillerIntensity): DistillResult;
|
|
29
|
+
distillForIngestion(input: string, contentType: ContentType, intensity: DistillerIntensity): DistillResult | DistillDeferredResult;
|
|
30
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { summarizeGeneric } from "./summarize.js";
|
|
2
|
+
const PENDING_MARKER = "[compression pending — full content available via retrieve_artifact]";
|
|
3
|
+
export function buildPendingSummary() {
|
|
4
|
+
return PENDING_MARKER;
|
|
5
|
+
}
|
|
6
|
+
export function isPendingSummary(summary) {
|
|
7
|
+
return summary.includes(PENDING_MARKER);
|
|
8
|
+
}
|
|
9
|
+
export class DistillerRegistry {
|
|
10
|
+
distillers = [];
|
|
11
|
+
register(distiller) {
|
|
12
|
+
this.distillers.push(distiller);
|
|
13
|
+
}
|
|
14
|
+
find(contentType) {
|
|
15
|
+
return this.distillers.find((d) => d.contentTypes.includes(contentType)) ?? null;
|
|
16
|
+
}
|
|
17
|
+
selectIntensity(rawTokens, defaultIntensity) {
|
|
18
|
+
if (rawTokens > 2000)
|
|
19
|
+
return "full";
|
|
20
|
+
if (rawTokens > 400)
|
|
21
|
+
return "lite";
|
|
22
|
+
return defaultIntensity;
|
|
23
|
+
}
|
|
24
|
+
distillSync(input, contentType, intensity) {
|
|
25
|
+
const distiller = this.find(contentType);
|
|
26
|
+
const start = performance.now();
|
|
27
|
+
if (!distiller || distiller.mode === "deferred") {
|
|
28
|
+
const summary = summarizeGeneric(input, contentType);
|
|
29
|
+
return {
|
|
30
|
+
summary,
|
|
31
|
+
distillerName: distiller?.name ?? "generic-fallback",
|
|
32
|
+
execTimeMs: performance.now() - start,
|
|
33
|
+
deferred: false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const summary = distiller.distill(input, intensity, contentType);
|
|
37
|
+
return {
|
|
38
|
+
summary,
|
|
39
|
+
distillerName: distiller.name,
|
|
40
|
+
execTimeMs: performance.now() - start,
|
|
41
|
+
deferred: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
distillForIngestion(input, contentType, intensity) {
|
|
45
|
+
const distiller = this.find(contentType);
|
|
46
|
+
if (!distiller) {
|
|
47
|
+
return this.distillSync(input, contentType, intensity);
|
|
48
|
+
}
|
|
49
|
+
if (distiller.mode === "deferred") {
|
|
50
|
+
return {
|
|
51
|
+
pendingSummary: buildPendingSummary(),
|
|
52
|
+
distillerName: distiller.name,
|
|
53
|
+
backfill: async () => {
|
|
54
|
+
const start = performance.now();
|
|
55
|
+
const summary = distiller.distill(input, intensity, contentType);
|
|
56
|
+
return {
|
|
57
|
+
summary,
|
|
58
|
+
distillerName: distiller.name,
|
|
59
|
+
execTimeMs: performance.now() - start,
|
|
60
|
+
deferred: true,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return this.distillSync(input, contentType, intensity);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CompileInput, CompileMetrics } from "../compiler.js";
|
|
2
|
+
import { recencyScore, scoreContextUnit } from "./scoring.js";
|
|
3
|
+
import type { ActivityEntry, CompileScoreRecord, PressureMode, TurnRecord } from "./types.js";
|
|
4
|
+
import type { ContextEngineConfig } from "../types.js";
|
|
5
|
+
export interface EngineCompileInput extends CompileInput {
|
|
6
|
+
currentTurn: number;
|
|
7
|
+
turnRecords: TurnRecord[];
|
|
8
|
+
activityEntries?: ActivityEntry[];
|
|
9
|
+
engineConfig: ContextEngineConfig;
|
|
10
|
+
/** Model input context window; pressure is measured against this when set. */
|
|
11
|
+
contextWindowTokens?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface EngineCompileResult {
|
|
14
|
+
prompt: string;
|
|
15
|
+
metrics: CompileMetrics;
|
|
16
|
+
scoreRecords: CompileScoreRecord[];
|
|
17
|
+
pressureRatio: number;
|
|
18
|
+
pressureMode: PressureMode;
|
|
19
|
+
excludedScoredUnits: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function compileEngineWithMetrics(input: EngineCompileInput): EngineCompileResult;
|
|
22
|
+
export declare function explainUnitScore(unitId: string, records: CompileScoreRecord[], currentTurn: number, userInput: string, weights: ContextEngineConfig["scoring"], bandTokenBudget: number, bandUsedTokens: number): string[];
|
|
23
|
+
export { recencyScore, scoreContextUnit };
|