@gramatr/mcp 0.7.5 → 0.7.6
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/hooks/lib/feedback.d.ts +0 -2
- package/dist/hooks/lib/feedback.d.ts.map +1 -1
- package/dist/hooks/lib/feedback.js +18 -19
- package/dist/hooks/lib/feedback.js.map +1 -1
- package/dist/hooks/lib/formatting-compat.d.ts.map +1 -1
- package/dist/hooks/lib/formatting-compat.js +82 -55
- package/dist/hooks/lib/formatting-compat.js.map +1 -1
- package/dist/hooks/lib/gramatr-hook-utils.d.ts +0 -59
- package/dist/hooks/lib/gramatr-hook-utils.d.ts.map +1 -1
- package/dist/hooks/lib/gramatr-hook-utils.js +1 -212
- package/dist/hooks/lib/gramatr-hook-utils.js.map +1 -1
- package/dist/hooks/lib/hook-state.d.ts +159 -0
- package/dist/hooks/lib/hook-state.d.ts.map +1 -0
- package/dist/hooks/lib/hook-state.js +430 -0
- package/dist/hooks/lib/hook-state.js.map +1 -0
- package/dist/hooks/lib/intelligence.d.ts.map +1 -1
- package/dist/hooks/lib/intelligence.js +112 -88
- package/dist/hooks/lib/intelligence.js.map +1 -1
- package/dist/hooks/lib/routing.d.ts +2 -2
- package/dist/hooks/lib/routing.d.ts.map +1 -1
- package/dist/hooks/lib/routing.js +71 -25
- package/dist/hooks/lib/routing.js.map +1 -1
- package/dist/hooks/lib/session-end.d.ts +7 -0
- package/dist/hooks/lib/session-end.d.ts.map +1 -1
- package/dist/hooks/lib/session-end.js +33 -16
- package/dist/hooks/lib/session-end.js.map +1 -1
- package/dist/hooks/lib/session.d.ts +7 -38
- package/dist/hooks/lib/session.d.ts.map +1 -1
- package/dist/hooks/lib/session.js +43 -87
- package/dist/hooks/lib/session.js.map +1 -1
- package/dist/hooks/lib/types.d.ts +174 -36
- package/dist/hooks/lib/types.d.ts.map +1 -1
- package/dist/hooks/session-end.d.ts.map +1 -1
- package/dist/hooks/session-end.js +53 -45
- package/dist/hooks/session-end.js.map +1 -1
- package/dist/hooks/session-start.d.ts.map +1 -1
- package/dist/hooks/session-start.js +87 -77
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop.d.ts.map +1 -1
- package/dist/hooks/stop.js +1 -10
- package/dist/hooks/stop.js.map +1 -1
- package/dist/hooks/user-prompt-submit.d.ts.map +1 -1
- package/dist/hooks/user-prompt-submit.js +57 -68
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/dist/intelligence/packet2-fetcher.d.ts +3 -3
- package/dist/intelligence/packet2-fetcher.js +8 -8
- package/dist/intelligence/packet2-fetcher.js.map +1 -1
- package/dist/proxy/local-client.d.ts +49 -0
- package/dist/proxy/local-client.d.ts.map +1 -0
- package/dist/proxy/local-client.js +135 -0
- package/dist/proxy/local-client.js.map +1 -0
- package/dist/server/hooks-listener.d.ts +32 -0
- package/dist/server/hooks-listener.d.ts.map +1 -0
- package/dist/server/hooks-listener.js +144 -0
- package/dist/server/hooks-listener.js.map +1 -0
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +16 -1
- package/dist/server/server.js.map +1 -1
- package/dist/setup/integrations.js +3 -3
- package/dist/setup/integrations.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hook-state.ts — Centralized SQLite state store for all gramatr hooks.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the scattered ~/.gramatr/.state/ JSON/JSONL files with a single
|
|
5
|
+
* ~/.gramatr/state.db SQLite database. All hook processes share this DB;
|
|
6
|
+
* WAL mode ensures safe concurrent writes from simultaneous hook invocations.
|
|
7
|
+
*
|
|
8
|
+
* Set GRAMATR_STATE_DB=:memory: in tests to avoid touching the filesystem.
|
|
9
|
+
*/
|
|
10
|
+
import Database from 'better-sqlite3';
|
|
11
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { getGramatrDirFromEnv, getHomeDir } from '../../config-runtime.js';
|
|
14
|
+
// ── DB singleton ──
|
|
15
|
+
let _db = null;
|
|
16
|
+
/** Set to false if the state DB directory cannot be created or the file cannot be opened. */
|
|
17
|
+
let _filesystemAvailable = true;
|
|
18
|
+
function getDbPath() {
|
|
19
|
+
if (process.env.GRAMATR_STATE_DB)
|
|
20
|
+
return process.env.GRAMATR_STATE_DB;
|
|
21
|
+
const dir = getGramatrDirFromEnv() || join(getHomeDir(), '.gramatr');
|
|
22
|
+
return join(dir, 'state.db');
|
|
23
|
+
}
|
|
24
|
+
function getDb() {
|
|
25
|
+
if (_db)
|
|
26
|
+
return _db;
|
|
27
|
+
const path = getDbPath();
|
|
28
|
+
if (path !== ':memory:') {
|
|
29
|
+
try {
|
|
30
|
+
const dir = dirname(path);
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
mkdirSync(dir, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
_filesystemAvailable = false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
_db = new Database(_filesystemAvailable ? path : ':memory:');
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
_filesystemAvailable = false;
|
|
43
|
+
_db = new Database(':memory:');
|
|
44
|
+
}
|
|
45
|
+
_db.pragma('journal_mode = WAL');
|
|
46
|
+
_db.pragma('synchronous = NORMAL');
|
|
47
|
+
_db.exec(`
|
|
48
|
+
CREATE TABLE IF NOT EXISTS session_context (
|
|
49
|
+
session_id TEXT PRIMARY KEY,
|
|
50
|
+
project_id TEXT,
|
|
51
|
+
interaction_id TEXT,
|
|
52
|
+
entity_id TEXT,
|
|
53
|
+
project_name TEXT,
|
|
54
|
+
git_root TEXT,
|
|
55
|
+
git_branch TEXT,
|
|
56
|
+
git_remote TEXT,
|
|
57
|
+
working_directory TEXT,
|
|
58
|
+
session_start TEXT,
|
|
59
|
+
updated_at TEXT NOT NULL,
|
|
60
|
+
client_type TEXT,
|
|
61
|
+
agent_name TEXT
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
CREATE TABLE IF NOT EXISTS turns (
|
|
65
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
66
|
+
session_id TEXT NOT NULL,
|
|
67
|
+
turn_number INTEGER,
|
|
68
|
+
timestamp TEXT,
|
|
69
|
+
prompt TEXT,
|
|
70
|
+
effort_level TEXT,
|
|
71
|
+
intent_type TEXT,
|
|
72
|
+
confidence REAL,
|
|
73
|
+
tokens_saved INTEGER
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS op_history (
|
|
77
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
78
|
+
session_id TEXT NOT NULL,
|
|
79
|
+
tool TEXT,
|
|
80
|
+
time_ms INTEGER,
|
|
81
|
+
tokens_saved INTEGER,
|
|
82
|
+
timestamp INTEGER
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
CREATE TABLE IF NOT EXISTS latest_classification (
|
|
86
|
+
session_id TEXT PRIMARY KEY,
|
|
87
|
+
classifier_model TEXT,
|
|
88
|
+
classifier_time_ms INTEGER,
|
|
89
|
+
tokens_saved INTEGER,
|
|
90
|
+
savings_ratio REAL,
|
|
91
|
+
effort TEXT,
|
|
92
|
+
intent TEXT,
|
|
93
|
+
confidence REAL,
|
|
94
|
+
memory_delivered INTEGER,
|
|
95
|
+
downstream_model TEXT,
|
|
96
|
+
server_version TEXT,
|
|
97
|
+
stage_timing TEXT,
|
|
98
|
+
recorded_at INTEGER NOT NULL,
|
|
99
|
+
original_prompt TEXT,
|
|
100
|
+
pending_feedback INTEGER DEFAULT 0,
|
|
101
|
+
feedback_submitted_at TEXT,
|
|
102
|
+
client_type TEXT,
|
|
103
|
+
agent_name TEXT,
|
|
104
|
+
memory_tier TEXT,
|
|
105
|
+
memory_scope TEXT
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
CREATE TABLE IF NOT EXISTS session_log (
|
|
109
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
110
|
+
session_id TEXT,
|
|
111
|
+
project_id TEXT,
|
|
112
|
+
ended_at TEXT,
|
|
113
|
+
reason TEXT,
|
|
114
|
+
commit_log TEXT,
|
|
115
|
+
interaction_id TEXT,
|
|
116
|
+
entity_id TEXT,
|
|
117
|
+
client_type TEXT,
|
|
118
|
+
agent_name TEXT,
|
|
119
|
+
synced_at TEXT
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
CREATE UNIQUE INDEX IF NOT EXISTS session_log_session_id
|
|
123
|
+
ON session_log(session_id)
|
|
124
|
+
WHERE session_id IS NOT NULL;
|
|
125
|
+
`);
|
|
126
|
+
// Migrate existing DBs: add columns that were added after initial schema.
|
|
127
|
+
// These are no-ops if the column already exists (SQLite 3.37+ IF NOT EXISTS).
|
|
128
|
+
// For older SQLite we catch and ignore.
|
|
129
|
+
const migrations = [
|
|
130
|
+
'ALTER TABLE session_context ADD COLUMN entity_id TEXT',
|
|
131
|
+
'ALTER TABLE session_context ADD COLUMN client_type TEXT',
|
|
132
|
+
'ALTER TABLE session_context ADD COLUMN agent_name TEXT',
|
|
133
|
+
'ALTER TABLE latest_classification ADD COLUMN original_prompt TEXT',
|
|
134
|
+
'ALTER TABLE latest_classification ADD COLUMN pending_feedback INTEGER DEFAULT 0',
|
|
135
|
+
'ALTER TABLE latest_classification ADD COLUMN feedback_submitted_at TEXT',
|
|
136
|
+
'ALTER TABLE latest_classification ADD COLUMN client_type TEXT',
|
|
137
|
+
'ALTER TABLE latest_classification ADD COLUMN agent_name TEXT',
|
|
138
|
+
'ALTER TABLE latest_classification ADD COLUMN memory_tier TEXT',
|
|
139
|
+
'ALTER TABLE latest_classification ADD COLUMN memory_scope TEXT',
|
|
140
|
+
'ALTER TABLE session_log ADD COLUMN interaction_id TEXT',
|
|
141
|
+
'ALTER TABLE session_log ADD COLUMN entity_id TEXT',
|
|
142
|
+
'ALTER TABLE session_log ADD COLUMN client_type TEXT',
|
|
143
|
+
'ALTER TABLE session_log ADD COLUMN agent_name TEXT',
|
|
144
|
+
'ALTER TABLE session_log ADD COLUMN synced_at TEXT',
|
|
145
|
+
// Unique index is safe to re-run (IF NOT EXISTS)
|
|
146
|
+
'CREATE UNIQUE INDEX IF NOT EXISTS session_log_session_id ON session_log(session_id) WHERE session_id IS NOT NULL',
|
|
147
|
+
];
|
|
148
|
+
for (const sql of migrations) {
|
|
149
|
+
try {
|
|
150
|
+
_db.exec(sql);
|
|
151
|
+
}
|
|
152
|
+
catch { /* column already exists */ }
|
|
153
|
+
}
|
|
154
|
+
return _db;
|
|
155
|
+
}
|
|
156
|
+
/** Close and reset the DB connection. Primarily for tests. */
|
|
157
|
+
export function closeDb() {
|
|
158
|
+
_db?.close();
|
|
159
|
+
_db = null;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Returns true when the state DB is backed by the real filesystem.
|
|
163
|
+
* Returns false when the process could not create/open the state DB file
|
|
164
|
+
* (e.g. a filesystem-locked sandbox) and fell back to :memory:.
|
|
165
|
+
* In-memory mode means cross-hook state (session context) is not shared
|
|
166
|
+
* between processes — hooks must call hydrateSessionContextFromServer()
|
|
167
|
+
* to fetch context from the server instead.
|
|
168
|
+
*/
|
|
169
|
+
export function isFilesystemAvailable() {
|
|
170
|
+
// Ensure the DB has been attempted so the flag is accurate.
|
|
171
|
+
getDb();
|
|
172
|
+
return _filesystemAvailable;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Fetch session context and populate the in-memory DB so synchronous readers
|
|
176
|
+
* (getSessionContext) work without hitting the filesystem.
|
|
177
|
+
*
|
|
178
|
+
* Called by user-prompt-submit and session-end when getSessionContext() returns
|
|
179
|
+
* null in a fresh process (SQLite cross-process IPC unavailable or filesystem
|
|
180
|
+
* locked). Priority:
|
|
181
|
+
*
|
|
182
|
+
* 1. Local hooks server in-memory store (Phase IV, fast, no auth needed)
|
|
183
|
+
* 2. Remote REST API (Phase III fallback, needs auth)
|
|
184
|
+
*
|
|
185
|
+
* No-op if both sources are unreachable — hooks already have null-safe logic.
|
|
186
|
+
*/
|
|
187
|
+
export async function hydrateSessionContextFromServer(sessionId) {
|
|
188
|
+
// ── Phase IV path: local hooks server ──
|
|
189
|
+
try {
|
|
190
|
+
const { pullSessionContextFromLocal } = await import('../../proxy/local-client.js');
|
|
191
|
+
const localCtx = await pullSessionContextFromLocal(sessionId);
|
|
192
|
+
if (localCtx) {
|
|
193
|
+
setSessionContext(localCtx);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
// Local server not available — fall through to remote REST
|
|
199
|
+
}
|
|
200
|
+
// ── Phase III fallback: remote REST API ──
|
|
201
|
+
try {
|
|
202
|
+
const { getServerUrl, getToken } = await import('../../server/auth.js');
|
|
203
|
+
const serverUrl = getServerUrl();
|
|
204
|
+
const token = getToken();
|
|
205
|
+
const url = `${serverUrl}/api/v1/sessions?session_id=${encodeURIComponent(sessionId)}`;
|
|
206
|
+
const headers = { Accept: 'application/json' };
|
|
207
|
+
if (token)
|
|
208
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
209
|
+
const response = await fetch(url, {
|
|
210
|
+
headers,
|
|
211
|
+
signal: AbortSignal.timeout(5000),
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok)
|
|
214
|
+
return;
|
|
215
|
+
const data = await response.json();
|
|
216
|
+
const session = data.sessions?.[0];
|
|
217
|
+
if (!session)
|
|
218
|
+
return;
|
|
219
|
+
setSessionContext({
|
|
220
|
+
session_id: session.client_session_id ?? sessionId,
|
|
221
|
+
project_id: session.project_id ?? null,
|
|
222
|
+
interaction_id: session.interaction_id ?? null,
|
|
223
|
+
entity_id: null,
|
|
224
|
+
project_name: null,
|
|
225
|
+
git_root: null,
|
|
226
|
+
git_branch: session.git_branch ?? null,
|
|
227
|
+
git_remote: session.git_remote ?? null,
|
|
228
|
+
working_directory: null,
|
|
229
|
+
session_start: session.started_at ?? null,
|
|
230
|
+
updated_at: session.updated_at ?? new Date().toISOString(),
|
|
231
|
+
client_type: session.client_type ?? null,
|
|
232
|
+
agent_name: session.agent_name ?? null,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
// Non-critical — hooks degrade gracefully with a null session context.
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// ── Session context — cross-hook IPC ──
|
|
240
|
+
/** Write current project/session context so other hooks can read it. */
|
|
241
|
+
export function setSessionContext(ctx) {
|
|
242
|
+
getDb().prepare(`
|
|
243
|
+
INSERT OR REPLACE INTO session_context
|
|
244
|
+
(session_id, project_id, interaction_id, entity_id, project_name, git_root,
|
|
245
|
+
git_branch, git_remote, working_directory, session_start, updated_at,
|
|
246
|
+
client_type, agent_name)
|
|
247
|
+
VALUES
|
|
248
|
+
(@session_id, @project_id, @interaction_id, @entity_id, @project_name, @git_root,
|
|
249
|
+
@git_branch, @git_remote, @working_directory, @session_start, @updated_at,
|
|
250
|
+
@client_type, @agent_name)
|
|
251
|
+
`).run(ctx);
|
|
252
|
+
}
|
|
253
|
+
/** Read the most recently written session context. */
|
|
254
|
+
export function getSessionContext() {
|
|
255
|
+
const row = getDb()
|
|
256
|
+
.prepare('SELECT * FROM session_context ORDER BY updated_at DESC LIMIT 1')
|
|
257
|
+
.get();
|
|
258
|
+
return row ?? null;
|
|
259
|
+
}
|
|
260
|
+
// ── Turns — per-prompt metadata accumulated across user-prompt-submit calls ──
|
|
261
|
+
/** Append one turn record. Called once per user-prompt-submit invocation. */
|
|
262
|
+
export function appendTurn(turn) {
|
|
263
|
+
getDb().prepare(`
|
|
264
|
+
INSERT INTO turns
|
|
265
|
+
(session_id, turn_number, timestamp, prompt, effort_level, intent_type, confidence, tokens_saved)
|
|
266
|
+
VALUES
|
|
267
|
+
(@session_id, @turn_number, @timestamp, @prompt, @effort_level, @intent_type, @confidence, @tokens_saved)
|
|
268
|
+
`).run(turn);
|
|
269
|
+
}
|
|
270
|
+
/** Return all turns for a session and delete them atomically. Called at session-end. */
|
|
271
|
+
export function flushTurns(sessionId) {
|
|
272
|
+
const db = getDb();
|
|
273
|
+
const rows = db
|
|
274
|
+
.prepare('SELECT * FROM turns WHERE session_id = ? ORDER BY id')
|
|
275
|
+
.all(sessionId);
|
|
276
|
+
db.prepare('DELETE FROM turns WHERE session_id = ?').run(sessionId);
|
|
277
|
+
return rows;
|
|
278
|
+
}
|
|
279
|
+
// ── Op history — tool call log accumulated per session ──
|
|
280
|
+
/** Append one op record. Called once per gramatr tool call. */
|
|
281
|
+
export function appendOpHistory(op) {
|
|
282
|
+
getDb().prepare(`
|
|
283
|
+
INSERT INTO op_history (session_id, tool, time_ms, tokens_saved, timestamp)
|
|
284
|
+
VALUES (@session_id, @tool, @time_ms, @tokens_saved, @timestamp)
|
|
285
|
+
`).run(op);
|
|
286
|
+
}
|
|
287
|
+
/** Return all op records for a session and delete them atomically. Called at session-end. */
|
|
288
|
+
export function flushOpHistory(sessionId) {
|
|
289
|
+
const db = getDb();
|
|
290
|
+
const rows = db
|
|
291
|
+
.prepare('SELECT * FROM op_history WHERE session_id = ? ORDER BY id')
|
|
292
|
+
.all(sessionId);
|
|
293
|
+
db.prepare('DELETE FROM op_history WHERE session_id = ?').run(sessionId);
|
|
294
|
+
return rows;
|
|
295
|
+
}
|
|
296
|
+
// ── Latest classification — for statusline display and feedback ──
|
|
297
|
+
/** Overwrite the latest classification result for a session. */
|
|
298
|
+
export function setLatestClassification(record) {
|
|
299
|
+
getDb().prepare(`
|
|
300
|
+
INSERT OR REPLACE INTO latest_classification
|
|
301
|
+
(session_id, classifier_model, classifier_time_ms, tokens_saved, savings_ratio,
|
|
302
|
+
effort, intent, confidence, memory_delivered, downstream_model,
|
|
303
|
+
server_version, stage_timing, recorded_at,
|
|
304
|
+
original_prompt, pending_feedback, feedback_submitted_at,
|
|
305
|
+
client_type, agent_name, memory_tier, memory_scope)
|
|
306
|
+
VALUES
|
|
307
|
+
(@session_id, @classifier_model, @classifier_time_ms, @tokens_saved, @savings_ratio,
|
|
308
|
+
@effort, @intent, @confidence, @memory_delivered, @downstream_model,
|
|
309
|
+
@server_version, @stage_timing, @recorded_at,
|
|
310
|
+
@original_prompt, @pending_feedback, @feedback_submitted_at,
|
|
311
|
+
@client_type, @agent_name, @memory_tier, @memory_scope)
|
|
312
|
+
`).run({
|
|
313
|
+
...record,
|
|
314
|
+
pending_feedback: record.pending_feedback ? 1 : 0,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
/** Read the latest classification record for a session. */
|
|
318
|
+
export function getLatestClassification(sessionId) {
|
|
319
|
+
const row = getDb()
|
|
320
|
+
.prepare('SELECT * FROM latest_classification WHERE session_id = ?')
|
|
321
|
+
.get(sessionId);
|
|
322
|
+
if (!row)
|
|
323
|
+
return null;
|
|
324
|
+
return { ...row, pending_feedback: row.pending_feedback === 1 };
|
|
325
|
+
}
|
|
326
|
+
/** Mark pending feedback as submitted for a session. */
|
|
327
|
+
export function markClassificationFeedbackSubmitted(sessionId, submittedAt) {
|
|
328
|
+
const ts = submittedAt ?? new Date().toISOString();
|
|
329
|
+
getDb().prepare(`
|
|
330
|
+
UPDATE latest_classification
|
|
331
|
+
SET pending_feedback = 0, feedback_submitted_at = ?
|
|
332
|
+
WHERE session_id = ?
|
|
333
|
+
`).run(ts, sessionId);
|
|
334
|
+
}
|
|
335
|
+
// ── Session log — persistent history across sessions ──
|
|
336
|
+
/** Append a session-end entry. Replaces session-history.log + last-session-commits.txt. */
|
|
337
|
+
export function appendSessionLog(entry) {
|
|
338
|
+
getDb().prepare(`
|
|
339
|
+
INSERT INTO session_log
|
|
340
|
+
(session_id, project_id, ended_at, reason, commit_log,
|
|
341
|
+
interaction_id, entity_id, client_type, agent_name, synced_at)
|
|
342
|
+
VALUES
|
|
343
|
+
(@session_id, @project_id, @ended_at, @reason, @commit_log,
|
|
344
|
+
@interaction_id, @entity_id, @client_type, @agent_name, @synced_at)
|
|
345
|
+
`).run(entry);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Return the most recent session log entry for a project.
|
|
349
|
+
* Used by session-start to find the prior interaction_id for cross-agent resumption.
|
|
350
|
+
*/
|
|
351
|
+
export function getLastSessionForProject(projectId) {
|
|
352
|
+
const row = getDb()
|
|
353
|
+
.prepare(`
|
|
354
|
+
SELECT session_id, interaction_id, entity_id, client_type, agent_name, ended_at
|
|
355
|
+
FROM session_log
|
|
356
|
+
WHERE project_id = ?
|
|
357
|
+
ORDER BY id DESC
|
|
358
|
+
LIMIT 1
|
|
359
|
+
`)
|
|
360
|
+
.get(projectId);
|
|
361
|
+
return row ?? null;
|
|
362
|
+
}
|
|
363
|
+
/** Return the commit log from the most recent session that had commits. */
|
|
364
|
+
export function getLastSessionCommits() {
|
|
365
|
+
const row = getDb()
|
|
366
|
+
.prepare('SELECT commit_log FROM session_log WHERE commit_log IS NOT NULL ORDER BY id DESC LIMIT 1')
|
|
367
|
+
.get();
|
|
368
|
+
return row?.commit_log ?? null;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Upsert sessions received from the server during session-start sync (Issue #720).
|
|
372
|
+
* Uses INSERT OR IGNORE to preserve local commit_log if the row already exists.
|
|
373
|
+
* The synced_at column records when the server record arrived locally.
|
|
374
|
+
*/
|
|
375
|
+
export function upsertSessionsFromServer(sessions) {
|
|
376
|
+
if (sessions.length === 0)
|
|
377
|
+
return;
|
|
378
|
+
const syncedAt = new Date().toISOString();
|
|
379
|
+
const stmt = getDb().prepare(`
|
|
380
|
+
INSERT OR IGNORE INTO session_log
|
|
381
|
+
(session_id, project_id, ended_at, reason, commit_log,
|
|
382
|
+
interaction_id, entity_id, client_type, agent_name, synced_at)
|
|
383
|
+
VALUES
|
|
384
|
+
(@session_id, @project_id, @ended_at, @reason, @commit_log,
|
|
385
|
+
@interaction_id, @entity_id, @client_type, @agent_name, @synced_at)
|
|
386
|
+
`);
|
|
387
|
+
for (const s of sessions) {
|
|
388
|
+
stmt.run({
|
|
389
|
+
session_id: s.client_session_id ?? s.id,
|
|
390
|
+
project_id: null,
|
|
391
|
+
ended_at: s.ended_at ?? null,
|
|
392
|
+
reason: s.reason ?? null,
|
|
393
|
+
commit_log: null,
|
|
394
|
+
interaction_id: s.interaction_id ?? null,
|
|
395
|
+
entity_id: null,
|
|
396
|
+
client_type: s.client_type ?? null,
|
|
397
|
+
agent_name: s.agent_name ?? null,
|
|
398
|
+
synced_at: syncedAt,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Backfill session_log rows when the server assigns a stable UUID to replace
|
|
404
|
+
* a locally-derived project_id (e.g. 'gramatr/gramatr' → 'a3f9b2c1-...').
|
|
405
|
+
* Called by persistSessionRegistration whenever project_id changes.
|
|
406
|
+
* Returns the number of rows updated.
|
|
407
|
+
*/
|
|
408
|
+
export function migrateProjectId(oldProjectId, newProjectId) {
|
|
409
|
+
if (oldProjectId === newProjectId)
|
|
410
|
+
return 0;
|
|
411
|
+
const result = getDb()
|
|
412
|
+
.prepare('UPDATE session_log SET project_id = ? WHERE project_id = ?')
|
|
413
|
+
.run(newProjectId, oldProjectId);
|
|
414
|
+
return result.changes;
|
|
415
|
+
}
|
|
416
|
+
/** Mark a local session as synced to the server after a successful session-end call. */
|
|
417
|
+
export function markSessionSynced(sessionId, syncedAt) {
|
|
418
|
+
const ts = syncedAt ?? new Date().toISOString();
|
|
419
|
+
getDb()
|
|
420
|
+
.prepare('UPDATE session_log SET synced_at = ? WHERE session_id = ?')
|
|
421
|
+
.run(ts, sessionId);
|
|
422
|
+
}
|
|
423
|
+
/** Count total sessions for a project (for "Session #N" display). */
|
|
424
|
+
export function getProjectSessionCount(projectId) {
|
|
425
|
+
const row = getDb()
|
|
426
|
+
.prepare('SELECT COUNT(*) as cnt FROM session_log WHERE project_id = ?')
|
|
427
|
+
.get(projectId);
|
|
428
|
+
return row?.cnt ?? 0;
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=hook-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-state.js","sourceRoot":"","sources":["../../../src/hooks/lib/hook-state.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intelligence.d.ts","sourceRoot":"","sources":["../../../src/hooks/lib/intelligence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"intelligence.d.ts","sourceRoot":"","sources":["../../../src/hooks/lib/intelligence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwC,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,cAAc,GAAG,aAAa,GAAG,SAAS,CAAC;IACxF,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA2CnE;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,aAAa,EACpB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACzC,IAAI,CAmBN;AASD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,aAAa,EACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAC1C,MAAM,CA2ZR;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,aAAa,GAAG,IAAI,EAC1B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,aAAa,GAAG,IAAI,GAChC,IAAI,CA8BN"}
|