codesesh 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-MWSJTNOW.js → chunk-7APNDHQ6.js} +1545 -1021
- package/dist/chunk-7APNDHQ6.js.map +1 -0
- package/dist/{chunk-EWC6IODK.js → chunk-A4U2SMJJ.js} +5 -2
- package/dist/{chunk-EWC6IODK.js.map → chunk-A4U2SMJJ.js.map} +1 -1
- package/dist/dist-KEPJFHOC.js +115 -0
- package/dist/index.js +967 -713
- package/dist/index.js.map +1 -1
- package/dist/scan-refresh-worker.js +70 -64
- package/dist/scan-refresh-worker.js.map +1 -1
- package/dist/search-index-worker.js +2 -7
- package/dist/search-index-worker.js.map +1 -1
- package/dist/smart-tag-worker.js +2 -2
- package/dist/web/assets/index-B076CRkD.js +109 -0
- package/dist/web/assets/index-CWNbVhet.css +2 -0
- package/dist/web/assets/react-D9s532MZ.js +1821 -0
- package/dist/web/assets/{vendor-BVE7Fps8.js → vendor-i9U7_fNF.js} +15 -15
- package/dist/web/index.html +4 -4
- package/package.json +5 -10
- package/dist/chunk-MWSJTNOW.js.map +0 -1
- package/dist/dist-AIDCOQZO.js +0 -237
- package/dist/web/assets/index-BpmbwB2_.js +0 -113
- package/dist/web/assets/index-Bxe686Vr.css +0 -2
- package/dist/web/assets/react-3acXkZNw.js +0 -1821
- package/dist/web/icon/agent/antigravity.svg +0 -1
- package/dist/web/icon/agent/kilocode.svg +0 -1
- /package/dist/{dist-AIDCOQZO.js.map → dist-KEPJFHOC.js.map} +0 -0
|
@@ -1,6 +1,149 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// ../core/dist/chunk-
|
|
3
|
+
// ../core/dist/chunk-BOTICQC6.mjs
|
|
4
|
+
function toRecord(value) {
|
|
5
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6
|
+
}
|
|
7
|
+
function optionalString(value) {
|
|
8
|
+
return typeof value === "string" && value ? value : void 0;
|
|
9
|
+
}
|
|
10
|
+
function optionalTime(value) {
|
|
11
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
12
|
+
}
|
|
13
|
+
function timeField(time_created) {
|
|
14
|
+
return time_created === void 0 ? {} : { time_created };
|
|
15
|
+
}
|
|
16
|
+
function firstDefined(...values) {
|
|
17
|
+
return values.find((value) => value !== void 0);
|
|
18
|
+
}
|
|
19
|
+
function normalizeStatus(state, output, error) {
|
|
20
|
+
const status = state.status;
|
|
21
|
+
if (status === "running" || status === "completed" || status === "error") return status;
|
|
22
|
+
if (status === "success") return "completed";
|
|
23
|
+
if (error != null) return "error";
|
|
24
|
+
if (output !== void 0) return "completed";
|
|
25
|
+
return "running";
|
|
26
|
+
}
|
|
27
|
+
function normalizeMetadata(state) {
|
|
28
|
+
const rawMetadata = state.metadata ?? state.meta;
|
|
29
|
+
const extras = Object.fromEntries(
|
|
30
|
+
Object.entries(state).filter(
|
|
31
|
+
([key]) => !["status", "input", "arguments", "output", "result", "error", "metadata", "meta"].includes(
|
|
32
|
+
key
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
);
|
|
36
|
+
if (Object.keys(extras).length === 0) return rawMetadata;
|
|
37
|
+
const metadata = toRecord(rawMetadata);
|
|
38
|
+
return metadata ? { ...metadata, ...extras } : extras;
|
|
39
|
+
}
|
|
40
|
+
function normalizeToolState(value, legacyPart) {
|
|
41
|
+
const state = toRecord(value) ?? {};
|
|
42
|
+
const input = firstDefined(state.input, state.arguments, legacyPart.input);
|
|
43
|
+
const output = firstDefined(state.output, state.result, legacyPart.output);
|
|
44
|
+
const error = state.error;
|
|
45
|
+
const metadata = normalizeMetadata(state);
|
|
46
|
+
return {
|
|
47
|
+
status: normalizeStatus(state, output, error),
|
|
48
|
+
...input !== void 0 ? { input } : {},
|
|
49
|
+
...output !== void 0 ? { output } : {},
|
|
50
|
+
...error !== void 0 ? { error } : {},
|
|
51
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function planText(value) {
|
|
55
|
+
if (typeof value === "string") return value;
|
|
56
|
+
const record = toRecord(value);
|
|
57
|
+
if (!record) return "";
|
|
58
|
+
return planText(record.text ?? record.plan ?? record.content);
|
|
59
|
+
}
|
|
60
|
+
function normalizePlanPart(value, time_created) {
|
|
61
|
+
const approval_status = value.approval_status === "fail" ? "fail" : "success";
|
|
62
|
+
const text = planText(value.text ?? (approval_status === "fail" ? value.output : value.input));
|
|
63
|
+
return text ? { type: "plan", text, approval_status, ...timeField(time_created) } : null;
|
|
64
|
+
}
|
|
65
|
+
function normalizeImagePart(value, time_created) {
|
|
66
|
+
const data = optionalString(value.data);
|
|
67
|
+
const url = optionalString(value.url);
|
|
68
|
+
const mime_type = optionalString(value.mime_type);
|
|
69
|
+
if (data && mime_type) {
|
|
70
|
+
return {
|
|
71
|
+
type: "image",
|
|
72
|
+
data,
|
|
73
|
+
mime_type,
|
|
74
|
+
...url ? { url } : {},
|
|
75
|
+
...timeField(time_created)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (url) {
|
|
79
|
+
return {
|
|
80
|
+
type: "image",
|
|
81
|
+
url,
|
|
82
|
+
...data ? { data } : {},
|
|
83
|
+
...mime_type ? { mime_type } : {},
|
|
84
|
+
...timeField(time_created)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
function normalizeToolPart(value, time_created) {
|
|
90
|
+
const title = optionalString(value.title);
|
|
91
|
+
const callID = optionalString(value.callID);
|
|
92
|
+
const tool = optionalString(value.tool)?.trim() || title?.replace(/^tool:\s*/i, "").trim();
|
|
93
|
+
if (!tool) return null;
|
|
94
|
+
return {
|
|
95
|
+
type: "tool",
|
|
96
|
+
tool,
|
|
97
|
+
...title ? { title } : {},
|
|
98
|
+
...callID ? { callID } : {},
|
|
99
|
+
state: normalizeToolState(value.state, value),
|
|
100
|
+
...timeField(time_created)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function normalizeMessagePart(value) {
|
|
104
|
+
const part = toRecord(value);
|
|
105
|
+
if (!part) return null;
|
|
106
|
+
const time_created = optionalTime(part.time_created);
|
|
107
|
+
if (part.type === "text" || part.type === "reasoning") {
|
|
108
|
+
if (typeof part.text !== "string") return null;
|
|
109
|
+
return { type: part.type, text: part.text, ...timeField(time_created) };
|
|
110
|
+
}
|
|
111
|
+
if (part.type === "plan") return normalizePlanPart(part, time_created);
|
|
112
|
+
if (part.type === "image") return normalizeImagePart(part, time_created);
|
|
113
|
+
if (part.type === "tool") return normalizeToolPart(part, time_created);
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function normalizeMessageParts(value) {
|
|
117
|
+
if (!Array.isArray(value)) return [];
|
|
118
|
+
return value.flatMap((part) => {
|
|
119
|
+
const normalized = normalizeMessagePart(part);
|
|
120
|
+
return normalized ? [normalized] : [];
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
var UNKNOWN_AGENT_NAME = "unknown";
|
|
124
|
+
function normalizeSessionReference(reference) {
|
|
125
|
+
return {
|
|
126
|
+
agentName: reference.agentName.trim().toLowerCase(),
|
|
127
|
+
sessionId: reference.sessionId
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function parseSessionReference(value) {
|
|
131
|
+
const separatorIndex = value.indexOf("/");
|
|
132
|
+
if (separatorIndex <= 0 || separatorIndex === value.length - 1) return null;
|
|
133
|
+
const agentName = value.slice(0, separatorIndex).trim().toLowerCase();
|
|
134
|
+
if (!agentName) return null;
|
|
135
|
+
return {
|
|
136
|
+
agentName,
|
|
137
|
+
sessionId: value.slice(separatorIndex + 1)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function formatSessionReference(reference) {
|
|
141
|
+
const normalized = normalizeSessionReference(reference);
|
|
142
|
+
return `${normalized.agentName}/${normalized.sessionId}`;
|
|
143
|
+
}
|
|
144
|
+
function getSessionAgentKey(session) {
|
|
145
|
+
return parseSessionReference(session.slug)?.agentName ?? UNKNOWN_AGENT_NAME;
|
|
146
|
+
}
|
|
4
147
|
function compareSessionActivityDesc(a, b) {
|
|
5
148
|
return (b.time_updated ?? b.time_created) - (a.time_updated ?? a.time_created);
|
|
6
149
|
}
|
|
@@ -12,55 +155,146 @@ function sortSessionsByActivity(sessions) {
|
|
|
12
155
|
}
|
|
13
156
|
return [...sessions];
|
|
14
157
|
}
|
|
158
|
+
function mergeSortedSessions(shards) {
|
|
159
|
+
const active = shards.filter((shard) => shard.length > 0);
|
|
160
|
+
if (active.length === 0) return [];
|
|
161
|
+
if (active.length === 1) return [...active[0]];
|
|
162
|
+
const total = active.reduce((sum, shard) => sum + shard.length, 0);
|
|
163
|
+
const merged = [];
|
|
164
|
+
const cursors = Array.from({ length: active.length }, () => 0);
|
|
165
|
+
for (let position = 0; position < total; position += 1) {
|
|
166
|
+
let pick = -1;
|
|
167
|
+
for (let shard = 0; shard < active.length; shard += 1) {
|
|
168
|
+
const cursor = cursors[shard];
|
|
169
|
+
if (cursor >= active[shard].length) continue;
|
|
170
|
+
if (pick === -1 || compareSessionActivityDesc(active[shard][cursor], active[pick][cursors[pick]]) < 0) {
|
|
171
|
+
pick = shard;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
merged.push(active[pick][cursors[pick]]);
|
|
175
|
+
cursors[pick] += 1;
|
|
176
|
+
}
|
|
177
|
+
return merged;
|
|
178
|
+
}
|
|
179
|
+
var SAMPLE_SESSION_HEAD = {
|
|
180
|
+
id: "session-1",
|
|
181
|
+
slug: "claudecode/session-1",
|
|
182
|
+
title: "Fix flaky search index test",
|
|
183
|
+
directory: "/Users/dev/project",
|
|
184
|
+
project_identity: {
|
|
185
|
+
kind: "git_remote",
|
|
186
|
+
key: "github.com/example/project",
|
|
187
|
+
displayName: "example/project"
|
|
188
|
+
},
|
|
189
|
+
time_created: 17e11,
|
|
190
|
+
time_updated: 17000036e5,
|
|
191
|
+
stats: {
|
|
192
|
+
message_count: 12,
|
|
193
|
+
total_input_tokens: 4200,
|
|
194
|
+
total_output_tokens: 1800,
|
|
195
|
+
total_cost: 0.042,
|
|
196
|
+
cost_source: "recorded",
|
|
197
|
+
total_tokens: 6e3,
|
|
198
|
+
total_cache_read_tokens: 3e3,
|
|
199
|
+
total_cache_create_tokens: 500
|
|
200
|
+
},
|
|
201
|
+
model_usage: { "claude-5-sonnet": 6e3 },
|
|
202
|
+
smart_tags: ["bugfix"],
|
|
203
|
+
smart_tags_source_updated_at: 17000036e5
|
|
204
|
+
};
|
|
205
|
+
var SAMPLE_SESSIONS_UPDATED_EVENT = {
|
|
206
|
+
type: "sessions-updated",
|
|
207
|
+
changedAgents: ["claudecode"],
|
|
208
|
+
newSessions: 1,
|
|
209
|
+
updatedSessions: 0,
|
|
210
|
+
removedSessions: 0,
|
|
211
|
+
totalSessions: 43,
|
|
212
|
+
timestamp: 170000002e4,
|
|
213
|
+
changedSessionHeads: [
|
|
214
|
+
{
|
|
215
|
+
reference: { agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id },
|
|
216
|
+
session: SAMPLE_SESSION_HEAD
|
|
217
|
+
}
|
|
218
|
+
],
|
|
219
|
+
removedSessionRefs: []
|
|
220
|
+
};
|
|
221
|
+
var SAMPLE_DASHBOARD_DATA = {
|
|
222
|
+
totals: {
|
|
223
|
+
sessions: 1,
|
|
224
|
+
messages: 12,
|
|
225
|
+
tokens: 6e3,
|
|
226
|
+
cost: 0.042,
|
|
227
|
+
cost_source: "recorded",
|
|
228
|
+
latestActivity: 17000036e5
|
|
229
|
+
},
|
|
230
|
+
perAgent: [
|
|
231
|
+
{
|
|
232
|
+
name: "claudecode",
|
|
233
|
+
displayName: "Claude Code",
|
|
234
|
+
icon: "claude",
|
|
235
|
+
sessions: 1,
|
|
236
|
+
messages: 12,
|
|
237
|
+
tokens: 6e3
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
dailyActivity: [{ date: "2023-11-14", sessions: 1, messages: 12 }],
|
|
241
|
+
dailyTokenActivity: [
|
|
242
|
+
{ date: "2023-11-14", input: 700, output: 1800, cache_read: 3e3, cache_create: 500 }
|
|
243
|
+
],
|
|
244
|
+
modelDistribution: [{ model: "claude-5-sonnet", tokens: 6e3, sessions: 1 }],
|
|
245
|
+
recentSessions: [
|
|
246
|
+
{
|
|
247
|
+
reference: { agentName: "claudecode", sessionId: SAMPLE_SESSION_HEAD.id },
|
|
248
|
+
session: SAMPLE_SESSION_HEAD
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
recentFileActivities: [],
|
|
252
|
+
window: { from: 16999e8, to: 17000036e5, days: 1 }
|
|
253
|
+
};
|
|
15
254
|
|
|
16
255
|
// ../core/dist/index.mjs
|
|
17
|
-
import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
18
|
-
import { join as
|
|
19
|
-
import { existsSync, statSync } from "fs";
|
|
256
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
257
|
+
import { join as join4, basename as basename2, dirname } from "path";
|
|
258
|
+
import { existsSync, readdirSync, statSync } from "fs";
|
|
259
|
+
import { join } from "path";
|
|
20
260
|
import { existsSync as existsSync2 } from "fs";
|
|
21
261
|
import { homedir, platform } from "os";
|
|
22
|
-
import { join } from "path";
|
|
262
|
+
import { join as join2 } from "path";
|
|
23
263
|
import { closeSync, openSync, readSync } from "fs";
|
|
24
264
|
import { StringDecoder } from "string_decoder";
|
|
25
265
|
import { basename } from "path";
|
|
26
266
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
27
267
|
import { homedir as homedir2 } from "os";
|
|
28
|
-
import { join as
|
|
29
|
-
import { join as
|
|
268
|
+
import { join as join3 } from "path";
|
|
269
|
+
import { join as join6 } from "path";
|
|
30
270
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
31
|
-
import { basename as basename3, dirname as dirname2, join as
|
|
271
|
+
import { basename as basename3, dirname as dirname2, join as join5 } from "path";
|
|
32
272
|
import { createRequire } from "module";
|
|
33
273
|
import { createHash } from "crypto";
|
|
34
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as
|
|
35
|
-
import { join as
|
|
36
|
-
import {
|
|
37
|
-
|
|
38
|
-
existsSync as existsSync7,
|
|
39
|
-
openSync as openSync2,
|
|
40
|
-
readFileSync as readFileSync4,
|
|
41
|
-
readSync as readSync2,
|
|
42
|
-
readdirSync as readdirSync3,
|
|
43
|
-
statSync as statSync4
|
|
44
|
-
} from "fs";
|
|
45
|
-
import { join as join7, basename as basename5 } from "path";
|
|
274
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
275
|
+
import { join as join7, basename as basename4, dirname as dirname3 } from "path";
|
|
276
|
+
import { closeSync as closeSync2, existsSync as existsSync7, openSync as openSync2, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync4 } from "fs";
|
|
277
|
+
import { join as join8, basename as basename5 } from "path";
|
|
46
278
|
import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync5 } from "fs";
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import { join as join10 } from "path";
|
|
279
|
+
import { homedir as homedir3, platform as platform2 } from "os";
|
|
280
|
+
import { join as join9, normalize } from "path";
|
|
281
|
+
import { existsSync as existsSync9 } from "fs";
|
|
282
|
+
import { basename as basename6, join as join10 } from "path";
|
|
283
|
+
import { homedir as homedir4, platform as platform3 } from "os";
|
|
284
|
+
import { join as join11 } from "path";
|
|
51
285
|
import { availableParallelism } from "os";
|
|
52
286
|
import { Worker } from "worker_threads";
|
|
53
|
-
import { existsSync as existsSync10, readFileSync as
|
|
287
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
54
288
|
import { spawnSync } from "child_process";
|
|
55
289
|
import * as os from "os";
|
|
56
290
|
import * as path from "path";
|
|
57
291
|
import { resolve, sep } from "path";
|
|
58
|
-
import { existsSync as existsSync11 } from "fs";
|
|
59
|
-
import { join as join11 } from "path";
|
|
60
|
-
import { homedir as homedir4 } from "os";
|
|
61
292
|
import { existsSync as existsSync12, rmSync, unlinkSync } from "fs";
|
|
62
|
-
import {
|
|
293
|
+
import { existsSync as existsSync11 } from "fs";
|
|
63
294
|
import { join as join12 } from "path";
|
|
295
|
+
import { homedir as homedir6 } from "os";
|
|
296
|
+
import { homedir as homedir7, platform as platform4 } from "os";
|
|
297
|
+
import { join as join13 } from "path";
|
|
64
298
|
var registrations = [];
|
|
65
299
|
function registerAgent(reg) {
|
|
66
300
|
registrations.push(reg);
|
|
@@ -79,16 +313,20 @@ function getAgentInfoMap(sessionsByAgent) {
|
|
|
79
313
|
displayName: agent.displayName,
|
|
80
314
|
icon: registration.icon,
|
|
81
315
|
iconColored: registration.iconColored,
|
|
316
|
+
resumeCommandPrefix: registration.resumeCommandPrefix,
|
|
82
317
|
count: sessionsByAgent[agent.name] ?? 0
|
|
83
318
|
};
|
|
84
319
|
});
|
|
85
320
|
}
|
|
86
|
-
function getAgentByName(name) {
|
|
87
|
-
return registrations.find((registration) => registration.create().name === name);
|
|
88
|
-
}
|
|
89
321
|
var diagnostics = null;
|
|
90
322
|
function toSafeSink(sink) {
|
|
91
323
|
return {
|
|
324
|
+
info(event, detail) {
|
|
325
|
+
try {
|
|
326
|
+
sink.info?.(event, detail);
|
|
327
|
+
} catch {
|
|
328
|
+
}
|
|
329
|
+
},
|
|
92
330
|
warn(event, detail) {
|
|
93
331
|
try {
|
|
94
332
|
sink.warn(event, detail);
|
|
@@ -120,6 +358,36 @@ function matchesScanWindow(activityTime, options) {
|
|
|
120
358
|
if (options?.to != null && activityTime > options.to) return false;
|
|
121
359
|
return true;
|
|
122
360
|
}
|
|
361
|
+
function readCachedMeta(meta, sessionId) {
|
|
362
|
+
if (meta instanceof Map) return meta.get(sessionId);
|
|
363
|
+
return meta[sessionId];
|
|
364
|
+
}
|
|
365
|
+
function fingerprintMatches(ref, cached) {
|
|
366
|
+
return typeof cached?.sourceFingerprint === "string" && cached.sourceFingerprint === ref.fingerprint;
|
|
367
|
+
}
|
|
368
|
+
function wasEnumeratedThisPass(cached, options) {
|
|
369
|
+
if (options?.from == null && options?.to == null) return true;
|
|
370
|
+
const mtimeMs = cached?.sourceMtimeMs;
|
|
371
|
+
return typeof mtimeMs === "number" && matchesScanWindow(mtimeMs, options);
|
|
372
|
+
}
|
|
373
|
+
function diffSessionSources(refs, cachedSessions, cachedMeta, options) {
|
|
374
|
+
const cachedIds = new Set(cachedSessions.map((session) => session.id));
|
|
375
|
+
const enumeratedIds = /* @__PURE__ */ new Set();
|
|
376
|
+
const changedIds = [];
|
|
377
|
+
for (const ref of refs) {
|
|
378
|
+
enumeratedIds.add(ref.sessionId);
|
|
379
|
+
const meta = readCachedMeta(cachedMeta, ref.sessionId);
|
|
380
|
+
const unchanged = cachedIds.has(ref.sessionId) && meta?.sourcePath === ref.sourcePath && fingerprintMatches(ref, meta);
|
|
381
|
+
if (!unchanged) changedIds.push(ref.sessionId);
|
|
382
|
+
}
|
|
383
|
+
const removedIds = [];
|
|
384
|
+
for (const session of cachedSessions) {
|
|
385
|
+
if (enumeratedIds.has(session.id)) continue;
|
|
386
|
+
if (!wasEnumeratedThisPass(readCachedMeta(cachedMeta, session.id), options)) continue;
|
|
387
|
+
removedIds.push(session.id);
|
|
388
|
+
}
|
|
389
|
+
return { changedIds, removedIds };
|
|
390
|
+
}
|
|
123
391
|
var BaseAgent = class {
|
|
124
392
|
getUri(sessionId) {
|
|
125
393
|
return `${this.name}://${sessionId}`;
|
|
@@ -127,6 +395,7 @@ var BaseAgent = class {
|
|
|
127
395
|
};
|
|
128
396
|
var FileSystemSessionSource = class extends BaseAgent {
|
|
129
397
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
398
|
+
sourceFileStats = /* @__PURE__ */ new Map();
|
|
130
399
|
scan(options) {
|
|
131
400
|
const sources = this.listSessionSources(options);
|
|
132
401
|
const sessions = [];
|
|
@@ -158,27 +427,62 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
158
427
|
setSessionMetaMap(meta) {
|
|
159
428
|
this.sessionMetaMap = meta;
|
|
160
429
|
}
|
|
430
|
+
walkFiles(roots, isSessionFile, options = {}) {
|
|
431
|
+
const files = [];
|
|
432
|
+
this.sourceFileStats.clear();
|
|
433
|
+
const walk = (directory) => {
|
|
434
|
+
let entries;
|
|
435
|
+
try {
|
|
436
|
+
entries = readdirSync(directory, { withFileTypes: true });
|
|
437
|
+
} catch {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
for (const entry of entries) {
|
|
441
|
+
const filePath = join(directory, entry.name);
|
|
442
|
+
if (entry.isDirectory()) {
|
|
443
|
+
if (options.recursive !== false) walk(filePath);
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
if (!isSessionFile(entry)) continue;
|
|
447
|
+
let stat;
|
|
448
|
+
try {
|
|
449
|
+
stat = statSync(filePath);
|
|
450
|
+
} catch {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (!matchesScanWindow(stat.mtimeMs, options.scanWindow)) continue;
|
|
454
|
+
files.push({ file: filePath, stat });
|
|
455
|
+
this.sourceFileStats.set(filePath, stat);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
for (const root of typeof roots === "string" ? [roots] : roots) walk(root);
|
|
459
|
+
return files;
|
|
460
|
+
}
|
|
461
|
+
sessionSourceFile(sourcePath) {
|
|
462
|
+
return {
|
|
463
|
+
file: sourcePath,
|
|
464
|
+
stat: this.sourceFileStats.get(sourcePath) ?? statSync(sourcePath)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
readFileMtimeMs(filePath) {
|
|
468
|
+
if (!filePath) return null;
|
|
469
|
+
try {
|
|
470
|
+
return statSync(filePath).mtimeMs;
|
|
471
|
+
} catch {
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
161
475
|
/**
|
|
162
|
-
* 变更检测:枚举当前源 →
|
|
476
|
+
* 变更检测:枚举当前源 → 交给 diffSessionSources 比对。
|
|
163
477
|
* 新增、变更、删除三类统一产出 changedIds。
|
|
164
478
|
*/
|
|
165
479
|
checkForChanges(_sinceTimestamp, cachedSessions) {
|
|
166
480
|
const currentRefs = this.listSessionSources();
|
|
167
|
-
const
|
|
168
|
-
const changedIds =
|
|
169
|
-
for (const ref of currentRefs) {
|
|
170
|
-
const meta = this.sessionMetaMap.get(ref.sessionId);
|
|
171
|
-
const samePath = meta?.sourcePath === ref.sourcePath;
|
|
172
|
-
const sameFingerprint = typeof meta?.sourceFingerprint === "string" && meta.sourceFingerprint === ref.fingerprint;
|
|
173
|
-
if (!samePath || !sameFingerprint) changedIds.add(ref.sessionId);
|
|
174
|
-
}
|
|
175
|
-
for (const session of cachedSessions) {
|
|
176
|
-
if (!currentIds.has(session.id)) changedIds.add(session.id);
|
|
177
|
-
}
|
|
178
|
-
const changedIdList = [...changedIds];
|
|
481
|
+
const diff = diffSessionSources(currentRefs, cachedSessions, this.sessionMetaMap);
|
|
482
|
+
const changedIds = [.../* @__PURE__ */ new Set([...diff.changedIds, ...diff.removedIds])];
|
|
179
483
|
return {
|
|
180
|
-
hasChanges:
|
|
181
|
-
changedIds
|
|
484
|
+
hasChanges: changedIds.length > 0,
|
|
485
|
+
changedIds,
|
|
182
486
|
timestamp: Date.now(),
|
|
183
487
|
refs: currentRefs
|
|
184
488
|
};
|
|
@@ -212,6 +516,37 @@ var FileSystemSessionSource = class extends BaseAgent {
|
|
|
212
516
|
return [...sessionMap.values()];
|
|
213
517
|
}
|
|
214
518
|
};
|
|
519
|
+
var SingleFileSessionSource = class extends FileSystemSessionSource {
|
|
520
|
+
scanSessionSource(sourcePath, options) {
|
|
521
|
+
const head = this.parseFileSessionHead(sourcePath, options);
|
|
522
|
+
if (head) {
|
|
523
|
+
this.sessionMetaMap.set(
|
|
524
|
+
head.id,
|
|
525
|
+
this.createFileSessionMeta(head, this.sessionSourceFile(sourcePath))
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
return head;
|
|
529
|
+
}
|
|
530
|
+
buildFileSessionMeta({
|
|
531
|
+
head,
|
|
532
|
+
source,
|
|
533
|
+
fingerprint,
|
|
534
|
+
extras
|
|
535
|
+
}) {
|
|
536
|
+
return {
|
|
537
|
+
...extras,
|
|
538
|
+
id: head.id,
|
|
539
|
+
title: head.title,
|
|
540
|
+
sourcePath: source.file,
|
|
541
|
+
sourceFingerprint: fingerprint,
|
|
542
|
+
sourceMtimeMs: source.stat.mtimeMs,
|
|
543
|
+
directory: head.directory,
|
|
544
|
+
messageCount: head.stats.message_count,
|
|
545
|
+
createdAt: head.time_created,
|
|
546
|
+
updatedAt: head.time_updated ?? head.time_created
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
};
|
|
215
550
|
var DatabaseSessionSource = class extends BaseAgent {
|
|
216
551
|
sessionMetaMap = /* @__PURE__ */ new Map();
|
|
217
552
|
/** 记录单个会话的缓存 meta(sourcePath = dbPath)。 */
|
|
@@ -227,10 +562,9 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
227
562
|
this.sessionMetaMap = meta;
|
|
228
563
|
}
|
|
229
564
|
/**
|
|
230
|
-
*
|
|
231
|
-
* 库有变更则标记全部缓存会话刷新。
|
|
565
|
+
* 变更检测:数据库内部变更难以按行定位,按库文件 mtime 判定。
|
|
232
566
|
*/
|
|
233
|
-
checkForChanges(sinceTimestamp,
|
|
567
|
+
checkForChanges(sinceTimestamp, _cachedSessions) {
|
|
234
568
|
const dbPath = this.getDatabasePath();
|
|
235
569
|
if (!dbPath || !existsSync(dbPath)) {
|
|
236
570
|
return { hasChanges: false, timestamp: Date.now() };
|
|
@@ -239,7 +573,6 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
239
573
|
const hasChanges = statSync(dbPath).mtimeMs > sinceTimestamp;
|
|
240
574
|
return {
|
|
241
575
|
hasChanges,
|
|
242
|
-
changedIds: hasChanges ? cachedSessions.map((session) => session.id) : [],
|
|
243
576
|
timestamp: Date.now()
|
|
244
577
|
};
|
|
245
578
|
} catch {
|
|
@@ -251,7 +584,7 @@ var DatabaseSessionSource = class extends BaseAgent {
|
|
|
251
584
|
return this.scan();
|
|
252
585
|
}
|
|
253
586
|
};
|
|
254
|
-
function
|
|
587
|
+
function readEnvPath(name) {
|
|
255
588
|
const value = process.env[name];
|
|
256
589
|
if (!value) return null;
|
|
257
590
|
return value;
|
|
@@ -262,49 +595,17 @@ function firstExisting(...paths) {
|
|
|
262
595
|
}
|
|
263
596
|
return null;
|
|
264
597
|
}
|
|
265
|
-
function
|
|
266
|
-
const xdg =
|
|
598
|
+
function resolveDataHome() {
|
|
599
|
+
const xdg = readEnvPath("XDG_DATA_HOME");
|
|
267
600
|
if (xdg) return xdg;
|
|
268
601
|
const p = platform();
|
|
269
602
|
if (p === "win32") {
|
|
270
|
-
return
|
|
271
|
-
}
|
|
272
|
-
return join(homedir(), ".local", "share");
|
|
273
|
-
}
|
|
274
|
-
function resolveProviderRoots() {
|
|
275
|
-
const home = homedir();
|
|
276
|
-
return {
|
|
277
|
-
codexRoot: envPath("CODEX_HOME") ?? join(home, ".codex"),
|
|
278
|
-
claudeRoot: envPath("CLAUDE_CONFIG_DIR") ?? join(home, ".claude"),
|
|
279
|
-
kimiRoot: envPath("KIMI_SHARE_DIR") ?? join(home, ".kimi"),
|
|
280
|
-
opencodeRoot: join(getDataHome(), "opencode"),
|
|
281
|
-
piRoot: envPath("PI_HOME") ?? join(home, ".pi"),
|
|
282
|
-
zcodeRoot: getZCodeDataPath()
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
function getCursorDataPath() {
|
|
286
|
-
const override = envPath("CURSOR_DATA_PATH");
|
|
287
|
-
if (override) return override;
|
|
288
|
-
const p = platform();
|
|
289
|
-
if (p === "darwin") {
|
|
290
|
-
return firstExisting(join(homedir(), "Library", "Application Support", "Cursor", "User"));
|
|
291
|
-
}
|
|
292
|
-
if (p === "linux") {
|
|
293
|
-
const xdg = envPath("XDG_CONFIG_HOME") ?? join(homedir(), ".config");
|
|
294
|
-
return firstExisting(join(xdg, "Cursor", "User"));
|
|
295
|
-
}
|
|
296
|
-
if (p === "win32") {
|
|
297
|
-
const appData = envPath("APPDATA") ?? join(homedir(), "AppData", "Roaming");
|
|
298
|
-
return firstExisting(join(appData, "Cursor", "User"));
|
|
603
|
+
return readEnvPath("LOCALAPPDATA") ?? readEnvPath("APPDATA") ?? join2(homedir(), "AppData", "Local");
|
|
299
604
|
}
|
|
300
|
-
return
|
|
605
|
+
return join2(homedir(), ".local", "share");
|
|
301
606
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
if (p === "darwin" || p === "win32") {
|
|
305
|
-
return join(homedir(), ".zcode");
|
|
306
|
-
}
|
|
307
|
-
return null;
|
|
607
|
+
function resolveHomePath(environmentVariable, fallbackDirectory) {
|
|
608
|
+
return readEnvPath(environmentVariable) ?? join2(homedir(), fallbackDirectory);
|
|
308
609
|
}
|
|
309
610
|
var READ_CHUNK_BYTES = 1 << 20;
|
|
310
611
|
function* parseJsonlLines(content) {
|
|
@@ -329,18 +630,26 @@ function* readJsonlFileLines(filePath, chunkBytes = READ_CHUNK_BYTES) {
|
|
|
329
630
|
try {
|
|
330
631
|
const buffer = Buffer.alloc(chunkBytes);
|
|
331
632
|
const decoder = new StringDecoder("utf8");
|
|
332
|
-
let
|
|
633
|
+
let pending = [];
|
|
333
634
|
let bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
334
635
|
while (bytesRead > 0) {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
636
|
+
const decoded = decoder.write(buffer.subarray(0, bytesRead));
|
|
637
|
+
const lastBreak = decoded.lastIndexOf("\n");
|
|
638
|
+
if (lastBreak === -1) {
|
|
639
|
+
if (decoded) pending.push(decoded);
|
|
640
|
+
} else {
|
|
641
|
+
pending.push(decoded.slice(0, lastBreak));
|
|
642
|
+
const complete = pending.join("");
|
|
643
|
+
pending = [decoded.slice(lastBreak + 1)];
|
|
644
|
+
for (const line of complete.split("\n")) {
|
|
645
|
+
const trimmed = line.trim();
|
|
646
|
+
if (trimmed) yield trimmed;
|
|
647
|
+
}
|
|
340
648
|
}
|
|
341
649
|
bytesRead = readSync(fd, buffer, 0, chunkBytes, -1);
|
|
342
650
|
}
|
|
343
|
-
|
|
651
|
+
pending.push(decoder.end());
|
|
652
|
+
const tail = pending.join("").trim();
|
|
344
653
|
if (tail) yield tail;
|
|
345
654
|
} finally {
|
|
346
655
|
closeSync(fd);
|
|
@@ -412,11 +721,6 @@ function cleanDisplayText(text) {
|
|
|
412
721
|
cleaned = cleaned.replace(/[ \t]+(?=\r?\n|$)/g, "").replace(/(?:\r?\n)+$/g, "");
|
|
413
722
|
return cleaned.trim() ? cleaned : null;
|
|
414
723
|
}
|
|
415
|
-
function firstVisibleLine(text) {
|
|
416
|
-
const cleaned = cleanDisplayText(text);
|
|
417
|
-
if (!cleaned) return null;
|
|
418
|
-
return cleaned.split("\n").find((line) => line.trim())?.trim() ?? null;
|
|
419
|
-
}
|
|
420
724
|
var TITLE_MAX_LENGTH = 100;
|
|
421
725
|
var UNTITLED_SESSION = "Untitled Session";
|
|
422
726
|
function normalizeTitleText(text) {
|
|
@@ -459,28 +763,25 @@ function cleanUnknown(value) {
|
|
|
459
763
|
return cleaned;
|
|
460
764
|
}
|
|
461
765
|
function cleanMessagePart(part) {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
next.state = cleanUnknown(next.state);
|
|
482
|
-
}
|
|
483
|
-
return next;
|
|
766
|
+
if (part.type === "text" || part.type === "reasoning" || part.type === "plan") {
|
|
767
|
+
const text = cleanInternalText(part.text);
|
|
768
|
+
return text ? { ...part, text } : null;
|
|
769
|
+
}
|
|
770
|
+
if (part.type !== "tool") return part;
|
|
771
|
+
const title = part.title ? cleanInternalText(part.title) : "";
|
|
772
|
+
const state = {
|
|
773
|
+
...part.state,
|
|
774
|
+
...part.state.input !== void 0 ? { input: cleanUnknown(part.state.input) } : {},
|
|
775
|
+
...part.state.output !== void 0 ? { output: cleanUnknown(part.state.output) } : {},
|
|
776
|
+
...part.state.error !== void 0 ? { error: cleanUnknown(part.state.error) } : {},
|
|
777
|
+
...part.state.metadata !== void 0 ? { metadata: cleanUnknown(part.state.metadata) } : {}
|
|
778
|
+
};
|
|
779
|
+
const { title: _title, ...withoutTitle } = part;
|
|
780
|
+
return {
|
|
781
|
+
...withoutTitle,
|
|
782
|
+
...title ? { title } : {},
|
|
783
|
+
state
|
|
784
|
+
};
|
|
484
785
|
}
|
|
485
786
|
function cleanMessageParts(parts) {
|
|
486
787
|
return parts.flatMap((part) => {
|
|
@@ -503,7 +804,7 @@ function firstUserMessageTitle(messages) {
|
|
|
503
804
|
for (const message of messages) {
|
|
504
805
|
if (message.role !== "user") continue;
|
|
505
806
|
for (const part of message.parts) {
|
|
506
|
-
if (part.type !== "text"
|
|
807
|
+
if (part.type !== "text") continue;
|
|
507
808
|
const title = normalizeTitleText(cleanInternalText(part.text));
|
|
508
809
|
if (title) return title;
|
|
509
810
|
}
|
|
@@ -587,10 +888,10 @@ function costNumber(value, fallback) {
|
|
|
587
888
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
588
889
|
}
|
|
589
890
|
function getCacheDir() {
|
|
590
|
-
return
|
|
891
|
+
return join3(homedir2(), ".cache", "codesesh");
|
|
591
892
|
}
|
|
592
893
|
function getCachePath() {
|
|
593
|
-
return
|
|
894
|
+
return join3(getCacheDir(), "litellm-pricing.json");
|
|
594
895
|
}
|
|
595
896
|
function loadSnapshot() {
|
|
596
897
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -794,42 +1095,6 @@ function estimateCostForTokens(model, usage) {
|
|
|
794
1095
|
const cost = input * pricing.inputCostPerToken + output * pricing.outputCostPerToken + reasoning * pricing.reasoningCostPerToken + cacheRead * pricing.cacheReadCostPerToken + cacheCreate * pricing.cacheCreateCostPerToken + webSearchRequests * pricing.webSearchCostPerRequest;
|
|
795
1096
|
return cost > 0 ? { cost: Number(cost.toFixed(8)), source: "estimated" } : null;
|
|
796
1097
|
}
|
|
797
|
-
function applyMessageCost(message) {
|
|
798
|
-
if ((message.cost ?? 0) > 0) {
|
|
799
|
-
message.cost_source = "recorded";
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
const estimate = estimateCostForTokens(message.model, message.tokens);
|
|
803
|
-
if (!estimate) return;
|
|
804
|
-
message.cost = estimate.cost;
|
|
805
|
-
message.cost_source = estimate.source;
|
|
806
|
-
}
|
|
807
|
-
function applyMessageCosts(messages) {
|
|
808
|
-
let totalCost = 0;
|
|
809
|
-
let source;
|
|
810
|
-
for (const message of messages) {
|
|
811
|
-
applyMessageCost(message);
|
|
812
|
-
const cost = message.cost ?? 0;
|
|
813
|
-
if (cost <= 0) continue;
|
|
814
|
-
totalCost += cost;
|
|
815
|
-
if (message.cost_source === "estimated") source = "estimated";
|
|
816
|
-
else if (!source) source = "recorded";
|
|
817
|
-
}
|
|
818
|
-
return { totalCost: Number(totalCost.toFixed(8)), source };
|
|
819
|
-
}
|
|
820
|
-
function withEstimatedSessionCost(stats, model) {
|
|
821
|
-
if (stats.total_cost > 0) {
|
|
822
|
-
return { ...stats, cost_source: stats.cost_source ?? "recorded" };
|
|
823
|
-
}
|
|
824
|
-
const estimate = estimateCostForTokens(model, {
|
|
825
|
-
input: stats.total_input_tokens,
|
|
826
|
-
output: stats.total_output_tokens,
|
|
827
|
-
cache_read: stats.total_cache_read_tokens,
|
|
828
|
-
cache_create: stats.total_cache_create_tokens
|
|
829
|
-
});
|
|
830
|
-
if (!estimate) return stats;
|
|
831
|
-
return { ...stats, total_cost: estimate.cost, cost_source: estimate.source };
|
|
832
|
-
}
|
|
833
1098
|
function estimateTokenCost(model, tokens) {
|
|
834
1099
|
return estimateCostForTokens(model, tokens)?.cost ?? null;
|
|
835
1100
|
}
|
|
@@ -943,9 +1208,12 @@ var TranscriptBuilder = class {
|
|
|
943
1208
|
}
|
|
944
1209
|
resolveToolCall(callId, resolution) {
|
|
945
1210
|
return this.updateToolCall(callId, (part) => {
|
|
946
|
-
const state = part.state
|
|
1211
|
+
const state = part.state;
|
|
947
1212
|
if (resolution.output !== void 0) state.output = resolution.output;
|
|
948
1213
|
if (resolution.status !== void 0) state.status = resolution.status;
|
|
1214
|
+
else if (resolution.output !== void 0 && state.status === "running") {
|
|
1215
|
+
state.status = "completed";
|
|
1216
|
+
}
|
|
949
1217
|
if (resolution.metadata !== void 0) state.metadata = resolution.metadata;
|
|
950
1218
|
if (resolution.consume) this.pendingToolCalls.delete(callId);
|
|
951
1219
|
});
|
|
@@ -1004,7 +1272,9 @@ var TranscriptBuilder = class {
|
|
|
1004
1272
|
}
|
|
1005
1273
|
appendPartIfNew(message, part) {
|
|
1006
1274
|
const tail = message.parts.at(-1);
|
|
1007
|
-
if (tail?.type === part.type && tail.text === part.text)
|
|
1275
|
+
if ("text" in part && tail?.type === part.type && "text" in tail && tail.text === part.text) {
|
|
1276
|
+
return;
|
|
1277
|
+
}
|
|
1008
1278
|
message.parts.push(part);
|
|
1009
1279
|
}
|
|
1010
1280
|
applyMissingMetadata(message, input) {
|
|
@@ -1044,6 +1314,9 @@ var TranscriptBuilder = class {
|
|
|
1044
1314
|
}
|
|
1045
1315
|
};
|
|
1046
1316
|
var HEAD_INDEX_VERSION = "claudecode-head-v2";
|
|
1317
|
+
function resolveClaudeCodeDataRoot() {
|
|
1318
|
+
return resolveHomePath("CLAUDE_CONFIG_DIR", ".claude");
|
|
1319
|
+
}
|
|
1047
1320
|
function parseTimestampMs(data) {
|
|
1048
1321
|
const raw = data["timestamp"];
|
|
1049
1322
|
const value = asString(raw);
|
|
@@ -1089,7 +1362,7 @@ function extractClaudeUsage(data, msg) {
|
|
|
1089
1362
|
cacheCreate: readUsageNumber(usage, "cache_creation_input_tokens")
|
|
1090
1363
|
};
|
|
1091
1364
|
}
|
|
1092
|
-
var ClaudeCodeAgent = class extends
|
|
1365
|
+
var ClaudeCodeAgent = class extends SingleFileSessionSource {
|
|
1093
1366
|
name = "claudecode";
|
|
1094
1367
|
displayName = "Claude Code";
|
|
1095
1368
|
basePath = null;
|
|
@@ -1097,16 +1370,22 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1097
1370
|
sessionsIndexCache = {};
|
|
1098
1371
|
sessionsIndexMtime = {};
|
|
1099
1372
|
findBasePath() {
|
|
1100
|
-
|
|
1101
|
-
|
|
1373
|
+
return firstExisting(join4(resolveClaudeCodeDataRoot(), "projects"), "data/claudecode");
|
|
1374
|
+
}
|
|
1375
|
+
getSessionWatchPlan() {
|
|
1376
|
+
const dataRoot = resolveClaudeCodeDataRoot();
|
|
1377
|
+
return {
|
|
1378
|
+
status: "supported",
|
|
1379
|
+
targets: [{ root: dataRoot, path: join4(dataRoot, "projects") }, { path: "data/claudecode" }]
|
|
1380
|
+
};
|
|
1102
1381
|
}
|
|
1103
1382
|
isAvailable() {
|
|
1104
1383
|
this.basePath = this.findBasePath();
|
|
1105
1384
|
if (!this.basePath) return false;
|
|
1106
1385
|
try {
|
|
1107
|
-
for (const entry of
|
|
1108
|
-
const dir =
|
|
1109
|
-
if (existsSync4(dir) &&
|
|
1386
|
+
for (const entry of readdirSync2(this.basePath)) {
|
|
1387
|
+
const dir = join4(this.basePath, entry);
|
|
1388
|
+
if (existsSync4(dir) && readdirSync2(dir).some((f) => f.endsWith(".jsonl"))) {
|
|
1110
1389
|
return true;
|
|
1111
1390
|
}
|
|
1112
1391
|
}
|
|
@@ -1116,34 +1395,24 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1116
1395
|
}
|
|
1117
1396
|
listSessionSources(options) {
|
|
1118
1397
|
if (!this.basePath) return [];
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1398
|
+
const projectDirs = this.listProjectDirs();
|
|
1399
|
+
const indexMtimes = /* @__PURE__ */ new Map();
|
|
1400
|
+
for (const projectDir of projectDirs) {
|
|
1121
1401
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1122
|
-
|
|
1123
|
-
let stat;
|
|
1124
|
-
try {
|
|
1125
|
-
stat = statSync2(file);
|
|
1126
|
-
} catch {
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
1130
|
-
const sessionId = basename2(file, ".jsonl");
|
|
1131
|
-
refs.push({
|
|
1132
|
-
sessionId,
|
|
1133
|
-
sourcePath: file,
|
|
1134
|
-
fingerprint: this.sourceFingerprint(stat, indexPath)
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1402
|
+
indexMtimes.set(projectDir, this.readFileMtimeMs(indexPath));
|
|
1137
1403
|
}
|
|
1138
|
-
return
|
|
1404
|
+
return this.walkFiles(projectDirs, (entry) => entry.name.endsWith(".jsonl"), {
|
|
1405
|
+
recursive: false,
|
|
1406
|
+
scanWindow: options
|
|
1407
|
+
}).map(({ file, stat }) => ({
|
|
1408
|
+
sessionId: basename2(file, ".jsonl"),
|
|
1409
|
+
sourcePath: file,
|
|
1410
|
+
fingerprint: this.sourceFingerprint(stat, indexMtimes.get(dirname(file)) ?? null)
|
|
1411
|
+
}));
|
|
1139
1412
|
}
|
|
1140
|
-
|
|
1413
|
+
parseFileSessionHead(sourcePath) {
|
|
1141
1414
|
const projectDir = dirname(sourcePath);
|
|
1142
|
-
|
|
1143
|
-
if (head) {
|
|
1144
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, sourcePath, projectDir));
|
|
1145
|
-
}
|
|
1146
|
-
return head;
|
|
1415
|
+
return getParsedSession(this.parseSessionHeadResult(sourcePath, projectDir));
|
|
1147
1416
|
}
|
|
1148
1417
|
getSessionData(sessionId) {
|
|
1149
1418
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
@@ -1153,11 +1422,10 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1153
1422
|
if (!existsSync4(meta.sourcePath)) {
|
|
1154
1423
|
throw new Error(`Session file missing: ${meta.sourcePath}`);
|
|
1155
1424
|
}
|
|
1156
|
-
const content = readFileSync2(meta.sourcePath, "utf-8");
|
|
1157
1425
|
const builder = new TranscriptBuilder();
|
|
1158
1426
|
const assistantUuidToToolCalls = /* @__PURE__ */ new Map();
|
|
1159
1427
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1160
|
-
for (const record of
|
|
1428
|
+
for (const record of readJsonlFile(meta.sourcePath)) {
|
|
1161
1429
|
try {
|
|
1162
1430
|
this.convertRecord(record, builder, assistantUuidToToolCalls, countedUsageKeys);
|
|
1163
1431
|
} catch {
|
|
@@ -1165,6 +1433,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1165
1433
|
}
|
|
1166
1434
|
const transcript = builder.finish();
|
|
1167
1435
|
return {
|
|
1436
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
1168
1437
|
id: meta.id,
|
|
1169
1438
|
title: meta.title,
|
|
1170
1439
|
slug: `claudecode/${meta.id}`,
|
|
@@ -1180,60 +1449,38 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1180
1449
|
listProjectDirs() {
|
|
1181
1450
|
if (!this.basePath) return [];
|
|
1182
1451
|
try {
|
|
1183
|
-
return
|
|
1184
|
-
} catch {
|
|
1185
|
-
return [];
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
listJsonlFiles(dir) {
|
|
1189
|
-
try {
|
|
1190
|
-
return readdirSync(dir).filter((f) => f.endsWith(".jsonl") && f !== "sessions-index.json").map((f) => join3(dir, f));
|
|
1452
|
+
return readdirSync2(this.basePath).map((e) => join4(this.basePath, e)).filter((p) => existsSync4(p));
|
|
1191
1453
|
} catch {
|
|
1192
1454
|
return [];
|
|
1193
1455
|
}
|
|
1194
1456
|
}
|
|
1195
|
-
|
|
1457
|
+
createFileSessionMeta(head, source) {
|
|
1458
|
+
const projectDir = dirname(source.file);
|
|
1196
1459
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1197
|
-
const
|
|
1198
|
-
return {
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
messageCount: head.stats.message_count,
|
|
1210
|
-
createdAt: head.time_created,
|
|
1211
|
-
updatedAt: head.time_updated ?? head.time_created
|
|
1212
|
-
};
|
|
1460
|
+
const indexMtime = this.readFileMtimeMs(indexPath);
|
|
1461
|
+
return this.buildFileSessionMeta({
|
|
1462
|
+
head,
|
|
1463
|
+
source,
|
|
1464
|
+
fingerprint: this.sourceFingerprint(source.stat, indexMtime),
|
|
1465
|
+
extras: {
|
|
1466
|
+
indexPath: indexMtime === null ? null : indexPath,
|
|
1467
|
+
indexMtimeMs: indexMtime,
|
|
1468
|
+
headIndexVersion: HEAD_INDEX_VERSION,
|
|
1469
|
+
model: head.stats.total_tokens ? "unknown" : void 0
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1213
1472
|
}
|
|
1214
1473
|
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
1215
|
-
sourceFingerprint(stat,
|
|
1216
|
-
return JSON.stringify([
|
|
1217
|
-
HEAD_INDEX_VERSION,
|
|
1218
|
-
stat.mtimeMs,
|
|
1219
|
-
stat.size,
|
|
1220
|
-
this.getFileMtimeMs(indexPath)
|
|
1221
|
-
]);
|
|
1474
|
+
sourceFingerprint(stat, indexMtime) {
|
|
1475
|
+
return JSON.stringify([HEAD_INDEX_VERSION, stat.mtimeMs, stat.size, indexMtime]);
|
|
1222
1476
|
}
|
|
1223
1477
|
getSessionsIndexPath(projectDir) {
|
|
1224
|
-
return
|
|
1225
|
-
}
|
|
1226
|
-
getFileMtimeMs(filePath) {
|
|
1227
|
-
try {
|
|
1228
|
-
return statSync2(filePath).mtimeMs;
|
|
1229
|
-
} catch {
|
|
1230
|
-
return null;
|
|
1231
|
-
}
|
|
1478
|
+
return join4(projectDir, "sessions-index.json");
|
|
1232
1479
|
}
|
|
1233
1480
|
loadSessionsIndex(projectDir) {
|
|
1234
1481
|
const cacheKey = basename2(projectDir);
|
|
1235
1482
|
const indexPath = this.getSessionsIndexPath(projectDir);
|
|
1236
|
-
const mtime = this.
|
|
1483
|
+
const mtime = this.readFileMtimeMs(indexPath);
|
|
1237
1484
|
if (cacheKey in this.sessionsIndexCache && this.sessionsIndexMtime[cacheKey] === mtime) {
|
|
1238
1485
|
return this.sessionsIndexCache[cacheKey];
|
|
1239
1486
|
}
|
|
@@ -1255,25 +1502,14 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1255
1502
|
this.sessionsIndexMtime[cacheKey] = mtime;
|
|
1256
1503
|
return map;
|
|
1257
1504
|
}
|
|
1258
|
-
parseSessionHead(filePath, projectDir) {
|
|
1259
|
-
return getParsedSession(this.parseSessionHeadResult(filePath, projectDir));
|
|
1260
|
-
}
|
|
1261
1505
|
parseSessionHeadResult(filePath, projectDir) {
|
|
1262
|
-
const content = readFileSync2(filePath, "utf-8");
|
|
1263
|
-
const lines = content.split("\n").filter((l) => l.trim());
|
|
1264
|
-
if (lines.length === 0) return skippedSession("empty file");
|
|
1265
1506
|
const sessionId = basename2(filePath, ".jsonl");
|
|
1266
|
-
let firstRecord;
|
|
1267
|
-
try {
|
|
1268
|
-
firstRecord = JSON.parse(lines[0]);
|
|
1269
|
-
} catch {
|
|
1270
|
-
return skippedSession("malformed first record");
|
|
1271
|
-
}
|
|
1272
|
-
const createdAt = parseTimestampMs(firstRecord) || statSync2(filePath).mtimeMs;
|
|
1273
1507
|
const index = this.loadSessionsIndex(projectDir);
|
|
1274
1508
|
const indexEntry = index.get(sessionId);
|
|
1275
1509
|
const explicitTitle = indexEntry?.summary ? String(indexEntry.summary) : null;
|
|
1276
|
-
let
|
|
1510
|
+
let createdAt = 0;
|
|
1511
|
+
let updatedAt = 0;
|
|
1512
|
+
let lineIndex = 0;
|
|
1277
1513
|
let messageCount = 0;
|
|
1278
1514
|
let model = null;
|
|
1279
1515
|
let cwd = null;
|
|
@@ -1285,9 +1521,22 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1285
1521
|
const modelUsageMap = {};
|
|
1286
1522
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
1287
1523
|
let messageTitle = null;
|
|
1288
|
-
for (const
|
|
1524
|
+
for (const line of readJsonlFileLines(filePath)) {
|
|
1525
|
+
let data;
|
|
1526
|
+
try {
|
|
1527
|
+
data = JSON.parse(line);
|
|
1528
|
+
} catch {
|
|
1529
|
+
if (lineIndex === 0) return skippedSession("malformed first record");
|
|
1530
|
+
lineIndex += 1;
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
if (lineIndex === 0) {
|
|
1534
|
+
createdAt = parseTimestampMs(data) || statSync2(filePath).mtimeMs;
|
|
1535
|
+
updatedAt = createdAt;
|
|
1536
|
+
}
|
|
1537
|
+
const recordIndex = lineIndex;
|
|
1538
|
+
lineIndex += 1;
|
|
1289
1539
|
try {
|
|
1290
|
-
const data = JSON.parse(line);
|
|
1291
1540
|
if (isInternalEventType(data["type"])) continue;
|
|
1292
1541
|
const ts = parseTimestampMs(data);
|
|
1293
1542
|
if (ts > updatedAt) updatedAt = ts;
|
|
@@ -1310,7 +1559,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1310
1559
|
const m = asString(msg["model"]);
|
|
1311
1560
|
if (m?.trim()) model = m.trim();
|
|
1312
1561
|
}
|
|
1313
|
-
if (messageTitle === null &&
|
|
1562
|
+
if (messageTitle === null && recordIndex < 20 && role === "user") {
|
|
1314
1563
|
const candidate = this.extractUserMessageTitle(msg["content"]);
|
|
1315
1564
|
if (candidate) messageTitle = candidate;
|
|
1316
1565
|
}
|
|
@@ -1345,6 +1594,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1345
1594
|
} catch {
|
|
1346
1595
|
}
|
|
1347
1596
|
}
|
|
1597
|
+
if (lineIndex === 0) return skippedSession("empty file");
|
|
1348
1598
|
const directory = cwd ?? projectDir;
|
|
1349
1599
|
const directoryTitle = basenameTitle(directory) || basenameTitle(projectDir);
|
|
1350
1600
|
const title = resolveSessionTitle(explicitTitle, messageTitle, directoryTitle);
|
|
@@ -1523,6 +1773,7 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1523
1773
|
callID: String(part["id"] ?? ""),
|
|
1524
1774
|
title: `Tool: ${toolName}`,
|
|
1525
1775
|
state: {
|
|
1776
|
+
status: "running",
|
|
1526
1777
|
input: part["input"] ?? {},
|
|
1527
1778
|
output: null
|
|
1528
1779
|
},
|
|
@@ -1609,15 +1860,16 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1609
1860
|
backfillToolOutput(builder, callId, outputParts, stateUpdates) {
|
|
1610
1861
|
if (!callId) return false;
|
|
1611
1862
|
return builder.updateToolCall(callId, (part) => {
|
|
1612
|
-
const state = part.state
|
|
1863
|
+
const state = part.state;
|
|
1613
1864
|
if (outputParts.length > 0) {
|
|
1614
1865
|
const existing = state.output;
|
|
1615
1866
|
if (Array.isArray(existing)) existing.push(...outputParts);
|
|
1616
1867
|
else if (existing == null) state.output = [...outputParts];
|
|
1617
1868
|
else state.output = [existing, ...outputParts];
|
|
1618
1869
|
}
|
|
1619
|
-
if (stateUpdates)
|
|
1620
|
-
if (
|
|
1870
|
+
if (stateUpdates?.status) state.status = stateUpdates.status;
|
|
1871
|
+
if (stateUpdates?.metadata !== void 0) state.metadata = stateUpdates.metadata;
|
|
1872
|
+
if (outputParts.length > 0 && state.status === "running") state.status = "completed";
|
|
1621
1873
|
});
|
|
1622
1874
|
}
|
|
1623
1875
|
resolveToolCallId(data, item, assistantUuidToToolCalls) {
|
|
@@ -1635,11 +1887,11 @@ var ClaudeCodeAgent = class extends FileSystemSessionSource {
|
|
|
1635
1887
|
const updates = {};
|
|
1636
1888
|
const success = result["success"];
|
|
1637
1889
|
if (typeof success === "boolean") {
|
|
1638
|
-
updates
|
|
1890
|
+
updates.status = success ? "completed" : "error";
|
|
1639
1891
|
}
|
|
1640
1892
|
const commandName = result["commandName"];
|
|
1641
1893
|
if (commandName) {
|
|
1642
|
-
updates
|
|
1894
|
+
updates.metadata = { commandName };
|
|
1643
1895
|
}
|
|
1644
1896
|
return updates;
|
|
1645
1897
|
}
|
|
@@ -1715,9 +1967,9 @@ function backupDatabase(db, dbPath, label) {
|
|
|
1715
1967
|
return null;
|
|
1716
1968
|
}
|
|
1717
1969
|
const timestamp = new Date(Date.now()).toISOString().replaceAll(":", "").replaceAll(".", "-");
|
|
1718
|
-
let backupPath =
|
|
1970
|
+
let backupPath = join5(dirname2(dbPath), `${basename3(dbPath)}.${timestamp}.${label}.bak`);
|
|
1719
1971
|
for (let counter = 1; existsSync5(backupPath); counter += 1) {
|
|
1720
|
-
backupPath =
|
|
1972
|
+
backupPath = join5(dirname2(dbPath), `${basename3(dbPath)}.${timestamp}.${label}.${counter}.bak`);
|
|
1721
1973
|
}
|
|
1722
1974
|
db.exec(`VACUUM INTO ${quoteSqlString(backupPath)}`);
|
|
1723
1975
|
return backupPath;
|
|
@@ -1738,23 +1990,53 @@ function runSchemaMigrations(db, options) {
|
|
|
1738
1990
|
if (migration.version > options.targetVersion) {
|
|
1739
1991
|
break;
|
|
1740
1992
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1993
|
+
const fromVersion = currentVersion;
|
|
1994
|
+
const startedAt = performance.now();
|
|
1995
|
+
getCoreDiagnostics()?.info?.("sqlite.migration.started", {
|
|
1996
|
+
label: options.backupLabel,
|
|
1997
|
+
from_version: fromVersion,
|
|
1998
|
+
to_version: migration.version,
|
|
1999
|
+
destructive: migration.destructive ?? false
|
|
2000
|
+
});
|
|
2001
|
+
try {
|
|
2002
|
+
let backupCreated = false;
|
|
2003
|
+
if (migration.destructive) {
|
|
2004
|
+
const backupPath = backupDatabaseIfPopulated(
|
|
2005
|
+
db,
|
|
2006
|
+
options.dbPath,
|
|
2007
|
+
options.backupLabel,
|
|
2008
|
+
options.backupTables
|
|
2009
|
+
);
|
|
2010
|
+
if (backupPath) {
|
|
2011
|
+
backups.push(backupPath);
|
|
2012
|
+
backupCreated = true;
|
|
2013
|
+
}
|
|
1750
2014
|
}
|
|
2015
|
+
const apply = db.transaction(() => {
|
|
2016
|
+
migration.migrate(db);
|
|
2017
|
+
setUserVersion(db, migration.version);
|
|
2018
|
+
});
|
|
2019
|
+
apply();
|
|
2020
|
+
currentVersion = migration.version;
|
|
2021
|
+
getCoreDiagnostics()?.info?.("sqlite.migration.completed", {
|
|
2022
|
+
label: options.backupLabel,
|
|
2023
|
+
from_version: fromVersion,
|
|
2024
|
+
to_version: migration.version,
|
|
2025
|
+
destructive: migration.destructive ?? false,
|
|
2026
|
+
backup_created: backupCreated,
|
|
2027
|
+
duration_ms: Math.round(performance.now() - startedAt)
|
|
2028
|
+
});
|
|
2029
|
+
} catch (error) {
|
|
2030
|
+
getCoreDiagnostics()?.warn("sqlite.migration.failed", {
|
|
2031
|
+
label: options.backupLabel,
|
|
2032
|
+
from_version: fromVersion,
|
|
2033
|
+
to_version: migration.version,
|
|
2034
|
+
destructive: migration.destructive ?? false,
|
|
2035
|
+
duration_ms: Math.round(performance.now() - startedAt),
|
|
2036
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2037
|
+
});
|
|
2038
|
+
throw error;
|
|
1751
2039
|
}
|
|
1752
|
-
const apply = db.transaction(() => {
|
|
1753
|
-
migration.migrate(db);
|
|
1754
|
-
setUserVersion(db, migration.version);
|
|
1755
|
-
});
|
|
1756
|
-
apply();
|
|
1757
|
-
currentVersion = migration.version;
|
|
1758
2040
|
}
|
|
1759
2041
|
if (currentVersion < options.targetVersion) {
|
|
1760
2042
|
setUserVersion(db, options.targetVersion);
|
|
@@ -1843,6 +2125,9 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1843
2125
|
}
|
|
1844
2126
|
return this.dbPath;
|
|
1845
2127
|
}
|
|
2128
|
+
getSessionWatchPlan() {
|
|
2129
|
+
return this.config.getSessionWatchPlan();
|
|
2130
|
+
}
|
|
1846
2131
|
findDbPath() {
|
|
1847
2132
|
return this.config.findDbPath();
|
|
1848
2133
|
}
|
|
@@ -1960,26 +2245,10 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
1960
2245
|
const partData = parseJsonRecord(partRow.data, this.name, "part.data");
|
|
1961
2246
|
const partType = String(partData.type ?? "");
|
|
1962
2247
|
if (isInternalEventType(partType)) return null;
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
type: partType,
|
|
1968
|
-
text,
|
|
1969
|
-
time_created: Number(partRow.time_created ?? 0)
|
|
1970
|
-
};
|
|
1971
|
-
}
|
|
1972
|
-
if (partType === "tool") {
|
|
1973
|
-
return {
|
|
1974
|
-
type: "tool",
|
|
1975
|
-
tool: String(partData.tool ?? ""),
|
|
1976
|
-
callID: String(partData.callID ?? ""),
|
|
1977
|
-
title: cleanInternalText(String(partData.title ?? "")),
|
|
1978
|
-
state: asRecord(partData.state) ?? {},
|
|
1979
|
-
time_created: Number(partRow.time_created ?? 0)
|
|
1980
|
-
};
|
|
1981
|
-
}
|
|
1982
|
-
return null;
|
|
2248
|
+
const [part] = normalizeMessageParts([
|
|
2249
|
+
{ ...partData, time_created: Number(partRow.time_created ?? 0) }
|
|
2250
|
+
]);
|
|
2251
|
+
return part ? cleanMessagePart(part) : null;
|
|
1983
2252
|
}
|
|
1984
2253
|
readMessageParts(db, messageId) {
|
|
1985
2254
|
const partRows = db.prepare("SELECT data, time_created FROM part WHERE message_id = ? ORDER BY time_created ASC").all(messageId);
|
|
@@ -2120,6 +2389,7 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2120
2389
|
if (message.cost_source === "estimated") hasEstimatedCost = true;
|
|
2121
2390
|
}
|
|
2122
2391
|
return {
|
|
2392
|
+
reference: { agentName: this.name, sessionId: id },
|
|
2123
2393
|
id,
|
|
2124
2394
|
title,
|
|
2125
2395
|
slug,
|
|
@@ -2142,17 +2412,30 @@ var OpenCodeSqliteAgent = class extends DatabaseSessionSource {
|
|
|
2142
2412
|
}
|
|
2143
2413
|
}
|
|
2144
2414
|
};
|
|
2415
|
+
function resolveOpenCodeDataRoot() {
|
|
2416
|
+
return join6(resolveDataHome(), "opencode");
|
|
2417
|
+
}
|
|
2145
2418
|
function findOpenCodeDbPath() {
|
|
2146
2419
|
if (!isSqliteAvailable()) return null;
|
|
2147
|
-
|
|
2148
|
-
|
|
2420
|
+
return firstExisting(join6(resolveOpenCodeDataRoot(), "opencode.db"), "data/opencode/opencode.db");
|
|
2421
|
+
}
|
|
2422
|
+
function getOpenCodeSessionWatchPlan() {
|
|
2423
|
+
const dataRoot = resolveOpenCodeDataRoot();
|
|
2424
|
+
return {
|
|
2425
|
+
status: "supported",
|
|
2426
|
+
targets: [
|
|
2427
|
+
{ root: dataRoot, path: join6(dataRoot, "opencode.db") },
|
|
2428
|
+
{ root: "data/opencode", path: "data/opencode/opencode.db" }
|
|
2429
|
+
]
|
|
2430
|
+
};
|
|
2149
2431
|
}
|
|
2150
2432
|
var OpenCodeAgent = class extends OpenCodeSqliteAgent {
|
|
2151
2433
|
constructor() {
|
|
2152
2434
|
super({
|
|
2153
2435
|
name: "opencode",
|
|
2154
2436
|
displayName: "OpenCode",
|
|
2155
|
-
findDbPath: findOpenCodeDbPath
|
|
2437
|
+
findDbPath: findOpenCodeDbPath,
|
|
2438
|
+
getSessionWatchPlan: getOpenCodeSessionWatchPlan
|
|
2156
2439
|
});
|
|
2157
2440
|
}
|
|
2158
2441
|
};
|
|
@@ -2165,6 +2448,9 @@ var KIMI_TOOL_TITLE_MAP = {
|
|
|
2165
2448
|
Shell: "bash"
|
|
2166
2449
|
};
|
|
2167
2450
|
var KIMI_IGNORED_TOOLS = /* @__PURE__ */ new Set(["SetTodoList"]);
|
|
2451
|
+
function resolveKimiDataRoot() {
|
|
2452
|
+
return resolveHomePath("KIMI_SHARE_DIR", ".kimi");
|
|
2453
|
+
}
|
|
2168
2454
|
function mapToolTitle(toolName) {
|
|
2169
2455
|
return KIMI_TOOL_TITLE_MAP[toolName] ?? toolName;
|
|
2170
2456
|
}
|
|
@@ -2236,16 +2522,14 @@ function kimiContentText(content) {
|
|
|
2236
2522
|
}
|
|
2237
2523
|
function extractFirstUserTitle(contextFile, wireFile) {
|
|
2238
2524
|
if (contextFile && existsSync6(contextFile)) {
|
|
2239
|
-
const
|
|
2240
|
-
for (const record of parseJsonlLines(content)) {
|
|
2525
|
+
for (const record of readJsonlFile(contextFile)) {
|
|
2241
2526
|
if (record.role !== "user") continue;
|
|
2242
2527
|
const title = normalizeTitleText(kimiContentText(record.content));
|
|
2243
2528
|
if (title) return title;
|
|
2244
2529
|
}
|
|
2245
2530
|
}
|
|
2246
2531
|
if (wireFile && existsSync6(wireFile)) {
|
|
2247
|
-
const
|
|
2248
|
-
for (const record of parseJsonlLines(content)) {
|
|
2532
|
+
for (const record of readJsonlFile(wireFile)) {
|
|
2249
2533
|
const message = asRecord(record.message) ?? {};
|
|
2250
2534
|
if (message.type !== "TurnBegin") continue;
|
|
2251
2535
|
const payload = asRecord(message.payload) ?? {};
|
|
@@ -2264,14 +2548,20 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2264
2548
|
projectMap = /* @__PURE__ */ new Map();
|
|
2265
2549
|
defaultModel = null;
|
|
2266
2550
|
findBasePath() {
|
|
2267
|
-
|
|
2268
|
-
|
|
2551
|
+
return firstExisting(join7(resolveKimiDataRoot(), "sessions"), "data/kimi");
|
|
2552
|
+
}
|
|
2553
|
+
getSessionWatchPlan() {
|
|
2554
|
+
const dataRoot = resolveKimiDataRoot();
|
|
2555
|
+
return {
|
|
2556
|
+
status: "supported",
|
|
2557
|
+
targets: [{ root: dataRoot, path: join7(dataRoot, "sessions") }, { path: "data/kimi" }]
|
|
2558
|
+
};
|
|
2269
2559
|
}
|
|
2270
2560
|
/** Parse kimi.json and build md5(project_path) → cwd mapping */
|
|
2271
2561
|
loadKimiConfig() {
|
|
2272
|
-
const
|
|
2273
|
-
const configPath =
|
|
2274
|
-
const tomlPath =
|
|
2562
|
+
const dataRoot = resolveKimiDataRoot();
|
|
2563
|
+
const configPath = join7(dataRoot, "kimi.json");
|
|
2564
|
+
const tomlPath = join7(dataRoot, "config.toml");
|
|
2275
2565
|
if (existsSync6(tomlPath)) {
|
|
2276
2566
|
const configText = readFileSync3(tomlPath, "utf-8");
|
|
2277
2567
|
this.defaultModel = configText.match(/^default_model\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
@@ -2305,14 +2595,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2305
2595
|
if (!this.basePath) return [];
|
|
2306
2596
|
const dirs = [];
|
|
2307
2597
|
try {
|
|
2308
|
-
for (const hashEntry of
|
|
2598
|
+
for (const hashEntry of readdirSync3(this.basePath, { withFileTypes: true })) {
|
|
2309
2599
|
if (!hashEntry.isDirectory()) continue;
|
|
2310
|
-
const hashPath =
|
|
2600
|
+
const hashPath = join7(this.basePath, hashEntry.name);
|
|
2311
2601
|
try {
|
|
2312
|
-
for (const sessionEntry of
|
|
2602
|
+
for (const sessionEntry of readdirSync3(hashPath, { withFileTypes: true })) {
|
|
2313
2603
|
if (!sessionEntry.isDirectory()) continue;
|
|
2314
|
-
const sessionPath =
|
|
2315
|
-
if (existsSync6(
|
|
2604
|
+
const sessionPath = join7(hashPath, sessionEntry.name);
|
|
2605
|
+
if (existsSync6(join7(sessionPath, "metadata.json")) || existsSync6(join7(sessionPath, "state.json"))) {
|
|
2316
2606
|
dirs.push(sessionPath);
|
|
2317
2607
|
}
|
|
2318
2608
|
}
|
|
@@ -2323,64 +2613,73 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2323
2613
|
}
|
|
2324
2614
|
return dirs;
|
|
2325
2615
|
}
|
|
2326
|
-
/**
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2616
|
+
/**
|
|
2617
|
+
* 解析会话源,优先 state.json 而非 metadata.json。
|
|
2618
|
+
* 只读小 JSON 文件与 statSync,不触碰 transcript——枚举路径依赖这一点。
|
|
2619
|
+
*/
|
|
2620
|
+
resolveSessionSourceResult(sessionDir) {
|
|
2331
2621
|
try {
|
|
2332
2622
|
const sessionId = basename4(sessionDir);
|
|
2333
2623
|
const projectHash = basename4(dirname3(sessionDir));
|
|
2334
|
-
const contextFile =
|
|
2335
|
-
const wireFile =
|
|
2336
|
-
|
|
2624
|
+
const contextFile = join7(sessionDir, "context.jsonl");
|
|
2625
|
+
const wireFile = join7(sessionDir, "wire.jsonl");
|
|
2626
|
+
const existingContextFile = existsSync6(contextFile) ? contextFile : null;
|
|
2627
|
+
const existingWireFile = existsSync6(wireFile) ? wireFile : null;
|
|
2628
|
+
if (!existingContextFile && !existingWireFile) {
|
|
2337
2629
|
return skippedSession("missing transcript");
|
|
2338
2630
|
}
|
|
2339
|
-
const statePath =
|
|
2340
|
-
const metaPath =
|
|
2341
|
-
let
|
|
2631
|
+
const statePath = join7(sessionDir, "state.json");
|
|
2632
|
+
const metaPath = join7(sessionDir, "metadata.json");
|
|
2633
|
+
let explicitTitle = "";
|
|
2342
2634
|
let wireMtime = null;
|
|
2343
2635
|
let metaFile = "";
|
|
2344
2636
|
if (existsSync6(statePath)) {
|
|
2345
2637
|
const state = asRecord(JSON.parse(readFileSync3(statePath, "utf-8"))) ?? {};
|
|
2346
|
-
|
|
2638
|
+
explicitTitle = String(state.custom_title ?? "");
|
|
2347
2639
|
wireMtime = readWireMtime(state);
|
|
2348
2640
|
metaFile = statePath;
|
|
2349
2641
|
} else if (existsSync6(metaPath)) {
|
|
2350
2642
|
const meta = asRecord(JSON.parse(readFileSync3(metaPath, "utf-8"))) ?? {};
|
|
2351
|
-
|
|
2643
|
+
explicitTitle = String(meta.title ?? "");
|
|
2352
2644
|
wireMtime = readWireMtime(meta);
|
|
2353
2645
|
metaFile = metaPath;
|
|
2354
2646
|
}
|
|
2355
|
-
const cwd = this.projectMap.get(projectHash) || "";
|
|
2356
|
-
const existingContextFile = existsSync6(contextFile) ? contextFile : null;
|
|
2357
|
-
const existingWireFile = existsSync6(wireFile) ? wireFile : null;
|
|
2358
|
-
const messageTitle = extractFirstUserTitle(existingContextFile, existingWireFile);
|
|
2359
2647
|
const createdAt = wireMtime !== null ? wireMtime * 1e3 : metaFile ? statSync3(metaFile).mtimeMs : statSync3(sessionDir).mtimeMs;
|
|
2360
2648
|
return parsedSession({
|
|
2361
2649
|
id: sessionId,
|
|
2362
|
-
title: resolveSessionTitle(title, messageTitle, null),
|
|
2363
2650
|
sourcePath: sessionDir,
|
|
2364
|
-
cwd,
|
|
2651
|
+
cwd: this.projectMap.get(projectHash) || "",
|
|
2365
2652
|
contextFile: existingContextFile,
|
|
2366
2653
|
wireFile: existingWireFile,
|
|
2367
2654
|
createdAt,
|
|
2368
|
-
metaFile
|
|
2655
|
+
metaFile,
|
|
2656
|
+
explicitTitle
|
|
2369
2657
|
});
|
|
2370
2658
|
} catch {
|
|
2371
2659
|
return skippedSession("malformed metadata");
|
|
2372
2660
|
}
|
|
2373
2661
|
}
|
|
2662
|
+
/**
|
|
2663
|
+
* 在会话源之上补齐 title。仅当 state/metadata 里没有可用标题时才回退去读
|
|
2664
|
+
* transcript 找首条用户消息,所以标题解析的成本只在真正需要重解析时付出。
|
|
2665
|
+
*/
|
|
2666
|
+
parseSessionDirResult(sessionDir) {
|
|
2667
|
+
const result = this.resolveSessionSourceResult(sessionDir);
|
|
2668
|
+
if (result.status !== "parsed") return result;
|
|
2669
|
+
const source = result.data;
|
|
2670
|
+
const title = normalizeTitleText(source.explicitTitle) ?? resolveSessionTitle(null, extractFirstUserTitle(source.contextFile, source.wireFile), null);
|
|
2671
|
+
return parsedSession({ ...source, title, sourceMtimeMs: source.createdAt });
|
|
2672
|
+
}
|
|
2374
2673
|
listSessionSources(options) {
|
|
2375
2674
|
if (!this.basePath) return [];
|
|
2376
2675
|
const refs = [];
|
|
2377
2676
|
for (const dir of this.listSessionDirs()) {
|
|
2378
|
-
const
|
|
2379
|
-
if (!
|
|
2677
|
+
const source = getParsedSession(this.resolveSessionSourceResult(dir));
|
|
2678
|
+
if (!source || !matchesScanWindow(source.createdAt, options)) continue;
|
|
2380
2679
|
refs.push({
|
|
2381
|
-
sessionId:
|
|
2382
|
-
sourcePath:
|
|
2383
|
-
fingerprint: this.sourceFingerprint(
|
|
2680
|
+
sessionId: source.id,
|
|
2681
|
+
sourcePath: source.sourcePath,
|
|
2682
|
+
fingerprint: this.sourceFingerprint(source)
|
|
2384
2683
|
});
|
|
2385
2684
|
}
|
|
2386
2685
|
return refs;
|
|
@@ -2411,12 +2710,11 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2411
2710
|
}
|
|
2412
2711
|
getSessionDataFromContext(meta) {
|
|
2413
2712
|
if (!meta.contextFile) throw new Error("context.jsonl is missing");
|
|
2414
|
-
const content = readFileSync3(meta.contextFile, "utf-8");
|
|
2415
2713
|
const builder = new TranscriptBuilder();
|
|
2416
2714
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
2417
2715
|
let seq = 0;
|
|
2418
2716
|
const fallbackTs = meta.createdAt;
|
|
2419
|
-
for (const record of
|
|
2717
|
+
for (const record of readJsonlFile(meta.contextFile)) {
|
|
2420
2718
|
seq++;
|
|
2421
2719
|
try {
|
|
2422
2720
|
const role = String(record.role ?? "");
|
|
@@ -2467,15 +2765,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2467
2765
|
return this.buildSessionData(meta, builder, stats);
|
|
2468
2766
|
}
|
|
2469
2767
|
getSessionDataFromWire(meta) {
|
|
2470
|
-
const wirePath = meta.wireFile ??
|
|
2768
|
+
const wirePath = meta.wireFile ?? join7(meta.sourcePath, "wire.jsonl");
|
|
2471
2769
|
if (!existsSync6(wirePath)) throw new Error("wire.jsonl is missing");
|
|
2472
|
-
const content = readFileSync3(wirePath, "utf-8");
|
|
2473
2770
|
const builder = new TranscriptBuilder();
|
|
2474
2771
|
const ignoredToolCallIds = /* @__PURE__ */ new Set();
|
|
2475
2772
|
const openToolArgumentBuffer = /* @__PURE__ */ new Map();
|
|
2476
2773
|
let openToolCallId = null;
|
|
2477
2774
|
let seq = 0;
|
|
2478
|
-
for (const record of
|
|
2775
|
+
for (const record of readJsonlFile(wirePath)) {
|
|
2479
2776
|
seq++;
|
|
2480
2777
|
try {
|
|
2481
2778
|
const message = asRecord(record.message) ?? {};
|
|
@@ -2555,7 +2852,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2555
2852
|
tool: toolName,
|
|
2556
2853
|
callID: callId,
|
|
2557
2854
|
title: mapToolTitle(toolName),
|
|
2558
|
-
state: {
|
|
2855
|
+
state: { status: "running", input: normalizedArgs, output: null },
|
|
2559
2856
|
time_created: timestampMs
|
|
2560
2857
|
};
|
|
2561
2858
|
builder.appendToolCall(
|
|
@@ -2605,18 +2902,10 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2605
2902
|
}
|
|
2606
2903
|
// --- Helpers ---
|
|
2607
2904
|
sourceFingerprint(meta) {
|
|
2608
|
-
const fileMtime = (path2) => {
|
|
2609
|
-
if (!path2) return null;
|
|
2610
|
-
try {
|
|
2611
|
-
return statSync3(path2).mtimeMs;
|
|
2612
|
-
} catch {
|
|
2613
|
-
return null;
|
|
2614
|
-
}
|
|
2615
|
-
};
|
|
2616
2905
|
return JSON.stringify([
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2906
|
+
this.readFileMtimeMs(meta.metaFile),
|
|
2907
|
+
this.readFileMtimeMs(meta.contextFile),
|
|
2908
|
+
this.readFileMtimeMs(meta.wireFile)
|
|
2620
2909
|
]);
|
|
2621
2910
|
}
|
|
2622
2911
|
buildContextAssistantMessage(record, seq, ignoredToolCallIds, fallbackTs) {
|
|
@@ -2654,7 +2943,11 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2654
2943
|
tool: toolName,
|
|
2655
2944
|
callID: callId,
|
|
2656
2945
|
title: mapToolTitle(toolName),
|
|
2657
|
-
state: {
|
|
2946
|
+
state: {
|
|
2947
|
+
status: "running",
|
|
2948
|
+
input: normalizeToolArguments(function_.arguments),
|
|
2949
|
+
output: null
|
|
2950
|
+
},
|
|
2658
2951
|
time_created: fallbackTs
|
|
2659
2952
|
};
|
|
2660
2953
|
parts.push(part);
|
|
@@ -2680,8 +2973,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2680
2973
|
try {
|
|
2681
2974
|
const parsed = JSON.parse(combined);
|
|
2682
2975
|
if (builder.updateToolCall(openCallId, (part) => {
|
|
2683
|
-
|
|
2684
|
-
state.arguments = parsed;
|
|
2976
|
+
part.state.input = parsed;
|
|
2685
2977
|
})) {
|
|
2686
2978
|
buffer.delete(openCallId);
|
|
2687
2979
|
}
|
|
@@ -2693,6 +2985,16 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2693
2985
|
if (!outputParts.length || !callId) return false;
|
|
2694
2986
|
return builder.resolveToolCall(callId, { output: [...outputParts] });
|
|
2695
2987
|
}
|
|
2988
|
+
/** Applies a `_usage` record's running total; other records are ignored. */
|
|
2989
|
+
applyUsageTotal(record, stats) {
|
|
2990
|
+
if (record.role !== "_usage") return;
|
|
2991
|
+
const tokenCount = asNumber(record.token_count);
|
|
2992
|
+
if (tokenCount === void 0) {
|
|
2993
|
+
reportFieldMismatch("kimi", "usage.token_count");
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
stats.total_tokens = tokenCount;
|
|
2997
|
+
}
|
|
2696
2998
|
extractStats(sessionDir) {
|
|
2697
2999
|
let totalCost = 0;
|
|
2698
3000
|
const stats = {
|
|
@@ -2702,15 +3004,14 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2702
3004
|
total_tokens: 0,
|
|
2703
3005
|
message_count: 0
|
|
2704
3006
|
};
|
|
2705
|
-
const wirePath =
|
|
3007
|
+
const wirePath = join7(sessionDir, "wire.jsonl");
|
|
2706
3008
|
if (!existsSync6(wirePath)) return stats;
|
|
3009
|
+
const contextPath = join7(sessionDir, "context.jsonl");
|
|
3010
|
+
const hasContext = existsSync6(contextPath);
|
|
2707
3011
|
try {
|
|
2708
|
-
const
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
const data = asRecord(JSON.parse(line));
|
|
2712
|
-
const tokenUsage = asRecord(asRecord(data?.message)?.usage);
|
|
2713
|
-
if (!tokenUsage) continue;
|
|
3012
|
+
for (const record of readJsonlFile(wirePath)) {
|
|
3013
|
+
const tokenUsage = asRecord(asRecord(record.message)?.usage);
|
|
3014
|
+
if (tokenUsage) {
|
|
2714
3015
|
const inputTokens = extractTokenField(tokenUsage, "input_tokens");
|
|
2715
3016
|
const outputTokens = extractTokenField(tokenUsage, "output_tokens");
|
|
2716
3017
|
stats.total_input_tokens += inputTokens;
|
|
@@ -2720,30 +3021,16 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2720
3021
|
output: outputTokens
|
|
2721
3022
|
});
|
|
2722
3023
|
if (cost !== null) totalCost += cost;
|
|
2723
|
-
} catch {
|
|
2724
3024
|
}
|
|
3025
|
+
if (!hasContext) this.applyUsageTotal(record, stats);
|
|
2725
3026
|
}
|
|
2726
3027
|
} catch {
|
|
2727
3028
|
}
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
const rawContent = readFileSync3(rawPath, "utf-8");
|
|
2733
|
-
for (const line of rawContent.split("\n").filter((l) => l.trim())) {
|
|
2734
|
-
try {
|
|
2735
|
-
const data = asRecord(JSON.parse(line));
|
|
2736
|
-
if (data?.role !== "_usage") continue;
|
|
2737
|
-
const tokenCount = asNumber(data.token_count);
|
|
2738
|
-
if (tokenCount === void 0) {
|
|
2739
|
-
reportFieldMismatch("kimi", "usage.token_count");
|
|
2740
|
-
continue;
|
|
2741
|
-
}
|
|
2742
|
-
stats.total_tokens = tokenCount;
|
|
2743
|
-
} catch {
|
|
2744
|
-
}
|
|
3029
|
+
if (hasContext) {
|
|
3030
|
+
try {
|
|
3031
|
+
for (const record of readJsonlFile(contextPath)) this.applyUsageTotal(record, stats);
|
|
3032
|
+
} catch {
|
|
2745
3033
|
}
|
|
2746
|
-
} catch {
|
|
2747
3034
|
}
|
|
2748
3035
|
stats.total_cost = Number(totalCost.toFixed(8));
|
|
2749
3036
|
if (stats.total_cost > 0) {
|
|
@@ -2754,6 +3041,7 @@ var KimiAgent = class extends FileSystemSessionSource {
|
|
|
2754
3041
|
buildSessionData(meta, builder, stats) {
|
|
2755
3042
|
const transcript = builder.finish(stats);
|
|
2756
3043
|
return {
|
|
3044
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
2757
3045
|
id: meta.id,
|
|
2758
3046
|
title: meta.title,
|
|
2759
3047
|
slug: `kimi/${meta.id}`,
|
|
@@ -3027,6 +3315,9 @@ var PLAN_APPROVAL_PREFIX = "PLEASE IMPLEMENT THIS PLAN";
|
|
|
3027
3315
|
var SUBAGENT_NOTIFICATION_PATTERN = /<subagent_notification>\s*([\s\S]*?)\s*<\/subagent_notification>/;
|
|
3028
3316
|
var HEAD_INDEX_VERSION2 = "codex-head-v1";
|
|
3029
3317
|
var PARSER_VERSION = "codex-parser-v4";
|
|
3318
|
+
function resolveCodexDataRoot() {
|
|
3319
|
+
return resolveHomePath("CODEX_HOME", ".codex");
|
|
3320
|
+
}
|
|
3030
3321
|
var DEVELOPER_LIKE_USER_MARKERS = [
|
|
3031
3322
|
"agents.md instructions for",
|
|
3032
3323
|
"<instructions>",
|
|
@@ -3209,26 +3500,31 @@ function extractPatchContent(lines, startIndex) {
|
|
|
3209
3500
|
}
|
|
3210
3501
|
return { text: contentLines.join("\n"), nextLineIndex: i };
|
|
3211
3502
|
}
|
|
3212
|
-
var CodexAgent = class extends
|
|
3503
|
+
var CodexAgent = class extends SingleFileSessionSource {
|
|
3213
3504
|
name = "codex";
|
|
3214
3505
|
displayName = "Codex";
|
|
3215
3506
|
basePath = null;
|
|
3216
3507
|
sessionIndexCache = /* @__PURE__ */ new Map();
|
|
3217
|
-
sessionIndexMtime
|
|
3508
|
+
sessionIndexMtime;
|
|
3509
|
+
sessionIndexPath;
|
|
3218
3510
|
// ---- BaseAgent implementation ----
|
|
3219
3511
|
findBasePath() {
|
|
3220
|
-
|
|
3221
|
-
|
|
3512
|
+
return firstExisting(join8(resolveCodexDataRoot(), "sessions"));
|
|
3513
|
+
}
|
|
3514
|
+
getSessionWatchPlan() {
|
|
3515
|
+
const dataRoot = resolveCodexDataRoot();
|
|
3516
|
+
return {
|
|
3517
|
+
status: "supported",
|
|
3518
|
+
targets: [
|
|
3519
|
+
{ path: join8(dataRoot, "sessions") },
|
|
3520
|
+
{ path: join8(dataRoot, "session_index.jsonl") }
|
|
3521
|
+
]
|
|
3522
|
+
};
|
|
3222
3523
|
}
|
|
3223
3524
|
isAvailable() {
|
|
3224
3525
|
this.basePath = this.findBasePath();
|
|
3225
3526
|
if (!this.basePath) return false;
|
|
3226
|
-
|
|
3227
|
-
const files = this.walkDirForRolloutFiles(this.basePath);
|
|
3228
|
-
return files.length > 0;
|
|
3229
|
-
} catch {
|
|
3230
|
-
return false;
|
|
3231
|
-
}
|
|
3527
|
+
return this.listRolloutFiles().length > 0;
|
|
3232
3528
|
}
|
|
3233
3529
|
listSessionSources(options) {
|
|
3234
3530
|
if (!this.basePath) return [];
|
|
@@ -3239,14 +3535,6 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3239
3535
|
fingerprint: this.sourceFingerprint(file, stat)
|
|
3240
3536
|
}));
|
|
3241
3537
|
}
|
|
3242
|
-
scanSessionSource(sourcePath, options) {
|
|
3243
|
-
this.loadSessionIndex();
|
|
3244
|
-
const head = getParsedSession(this.parseSessionHeadResult(sourcePath, options));
|
|
3245
|
-
if (head) {
|
|
3246
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, sourcePath));
|
|
3247
|
-
}
|
|
3248
|
-
return head;
|
|
3249
|
-
}
|
|
3250
3538
|
getSessionData(sessionId) {
|
|
3251
3539
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
3252
3540
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
@@ -3334,6 +3622,7 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3334
3622
|
cost_source: totalCost > 0 ? "estimated" : void 0
|
|
3335
3623
|
});
|
|
3336
3624
|
return {
|
|
3625
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
3337
3626
|
id: meta.id,
|
|
3338
3627
|
title: meta.title,
|
|
3339
3628
|
slug: `codex/${meta.id}`,
|
|
@@ -3347,54 +3636,27 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3347
3636
|
// ---- File listing ----
|
|
3348
3637
|
listRolloutFiles(options) {
|
|
3349
3638
|
if (!this.basePath) return [];
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
}
|
|
3356
|
-
/** Stats each rollout file once during the walk; caller reuses it for the scan window check and the fingerprint. */
|
|
3357
|
-
walkDirForRolloutFiles(dir, options) {
|
|
3358
|
-
const files = [];
|
|
3359
|
-
try {
|
|
3360
|
-
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
3361
|
-
const fullPath = join7(dir, entry.name);
|
|
3362
|
-
if (entry.isDirectory()) {
|
|
3363
|
-
files.push(...this.walkDirForRolloutFiles(fullPath, options));
|
|
3364
|
-
} else if (entry.name.endsWith(".jsonl") && entry.name.startsWith("rollout-")) {
|
|
3365
|
-
let stat;
|
|
3366
|
-
try {
|
|
3367
|
-
stat = statSync4(fullPath);
|
|
3368
|
-
} catch {
|
|
3369
|
-
continue;
|
|
3370
|
-
}
|
|
3371
|
-
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
3372
|
-
files.push({ file: fullPath, stat });
|
|
3373
|
-
}
|
|
3374
|
-
}
|
|
3375
|
-
} catch {
|
|
3376
|
-
}
|
|
3377
|
-
return files;
|
|
3639
|
+
return this.walkFiles(
|
|
3640
|
+
this.basePath,
|
|
3641
|
+
(entry) => entry.name.endsWith(".jsonl") && entry.name.startsWith("rollout-"),
|
|
3642
|
+
{ scanWindow: options }
|
|
3643
|
+
);
|
|
3378
3644
|
}
|
|
3379
|
-
|
|
3645
|
+
createFileSessionMeta(head, source) {
|
|
3380
3646
|
const indexPath = this.getSessionIndexPath();
|
|
3381
|
-
const
|
|
3382
|
-
return {
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
messageCount: head.stats.message_count,
|
|
3395
|
-
createdAt: head.time_created,
|
|
3396
|
-
updatedAt: head.time_updated ?? head.time_created
|
|
3397
|
-
};
|
|
3647
|
+
const indexMtime = this.sessionIndexMtime ?? null;
|
|
3648
|
+
return this.buildFileSessionMeta({
|
|
3649
|
+
head,
|
|
3650
|
+
source,
|
|
3651
|
+
fingerprint: this.sourceFingerprint(source.file, source.stat),
|
|
3652
|
+
extras: {
|
|
3653
|
+
indexPath: indexMtime === null ? null : indexPath,
|
|
3654
|
+
indexMtimeMs: indexMtime,
|
|
3655
|
+
headIndexVersion: HEAD_INDEX_VERSION2,
|
|
3656
|
+
parserVersion: PARSER_VERSION,
|
|
3657
|
+
model: null
|
|
3658
|
+
}
|
|
3659
|
+
});
|
|
3398
3660
|
}
|
|
3399
3661
|
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
3400
3662
|
sourceFingerprint(file, stat) {
|
|
@@ -3408,38 +3670,37 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3408
3670
|
]);
|
|
3409
3671
|
}
|
|
3410
3672
|
getSessionIndexPath() {
|
|
3411
|
-
|
|
3412
|
-
return
|
|
3413
|
-
}
|
|
3414
|
-
getFileMtimeMs(filePath) {
|
|
3415
|
-
try {
|
|
3416
|
-
return statSync4(filePath).mtimeMs;
|
|
3417
|
-
} catch {
|
|
3418
|
-
return null;
|
|
3419
|
-
}
|
|
3673
|
+
this.sessionIndexPath ??= join8(resolveCodexDataRoot(), "session_index.jsonl");
|
|
3674
|
+
return this.sessionIndexPath;
|
|
3420
3675
|
}
|
|
3421
3676
|
// ---- Session index ----
|
|
3422
3677
|
loadSessionIndex() {
|
|
3423
3678
|
const indexPath = this.getSessionIndexPath();
|
|
3424
|
-
const mtime = this.
|
|
3425
|
-
if (this.
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3679
|
+
const mtime = this.readFileMtimeMs(indexPath);
|
|
3680
|
+
if (this.sessionIndexMtime !== void 0 && this.sessionIndexMtime === mtime) return;
|
|
3681
|
+
if (mtime === null) {
|
|
3682
|
+
this.sessionIndexCache.clear();
|
|
3683
|
+
this.sessionIndexMtime = null;
|
|
3684
|
+
return;
|
|
3685
|
+
}
|
|
3429
3686
|
try {
|
|
3430
3687
|
const content = readFileSync4(indexPath, "utf-8");
|
|
3688
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3431
3689
|
for (const record of parseJsonlLines(content)) {
|
|
3432
3690
|
const sid = String(record["id"] ?? "").trim();
|
|
3433
3691
|
const threadName = String(record["thread_name"] ?? "").trim();
|
|
3434
3692
|
if (sid && threadName) {
|
|
3435
|
-
|
|
3693
|
+
cache.set(sid, threadName);
|
|
3436
3694
|
}
|
|
3437
3695
|
}
|
|
3696
|
+
this.sessionIndexCache = cache;
|
|
3697
|
+
this.sessionIndexMtime = mtime;
|
|
3438
3698
|
} catch {
|
|
3699
|
+
this.sessionIndexCache.clear();
|
|
3700
|
+
this.sessionIndexMtime = void 0;
|
|
3439
3701
|
}
|
|
3440
3702
|
}
|
|
3441
3703
|
getTitleForSession(sessionId) {
|
|
3442
|
-
this.loadSessionIndex();
|
|
3443
3704
|
return this.sessionIndexCache.get(sessionId) ?? null;
|
|
3444
3705
|
}
|
|
3445
3706
|
// ---- Session head parsing ----
|
|
@@ -3453,6 +3714,10 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3453
3714
|
closeSync2(fd);
|
|
3454
3715
|
}
|
|
3455
3716
|
}
|
|
3717
|
+
parseFileSessionHead(filePath, options) {
|
|
3718
|
+
this.loadSessionIndex();
|
|
3719
|
+
return this.parseSessionHead(filePath, options);
|
|
3720
|
+
}
|
|
3456
3721
|
parseSessionHead(filePath, options) {
|
|
3457
3722
|
return getParsedSession(this.parseSessionHeadResult(filePath, options));
|
|
3458
3723
|
}
|
|
@@ -3735,10 +4000,10 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3735
4000
|
const fullText = textParts.join("\n");
|
|
3736
4001
|
const planMatch = fullText.match(PROPOSED_PLAN_PATTERN);
|
|
3737
4002
|
if (planMatch) {
|
|
3738
|
-
const
|
|
4003
|
+
const planText2 = planMatch[1].trim();
|
|
3739
4004
|
const planPart = {
|
|
3740
4005
|
type: "plan",
|
|
3741
|
-
text:
|
|
4006
|
+
text: planText2,
|
|
3742
4007
|
approval_status: "success",
|
|
3743
4008
|
time_created: timestampMs
|
|
3744
4009
|
};
|
|
@@ -3854,7 +4119,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3854
4119
|
callID: callId,
|
|
3855
4120
|
title: `Tool: ${toolIdentity.tool}`,
|
|
3856
4121
|
state: {
|
|
3857
|
-
|
|
4122
|
+
status: "running",
|
|
4123
|
+
input: arguments_,
|
|
3858
4124
|
output: null,
|
|
3859
4125
|
metadata: toolIdentity.metadata
|
|
3860
4126
|
},
|
|
@@ -3899,7 +4165,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3899
4165
|
callID: callId,
|
|
3900
4166
|
title: `Tool: ${toolIdentity.tool}`,
|
|
3901
4167
|
state: {
|
|
3902
|
-
|
|
4168
|
+
status: "running",
|
|
4169
|
+
input: normalizedInput,
|
|
3903
4170
|
output: null,
|
|
3904
4171
|
metadata: toolIdentity.metadata
|
|
3905
4172
|
},
|
|
@@ -3929,7 +4196,8 @@ var CodexAgent = class extends FileSystemSessionSource {
|
|
|
3929
4196
|
callID: callId,
|
|
3930
4197
|
title: `Tool: ${toolIdentity.tool}`,
|
|
3931
4198
|
state: {
|
|
3932
|
-
|
|
4199
|
+
status: "running",
|
|
4200
|
+
input: arguments_,
|
|
3933
4201
|
output: null,
|
|
3934
4202
|
metadata: toolIdentity.metadata
|
|
3935
4203
|
},
|
|
@@ -4016,6 +4284,23 @@ var PerfTracer = class {
|
|
|
4016
4284
|
}
|
|
4017
4285
|
};
|
|
4018
4286
|
var perf = new PerfTracer();
|
|
4287
|
+
function resolveCursorDataRoot() {
|
|
4288
|
+
const override = readEnvPath("CURSOR_DATA_PATH");
|
|
4289
|
+
if (override) return override;
|
|
4290
|
+
const currentPlatform = platform2();
|
|
4291
|
+
if (currentPlatform === "darwin") {
|
|
4292
|
+
return firstExisting(join9(homedir3(), "Library", "Application Support", "Cursor", "User"));
|
|
4293
|
+
}
|
|
4294
|
+
if (currentPlatform === "linux") {
|
|
4295
|
+
const configRoot = readEnvPath("XDG_CONFIG_HOME") ?? join9(homedir3(), ".config");
|
|
4296
|
+
return firstExisting(join9(configRoot, "Cursor", "User"));
|
|
4297
|
+
}
|
|
4298
|
+
if (currentPlatform === "win32") {
|
|
4299
|
+
const appData = readEnvPath("APPDATA") ?? join9(homedir3(), "AppData", "Roaming");
|
|
4300
|
+
return firstExisting(join9(appData, "Cursor", "User"));
|
|
4301
|
+
}
|
|
4302
|
+
return null;
|
|
4303
|
+
}
|
|
4019
4304
|
function narrowString(field, value) {
|
|
4020
4305
|
return narrowField("cursor", field, value, asString);
|
|
4021
4306
|
}
|
|
@@ -4163,29 +4448,22 @@ function isInternalBubble(bubble) {
|
|
|
4163
4448
|
return ["eventType", "kind", "subtype", "name"].some((key) => isInternalEventType(bubble[key]));
|
|
4164
4449
|
}
|
|
4165
4450
|
function buildToolState(action) {
|
|
4166
|
-
|
|
4167
|
-
if (action.input) {
|
|
4168
|
-
state.input = action.input;
|
|
4169
|
-
}
|
|
4451
|
+
let output = action.output;
|
|
4170
4452
|
if (action.output != null) {
|
|
4171
4453
|
const ts = 0;
|
|
4172
4454
|
const outputParts = normalizeToolOutputParts2(action.output, ts);
|
|
4173
|
-
|
|
4174
|
-
}
|
|
4175
|
-
if (action.state) {
|
|
4176
|
-
Object.assign(state, action.state);
|
|
4455
|
+
output = outputParts.length > 0 ? outputParts : action.output;
|
|
4177
4456
|
}
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
state.status = "completed";
|
|
4457
|
+
const [part] = normalizeMessageParts([
|
|
4458
|
+
{
|
|
4459
|
+
type: "tool",
|
|
4460
|
+
tool: action.tool ?? "unknown",
|
|
4461
|
+
input: action.input,
|
|
4462
|
+
output,
|
|
4463
|
+
state: action.state
|
|
4186
4464
|
}
|
|
4187
|
-
|
|
4188
|
-
return state;
|
|
4465
|
+
]);
|
|
4466
|
+
return part?.type === "tool" ? part.state : { status: "running" };
|
|
4189
4467
|
}
|
|
4190
4468
|
function buildToolPart(action, timestampMs) {
|
|
4191
4469
|
const toolName = action.tool ?? "unknown";
|
|
@@ -4201,15 +4479,15 @@ function buildToolPart(action, timestampMs) {
|
|
|
4201
4479
|
function buildTerminalToolPart(action, timestampMs) {
|
|
4202
4480
|
const command = String(action.input?.command ?? "");
|
|
4203
4481
|
const description = cleanInternalText(String(action.input?.commandDescription ?? ""));
|
|
4482
|
+
const state = buildToolState(action);
|
|
4483
|
+
state.input = { command };
|
|
4484
|
+
state.output = typeof action.output === "string" ? [{ type: "text", text: action.output, time_created: timestampMs }] : normalizeToolOutputParts2(action.output, timestampMs);
|
|
4204
4485
|
return {
|
|
4205
4486
|
type: "tool",
|
|
4206
4487
|
tool: "bash",
|
|
4207
4488
|
callID: "",
|
|
4208
4489
|
title: description || `bash: ${command.slice(0, 60)}`,
|
|
4209
|
-
state
|
|
4210
|
-
input: { command },
|
|
4211
|
-
output: typeof action.output === "string" ? [{ type: "text", text: action.output, time_created: timestampMs }] : normalizeToolOutputParts2(action.output, timestampMs)
|
|
4212
|
-
},
|
|
4490
|
+
state,
|
|
4213
4491
|
time_created: timestampMs
|
|
4214
4492
|
};
|
|
4215
4493
|
}
|
|
@@ -4237,9 +4515,22 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4237
4515
|
}
|
|
4238
4516
|
findDbPath() {
|
|
4239
4517
|
if (!isSqliteAvailable()) return null;
|
|
4240
|
-
const dataPath =
|
|
4518
|
+
const dataPath = resolveCursorDataRoot();
|
|
4241
4519
|
if (!dataPath) return null;
|
|
4242
|
-
return
|
|
4520
|
+
return join9(dataPath, "globalStorage", "state.vscdb");
|
|
4521
|
+
}
|
|
4522
|
+
getSessionWatchPlan() {
|
|
4523
|
+
const dataPath = resolveCursorDataRoot();
|
|
4524
|
+
return {
|
|
4525
|
+
status: "supported",
|
|
4526
|
+
targets: dataPath ? [
|
|
4527
|
+
{
|
|
4528
|
+
root: dataPath,
|
|
4529
|
+
path: join9(dataPath, "globalStorage", "state.vscdb")
|
|
4530
|
+
},
|
|
4531
|
+
{ root: dataPath, path: join9(dataPath, "workspaceStorage") }
|
|
4532
|
+
] : []
|
|
4533
|
+
};
|
|
4243
4534
|
}
|
|
4244
4535
|
/**
|
|
4245
4536
|
* Build a map of composerId → workspace folder path by reading
|
|
@@ -4247,9 +4538,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4247
4538
|
*/
|
|
4248
4539
|
buildWorkspacePathMap() {
|
|
4249
4540
|
const map = /* @__PURE__ */ new Map();
|
|
4250
|
-
const dataPath =
|
|
4541
|
+
const dataPath = resolveCursorDataRoot();
|
|
4251
4542
|
if (!dataPath) return map;
|
|
4252
|
-
const wsStoragePath =
|
|
4543
|
+
const wsStoragePath = join9(dataPath, "workspaceStorage");
|
|
4253
4544
|
if (!existsSync8(wsStoragePath)) return map;
|
|
4254
4545
|
let entryNames;
|
|
4255
4546
|
try {
|
|
@@ -4258,13 +4549,13 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4258
4549
|
return map;
|
|
4259
4550
|
}
|
|
4260
4551
|
for (const name of entryNames) {
|
|
4261
|
-
const wsDir =
|
|
4552
|
+
const wsDir = join9(wsStoragePath, name);
|
|
4262
4553
|
try {
|
|
4263
4554
|
if (!statSync5(wsDir).isDirectory()) continue;
|
|
4264
4555
|
} catch {
|
|
4265
4556
|
continue;
|
|
4266
4557
|
}
|
|
4267
|
-
const wsJsonPath =
|
|
4558
|
+
const wsJsonPath = join9(wsDir, "workspace.json");
|
|
4268
4559
|
if (!existsSync8(wsJsonPath)) continue;
|
|
4269
4560
|
let workspacePath;
|
|
4270
4561
|
try {
|
|
@@ -4275,7 +4566,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4275
4566
|
} catch {
|
|
4276
4567
|
continue;
|
|
4277
4568
|
}
|
|
4278
|
-
const wsDbPath =
|
|
4569
|
+
const wsDbPath = join9(wsDir, "state.vscdb");
|
|
4279
4570
|
if (!existsSync8(wsDbPath)) continue;
|
|
4280
4571
|
const wsDb = openDbReadOnly(wsDbPath);
|
|
4281
4572
|
if (!wsDb) continue;
|
|
@@ -4490,6 +4781,7 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4490
4781
|
const cachedDir = this.composerCache.get(`__dir__${composerId}`);
|
|
4491
4782
|
const directory = cachedDir?.directory ?? this.buildWorkspacePathMap().get(composerId) ?? "";
|
|
4492
4783
|
return {
|
|
4784
|
+
reference: { agentName: this.name, sessionId: resolvedSessionId },
|
|
4493
4785
|
id: resolvedSessionId,
|
|
4494
4786
|
title,
|
|
4495
4787
|
slug: `cursor/${resolvedSessionId}`,
|
|
@@ -4675,15 +4967,15 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4675
4967
|
}
|
|
4676
4968
|
}
|
|
4677
4969
|
if (toolName === "create_plan") {
|
|
4678
|
-
const
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
}
|
|
4970
|
+
const planText2 = String(asRecord(state.input)?.plan ?? "").trim();
|
|
4971
|
+
if (planText2) {
|
|
4972
|
+
return {
|
|
4973
|
+
type: "plan",
|
|
4974
|
+
text: planText2,
|
|
4975
|
+
approval_status: state.status === "completed" ? "success" : "fail",
|
|
4976
|
+
time_created: timestampMs
|
|
4977
|
+
};
|
|
4978
|
+
}
|
|
4687
4979
|
}
|
|
4688
4980
|
return {
|
|
4689
4981
|
type: "tool",
|
|
@@ -4749,6 +5041,9 @@ var CursorAgent = class extends DatabaseSessionSource {
|
|
|
4749
5041
|
};
|
|
4750
5042
|
var HEAD_INDEX_VERSION3 = "pi-head-v1";
|
|
4751
5043
|
var PARSER_VERSION2 = "pi-parser-v1";
|
|
5044
|
+
function resolvePiDataRoot() {
|
|
5045
|
+
return resolveHomePath("PI_HOME", ".pi");
|
|
5046
|
+
}
|
|
4752
5047
|
function parseTimestampMs3(value) {
|
|
4753
5048
|
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
4754
5049
|
const text = String(value ?? "").trim();
|
|
@@ -4819,13 +5114,22 @@ function buildCurrentPathEntries(entries) {
|
|
|
4819
5114
|
}
|
|
4820
5115
|
return path2.reverse();
|
|
4821
5116
|
}
|
|
4822
|
-
var PiAgent = class extends
|
|
5117
|
+
var PiAgent = class extends SingleFileSessionSource {
|
|
4823
5118
|
name = "pi";
|
|
4824
5119
|
displayName = "Pi";
|
|
4825
5120
|
basePath = null;
|
|
4826
5121
|
findBasePath() {
|
|
4827
|
-
|
|
4828
|
-
|
|
5122
|
+
return firstExisting(join10(resolvePiDataRoot(), "agent", "sessions"), "data/pi");
|
|
5123
|
+
}
|
|
5124
|
+
getSessionWatchPlan() {
|
|
5125
|
+
const dataRoot = resolvePiDataRoot();
|
|
5126
|
+
return {
|
|
5127
|
+
status: "supported",
|
|
5128
|
+
targets: [
|
|
5129
|
+
{ root: dataRoot, path: join10(dataRoot, "agent", "sessions") },
|
|
5130
|
+
{ root: "data/pi", path: "data/pi" }
|
|
5131
|
+
]
|
|
5132
|
+
};
|
|
4829
5133
|
}
|
|
4830
5134
|
isAvailable() {
|
|
4831
5135
|
this.basePath = this.findBasePath();
|
|
@@ -4834,19 +5138,12 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4834
5138
|
}
|
|
4835
5139
|
listSessionSources(options) {
|
|
4836
5140
|
if (!this.basePath) return [];
|
|
4837
|
-
return this.
|
|
5141
|
+
return this.listSessionFiles(options).map(({ file, stat }) => ({
|
|
4838
5142
|
sessionId: extractSessionIdFromFilename(file),
|
|
4839
5143
|
sourcePath: file,
|
|
4840
5144
|
fingerprint: this.sourceFingerprint(stat)
|
|
4841
5145
|
}));
|
|
4842
5146
|
}
|
|
4843
|
-
scanSessionSource(sourcePath) {
|
|
4844
|
-
const head = getParsedSession(this.parseSessionHeadResult(sourcePath));
|
|
4845
|
-
if (head) {
|
|
4846
|
-
this.sessionMetaMap.set(head.id, this.buildSessionMeta(head, sourcePath));
|
|
4847
|
-
}
|
|
4848
|
-
return head;
|
|
4849
|
-
}
|
|
4850
5147
|
getSessionData(sessionId) {
|
|
4851
5148
|
const meta = this.sessionMetaMap.get(sessionId);
|
|
4852
5149
|
if (!meta) throw new Error(`Session not found: ${sessionId}`);
|
|
@@ -4854,6 +5151,7 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4854
5151
|
const parsed = this.parsePiFile(meta.sourcePath);
|
|
4855
5152
|
const state = this.convertEntries(parsed.pathEntries);
|
|
4856
5153
|
return {
|
|
5154
|
+
reference: { agentName: this.name, sessionId: meta.id },
|
|
4857
5155
|
id: meta.id,
|
|
4858
5156
|
title: meta.title,
|
|
4859
5157
|
slug: `pi/${meta.id}`,
|
|
@@ -4874,52 +5172,30 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4874
5172
|
}
|
|
4875
5173
|
listSessionFiles(options) {
|
|
4876
5174
|
if (!this.basePath) return [];
|
|
4877
|
-
return this.
|
|
5175
|
+
return this.walkFiles(
|
|
5176
|
+
this.basePath,
|
|
5177
|
+
(entry) => entry.isFile() && entry.name.endsWith(".jsonl"),
|
|
5178
|
+
{ scanWindow: options }
|
|
5179
|
+
);
|
|
4878
5180
|
}
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
continue;
|
|
4888
|
-
}
|
|
4889
|
-
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) continue;
|
|
4890
|
-
let stat;
|
|
4891
|
-
try {
|
|
4892
|
-
stat = statSync6(fullPath);
|
|
4893
|
-
} catch {
|
|
4894
|
-
continue;
|
|
4895
|
-
}
|
|
4896
|
-
if (!matchesScanWindow(stat.mtimeMs, options)) continue;
|
|
4897
|
-
files.push({ file: fullPath, stat });
|
|
5181
|
+
createFileSessionMeta(head, source) {
|
|
5182
|
+
return this.buildFileSessionMeta({
|
|
5183
|
+
head,
|
|
5184
|
+
source,
|
|
5185
|
+
fingerprint: this.sourceFingerprint(source.stat),
|
|
5186
|
+
extras: {
|
|
5187
|
+
headIndexVersion: HEAD_INDEX_VERSION3,
|
|
5188
|
+
parserVersion: PARSER_VERSION2
|
|
4898
5189
|
}
|
|
4899
|
-
}
|
|
4900
|
-
}
|
|
4901
|
-
return files;
|
|
4902
|
-
}
|
|
4903
|
-
buildSessionMeta(head, file) {
|
|
4904
|
-
const stat = statSync6(file);
|
|
4905
|
-
return {
|
|
4906
|
-
id: head.id,
|
|
4907
|
-
title: head.title,
|
|
4908
|
-
sourcePath: file,
|
|
4909
|
-
sourceFingerprint: this.sourceFingerprint(stat),
|
|
4910
|
-
sourceMtimeMs: stat.mtimeMs,
|
|
4911
|
-
headIndexVersion: HEAD_INDEX_VERSION3,
|
|
4912
|
-
parserVersion: PARSER_VERSION2,
|
|
4913
|
-
directory: head.directory,
|
|
4914
|
-
messageCount: head.stats.message_count,
|
|
4915
|
-
createdAt: head.time_created,
|
|
4916
|
-
updatedAt: head.time_updated ?? head.time_created
|
|
4917
|
-
};
|
|
5190
|
+
});
|
|
4918
5191
|
}
|
|
4919
5192
|
/** Fingerprint depends on an already-fetched stat to avoid re-statting the same file. */
|
|
4920
5193
|
sourceFingerprint(stat) {
|
|
4921
5194
|
return JSON.stringify([HEAD_INDEX_VERSION3, PARSER_VERSION2, stat.mtimeMs, stat.size]);
|
|
4922
5195
|
}
|
|
5196
|
+
parseFileSessionHead(filePath) {
|
|
5197
|
+
return getParsedSession(this.parseSessionHeadResult(filePath));
|
|
5198
|
+
}
|
|
4923
5199
|
parseSessionHeadResult(filePath) {
|
|
4924
5200
|
const parsed = this.parsePiFile(filePath);
|
|
4925
5201
|
const state = this.convertEntries(parsed.pathEntries);
|
|
@@ -4946,16 +5222,24 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
4946
5222
|
});
|
|
4947
5223
|
}
|
|
4948
5224
|
parsePiFile(filePath) {
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
const
|
|
5225
|
+
let header = null;
|
|
5226
|
+
let recordCount = 0;
|
|
5227
|
+
const entries = [];
|
|
5228
|
+
for (const record of readJsonlFile(filePath)) {
|
|
5229
|
+
recordCount += 1;
|
|
5230
|
+
if (record["type"] === "session") {
|
|
5231
|
+
header ??= record;
|
|
5232
|
+
continue;
|
|
5233
|
+
}
|
|
5234
|
+
entries.push(record);
|
|
5235
|
+
}
|
|
5236
|
+
if (recordCount === 0) throw new Error("empty file");
|
|
4952
5237
|
if (!header) throw new Error("missing session header");
|
|
4953
|
-
const entries = records.filter((record) => record["type"] !== "session");
|
|
4954
5238
|
const pathEntries = buildCurrentPathEntries(entries);
|
|
4955
5239
|
if (pathEntries.length === 0) throw new Error("empty session tree");
|
|
4956
5240
|
const sessionId = String(header["id"] ?? extractSessionIdFromFilename(filePath)).trim();
|
|
4957
5241
|
if (!sessionId) throw new Error("missing session id");
|
|
4958
|
-
const stat =
|
|
5242
|
+
const stat = this.sessionSourceFile(filePath).stat;
|
|
4959
5243
|
const directory = String(header["cwd"] ?? "").trim() || basename6(filePath, ".jsonl");
|
|
4960
5244
|
const createdAt = narrowTimestampMs("session.timestamp", header["timestamp"]) || stat.mtimeMs;
|
|
4961
5245
|
const updatedAt = pathEntries.reduce(
|
|
@@ -5203,48 +5487,82 @@ var PiAgent = class extends FileSystemSessionSource {
|
|
|
5203
5487
|
};
|
|
5204
5488
|
}
|
|
5205
5489
|
};
|
|
5490
|
+
function resolveZCodeDataRoot() {
|
|
5491
|
+
const currentPlatform = platform3();
|
|
5492
|
+
if (currentPlatform !== "darwin" && currentPlatform !== "win32") return null;
|
|
5493
|
+
return join11(homedir4(), ".zcode");
|
|
5494
|
+
}
|
|
5206
5495
|
function findZCodeDbPath() {
|
|
5207
5496
|
if (!isSqliteAvailable()) return null;
|
|
5208
|
-
const
|
|
5209
|
-
if (!
|
|
5210
|
-
return firstExisting(
|
|
5497
|
+
const dataRoot = resolveZCodeDataRoot();
|
|
5498
|
+
if (!dataRoot) return null;
|
|
5499
|
+
return firstExisting(join11(dataRoot, "cli", "db", "db.sqlite"));
|
|
5500
|
+
}
|
|
5501
|
+
function getZCodeSessionWatchPlan() {
|
|
5502
|
+
const dataRoot = resolveZCodeDataRoot();
|
|
5503
|
+
return {
|
|
5504
|
+
status: "supported",
|
|
5505
|
+
targets: dataRoot ? [{ root: dataRoot, path: join11(dataRoot, "cli", "db", "db.sqlite") }] : []
|
|
5506
|
+
};
|
|
5211
5507
|
}
|
|
5212
5508
|
var ZCodeAgent = class extends OpenCodeSqliteAgent {
|
|
5213
5509
|
constructor() {
|
|
5214
5510
|
super({
|
|
5215
5511
|
name: "zcode",
|
|
5216
5512
|
displayName: "ZCode",
|
|
5217
|
-
findDbPath: findZCodeDbPath
|
|
5513
|
+
findDbPath: findZCodeDbPath,
|
|
5514
|
+
getSessionWatchPlan: getZCodeSessionWatchPlan
|
|
5218
5515
|
});
|
|
5219
5516
|
}
|
|
5220
5517
|
};
|
|
5221
5518
|
registerAgent({
|
|
5222
5519
|
icon: "/icon/agent/claudecode.svg",
|
|
5223
5520
|
iconColored: true,
|
|
5521
|
+
resolveDataRoot: resolveClaudeCodeDataRoot,
|
|
5522
|
+
resumeCommandPrefix: "claude --resume",
|
|
5523
|
+
toolStrategy: "custom",
|
|
5224
5524
|
create: () => new ClaudeCodeAgent()
|
|
5225
5525
|
});
|
|
5226
5526
|
registerAgent({
|
|
5227
5527
|
icon: "/icon/agent/opencode.svg",
|
|
5528
|
+
resolveDataRoot: resolveOpenCodeDataRoot,
|
|
5529
|
+
resumeCommandPrefix: "opencode -s",
|
|
5530
|
+
toolStrategy: "custom",
|
|
5228
5531
|
create: () => new OpenCodeAgent()
|
|
5229
5532
|
});
|
|
5230
5533
|
registerAgent({
|
|
5231
5534
|
icon: "/icon/agent/zcode.svg",
|
|
5535
|
+
resolveDataRoot: resolveZCodeDataRoot,
|
|
5536
|
+
resumeCommandPrefix: null,
|
|
5537
|
+
toolStrategy: "custom",
|
|
5232
5538
|
create: () => new ZCodeAgent()
|
|
5233
5539
|
});
|
|
5234
5540
|
registerAgent({
|
|
5235
5541
|
icon: "/icon/agent/kimi.svg",
|
|
5542
|
+
resolveDataRoot: resolveKimiDataRoot,
|
|
5543
|
+
resumeCommandPrefix: "kimi -r",
|
|
5544
|
+
toolStrategy: "custom",
|
|
5236
5545
|
create: () => new KimiAgent()
|
|
5237
5546
|
});
|
|
5238
5547
|
registerAgent({
|
|
5239
5548
|
icon: "/icon/agent/codex.svg",
|
|
5549
|
+
resolveDataRoot: resolveCodexDataRoot,
|
|
5550
|
+
resumeCommandPrefix: "codex resume",
|
|
5551
|
+
toolStrategy: "custom",
|
|
5240
5552
|
create: () => new CodexAgent()
|
|
5241
5553
|
});
|
|
5242
5554
|
registerAgent({
|
|
5243
5555
|
icon: "/icon/agent/pi.svg",
|
|
5556
|
+
resolveDataRoot: resolvePiDataRoot,
|
|
5557
|
+
resumeCommandPrefix: "pi --session",
|
|
5558
|
+
toolStrategy: "custom",
|
|
5244
5559
|
create: () => new PiAgent()
|
|
5245
5560
|
});
|
|
5246
5561
|
registerAgent({
|
|
5247
5562
|
icon: "/icon/agent/cursor.svg",
|
|
5563
|
+
resolveDataRoot: resolveCursorDataRoot,
|
|
5564
|
+
resumeCommandPrefix: null,
|
|
5565
|
+
toolStrategy: "custom",
|
|
5248
5566
|
create: () => new CursorAgent()
|
|
5249
5567
|
});
|
|
5250
5568
|
function fallbackDisplayName(input) {
|
|
@@ -5259,7 +5577,7 @@ var realFs = {
|
|
|
5259
5577
|
},
|
|
5260
5578
|
readText(path2) {
|
|
5261
5579
|
try {
|
|
5262
|
-
return
|
|
5580
|
+
return readFileSync6(path2, "utf8");
|
|
5263
5581
|
} catch {
|
|
5264
5582
|
return null;
|
|
5265
5583
|
}
|
|
@@ -5312,9 +5630,6 @@ function normalizeGitRemote(url) {
|
|
|
5312
5630
|
}
|
|
5313
5631
|
var IDENTITY_CACHE_TTL_MS = 10 * 60 * 1e3;
|
|
5314
5632
|
var identityCache = /* @__PURE__ */ new Map();
|
|
5315
|
-
function clearIdentityCache() {
|
|
5316
|
-
identityCache.clear();
|
|
5317
|
-
}
|
|
5318
5633
|
function computeIdentity(cwd, fs) {
|
|
5319
5634
|
if (fs !== realFs) return resolveIdentity(cwd, fs);
|
|
5320
5635
|
const key = cwd ?? "";
|
|
@@ -5447,9 +5762,6 @@ function parseManifestName(file, text) {
|
|
|
5447
5762
|
}
|
|
5448
5763
|
return null;
|
|
5449
5764
|
}
|
|
5450
|
-
function getAgentName(session) {
|
|
5451
|
-
return session.slug.split("/")[0]?.toLowerCase() || "unknown";
|
|
5452
|
-
}
|
|
5453
5765
|
function buildProjectGroups(sessions) {
|
|
5454
5766
|
const groups = /* @__PURE__ */ new Map();
|
|
5455
5767
|
for (const session of sessions) {
|
|
@@ -5459,13 +5771,13 @@ function buildProjectGroups(sessions) {
|
|
|
5459
5771
|
const groupKey = getProjectIdentityKey(identity);
|
|
5460
5772
|
const current = groups.get(groupKey);
|
|
5461
5773
|
if (current) {
|
|
5462
|
-
current.sources.add(
|
|
5774
|
+
current.sources.add(getSessionAgentKey(session));
|
|
5463
5775
|
current.sessionCount += 1;
|
|
5464
5776
|
current.lastActivity = Math.max(current.lastActivity, activity);
|
|
5465
5777
|
} else {
|
|
5466
5778
|
groups.set(groupKey, {
|
|
5467
5779
|
identity,
|
|
5468
|
-
sources: /* @__PURE__ */ new Set([
|
|
5780
|
+
sources: /* @__PURE__ */ new Set([getSessionAgentKey(session)]),
|
|
5469
5781
|
sessionCount: 1,
|
|
5470
5782
|
lastActivity: activity
|
|
5471
5783
|
});
|
|
@@ -5548,7 +5860,7 @@ function classifySessionTags(session) {
|
|
|
5548
5860
|
for (const part of message.parts) {
|
|
5549
5861
|
if (part.type === "plan") tags.add("planning");
|
|
5550
5862
|
if (part.type !== "tool") continue;
|
|
5551
|
-
const toolName = `${part.tool
|
|
5863
|
+
const toolName = `${part.tool} ${part.title ?? ""}`;
|
|
5552
5864
|
const toolPayload = stringifyToolPayload(part);
|
|
5553
5865
|
if (PLAN_TOOL_RE.test(toolName)) tags.add("planning");
|
|
5554
5866
|
if (READ_TOOL_RE.test(toolName)) readToolCount += 1;
|
|
@@ -5556,7 +5868,7 @@ function classifySessionTags(session) {
|
|
|
5556
5868
|
if (TESTING_COMMAND_RE.test(toolPayload)) tags.add("testing");
|
|
5557
5869
|
if (GIT_COMMAND_RE.test(toolPayload)) tags.add("git-ops");
|
|
5558
5870
|
if (BUILD_DEPLOY_COMMAND_RE.test(toolPayload)) tags.add("build-deploy");
|
|
5559
|
-
if (hasEditedDocPath(part.state
|
|
5871
|
+
if (hasEditedDocPath(part.state.input)) {
|
|
5560
5872
|
tags.add("docs");
|
|
5561
5873
|
}
|
|
5562
5874
|
}
|
|
@@ -5567,16 +5879,10 @@ function classifySessionTags(session) {
|
|
|
5567
5879
|
return TAG_ORDER.filter((tag) => tags.has(tag));
|
|
5568
5880
|
}
|
|
5569
5881
|
function partText(part) {
|
|
5570
|
-
return
|
|
5882
|
+
return part.type === "text" || part.type === "reasoning" || part.type === "plan" ? part.text : "";
|
|
5571
5883
|
}
|
|
5572
5884
|
function stringifyToolPayload(part) {
|
|
5573
|
-
return [
|
|
5574
|
-
part.tool,
|
|
5575
|
-
part.title,
|
|
5576
|
-
valueToText(part.input),
|
|
5577
|
-
valueToText(part.output),
|
|
5578
|
-
valueToText(part.state)
|
|
5579
|
-
].filter(Boolean).join("\n");
|
|
5885
|
+
return [part.tool, part.title, valueToText(part.state)].filter(Boolean).join("\n");
|
|
5580
5886
|
}
|
|
5581
5887
|
function valueToText(value) {
|
|
5582
5888
|
if (value == null) return "";
|
|
@@ -5600,7 +5906,7 @@ function hasEditedDocPath(value) {
|
|
|
5600
5906
|
}
|
|
5601
5907
|
return Object.values(record).some(hasEditedDocPath);
|
|
5602
5908
|
}
|
|
5603
|
-
function
|
|
5909
|
+
function toRecord2(value) {
|
|
5604
5910
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
5605
5911
|
return value;
|
|
5606
5912
|
}
|
|
@@ -5610,10 +5916,10 @@ function toStringValue(value) {
|
|
|
5610
5916
|
return typeof value === "string" ? value : "";
|
|
5611
5917
|
}
|
|
5612
5918
|
function normalizeToolLabel(part) {
|
|
5613
|
-
if (
|
|
5919
|
+
if (part.title?.trim()) {
|
|
5614
5920
|
return part.title.trim().replace(/^tool:\s*/i, "");
|
|
5615
5921
|
}
|
|
5616
|
-
if (
|
|
5922
|
+
if (part.tool.trim()) return part.tool.trim();
|
|
5617
5923
|
return "tool";
|
|
5618
5924
|
}
|
|
5619
5925
|
function normalizeToolName(part) {
|
|
@@ -5664,7 +5970,7 @@ function extractPathsFromToolInput(inputValue) {
|
|
|
5664
5970
|
return [...paths];
|
|
5665
5971
|
}
|
|
5666
5972
|
function getToolInputValue(part) {
|
|
5667
|
-
return part.state
|
|
5973
|
+
return part.state.input ?? null;
|
|
5668
5974
|
}
|
|
5669
5975
|
function classifyToolKind(part) {
|
|
5670
5976
|
const toolName = normalizeToolName(part);
|
|
@@ -5681,7 +5987,7 @@ function classifyToolKind(part) {
|
|
|
5681
5987
|
return null;
|
|
5682
5988
|
}
|
|
5683
5989
|
function normalizeCodexPatchEntry(entry) {
|
|
5684
|
-
const record =
|
|
5990
|
+
const record = toRecord2(entry);
|
|
5685
5991
|
const type = toStringValue(record.type);
|
|
5686
5992
|
if (!type) return null;
|
|
5687
5993
|
return {
|
|
@@ -5691,7 +5997,7 @@ function normalizeCodexPatchEntry(entry) {
|
|
|
5691
5997
|
};
|
|
5692
5998
|
}
|
|
5693
5999
|
function getCodexPatchEntries(inputValue) {
|
|
5694
|
-
const input =
|
|
6000
|
+
const input = toRecord2(inputValue);
|
|
5695
6001
|
const rawContent = Array.isArray(inputValue) ? inputValue : Array.isArray(input.content) ? input.content : [];
|
|
5696
6002
|
return rawContent.map((entry) => normalizeCodexPatchEntry(entry)).filter((entry) => entry != null);
|
|
5697
6003
|
}
|
|
@@ -5750,21 +6056,20 @@ function summarizeFileActivity(agentName, sessionId, projectIdentityKey, occurre
|
|
|
5750
6056
|
const current = grouped.get(key);
|
|
5751
6057
|
if (current) {
|
|
5752
6058
|
current.count += 1;
|
|
5753
|
-
current.
|
|
6059
|
+
current.latestTime = Math.max(current.latestTime, occurrence.time);
|
|
5754
6060
|
continue;
|
|
5755
6061
|
}
|
|
5756
6062
|
grouped.set(key, {
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
project_identity_key: projectIdentityKey,
|
|
6063
|
+
reference: { agentName, sessionId },
|
|
6064
|
+
projectIdentityKey,
|
|
5760
6065
|
path: occurrence.path,
|
|
5761
6066
|
kind: occurrence.kind,
|
|
5762
6067
|
count: 1,
|
|
5763
|
-
|
|
6068
|
+
latestTime: occurrence.time
|
|
5764
6069
|
});
|
|
5765
6070
|
}
|
|
5766
6071
|
return [...grouped.values()].sort((a, b) => {
|
|
5767
|
-
if (b.
|
|
6072
|
+
if (b.latestTime !== a.latestTime) return b.latestTime - a.latestTime;
|
|
5768
6073
|
return a.path.localeCompare(b.path);
|
|
5769
6074
|
});
|
|
5770
6075
|
}
|
|
@@ -5779,13 +6084,6 @@ function extractSessionFileActivity(agentName, sessionId, projectIdentityKey, me
|
|
|
5779
6084
|
var CACHE_FILENAME = "codesesh.db";
|
|
5780
6085
|
var LEGACY_CACHE_FILENAME = "scan-cache.json";
|
|
5781
6086
|
var SEARCH_INDEX_BULK_SYNC_THRESHOLD = 100;
|
|
5782
|
-
var ftsIntegrityCheckedPath = null;
|
|
5783
|
-
function getFtsIntegrityCheckedPath() {
|
|
5784
|
-
return ftsIntegrityCheckedPath;
|
|
5785
|
-
}
|
|
5786
|
-
function setFtsIntegrityCheckedPath(path2) {
|
|
5787
|
-
ftsIntegrityCheckedPath = path2;
|
|
5788
|
-
}
|
|
5789
6087
|
var schemaEnsuredPath = null;
|
|
5790
6088
|
function getSchemaEnsuredPath() {
|
|
5791
6089
|
return schemaEnsuredPath;
|
|
@@ -5794,13 +6092,13 @@ function setSchemaEnsuredPath(path2) {
|
|
|
5794
6092
|
schemaEnsuredPath = path2;
|
|
5795
6093
|
}
|
|
5796
6094
|
function getCacheDir2() {
|
|
5797
|
-
return
|
|
6095
|
+
return join12(homedir6(), ".cache", "codesesh");
|
|
5798
6096
|
}
|
|
5799
6097
|
function getCachePath2() {
|
|
5800
|
-
return
|
|
6098
|
+
return join12(getCacheDir2(), CACHE_FILENAME);
|
|
5801
6099
|
}
|
|
5802
6100
|
function getLegacyCachePath() {
|
|
5803
|
-
return
|
|
6101
|
+
return join12(getCacheDir2(), LEGACY_CACHE_FILENAME);
|
|
5804
6102
|
}
|
|
5805
6103
|
function hasCacheStorage() {
|
|
5806
6104
|
return existsSync11(getCachePath2());
|
|
@@ -5819,6 +6117,7 @@ function escapeRegExp(value) {
|
|
|
5819
6117
|
function normalizeFilePathSearch(value) {
|
|
5820
6118
|
return value.trim().replace(/^"|"$/g, "");
|
|
5821
6119
|
}
|
|
6120
|
+
var MESSAGE_PARTS_FORMAT_VERSION = 1;
|
|
5822
6121
|
function stringifyOptionalJson(value) {
|
|
5823
6122
|
return value == null ? null : JSON.stringify(value);
|
|
5824
6123
|
}
|
|
@@ -5833,34 +6132,6 @@ function sourcePathFromMetaJson(metaJson) {
|
|
|
5833
6132
|
const meta = JSON.parse(metaJson);
|
|
5834
6133
|
return sourcePathFromMeta(meta);
|
|
5835
6134
|
}
|
|
5836
|
-
function prepareUpsertCachedSession(db) {
|
|
5837
|
-
return db.prepare(`
|
|
5838
|
-
INSERT INTO cached_sessions(agent_name, session_id, session_json, meta_json)
|
|
5839
|
-
VALUES (?, ?, ?, ?)
|
|
5840
|
-
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5841
|
-
session_json = excluded.session_json,
|
|
5842
|
-
meta_json = excluded.meta_json
|
|
5843
|
-
`);
|
|
5844
|
-
}
|
|
5845
|
-
function prepareUpsertProjectSession(db) {
|
|
5846
|
-
return db.prepare(`
|
|
5847
|
-
INSERT INTO project_sessions(
|
|
5848
|
-
agent_name,
|
|
5849
|
-
session_id,
|
|
5850
|
-
identity_kind,
|
|
5851
|
-
identity_key,
|
|
5852
|
-
display_name,
|
|
5853
|
-
directory,
|
|
5854
|
-
activity_time
|
|
5855
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
5856
|
-
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
5857
|
-
identity_kind = excluded.identity_kind,
|
|
5858
|
-
identity_key = excluded.identity_key,
|
|
5859
|
-
display_name = excluded.display_name,
|
|
5860
|
-
directory = excluded.directory,
|
|
5861
|
-
activity_time = excluded.activity_time
|
|
5862
|
-
`);
|
|
5863
|
-
}
|
|
5864
6135
|
function prepareUpsertSession(db) {
|
|
5865
6136
|
return db.prepare(`
|
|
5866
6137
|
INSERT INTO sessions(
|
|
@@ -6025,27 +6296,16 @@ function prepareInsertMessageTool(db) {
|
|
|
6025
6296
|
function writeFileActivityRows(statement, activities) {
|
|
6026
6297
|
for (const activity of activities) {
|
|
6027
6298
|
statement.run(
|
|
6028
|
-
activity.
|
|
6029
|
-
activity.
|
|
6030
|
-
activity.
|
|
6299
|
+
activity.reference.agentName,
|
|
6300
|
+
activity.reference.sessionId,
|
|
6301
|
+
activity.projectIdentityKey,
|
|
6031
6302
|
activity.path,
|
|
6032
6303
|
activity.kind,
|
|
6033
6304
|
activity.count,
|
|
6034
|
-
activity.
|
|
6305
|
+
activity.latestTime
|
|
6035
6306
|
);
|
|
6036
6307
|
}
|
|
6037
6308
|
}
|
|
6038
|
-
function writeProjectSessionRow(statement, agentName, session, identity) {
|
|
6039
|
-
statement.run(
|
|
6040
|
-
agentName,
|
|
6041
|
-
session.id,
|
|
6042
|
-
identity.kind,
|
|
6043
|
-
identity.key,
|
|
6044
|
-
identity.displayName,
|
|
6045
|
-
session.directory,
|
|
6046
|
-
session.time_updated ?? session.time_created
|
|
6047
|
-
);
|
|
6048
|
-
}
|
|
6049
6309
|
function sessionFromRow(row) {
|
|
6050
6310
|
const session = {
|
|
6051
6311
|
id: String(row.session_id),
|
|
@@ -6095,7 +6355,7 @@ function sessionFromRow(row) {
|
|
|
6095
6355
|
}
|
|
6096
6356
|
return session;
|
|
6097
6357
|
}
|
|
6098
|
-
function
|
|
6358
|
+
function messageMetadataFromBackfillRow(row) {
|
|
6099
6359
|
const role = row.role === "assistant" || row.role === "tool" ? row.role : "user";
|
|
6100
6360
|
return {
|
|
6101
6361
|
id: String(row.message_id ?? ""),
|
|
@@ -6106,13 +6366,12 @@ function messageFromBackfillRow(row) {
|
|
|
6106
6366
|
mode: row.mode ?? null,
|
|
6107
6367
|
model: row.model ?? null,
|
|
6108
6368
|
provider: row.provider ?? null,
|
|
6109
|
-
parts: JSON.parse(String(row.parts_json ?? "[]")),
|
|
6110
6369
|
subagent_id: row.subagent_id ?? void 0,
|
|
6111
6370
|
nickname: row.nickname ?? void 0
|
|
6112
6371
|
};
|
|
6113
6372
|
}
|
|
6114
|
-
function
|
|
6115
|
-
const message =
|
|
6373
|
+
function messageMetadataFromCachedRow(row) {
|
|
6374
|
+
const message = messageMetadataFromBackfillRow(row);
|
|
6116
6375
|
const tokens = parseOptionalJson(row.tokens_json);
|
|
6117
6376
|
if (tokens) {
|
|
6118
6377
|
message.tokens = tokens;
|
|
@@ -6125,6 +6384,44 @@ function messageFromCachedRow(row) {
|
|
|
6125
6384
|
}
|
|
6126
6385
|
return message;
|
|
6127
6386
|
}
|
|
6387
|
+
function messageFromBackfillRow(row) {
|
|
6388
|
+
return {
|
|
6389
|
+
...messageMetadataFromBackfillRow(row),
|
|
6390
|
+
parts: messagePartsFromJson(row.parts_json)
|
|
6391
|
+
};
|
|
6392
|
+
}
|
|
6393
|
+
function messageFromCachedRow(row) {
|
|
6394
|
+
return {
|
|
6395
|
+
...messageMetadataFromCachedRow(row),
|
|
6396
|
+
parts: messagePartsFromJson(row.parts_json)
|
|
6397
|
+
};
|
|
6398
|
+
}
|
|
6399
|
+
function messagePartsFromJson(value) {
|
|
6400
|
+
try {
|
|
6401
|
+
return normalizeMessageParts(JSON.parse(String(value ?? "[]")));
|
|
6402
|
+
} catch {
|
|
6403
|
+
return [];
|
|
6404
|
+
}
|
|
6405
|
+
}
|
|
6406
|
+
function normalizeMessagePartsJson(value) {
|
|
6407
|
+
return JSON.stringify(messagePartsFromJson(value));
|
|
6408
|
+
}
|
|
6409
|
+
function messageJsonFromCachedRow(row) {
|
|
6410
|
+
const metadataJson = JSON.stringify(messageMetadataFromBackfillRow(row));
|
|
6411
|
+
const fields = [];
|
|
6412
|
+
if (row.tokens_json != null) {
|
|
6413
|
+
fields.push(`"tokens":${String(row.tokens_json)}`);
|
|
6414
|
+
}
|
|
6415
|
+
if (row.cost != null) {
|
|
6416
|
+
fields.push(`"cost":${JSON.stringify(Number(row.cost))}`);
|
|
6417
|
+
}
|
|
6418
|
+
if (row.cost_source) {
|
|
6419
|
+
fields.push(`"cost_source":${JSON.stringify(row.cost_source)}`);
|
|
6420
|
+
}
|
|
6421
|
+
const partsJson = Number(row.parts_format_version) >= MESSAGE_PARTS_FORMAT_VERSION ? String(row.parts_json ?? "[]") : normalizeMessagePartsJson(row.parts_json);
|
|
6422
|
+
fields.push(`"parts":${partsJson}`);
|
|
6423
|
+
return `${metadataJson.slice(0, -1)},${fields.join(",")}}`;
|
|
6424
|
+
}
|
|
6128
6425
|
function appendPlainText(value, chunks) {
|
|
6129
6426
|
if (value == null) return;
|
|
6130
6427
|
if (typeof value === "string") {
|
|
@@ -6184,7 +6481,7 @@ function toolNamesFromMessage(message) {
|
|
|
6184
6481
|
return [...tools];
|
|
6185
6482
|
}
|
|
6186
6483
|
function summarizeToolPart(part) {
|
|
6187
|
-
const state =
|
|
6484
|
+
const state = compactRecord({
|
|
6188
6485
|
status: part.state.status,
|
|
6189
6486
|
error: part.state.error,
|
|
6190
6487
|
metadata: part.state.metadata
|
|
@@ -6193,9 +6490,7 @@ function summarizeToolPart(part) {
|
|
|
6193
6490
|
type: part.type,
|
|
6194
6491
|
tool: part.tool,
|
|
6195
6492
|
title: part.title,
|
|
6196
|
-
nickname: part.nickname,
|
|
6197
6493
|
callID: part.callID,
|
|
6198
|
-
approval_status: part.approval_status,
|
|
6199
6494
|
state
|
|
6200
6495
|
});
|
|
6201
6496
|
}
|
|
@@ -6206,13 +6501,13 @@ function buildMessageText(message) {
|
|
|
6206
6501
|
appendPlainText(message.model, chunks);
|
|
6207
6502
|
for (const part of message.parts) {
|
|
6208
6503
|
appendPlainText(part.type, chunks);
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6504
|
+
if (part.type === "text" || part.type === "reasoning" || part.type === "plan") {
|
|
6505
|
+
appendPlainText(part.text, chunks);
|
|
6506
|
+
} else if (part.type === "tool") {
|
|
6507
|
+
appendPlainText(part.title, chunks);
|
|
6508
|
+
appendPlainText(part.tool, chunks);
|
|
6509
|
+
appendPlainText(part.state, chunks);
|
|
6510
|
+
}
|
|
6216
6511
|
}
|
|
6217
6512
|
return chunks.join("\n");
|
|
6218
6513
|
}
|
|
@@ -6249,7 +6544,7 @@ function buildSessionContentFromMessages(title, messages) {
|
|
|
6249
6544
|
}
|
|
6250
6545
|
return chunks.join("\n");
|
|
6251
6546
|
}
|
|
6252
|
-
var CACHE_SCHEMA_VERSION =
|
|
6547
|
+
var CACHE_SCHEMA_VERSION = 17;
|
|
6253
6548
|
function withCacheDb(fn) {
|
|
6254
6549
|
const cachePath = getCachePath2();
|
|
6255
6550
|
const db = openDb(cachePath);
|
|
@@ -6280,6 +6575,31 @@ function withCacheDbReadOnly(fn) {
|
|
|
6280
6575
|
db.close();
|
|
6281
6576
|
}
|
|
6282
6577
|
}
|
|
6578
|
+
function withSearchDb(fn) {
|
|
6579
|
+
return withCacheDb((db) => runWithFtsRecovery(db, fn));
|
|
6580
|
+
}
|
|
6581
|
+
function withSearchIndexDb(fn) {
|
|
6582
|
+
return withCacheDb((db) => runWithFtsRecovery(db, fn));
|
|
6583
|
+
}
|
|
6584
|
+
function runSearchIndexWrite(db, rebuild, write) {
|
|
6585
|
+
return db.transaction(() => {
|
|
6586
|
+
if (rebuild) {
|
|
6587
|
+
dropSearchTriggers(db);
|
|
6588
|
+
dropMessageSearchTriggers(db);
|
|
6589
|
+
}
|
|
6590
|
+
const value = write();
|
|
6591
|
+
let rebuildDurationMs;
|
|
6592
|
+
if (rebuild) {
|
|
6593
|
+
const rebuildStartedAt = performance.now();
|
|
6594
|
+
rebuildSearchIndex(db);
|
|
6595
|
+
rebuildMessageSearchIndex(db);
|
|
6596
|
+
rebuildDurationMs = performance.now() - rebuildStartedAt;
|
|
6597
|
+
createSearchTriggers(db);
|
|
6598
|
+
createMessageSearchTriggers(db);
|
|
6599
|
+
}
|
|
6600
|
+
return { value, rebuildDurationMs };
|
|
6601
|
+
})();
|
|
6602
|
+
}
|
|
6283
6603
|
function createCacheTables(db) {
|
|
6284
6604
|
db.exec(`
|
|
6285
6605
|
CREATE TABLE IF NOT EXISTS cache_meta (
|
|
@@ -6367,6 +6687,7 @@ function createSessionTables(db) {
|
|
|
6367
6687
|
cost REAL,
|
|
6368
6688
|
cost_source TEXT,
|
|
6369
6689
|
parts_json TEXT NOT NULL,
|
|
6690
|
+
parts_format_version INTEGER NOT NULL DEFAULT 0,
|
|
6370
6691
|
subagent_id TEXT,
|
|
6371
6692
|
nickname TEXT,
|
|
6372
6693
|
content_text TEXT NOT NULL,
|
|
@@ -6523,12 +6844,7 @@ function createSearchTables(db) {
|
|
|
6523
6844
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6524
6845
|
agent_name TEXT NOT NULL,
|
|
6525
6846
|
session_id TEXT NOT NULL,
|
|
6526
|
-
slug TEXT NOT NULL,
|
|
6527
6847
|
title TEXT NOT NULL,
|
|
6528
|
-
directory TEXT NOT NULL,
|
|
6529
|
-
time_created INTEGER NOT NULL,
|
|
6530
|
-
time_updated INTEGER,
|
|
6531
|
-
activity_time INTEGER NOT NULL,
|
|
6532
6848
|
content_text TEXT NOT NULL,
|
|
6533
6849
|
content_hash TEXT NOT NULL,
|
|
6534
6850
|
indexed_message_count INTEGER NOT NULL,
|
|
@@ -6590,28 +6906,27 @@ function dropSearchTriggers(db) {
|
|
|
6590
6906
|
DROP TRIGGER IF EXISTS session_documents_au;
|
|
6591
6907
|
`);
|
|
6592
6908
|
}
|
|
6593
|
-
|
|
6909
|
+
var LEGACY_SESSION_DOCUMENT_COLUMNS = [
|
|
6910
|
+
["slug", "TEXT NOT NULL DEFAULT ''"],
|
|
6911
|
+
["directory", "TEXT NOT NULL DEFAULT ''"],
|
|
6912
|
+
["time_created", "INTEGER NOT NULL DEFAULT 0"],
|
|
6913
|
+
["time_updated", "INTEGER"],
|
|
6914
|
+
["activity_time", "INTEGER NOT NULL DEFAULT 0"],
|
|
6915
|
+
["project_identity_kind", "TEXT NOT NULL DEFAULT 'path'"],
|
|
6916
|
+
["project_identity_key", "TEXT NOT NULL DEFAULT ''"],
|
|
6917
|
+
["project_display_name", "TEXT NOT NULL DEFAULT ''"]
|
|
6918
|
+
];
|
|
6919
|
+
function ensureLegacySessionDocumentColumns(db) {
|
|
6594
6920
|
if (!tableExists(db, "session_documents")) {
|
|
6595
6921
|
return;
|
|
6596
6922
|
}
|
|
6597
|
-
|
|
6598
|
-
db
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
}
|
|
6602
|
-
if (!columnExists(db, "session_documents", "project_identity_key")) {
|
|
6603
|
-
db.exec(
|
|
6604
|
-
"ALTER TABLE session_documents ADD COLUMN project_identity_key TEXT NOT NULL DEFAULT ''"
|
|
6605
|
-
);
|
|
6606
|
-
}
|
|
6607
|
-
if (!columnExists(db, "session_documents", "project_display_name")) {
|
|
6608
|
-
db.exec(
|
|
6609
|
-
"ALTER TABLE session_documents ADD COLUMN project_display_name TEXT NOT NULL DEFAULT ''"
|
|
6610
|
-
);
|
|
6923
|
+
for (const [name, definition] of LEGACY_SESSION_DOCUMENT_COLUMNS) {
|
|
6924
|
+
if (!columnExists(db, "session_documents", name)) {
|
|
6925
|
+
db.exec(`ALTER TABLE session_documents ADD COLUMN ${name} ${definition}`);
|
|
6926
|
+
}
|
|
6611
6927
|
}
|
|
6612
6928
|
}
|
|
6613
6929
|
function createProjectTables(db) {
|
|
6614
|
-
ensureProjectColumns(db);
|
|
6615
6930
|
db.exec(`
|
|
6616
6931
|
CREATE TABLE IF NOT EXISTS project_sessions (
|
|
6617
6932
|
agent_name TEXT NOT NULL,
|
|
@@ -6665,10 +6980,9 @@ function recreateProjectGroupsView(db) {
|
|
|
6665
6980
|
function createLatestCacheSchema(db) {
|
|
6666
6981
|
createCacheTables(db);
|
|
6667
6982
|
createSessionTables(db);
|
|
6668
|
-
createMessageSearchTables(db);
|
|
6669
6983
|
createFileActivityTables(db);
|
|
6670
|
-
createSearchTables(db);
|
|
6671
6984
|
createProjectTables(db);
|
|
6985
|
+
ensureFtsReady(db);
|
|
6672
6986
|
}
|
|
6673
6987
|
function recreateSearchIndexSchema(db) {
|
|
6674
6988
|
db.exec(`
|
|
@@ -6689,7 +7003,7 @@ function readLegacyCacheVersion(db) {
|
|
|
6689
7003
|
}
|
|
6690
7004
|
function inferCacheSchemaVersion(db) {
|
|
6691
7005
|
if (columnExists(db, "session_documents", "indexed_message_count")) {
|
|
6692
|
-
return 14;
|
|
7006
|
+
return columnExists(db, "session_documents", "slug") ? 14 : 15;
|
|
6693
7007
|
}
|
|
6694
7008
|
if (tableExists(db, "message_tools")) {
|
|
6695
7009
|
return 11;
|
|
@@ -6802,6 +7116,7 @@ function backfillSessionDocumentProjects(db) {
|
|
|
6802
7116
|
}
|
|
6803
7117
|
}
|
|
6804
7118
|
function migrateProjectIdentity(db) {
|
|
7119
|
+
ensureLegacySessionDocumentColumns(db);
|
|
6805
7120
|
createProjectTables(db);
|
|
6806
7121
|
backfillProjectSessions(db);
|
|
6807
7122
|
backfillSessionDocumentProjects(db);
|
|
@@ -7009,6 +7324,49 @@ function invalidateSearchContentHashes(db) {
|
|
|
7009
7324
|
db.exec("UPDATE session_documents SET content_hash = ''");
|
|
7010
7325
|
}
|
|
7011
7326
|
}
|
|
7327
|
+
function compactSessionDocuments(db) {
|
|
7328
|
+
if (!tableExists(db, "session_documents")) {
|
|
7329
|
+
createSearchTables(db);
|
|
7330
|
+
return;
|
|
7331
|
+
}
|
|
7332
|
+
dropSearchTriggers(db);
|
|
7333
|
+
db.exec(`
|
|
7334
|
+
DROP TABLE IF EXISTS session_documents_fts;
|
|
7335
|
+
DROP TABLE IF EXISTS session_documents_legacy_v14;
|
|
7336
|
+
ALTER TABLE session_documents RENAME TO session_documents_legacy_v14;
|
|
7337
|
+
`);
|
|
7338
|
+
createSearchTables(db);
|
|
7339
|
+
db.exec(`
|
|
7340
|
+
INSERT INTO session_documents(
|
|
7341
|
+
id,
|
|
7342
|
+
agent_name,
|
|
7343
|
+
session_id,
|
|
7344
|
+
title,
|
|
7345
|
+
content_text,
|
|
7346
|
+
content_hash,
|
|
7347
|
+
indexed_message_count,
|
|
7348
|
+
indexed_at
|
|
7349
|
+
)
|
|
7350
|
+
SELECT
|
|
7351
|
+
id,
|
|
7352
|
+
agent_name,
|
|
7353
|
+
session_id,
|
|
7354
|
+
title,
|
|
7355
|
+
content_text,
|
|
7356
|
+
content_hash,
|
|
7357
|
+
indexed_message_count,
|
|
7358
|
+
indexed_at
|
|
7359
|
+
FROM session_documents_legacy_v14;
|
|
7360
|
+
|
|
7361
|
+
DROP TABLE session_documents_legacy_v14;
|
|
7362
|
+
`);
|
|
7363
|
+
}
|
|
7364
|
+
function addMessagePartsFormatVersion(db) {
|
|
7365
|
+
if (!tableExists(db, "messages") || columnExists(db, "messages", "parts_format_version")) {
|
|
7366
|
+
return;
|
|
7367
|
+
}
|
|
7368
|
+
db.exec("ALTER TABLE messages ADD COLUMN parts_format_version INTEGER NOT NULL DEFAULT 0");
|
|
7369
|
+
}
|
|
7012
7370
|
var CODEX_EXEC_DECODE_MIGRATION_KEY = "codex_exec_decode_migrated_v3";
|
|
7013
7371
|
function migrateCodexExecDecode(db) {
|
|
7014
7372
|
if (!tableExists(db, "cache_meta")) return;
|
|
@@ -7035,33 +7393,84 @@ function rebuildMessageSearchIndex(db) {
|
|
|
7035
7393
|
}
|
|
7036
7394
|
db.exec("INSERT INTO messages_fts(messages_fts) VALUES ('rebuild')");
|
|
7037
7395
|
}
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7396
|
+
var SEARCH_FTS_INDEXES = ["session_documents_fts", "messages_fts"];
|
|
7397
|
+
function triggerExists(db, triggerName) {
|
|
7398
|
+
return db.prepare("SELECT 1 AS value FROM sqlite_master WHERE name = ? AND type = 'trigger' LIMIT 1").get(triggerName) !== void 0;
|
|
7399
|
+
}
|
|
7400
|
+
function hasAllTriggers(db, triggerNames) {
|
|
7401
|
+
return triggerNames.every((triggerName) => triggerExists(db, triggerName));
|
|
7402
|
+
}
|
|
7403
|
+
function rebuildSearchFtsIndexes(db, indexes, reason) {
|
|
7404
|
+
if (indexes.length === 0) return;
|
|
7405
|
+
const startedAt = performance.now();
|
|
7406
|
+
getCoreDiagnostics()?.info?.("sqlite.fts_rebuild.started", { indexes, reason });
|
|
7407
|
+
try {
|
|
7408
|
+
for (const index of indexes) {
|
|
7409
|
+
if (index === "session_documents_fts") rebuildSearchIndex(db);
|
|
7410
|
+
else rebuildMessageSearchIndex(db);
|
|
7411
|
+
}
|
|
7412
|
+
getCoreDiagnostics()?.info?.("sqlite.fts_rebuild.completed", {
|
|
7413
|
+
indexes,
|
|
7414
|
+
reason,
|
|
7415
|
+
duration_ms: Math.round(performance.now() - startedAt)
|
|
7416
|
+
});
|
|
7417
|
+
} catch (error) {
|
|
7418
|
+
getCoreDiagnostics()?.warn("sqlite.fts_rebuild.failed", {
|
|
7419
|
+
indexes,
|
|
7420
|
+
reason,
|
|
7421
|
+
duration_ms: Math.round(performance.now() - startedAt),
|
|
7422
|
+
message: error instanceof Error ? error.message : String(error)
|
|
7423
|
+
});
|
|
7424
|
+
throw error;
|
|
7041
7425
|
}
|
|
7042
|
-
|
|
7043
|
-
|
|
7426
|
+
}
|
|
7427
|
+
function ensureFtsReady(db) {
|
|
7428
|
+
const needsSearchRebuild = !tableExists(db, "session_documents_fts") || !hasAllTriggers(db, ["session_documents_ai", "session_documents_ad", "session_documents_au"]);
|
|
7429
|
+
const needsMessageSearchRebuild = !tableExists(db, "messages_fts") || !hasAllTriggers(db, ["messages_ai", "messages_ad", "messages_au"]);
|
|
7430
|
+
createSearchTables(db);
|
|
7044
7431
|
createMessageSearchTables(db);
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7432
|
+
const indexes = [];
|
|
7433
|
+
if (needsSearchRebuild) indexes.push("session_documents_fts");
|
|
7434
|
+
if (needsMessageSearchRebuild) indexes.push("messages_fts");
|
|
7435
|
+
rebuildSearchFtsIndexes(db, indexes, "schema_missing");
|
|
7048
7436
|
}
|
|
7049
7437
|
function ensureFtsConsistency(db) {
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
}
|
|
7438
|
+
const startedAt = performance.now();
|
|
7439
|
+
getCoreDiagnostics()?.info?.("sqlite.fts_integrity.started", {
|
|
7440
|
+
indexes: 2
|
|
7441
|
+
});
|
|
7055
7442
|
try {
|
|
7056
7443
|
db.exec(
|
|
7057
7444
|
"INSERT INTO session_documents_fts(session_documents_fts, rank) VALUES ('integrity-check', 1)"
|
|
7058
7445
|
);
|
|
7059
7446
|
db.exec("INSERT INTO messages_fts(messages_fts, rank) VALUES ('integrity-check', 1)");
|
|
7060
|
-
|
|
7061
|
-
|
|
7062
|
-
|
|
7063
|
-
|
|
7064
|
-
|
|
7447
|
+
getCoreDiagnostics()?.info?.("sqlite.fts_integrity.completed", {
|
|
7448
|
+
indexes: 2,
|
|
7449
|
+
duration_ms: Math.round(performance.now() - startedAt)
|
|
7450
|
+
});
|
|
7451
|
+
} catch (error) {
|
|
7452
|
+
getCoreDiagnostics()?.warn("sqlite.fts_integrity.failed", {
|
|
7453
|
+
indexes: 2,
|
|
7454
|
+
duration_ms: Math.round(performance.now() - startedAt),
|
|
7455
|
+
message: error instanceof Error ? error.message : String(error)
|
|
7456
|
+
});
|
|
7457
|
+
rebuildSearchFtsIndexes(db, [...SEARCH_FTS_INDEXES], "corruption");
|
|
7458
|
+
}
|
|
7459
|
+
}
|
|
7460
|
+
function isFtsCorruptionError(error) {
|
|
7461
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "SQLITE_CORRUPT_VTAB";
|
|
7462
|
+
}
|
|
7463
|
+
function runWithFtsRecovery(db, fn) {
|
|
7464
|
+
ensureFtsReady(db);
|
|
7465
|
+
try {
|
|
7466
|
+
return fn(db);
|
|
7467
|
+
} catch (error) {
|
|
7468
|
+
if (!isFtsCorruptionError(error)) throw error;
|
|
7469
|
+
getCoreDiagnostics()?.warn("sqlite.fts_corruption.detected", {
|
|
7470
|
+
message: error instanceof Error ? error.message : String(error)
|
|
7471
|
+
});
|
|
7472
|
+
ensureFtsConsistency(db);
|
|
7473
|
+
return fn(db);
|
|
7065
7474
|
}
|
|
7066
7475
|
}
|
|
7067
7476
|
function setCacheSchemaVersion(db) {
|
|
@@ -7141,7 +7550,9 @@ function ensureSchema(db, dbPath) {
|
|
|
7141
7550
|
}
|
|
7142
7551
|
},
|
|
7143
7552
|
{ version: 13, migrate: createCacheTables },
|
|
7144
|
-
{ version: 14, migrate: addIndexedMessageCount }
|
|
7553
|
+
{ version: 14, migrate: addIndexedMessageCount },
|
|
7554
|
+
{ version: 15, destructive: true, migrate: compactSessionDocuments },
|
|
7555
|
+
{ version: 17, migrate: addMessagePartsFormatVersion }
|
|
7145
7556
|
]
|
|
7146
7557
|
});
|
|
7147
7558
|
createLatestCacheSchema(db);
|
|
@@ -7357,7 +7768,6 @@ function loadSearchIndexEntry(agentName, change, loadSessionData) {
|
|
|
7357
7768
|
const identity = change.session.project_identity ?? data.project_identity ?? computeIdentity(change.session.directory, realFs);
|
|
7358
7769
|
return {
|
|
7359
7770
|
session: change.session,
|
|
7360
|
-
identity,
|
|
7361
7771
|
messages,
|
|
7362
7772
|
contentText: buildSessionContentFromMessages(data.title ?? change.session.title, messages),
|
|
7363
7773
|
contentHash: sessionContentHash(change.session),
|
|
@@ -7412,11 +7822,12 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7412
7822
|
cost,
|
|
7413
7823
|
cost_source,
|
|
7414
7824
|
parts_json,
|
|
7825
|
+
parts_format_version,
|
|
7415
7826
|
subagent_id,
|
|
7416
7827
|
nickname,
|
|
7417
7828
|
content_text,
|
|
7418
7829
|
tool_metadata_json
|
|
7419
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7830
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
7420
7831
|
ON CONFLICT(agent_name, session_id, message_index) DO UPDATE SET
|
|
7421
7832
|
message_id = excluded.message_id,
|
|
7422
7833
|
role = excluded.role,
|
|
@@ -7430,6 +7841,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7430
7841
|
cost = excluded.cost,
|
|
7431
7842
|
cost_source = excluded.cost_source,
|
|
7432
7843
|
parts_json = excluded.parts_json,
|
|
7844
|
+
parts_format_version = excluded.parts_format_version,
|
|
7433
7845
|
subagent_id = excluded.subagent_id,
|
|
7434
7846
|
nickname = excluded.nickname,
|
|
7435
7847
|
content_text = excluded.content_text,
|
|
@@ -7439,30 +7851,14 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7439
7851
|
INSERT INTO session_documents(
|
|
7440
7852
|
agent_name,
|
|
7441
7853
|
session_id,
|
|
7442
|
-
slug,
|
|
7443
7854
|
title,
|
|
7444
|
-
directory,
|
|
7445
|
-
project_identity_kind,
|
|
7446
|
-
project_identity_key,
|
|
7447
|
-
project_display_name,
|
|
7448
|
-
time_created,
|
|
7449
|
-
time_updated,
|
|
7450
|
-
activity_time,
|
|
7451
7855
|
content_text,
|
|
7452
7856
|
content_hash,
|
|
7453
7857
|
indexed_message_count,
|
|
7454
7858
|
indexed_at
|
|
7455
|
-
) VALUES (?, ?, ?, ?, ?, ?,
|
|
7859
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
7456
7860
|
ON CONFLICT(agent_name, session_id) DO UPDATE SET
|
|
7457
|
-
slug = excluded.slug,
|
|
7458
7861
|
title = excluded.title,
|
|
7459
|
-
directory = excluded.directory,
|
|
7460
|
-
project_identity_kind = excluded.project_identity_kind,
|
|
7461
|
-
project_identity_key = excluded.project_identity_key,
|
|
7462
|
-
project_display_name = excluded.project_display_name,
|
|
7463
|
-
time_created = excluded.time_created,
|
|
7464
|
-
time_updated = excluded.time_updated,
|
|
7465
|
-
activity_time = excluded.activity_time,
|
|
7466
7862
|
content_text = excluded.content_text,
|
|
7467
7863
|
content_hash = excluded.content_hash,
|
|
7468
7864
|
indexed_message_count = excluded.indexed_message_count,
|
|
@@ -7480,7 +7876,6 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7480
7876
|
}
|
|
7481
7877
|
let indexed = 0;
|
|
7482
7878
|
for (const entry of entries) {
|
|
7483
|
-
const activityTime = entry.session.time_updated ?? entry.session.time_created;
|
|
7484
7879
|
upsertSessionRow(upsertIndexedSession, agentName, entry.session, null, entry.sortIndex, null);
|
|
7485
7880
|
deleteFileActivity.run(agentName, entry.session.id);
|
|
7486
7881
|
deleteMessageTools.run(agentName, entry.session.id, 0);
|
|
@@ -7503,6 +7898,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7503
7898
|
message.cost ?? null,
|
|
7504
7899
|
message.costSource ?? null,
|
|
7505
7900
|
message.partsJson,
|
|
7901
|
+
MESSAGE_PARTS_FORMAT_VERSION,
|
|
7506
7902
|
message.subagentId ?? null,
|
|
7507
7903
|
message.nickname ?? null,
|
|
7508
7904
|
message.contentText,
|
|
@@ -7516,15 +7912,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7516
7912
|
upsertRow.run(
|
|
7517
7913
|
agentName,
|
|
7518
7914
|
entry.session.id,
|
|
7519
|
-
entry.session.slug,
|
|
7520
7915
|
entry.session.title,
|
|
7521
|
-
entry.session.directory,
|
|
7522
|
-
entry.identity.kind,
|
|
7523
|
-
entry.identity.key,
|
|
7524
|
-
entry.identity.displayName,
|
|
7525
|
-
entry.session.time_created,
|
|
7526
|
-
entry.session.time_updated ?? null,
|
|
7527
|
-
activityTime,
|
|
7528
7916
|
entry.contentText,
|
|
7529
7917
|
entry.contentHash,
|
|
7530
7918
|
entry.messages.length,
|
|
@@ -7535,8 +7923,7 @@ function writeSearchIndexRows(db, agentName, removedSessionIds, entries) {
|
|
|
7535
7923
|
return indexed;
|
|
7536
7924
|
}
|
|
7537
7925
|
function syncSessionSearchIndex(agentName, sessions, loadSessionData, options = {}) {
|
|
7538
|
-
return
|
|
7539
|
-
ensureFtsConsistency(db);
|
|
7926
|
+
return withSearchIndexDb((db) => {
|
|
7540
7927
|
const startedAt = performance.now();
|
|
7541
7928
|
const existingRows = db.prepare(
|
|
7542
7929
|
"SELECT session_id, content_hash, indexed_message_count FROM session_documents WHERE agent_name = ? ORDER BY id"
|
|
@@ -7570,23 +7957,8 @@ function syncSessionSearchIndex(agentName, sessions, loadSessionData, options =
|
|
|
7570
7957
|
loadSearchIndexEntries(agentName, changes, loadSessionData)
|
|
7571
7958
|
);
|
|
7572
7959
|
};
|
|
7573
|
-
let rebuildDurationMs;
|
|
7574
7960
|
const needsRebuild = isBulk && changedCount > 0;
|
|
7575
|
-
|
|
7576
|
-
db.transaction(() => {
|
|
7577
|
-
dropSearchTriggers(db);
|
|
7578
|
-
dropMessageSearchTriggers(db);
|
|
7579
|
-
writeRows();
|
|
7580
|
-
const rebuildStartedAt = performance.now();
|
|
7581
|
-
rebuildSearchIndex(db);
|
|
7582
|
-
rebuildMessageSearchIndex(db);
|
|
7583
|
-
rebuildDurationMs = performance.now() - rebuildStartedAt;
|
|
7584
|
-
createSearchTriggers(db);
|
|
7585
|
-
createMessageSearchTriggers(db);
|
|
7586
|
-
})();
|
|
7587
|
-
} else {
|
|
7588
|
-
db.transaction(writeRows)();
|
|
7589
|
-
}
|
|
7961
|
+
const { rebuildDurationMs } = runSearchIndexWrite(db, needsRebuild, writeRows);
|
|
7590
7962
|
return {
|
|
7591
7963
|
agentName,
|
|
7592
7964
|
mode: isBulk ? "bulk" : "incremental",
|
|
@@ -7613,8 +7985,7 @@ function syncSessionSearchIndexChanges(agentName, changes, removedSessionIds, lo
|
|
|
7613
7985
|
durationMs: 0
|
|
7614
7986
|
};
|
|
7615
7987
|
}
|
|
7616
|
-
return
|
|
7617
|
-
ensureFtsConsistency(db);
|
|
7988
|
+
return withSearchIndexDb((db) => {
|
|
7618
7989
|
const startedAt = performance.now();
|
|
7619
7990
|
const searchIndexState = readSearchIndexState(
|
|
7620
7991
|
db,
|
|
@@ -7636,23 +8007,8 @@ function syncSessionSearchIndexChanges(agentName, changes, removedSessionIds, lo
|
|
|
7636
8007
|
loadSearchIndexEntries(agentName, toUpsert, loadSessionData)
|
|
7637
8008
|
);
|
|
7638
8009
|
};
|
|
7639
|
-
let rebuildDurationMs;
|
|
7640
8010
|
const needsRebuild = isBulk && changedCount > 0;
|
|
7641
|
-
|
|
7642
|
-
db.transaction(() => {
|
|
7643
|
-
dropSearchTriggers(db);
|
|
7644
|
-
dropMessageSearchTriggers(db);
|
|
7645
|
-
writeRows();
|
|
7646
|
-
const rebuildStartedAt = performance.now();
|
|
7647
|
-
rebuildSearchIndex(db);
|
|
7648
|
-
rebuildMessageSearchIndex(db);
|
|
7649
|
-
rebuildDurationMs = performance.now() - rebuildStartedAt;
|
|
7650
|
-
createSearchTriggers(db);
|
|
7651
|
-
createMessageSearchTriggers(db);
|
|
7652
|
-
})();
|
|
7653
|
-
} else {
|
|
7654
|
-
db.transaction(writeRows)();
|
|
7655
|
-
}
|
|
8011
|
+
const { rebuildDurationMs } = runSearchIndexWrite(db, needsRebuild, writeRows);
|
|
7656
8012
|
return {
|
|
7657
8013
|
agentName,
|
|
7658
8014
|
mode: isBulk ? "bulk" : "incremental",
|
|
@@ -7794,6 +8150,32 @@ function buildSessionSearchFilters(options) {
|
|
|
7794
8150
|
params
|
|
7795
8151
|
};
|
|
7796
8152
|
}
|
|
8153
|
+
var SQLITE_REFERENCE_BATCH_SIZE = 200;
|
|
8154
|
+
function filterIndexedSessionReferences(references, options) {
|
|
8155
|
+
if (references.length === 0 || !hasCacheStorage()) return /* @__PURE__ */ new Set();
|
|
8156
|
+
const matches = withSearchDb((db) => {
|
|
8157
|
+
const filters = buildSessionSearchFilters(options);
|
|
8158
|
+
const result = /* @__PURE__ */ new Set();
|
|
8159
|
+
for (let offset = 0; offset < references.length; offset += SQLITE_REFERENCE_BATCH_SIZE) {
|
|
8160
|
+
const batch = references.slice(offset, offset + SQLITE_REFERENCE_BATCH_SIZE);
|
|
8161
|
+
const referenceClauses = batch.map(() => "(s.agent_name = ? AND s.session_id = ?)");
|
|
8162
|
+
const referenceParams = batch.flatMap(({ agentName, sessionId }) => [agentName, sessionId]);
|
|
8163
|
+
const rows = db.prepare(
|
|
8164
|
+
`
|
|
8165
|
+
SELECT s.agent_name, s.session_id
|
|
8166
|
+
FROM sessions s
|
|
8167
|
+
WHERE (${referenceClauses.join(" OR ")})
|
|
8168
|
+
${filters.where}
|
|
8169
|
+
`
|
|
8170
|
+
).all(...referenceParams, ...filters.params);
|
|
8171
|
+
for (const row of rows) {
|
|
8172
|
+
result.add(searchResultRowKey(row));
|
|
8173
|
+
}
|
|
8174
|
+
}
|
|
8175
|
+
return result;
|
|
8176
|
+
});
|
|
8177
|
+
return matches ?? /* @__PURE__ */ new Set();
|
|
8178
|
+
}
|
|
7797
8179
|
function searchSessionColumns() {
|
|
7798
8180
|
return `
|
|
7799
8181
|
s.agent_name,
|
|
@@ -7919,7 +8301,10 @@ function rowsToSearchResults(db, rows, textQuery, ftsQuery = toFtsQuery(textQuer
|
|
|
7919
8301
|
return rows.map((row) => {
|
|
7920
8302
|
const match = resolveSearchMatch(row, terms, messageMatches);
|
|
7921
8303
|
return {
|
|
7922
|
-
|
|
8304
|
+
reference: {
|
|
8305
|
+
agentName: String(row.agent_name),
|
|
8306
|
+
sessionId: String(row.session_id)
|
|
8307
|
+
},
|
|
7923
8308
|
session: sessionHeadFromSearchRow(row),
|
|
7924
8309
|
snippet: match.snippet,
|
|
7925
8310
|
matchType: match.matchType
|
|
@@ -7932,8 +8317,7 @@ function searchSessions(query, options = {}) {
|
|
|
7932
8317
|
if (!hasCacheStorage()) {
|
|
7933
8318
|
return [];
|
|
7934
8319
|
}
|
|
7935
|
-
const results =
|
|
7936
|
-
ensureFtsReady(db);
|
|
8320
|
+
const results = withSearchDb((db) => {
|
|
7937
8321
|
const filters = buildSessionSearchFilters(search.options);
|
|
7938
8322
|
if (!normalizedQuery) {
|
|
7939
8323
|
const rows2 = db.prepare(
|
|
@@ -7989,13 +8373,15 @@ function fileActivityFilters(options) {
|
|
|
7989
8373
|
}
|
|
7990
8374
|
function fileActivityFromRow(row) {
|
|
7991
8375
|
return {
|
|
7992
|
-
|
|
7993
|
-
|
|
7994
|
-
|
|
8376
|
+
reference: {
|
|
8377
|
+
agentName: String(row.agent_name),
|
|
8378
|
+
sessionId: String(row.session_id)
|
|
8379
|
+
},
|
|
8380
|
+
projectIdentityKey: String(row.project_identity_key ?? ""),
|
|
7995
8381
|
path: String(row.path ?? ""),
|
|
7996
8382
|
kind: row.kind ?? "read",
|
|
7997
8383
|
count: Number(row.count ?? 0),
|
|
7998
|
-
|
|
8384
|
+
latestTime: Number(row.latest_time ?? 0)
|
|
7999
8385
|
};
|
|
8000
8386
|
}
|
|
8001
8387
|
function buildFileActivityWhere(options) {
|
|
@@ -8060,10 +8446,19 @@ function buildFileActivityWhere(options) {
|
|
|
8060
8446
|
};
|
|
8061
8447
|
}
|
|
8062
8448
|
function listFileActivity(options = {}) {
|
|
8449
|
+
return queryFileActivity(options);
|
|
8450
|
+
}
|
|
8451
|
+
function queryFileActivity(options, sessionSearchOptions) {
|
|
8063
8452
|
if (!hasCacheStorage()) {
|
|
8064
8453
|
return [];
|
|
8065
8454
|
}
|
|
8066
8455
|
const filters = buildFileActivityWhere(options);
|
|
8456
|
+
const sessionFilters = sessionSearchOptions ? buildSessionSearchFilters(sessionSearchOptions) : { where: "", params: [] };
|
|
8457
|
+
const whereClauses = [
|
|
8458
|
+
filters.where.replace(/^WHERE /, ""),
|
|
8459
|
+
sessionFilters.where.replace(/^ AND /, "")
|
|
8460
|
+
].filter(Boolean);
|
|
8461
|
+
const where = whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "";
|
|
8067
8462
|
const queryRows = (db) => db.prepare(
|
|
8068
8463
|
`
|
|
8069
8464
|
SELECT
|
|
@@ -8088,14 +8483,17 @@ function listFileActivity(options = {}) {
|
|
|
8088
8483
|
s.total_cache_create_tokens,
|
|
8089
8484
|
s.total_cost,
|
|
8090
8485
|
s.cost_source,
|
|
8091
|
-
s.total_tokens
|
|
8486
|
+
s.total_tokens,
|
|
8487
|
+
s.model_usage_json,
|
|
8488
|
+
s.smart_tags_json,
|
|
8489
|
+
s.smart_tags_source_updated_at
|
|
8092
8490
|
FROM session_file_activity fa
|
|
8093
8491
|
JOIN sessions s ON s.agent_name = fa.agent_name AND s.session_id = fa.session_id
|
|
8094
|
-
${
|
|
8492
|
+
${where}
|
|
8095
8493
|
ORDER BY fa.latest_time DESC, fa.count DESC, fa.path
|
|
8096
8494
|
LIMIT ?
|
|
8097
8495
|
`
|
|
8098
|
-
).all(...filters.params, options.limit ?? 50);
|
|
8496
|
+
).all(...filters.params, ...sessionFilters.params, options.limit ?? 50);
|
|
8099
8497
|
let rows = withCacheDbReadOnly(queryRows);
|
|
8100
8498
|
if (rows == null && options.path) {
|
|
8101
8499
|
rows = withCacheDb(queryRows);
|
|
@@ -8124,27 +8522,22 @@ function searchFileActivitySessions(query, options = {}) {
|
|
|
8124
8522
|
const search = mergeSearchQueryOptions(query, options);
|
|
8125
8523
|
const path2 = normalizeFilePathSearch(search.options.file ?? search.text);
|
|
8126
8524
|
if (!path2) return [];
|
|
8127
|
-
const rows =
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
from: search.options.from,
|
|
8136
|
-
to: search.options.to,
|
|
8137
|
-
limit: (search.options.limit ?? 50) * 3
|
|
8138
|
-
});
|
|
8525
|
+
const rows = queryFileActivity(
|
|
8526
|
+
{
|
|
8527
|
+
path: path2,
|
|
8528
|
+
kind: search.options.fileKind,
|
|
8529
|
+
limit: (search.options.limit ?? 50) * 3
|
|
8530
|
+
},
|
|
8531
|
+
search.options
|
|
8532
|
+
);
|
|
8139
8533
|
const seen = /* @__PURE__ */ new Set();
|
|
8140
8534
|
const results = [];
|
|
8141
8535
|
for (const row of rows) {
|
|
8142
|
-
const key = `${row.
|
|
8536
|
+
const key = `${row.reference.agentName}/${row.reference.sessionId}`;
|
|
8143
8537
|
if (seen.has(key)) continue;
|
|
8144
|
-
if (!sessionMatchesSearchCost(row.session, search.options)) continue;
|
|
8145
8538
|
seen.add(key);
|
|
8146
8539
|
results.push({
|
|
8147
|
-
|
|
8540
|
+
reference: row.reference,
|
|
8148
8541
|
session: row.session,
|
|
8149
8542
|
snippet: `${row.kind} ${highlightFilePath(row.path, path2)} \xB7 ${row.count} events`,
|
|
8150
8543
|
matchType: "file_path"
|
|
@@ -8280,7 +8673,7 @@ function markAgentFullSyncCompleted(agentName) {
|
|
|
8280
8673
|
).run(Date.now(), agentName);
|
|
8281
8674
|
});
|
|
8282
8675
|
}
|
|
8283
|
-
function
|
|
8676
|
+
function loadCachedSessionRawEntry(agentName, sessionId) {
|
|
8284
8677
|
if (!hasCacheStorage()) {
|
|
8285
8678
|
return null;
|
|
8286
8679
|
}
|
|
@@ -8334,6 +8727,7 @@ function loadCachedSessionDataEntry(agentName, sessionId) {
|
|
|
8334
8727
|
cost,
|
|
8335
8728
|
cost_source,
|
|
8336
8729
|
parts_json,
|
|
8730
|
+
parts_format_version,
|
|
8337
8731
|
subagent_id,
|
|
8338
8732
|
nickname
|
|
8339
8733
|
FROM messages
|
|
@@ -8354,20 +8748,16 @@ function loadCachedSessionDataEntry(agentName, sessionId) {
|
|
|
8354
8748
|
return {
|
|
8355
8749
|
data: {
|
|
8356
8750
|
...head,
|
|
8357
|
-
|
|
8751
|
+
reference: { agentName, sessionId },
|
|
8358
8752
|
file_activity: fileActivityRows.map((activityRow) => fileActivityFromRow(activityRow))
|
|
8359
8753
|
},
|
|
8754
|
+
messageRows,
|
|
8360
8755
|
meta: parseCachedSessionMeta(row.meta_json)
|
|
8361
8756
|
};
|
|
8362
8757
|
});
|
|
8363
8758
|
}
|
|
8364
|
-
function loadCachedSessionData(agentName, sessionId) {
|
|
8365
|
-
return loadCachedSessionDataEntry(agentName, sessionId)?.data ?? null;
|
|
8366
|
-
}
|
|
8367
8759
|
function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
8368
8760
|
withCacheDb((db) => {
|
|
8369
|
-
const deleteAgent = db.prepare("DELETE FROM agent_cache WHERE agent_name = ?");
|
|
8370
|
-
const deleteLegacySessions = db.prepare("DELETE FROM cached_sessions WHERE agent_name = ?");
|
|
8371
8761
|
const deleteSession = db.prepare(
|
|
8372
8762
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
8373
8763
|
);
|
|
@@ -8383,25 +8773,16 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
8383
8773
|
const deleteFileActivity = db.prepare(
|
|
8384
8774
|
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
8385
8775
|
);
|
|
8386
|
-
const deleteProjectSession = db.prepare(
|
|
8387
|
-
"DELETE FROM project_sessions WHERE agent_name = ? AND session_id = ?"
|
|
8388
|
-
);
|
|
8389
|
-
const deleteProjectSessions = db.prepare("DELETE FROM project_sessions WHERE agent_name = ?");
|
|
8390
8776
|
const upsertAgent = db.prepare(`
|
|
8391
8777
|
INSERT INTO agent_cache(agent_name, timestamp)
|
|
8392
8778
|
VALUES (?, ?)
|
|
8393
8779
|
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
8394
8780
|
`);
|
|
8395
|
-
const upsertCachedSession = prepareUpsertCachedSession(db);
|
|
8396
8781
|
const upsertSession = prepareUpsertSession(db);
|
|
8397
|
-
const upsertProjectSession = prepareUpsertProjectSession(db);
|
|
8398
8782
|
const write = db.transaction(() => {
|
|
8399
8783
|
const timestamp = Date.now();
|
|
8400
8784
|
const sessionIds = new Set(sessions.map((session) => session.id));
|
|
8401
8785
|
const existingSessionIds = db.prepare("SELECT session_id FROM sessions WHERE agent_name = ?").all(agentName);
|
|
8402
|
-
deleteAgent.run(agentName);
|
|
8403
|
-
deleteLegacySessions.run(agentName);
|
|
8404
|
-
deleteProjectSessions.run(agentName);
|
|
8405
8786
|
upsertAgent.run(agentName, timestamp);
|
|
8406
8787
|
for (const row of existingSessionIds) {
|
|
8407
8788
|
const sessionId = String(row.session_id);
|
|
@@ -8410,15 +8791,12 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
8410
8791
|
deleteMessageTools.run(agentName, sessionId);
|
|
8411
8792
|
deleteMessages.run(agentName, sessionId);
|
|
8412
8793
|
deleteFileActivity.run(agentName, sessionId);
|
|
8413
|
-
deleteProjectSession.run(agentName, sessionId);
|
|
8414
8794
|
deleteSession.run(agentName, sessionId);
|
|
8415
8795
|
}
|
|
8416
8796
|
}
|
|
8417
8797
|
sessions.forEach((session, index) => {
|
|
8418
|
-
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
8419
8798
|
const sessionMeta = meta[session.id];
|
|
8420
8799
|
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
8421
|
-
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
8422
8800
|
upsertSessionRow(
|
|
8423
8801
|
upsertSession,
|
|
8424
8802
|
agentName,
|
|
@@ -8427,7 +8805,6 @@ function saveCachedSessions(agentName, sessions, meta = {}) {
|
|
|
8427
8805
|
index,
|
|
8428
8806
|
sourcePathFromMeta(sessionMeta)
|
|
8429
8807
|
);
|
|
8430
|
-
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
8431
8808
|
});
|
|
8432
8809
|
});
|
|
8433
8810
|
write();
|
|
@@ -8439,9 +8816,6 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
8439
8816
|
return;
|
|
8440
8817
|
}
|
|
8441
8818
|
withCacheDb((db) => {
|
|
8442
|
-
const deleteLegacySession = db.prepare(
|
|
8443
|
-
"DELETE FROM cached_sessions WHERE agent_name = ? AND session_id = ?"
|
|
8444
|
-
);
|
|
8445
8819
|
const deleteSession = db.prepare(
|
|
8446
8820
|
"DELETE FROM sessions WHERE agent_name = ? AND session_id = ?"
|
|
8447
8821
|
);
|
|
@@ -8457,33 +8831,24 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
8457
8831
|
const deleteFileActivity = db.prepare(
|
|
8458
8832
|
"DELETE FROM session_file_activity WHERE agent_name = ? AND session_id = ?"
|
|
8459
8833
|
);
|
|
8460
|
-
const deleteProjectSession = db.prepare(
|
|
8461
|
-
"DELETE FROM project_sessions WHERE agent_name = ? AND session_id = ?"
|
|
8462
|
-
);
|
|
8463
8834
|
const upsertAgent = db.prepare(`
|
|
8464
8835
|
INSERT INTO agent_cache(agent_name, timestamp)
|
|
8465
8836
|
VALUES (?, ?)
|
|
8466
8837
|
ON CONFLICT(agent_name) DO UPDATE SET timestamp = excluded.timestamp
|
|
8467
8838
|
`);
|
|
8468
|
-
const upsertCachedSession = prepareUpsertCachedSession(db);
|
|
8469
8839
|
const upsertSession = prepareUpsertSession(db);
|
|
8470
|
-
const upsertProjectSession = prepareUpsertProjectSession(db);
|
|
8471
8840
|
const write = db.transaction(() => {
|
|
8472
8841
|
upsertAgent.run(agentName, Date.now());
|
|
8473
8842
|
for (const sessionId of new Set(removedSessionIds)) {
|
|
8474
|
-
deleteLegacySession.run(agentName, sessionId);
|
|
8475
8843
|
deleteSearchDocument.run(agentName, sessionId);
|
|
8476
8844
|
deleteMessageTools.run(agentName, sessionId);
|
|
8477
8845
|
deleteMessages.run(agentName, sessionId);
|
|
8478
8846
|
deleteFileActivity.run(agentName, sessionId);
|
|
8479
|
-
deleteProjectSession.run(agentName, sessionId);
|
|
8480
8847
|
deleteSession.run(agentName, sessionId);
|
|
8481
8848
|
}
|
|
8482
8849
|
for (const { session, sortIndex } of changes) {
|
|
8483
|
-
const identity = session.project_identity ?? computeIdentity(session.directory, realFs);
|
|
8484
8850
|
const sessionMeta = meta[session.id];
|
|
8485
8851
|
const metaJson = sessionMeta ? JSON.stringify(sessionMeta) : null;
|
|
8486
|
-
upsertCachedSession.run(agentName, session.id, JSON.stringify(session), metaJson);
|
|
8487
8852
|
upsertSessionRow(
|
|
8488
8853
|
upsertSession,
|
|
8489
8854
|
agentName,
|
|
@@ -8492,7 +8857,6 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
8492
8857
|
sortIndex,
|
|
8493
8858
|
sourcePathFromMeta(sessionMeta)
|
|
8494
8859
|
);
|
|
8495
|
-
writeProjectSessionRow(upsertProjectSession, agentName, session, identity);
|
|
8496
8860
|
}
|
|
8497
8861
|
});
|
|
8498
8862
|
write();
|
|
@@ -8500,7 +8864,6 @@ function saveCachedSessionChanges(agentName, changes, removedSessionIds, meta =
|
|
|
8500
8864
|
});
|
|
8501
8865
|
}
|
|
8502
8866
|
function clearCache() {
|
|
8503
|
-
setFtsIntegrityCheckedPath(null);
|
|
8504
8867
|
setSchemaEnsuredPath(null);
|
|
8505
8868
|
if (!hasCacheStorage()) {
|
|
8506
8869
|
deleteLegacyCacheFile();
|
|
@@ -8533,49 +8896,6 @@ function clearCache() {
|
|
|
8533
8896
|
}
|
|
8534
8897
|
}
|
|
8535
8898
|
}
|
|
8536
|
-
function getCacheInfo() {
|
|
8537
|
-
if (!hasCacheStorage()) {
|
|
8538
|
-
return { lastScanTime: null, size: 0 };
|
|
8539
|
-
}
|
|
8540
|
-
const info = withCacheDb((db) => {
|
|
8541
|
-
const timestampRow = db.prepare("SELECT MAX(timestamp) AS value FROM agent_cache").get();
|
|
8542
|
-
const sizeRow = db.prepare("SELECT COUNT(*) AS value FROM sessions").get();
|
|
8543
|
-
const lastScanTime = Number(timestampRow?.value ?? 0) || null;
|
|
8544
|
-
const size = Number(sizeRow?.value ?? 0);
|
|
8545
|
-
return { lastScanTime, size };
|
|
8546
|
-
});
|
|
8547
|
-
return info ?? { lastScanTime: null, size: 0 };
|
|
8548
|
-
}
|
|
8549
|
-
function listCachedProjectGroups(sessions) {
|
|
8550
|
-
if (sessions) {
|
|
8551
|
-
return buildProjectGroups(sessions);
|
|
8552
|
-
}
|
|
8553
|
-
if (!hasCacheStorage()) {
|
|
8554
|
-
return [];
|
|
8555
|
-
}
|
|
8556
|
-
const queryRows = (db) => db.prepare(
|
|
8557
|
-
`
|
|
8558
|
-
SELECT identity_kind, identity_key, display_name, sources_csv, session_count, last_activity
|
|
8559
|
-
FROM project_groups_v
|
|
8560
|
-
ORDER BY
|
|
8561
|
-
CASE identity_kind WHEN 'loose' THEN 1 ELSE 0 END,
|
|
8562
|
-
last_activity IS NULL,
|
|
8563
|
-
last_activity DESC
|
|
8564
|
-
`
|
|
8565
|
-
).all();
|
|
8566
|
-
let rows = withCacheDbReadOnly(queryRows);
|
|
8567
|
-
if (rows == null) {
|
|
8568
|
-
rows = withCacheDb(queryRows);
|
|
8569
|
-
}
|
|
8570
|
-
return (rows ?? []).map((row) => ({
|
|
8571
|
-
identityKind: row.identity_kind ?? "path",
|
|
8572
|
-
identityKey: String(row.identity_key ?? ""),
|
|
8573
|
-
displayName: String(row.display_name ?? ""),
|
|
8574
|
-
sources: String(row.sources_csv ?? "").split(",").filter(Boolean).sort(),
|
|
8575
|
-
sessionCount: Number(row.session_count ?? 0),
|
|
8576
|
-
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
8577
|
-
}));
|
|
8578
|
-
}
|
|
8579
8899
|
function attachMissingProjectIdentities(sessions) {
|
|
8580
8900
|
return sessions.map((session) => {
|
|
8581
8901
|
if (session.project_identity) return session;
|
|
@@ -8965,8 +9285,140 @@ async function scanSessions(options = {}, onProgress) {
|
|
|
8965
9285
|
cacheTimestamps: Object.keys(cacheTimestamps).length > 0 ? cacheTimestamps : void 0
|
|
8966
9286
|
};
|
|
8967
9287
|
}
|
|
8968
|
-
|
|
8969
|
-
|
|
9288
|
+
var sessionDetailLookups = /* @__PURE__ */ new WeakMap();
|
|
9289
|
+
function sessionReferenceKey(agentName, sessionId) {
|
|
9290
|
+
return `${agentName}\0${sessionId}`;
|
|
9291
|
+
}
|
|
9292
|
+
function getSessionDetailLookup(scanResult) {
|
|
9293
|
+
const cached = sessionDetailLookups.get(scanResult.sessions);
|
|
9294
|
+
if (cached) return cached;
|
|
9295
|
+
const agentsByName = /* @__PURE__ */ new Map();
|
|
9296
|
+
for (const agent of scanResult.agents) {
|
|
9297
|
+
if (!agentsByName.has(agent.name)) agentsByName.set(agent.name, agent);
|
|
9298
|
+
}
|
|
9299
|
+
const headsByReference = /* @__PURE__ */ new Map();
|
|
9300
|
+
for (const [agentName, sessions] of Object.entries(scanResult.byAgent)) {
|
|
9301
|
+
for (const session of sessions) {
|
|
9302
|
+
const key = sessionReferenceKey(agentName, session.id);
|
|
9303
|
+
if (!headsByReference.has(key)) headsByReference.set(key, session);
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9306
|
+
const lookup = { agentsByName, headsByReference };
|
|
9307
|
+
sessionDetailLookups.set(scanResult.sessions, lookup);
|
|
9308
|
+
return lookup;
|
|
9309
|
+
}
|
|
9310
|
+
function getSessionDetailContext(scanResult, reference) {
|
|
9311
|
+
const lookup = getSessionDetailLookup(scanResult);
|
|
9312
|
+
const agent = lookup.agentsByName.get(reference.agentName);
|
|
9313
|
+
if (!agent) return null;
|
|
9314
|
+
return {
|
|
9315
|
+
agent,
|
|
9316
|
+
head: lookup.headsByReference.get(
|
|
9317
|
+
sessionReferenceKey(reference.agentName, reference.sessionId)
|
|
9318
|
+
)
|
|
9319
|
+
};
|
|
9320
|
+
}
|
|
9321
|
+
function cacheMatchesCurrentSource(cachedMeta, currentMeta) {
|
|
9322
|
+
const currentFingerprint = currentMeta?.sourceFingerprint;
|
|
9323
|
+
if (typeof currentFingerprint !== "string") return true;
|
|
9324
|
+
return cachedMeta?.sourceFingerprint === currentFingerprint;
|
|
9325
|
+
}
|
|
9326
|
+
function cacheHasCompleteDetail(cachedEntry, currentMeta) {
|
|
9327
|
+
if (!cachedEntry || !cacheMatchesCurrentSource(cachedEntry.meta, currentMeta)) {
|
|
9328
|
+
return false;
|
|
9329
|
+
}
|
|
9330
|
+
return cachedEntry.messageRows.length > 0 || cachedEntry.data.stats.message_count === 0;
|
|
9331
|
+
}
|
|
9332
|
+
function getProjectIdentity(data, head) {
|
|
9333
|
+
return data.project_identity ?? head?.project_identity ?? computeIdentity(data.directory, realFs);
|
|
9334
|
+
}
|
|
9335
|
+
function materializeStructuredSessionDetail(context, reference, cachedEntry = loadCachedSessionRawEntry(reference.agentName, reference.sessionId)) {
|
|
9336
|
+
const { agent, head } = context;
|
|
9337
|
+
const currentMeta = head ? agent.getSessionMetaMap().get(reference.sessionId) : void 0;
|
|
9338
|
+
const useCache = cacheHasCompleteDetail(cachedEntry, currentMeta);
|
|
9339
|
+
const data = useCache ? {
|
|
9340
|
+
...cachedEntry.data,
|
|
9341
|
+
messages: cachedEntry.messageRows.map((messageRow) => messageFromCachedRow(messageRow))
|
|
9342
|
+
} : head ? agent.getSessionData(reference.sessionId) : null;
|
|
9343
|
+
if (!data) {
|
|
9344
|
+
return { status: "not-ready" };
|
|
9345
|
+
}
|
|
9346
|
+
const projectIdentity = getProjectIdentity(data, head);
|
|
9347
|
+
const fileActivity = data.file_activity ?? (useCache ? listSessionFileActivity(reference.agentName, reference.sessionId) : extractSessionFileActivity(
|
|
9348
|
+
reference.agentName,
|
|
9349
|
+
reference.sessionId,
|
|
9350
|
+
projectIdentity.key,
|
|
9351
|
+
data.messages
|
|
9352
|
+
));
|
|
9353
|
+
return {
|
|
9354
|
+
status: "found",
|
|
9355
|
+
data: {
|
|
9356
|
+
...data,
|
|
9357
|
+
reference,
|
|
9358
|
+
project_identity: projectIdentity,
|
|
9359
|
+
smart_tags: data.smart_tags ?? classifySessionTags(data),
|
|
9360
|
+
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data),
|
|
9361
|
+
file_activity: fileActivity
|
|
9362
|
+
}
|
|
9363
|
+
};
|
|
9364
|
+
}
|
|
9365
|
+
function* serializeCachedMessages(entry) {
|
|
9366
|
+
for (const messageRow of entry.messageRows) {
|
|
9367
|
+
yield messageJsonFromCachedRow(messageRow);
|
|
9368
|
+
}
|
|
9369
|
+
}
|
|
9370
|
+
function materializeSessionDetailResponse(scanResult, reference) {
|
|
9371
|
+
const context = getSessionDetailContext(scanResult, reference);
|
|
9372
|
+
if (!context) return { status: "unknown-agent" };
|
|
9373
|
+
const cachedEntry = loadCachedSessionRawEntry(reference.agentName, reference.sessionId);
|
|
9374
|
+
const currentMeta = context.head ? context.agent.getSessionMetaMap().get(reference.sessionId) : void 0;
|
|
9375
|
+
const useCache = cacheHasCompleteDetail(cachedEntry, currentMeta);
|
|
9376
|
+
if (!useCache || cachedEntry.data.smart_tags == null) {
|
|
9377
|
+
return materializeStructuredSessionDetail(context, reference, cachedEntry);
|
|
9378
|
+
}
|
|
9379
|
+
const data = cachedEntry.data;
|
|
9380
|
+
return {
|
|
9381
|
+
status: "found-json",
|
|
9382
|
+
data: {
|
|
9383
|
+
...data,
|
|
9384
|
+
reference,
|
|
9385
|
+
project_identity: getProjectIdentity(data, context.head),
|
|
9386
|
+
smart_tags_source_updated_at: getSmartTagSourceTimestamp(data),
|
|
9387
|
+
file_activity: data.file_activity ?? listSessionFileActivity(reference.agentName, reference.sessionId)
|
|
9388
|
+
},
|
|
9389
|
+
messages: serializeCachedMessages(cachedEntry),
|
|
9390
|
+
messageCount: cachedEntry.messageRows.length
|
|
9391
|
+
};
|
|
9392
|
+
}
|
|
9393
|
+
function listCachedProjectGroups(sessions) {
|
|
9394
|
+
if (sessions) {
|
|
9395
|
+
return buildProjectGroups(sessions);
|
|
9396
|
+
}
|
|
9397
|
+
if (!hasCacheStorage()) {
|
|
9398
|
+
return [];
|
|
9399
|
+
}
|
|
9400
|
+
const queryRows = (db) => db.prepare(
|
|
9401
|
+
`
|
|
9402
|
+
SELECT identity_kind, identity_key, display_name, sources_csv, session_count, last_activity
|
|
9403
|
+
FROM project_groups_v
|
|
9404
|
+
ORDER BY
|
|
9405
|
+
CASE identity_kind WHEN 'loose' THEN 1 ELSE 0 END,
|
|
9406
|
+
last_activity IS NULL,
|
|
9407
|
+
last_activity DESC
|
|
9408
|
+
`
|
|
9409
|
+
).all();
|
|
9410
|
+
let rows = withCacheDbReadOnly(queryRows);
|
|
9411
|
+
if (rows == null) {
|
|
9412
|
+
rows = withCacheDb(queryRows);
|
|
9413
|
+
}
|
|
9414
|
+
return (rows ?? []).map((row) => ({
|
|
9415
|
+
identityKind: row.identity_kind ?? "path",
|
|
9416
|
+
identityKey: String(row.identity_key ?? ""),
|
|
9417
|
+
displayName: String(row.display_name ?? ""),
|
|
9418
|
+
sources: String(row.sources_csv ?? "").split(",").filter(Boolean).sort(),
|
|
9419
|
+
sessionCount: Number(row.session_count ?? 0),
|
|
9420
|
+
lastActivity: row.last_activity == null ? null : Number(row.last_activity)
|
|
9421
|
+
}));
|
|
8970
9422
|
}
|
|
8971
9423
|
var STATE_DB_FILENAME = "state.db";
|
|
8972
9424
|
var STATE_SCHEMA_VERSION = 2;
|
|
@@ -8986,18 +9438,18 @@ var StateStorageUnavailableError = class extends Error {
|
|
|
8986
9438
|
};
|
|
8987
9439
|
function getStateDir() {
|
|
8988
9440
|
if (process.env.CODESESH_STATE_DIR) return process.env.CODESESH_STATE_DIR;
|
|
8989
|
-
const currentPlatform =
|
|
9441
|
+
const currentPlatform = platform4();
|
|
8990
9442
|
if (currentPlatform === "darwin") {
|
|
8991
|
-
return
|
|
9443
|
+
return join13(homedir7(), "Library", "Application Support", "codesesh");
|
|
8992
9444
|
}
|
|
8993
9445
|
if (currentPlatform === "win32") {
|
|
8994
9446
|
const appData = process.env.APPDATA ?? process.env.LOCALAPPDATA;
|
|
8995
|
-
return
|
|
9447
|
+
return join13(appData ?? join13(homedir7(), "AppData", "Roaming"), "codesesh");
|
|
8996
9448
|
}
|
|
8997
|
-
return
|
|
9449
|
+
return join13(process.env.XDG_DATA_HOME ?? join13(homedir7(), ".local", "share"), "codesesh");
|
|
8998
9450
|
}
|
|
8999
9451
|
function getStateDbPath() {
|
|
9000
|
-
return
|
|
9452
|
+
return join13(getStateDir(), STATE_DB_FILENAME);
|
|
9001
9453
|
}
|
|
9002
9454
|
function useMemoryStateStore() {
|
|
9003
9455
|
return process.env.CODESESH_STATE_STORE === MEMORY_STATE_STORE;
|
|
@@ -9103,41 +9555,59 @@ function withStateDb(fn) {
|
|
|
9103
9555
|
}
|
|
9104
9556
|
}
|
|
9105
9557
|
var memoryBookmarks = /* @__PURE__ */ new Map();
|
|
9106
|
-
function getBookmarkKey(
|
|
9107
|
-
|
|
9558
|
+
function getBookmarkKey(reference) {
|
|
9559
|
+
const normalized = normalizeSessionReference(reference);
|
|
9560
|
+
return JSON.stringify([normalized.agentName, normalized.sessionId]);
|
|
9108
9561
|
}
|
|
9109
9562
|
function getActivityTime(bookmark) {
|
|
9110
|
-
return bookmark.time_updated ?? bookmark.time_created;
|
|
9563
|
+
return bookmark.session.time_updated ?? bookmark.session.time_created;
|
|
9111
9564
|
}
|
|
9112
9565
|
function sortBookmarks(bookmarks) {
|
|
9113
9566
|
return bookmarks.sort((a, b) => {
|
|
9114
9567
|
const activityDelta = getActivityTime(b) - getActivityTime(a);
|
|
9115
|
-
return activityDelta || b.
|
|
9568
|
+
return activityDelta || b.bookmarkedAt - a.bookmarkedAt;
|
|
9116
9569
|
});
|
|
9117
9570
|
}
|
|
9118
9571
|
function listMemoryBookmarks() {
|
|
9119
9572
|
return sortBookmarks(Array.from(memoryBookmarks.values()));
|
|
9120
9573
|
}
|
|
9574
|
+
function normalizeBookmark(bookmark) {
|
|
9575
|
+
const reference = normalizeSessionReference(bookmark.reference);
|
|
9576
|
+
return {
|
|
9577
|
+
reference,
|
|
9578
|
+
session: {
|
|
9579
|
+
...bookmark.session,
|
|
9580
|
+
id: reference.sessionId,
|
|
9581
|
+
slug: formatSessionReference(reference)
|
|
9582
|
+
}
|
|
9583
|
+
};
|
|
9584
|
+
}
|
|
9121
9585
|
function upsertMemoryBookmark(bookmark) {
|
|
9122
|
-
const key = getBookmarkKey(bookmark.
|
|
9586
|
+
const key = getBookmarkKey(bookmark.reference);
|
|
9123
9587
|
const saved = {
|
|
9124
9588
|
...bookmark,
|
|
9125
|
-
|
|
9589
|
+
bookmarkedAt: memoryBookmarks.get(key)?.bookmarkedAt ?? Date.now()
|
|
9126
9590
|
};
|
|
9127
9591
|
memoryBookmarks.set(key, saved);
|
|
9128
9592
|
return saved;
|
|
9129
9593
|
}
|
|
9130
9594
|
function toBookmarkRecord(row) {
|
|
9595
|
+
const reference = normalizeSessionReference({
|
|
9596
|
+
agentName: String(row.agent_name ?? ""),
|
|
9597
|
+
sessionId: String(row.session_id ?? "")
|
|
9598
|
+
});
|
|
9131
9599
|
return {
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9600
|
+
reference,
|
|
9601
|
+
session: {
|
|
9602
|
+
id: reference.sessionId,
|
|
9603
|
+
slug: formatSessionReference(reference),
|
|
9604
|
+
title: String(row.title ?? ""),
|
|
9605
|
+
directory: String(row.directory ?? ""),
|
|
9606
|
+
time_created: Number(row.time_created ?? 0),
|
|
9607
|
+
time_updated: row.time_updated == null ? void 0 : Number(row.time_updated),
|
|
9608
|
+
stats: JSON.parse(String(row.stats_json ?? "{}"))
|
|
9609
|
+
},
|
|
9610
|
+
bookmarkedAt: Number(row.bookmarked_at ?? 0)
|
|
9141
9611
|
};
|
|
9142
9612
|
}
|
|
9143
9613
|
function listBookmarks() {
|
|
@@ -9150,7 +9620,6 @@ function listBookmarks() {
|
|
|
9150
9620
|
SELECT
|
|
9151
9621
|
agent_name,
|
|
9152
9622
|
session_id,
|
|
9153
|
-
slug,
|
|
9154
9623
|
title,
|
|
9155
9624
|
directory,
|
|
9156
9625
|
time_created,
|
|
@@ -9165,8 +9634,9 @@ function listBookmarks() {
|
|
|
9165
9634
|
});
|
|
9166
9635
|
}
|
|
9167
9636
|
function upsertBookmark(bookmark) {
|
|
9637
|
+
const normalized = normalizeBookmark(bookmark);
|
|
9168
9638
|
if (useMemoryStateStore()) {
|
|
9169
|
-
return upsertMemoryBookmark(
|
|
9639
|
+
return upsertMemoryBookmark(normalized);
|
|
9170
9640
|
}
|
|
9171
9641
|
return withStateDb((db) => {
|
|
9172
9642
|
const existing = db.prepare(
|
|
@@ -9175,7 +9645,7 @@ function upsertBookmark(bookmark) {
|
|
|
9175
9645
|
FROM bookmarks
|
|
9176
9646
|
WHERE agent_name = ? AND session_id = ?
|
|
9177
9647
|
`
|
|
9178
|
-
).get(
|
|
9648
|
+
).get(normalized.reference.agentName, normalized.reference.sessionId);
|
|
9179
9649
|
const bookmarkedAt = Number(existing?.bookmarked_at ?? Date.now());
|
|
9180
9650
|
db.prepare(
|
|
9181
9651
|
`
|
|
@@ -9199,22 +9669,23 @@ function upsertBookmark(bookmark) {
|
|
|
9199
9669
|
stats_json = excluded.stats_json
|
|
9200
9670
|
`
|
|
9201
9671
|
).run(
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
JSON.stringify(
|
|
9672
|
+
normalized.reference.agentName,
|
|
9673
|
+
normalized.reference.sessionId,
|
|
9674
|
+
formatSessionReference(normalized.reference),
|
|
9675
|
+
normalized.session.title,
|
|
9676
|
+
normalized.session.directory,
|
|
9677
|
+
normalized.session.time_created,
|
|
9678
|
+
normalized.session.time_updated ?? null,
|
|
9679
|
+
JSON.stringify(normalized.session.stats),
|
|
9210
9680
|
bookmarkedAt
|
|
9211
9681
|
);
|
|
9212
|
-
return { ...
|
|
9682
|
+
return { ...normalized, bookmarkedAt };
|
|
9213
9683
|
});
|
|
9214
9684
|
}
|
|
9215
9685
|
function importBookmarks(bookmarks) {
|
|
9686
|
+
const normalizedBookmarks = bookmarks.map(normalizeBookmark);
|
|
9216
9687
|
if (useMemoryStateStore()) {
|
|
9217
|
-
for (const bookmark of
|
|
9688
|
+
for (const bookmark of normalizedBookmarks) {
|
|
9218
9689
|
upsertMemoryBookmark(bookmark);
|
|
9219
9690
|
}
|
|
9220
9691
|
return listMemoryBookmarks();
|
|
@@ -9223,7 +9694,10 @@ function importBookmarks(bookmarks) {
|
|
|
9223
9694
|
const existingRows = db.prepare("SELECT agent_name, session_id, bookmarked_at FROM bookmarks").all();
|
|
9224
9695
|
const existingTimes = new Map(
|
|
9225
9696
|
existingRows.map((row) => [
|
|
9226
|
-
|
|
9697
|
+
getBookmarkKey({
|
|
9698
|
+
agentName: String(row.agent_name ?? ""),
|
|
9699
|
+
sessionId: String(row.session_id ?? "")
|
|
9700
|
+
}),
|
|
9227
9701
|
Number(row.bookmarked_at ?? 0)
|
|
9228
9702
|
])
|
|
9229
9703
|
);
|
|
@@ -9250,17 +9724,17 @@ function importBookmarks(bookmarks) {
|
|
|
9250
9724
|
`
|
|
9251
9725
|
);
|
|
9252
9726
|
const write = db.transaction(() => {
|
|
9253
|
-
for (const bookmark of
|
|
9254
|
-
const key =
|
|
9727
|
+
for (const bookmark of normalizedBookmarks) {
|
|
9728
|
+
const key = getBookmarkKey(bookmark.reference);
|
|
9255
9729
|
upsert.run(
|
|
9256
|
-
bookmark.
|
|
9257
|
-
bookmark.sessionId,
|
|
9258
|
-
bookmark.
|
|
9259
|
-
bookmark.title,
|
|
9260
|
-
bookmark.directory,
|
|
9261
|
-
bookmark.time_created,
|
|
9262
|
-
bookmark.time_updated ?? null,
|
|
9263
|
-
JSON.stringify(bookmark.stats),
|
|
9730
|
+
bookmark.reference.agentName,
|
|
9731
|
+
bookmark.reference.sessionId,
|
|
9732
|
+
formatSessionReference(bookmark.reference),
|
|
9733
|
+
bookmark.session.title,
|
|
9734
|
+
bookmark.session.directory,
|
|
9735
|
+
bookmark.session.time_created,
|
|
9736
|
+
bookmark.session.time_updated ?? null,
|
|
9737
|
+
JSON.stringify(bookmark.session.stats),
|
|
9264
9738
|
existingTimes.get(key) ?? Date.now()
|
|
9265
9739
|
);
|
|
9266
9740
|
}
|
|
@@ -9271,7 +9745,6 @@ function importBookmarks(bookmarks) {
|
|
|
9271
9745
|
SELECT
|
|
9272
9746
|
agent_name,
|
|
9273
9747
|
session_id,
|
|
9274
|
-
slug,
|
|
9275
9748
|
title,
|
|
9276
9749
|
directory,
|
|
9277
9750
|
time_created,
|
|
@@ -9285,9 +9758,10 @@ function importBookmarks(bookmarks) {
|
|
|
9285
9758
|
return rows.map(toBookmarkRecord);
|
|
9286
9759
|
});
|
|
9287
9760
|
}
|
|
9288
|
-
function deleteBookmark(
|
|
9761
|
+
function deleteBookmark(reference) {
|
|
9762
|
+
const normalized = normalizeSessionReference(reference);
|
|
9289
9763
|
if (useMemoryStateStore()) {
|
|
9290
|
-
memoryBookmarks.delete(getBookmarkKey(
|
|
9764
|
+
memoryBookmarks.delete(getBookmarkKey(normalized));
|
|
9291
9765
|
return;
|
|
9292
9766
|
}
|
|
9293
9767
|
withStateDb((db) => {
|
|
@@ -9296,20 +9770,30 @@ function deleteBookmark(agentKey, sessionId) {
|
|
|
9296
9770
|
DELETE FROM bookmarks
|
|
9297
9771
|
WHERE agent_name = ? AND session_id = ?
|
|
9298
9772
|
`
|
|
9299
|
-
).run(
|
|
9773
|
+
).run(normalized.agentName, normalized.sessionId);
|
|
9300
9774
|
});
|
|
9301
9775
|
}
|
|
9302
9776
|
var SESSION_ALIAS_MAX_LENGTH = 160;
|
|
9777
|
+
var SessionAliasValidationError = class extends Error {
|
|
9778
|
+
constructor() {
|
|
9779
|
+
super("Invalid session alias");
|
|
9780
|
+
this.name = "SessionAliasValidationError";
|
|
9781
|
+
}
|
|
9782
|
+
};
|
|
9303
9783
|
var memoryAliases = /* @__PURE__ */ new Map();
|
|
9304
|
-
function getAliasKey(
|
|
9305
|
-
|
|
9784
|
+
function getAliasKey(reference) {
|
|
9785
|
+
const normalized = normalizeSessionReference(reference);
|
|
9786
|
+
return JSON.stringify([normalized.agentName, normalized.sessionId]);
|
|
9306
9787
|
}
|
|
9307
9788
|
function toSessionAlias(row) {
|
|
9789
|
+
const reference = normalizeSessionReference({
|
|
9790
|
+
agentName: String(row.agent_name ?? ""),
|
|
9791
|
+
sessionId: String(row.session_id ?? "")
|
|
9792
|
+
});
|
|
9308
9793
|
return {
|
|
9309
|
-
|
|
9310
|
-
sessionId: String(row.session_id ?? ""),
|
|
9794
|
+
reference,
|
|
9311
9795
|
alias: String(row.alias ?? ""),
|
|
9312
|
-
|
|
9796
|
+
updatedAt: Number(row.updated_at ?? 0)
|
|
9313
9797
|
};
|
|
9314
9798
|
}
|
|
9315
9799
|
function normalizeSessionAlias(value) {
|
|
@@ -9329,19 +9813,19 @@ function listSessionAliases() {
|
|
|
9329
9813
|
).all().map(toSessionAlias)
|
|
9330
9814
|
);
|
|
9331
9815
|
}
|
|
9332
|
-
function upsertSessionAlias(
|
|
9816
|
+
function upsertSessionAlias(reference, alias) {
|
|
9333
9817
|
const normalizedAlias = normalizeSessionAlias(alias);
|
|
9334
9818
|
if (!normalizedAlias) {
|
|
9335
|
-
throw new
|
|
9819
|
+
throw new SessionAliasValidationError();
|
|
9336
9820
|
}
|
|
9821
|
+
const normalizedReference = normalizeSessionReference(reference);
|
|
9337
9822
|
const saved = {
|
|
9338
|
-
|
|
9339
|
-
sessionId,
|
|
9823
|
+
reference: normalizedReference,
|
|
9340
9824
|
alias: normalizedAlias,
|
|
9341
|
-
|
|
9825
|
+
updatedAt: Date.now()
|
|
9342
9826
|
};
|
|
9343
9827
|
if (useMemoryStateStore()) {
|
|
9344
|
-
memoryAliases.set(getAliasKey(
|
|
9828
|
+
memoryAliases.set(getAliasKey(normalizedReference), saved);
|
|
9345
9829
|
return saved;
|
|
9346
9830
|
}
|
|
9347
9831
|
return withStateDb((db) => {
|
|
@@ -9353,13 +9837,14 @@ function upsertSessionAlias(agentKey, sessionId, alias) {
|
|
|
9353
9837
|
alias = excluded.alias,
|
|
9354
9838
|
updated_at = excluded.updated_at
|
|
9355
9839
|
`
|
|
9356
|
-
).run(saved.
|
|
9840
|
+
).run(saved.reference.agentName, saved.reference.sessionId, saved.alias, saved.updatedAt);
|
|
9357
9841
|
return saved;
|
|
9358
9842
|
});
|
|
9359
9843
|
}
|
|
9360
|
-
function deleteSessionAlias(
|
|
9844
|
+
function deleteSessionAlias(reference) {
|
|
9845
|
+
const normalizedReference = normalizeSessionReference(reference);
|
|
9361
9846
|
if (useMemoryStateStore()) {
|
|
9362
|
-
memoryAliases.delete(getAliasKey(
|
|
9847
|
+
memoryAliases.delete(getAliasKey(normalizedReference));
|
|
9363
9848
|
return;
|
|
9364
9849
|
}
|
|
9365
9850
|
withStateDb((db) => {
|
|
@@ -9368,7 +9853,7 @@ function deleteSessionAlias(agentKey, sessionId) {
|
|
|
9368
9853
|
DELETE FROM session_aliases
|
|
9369
9854
|
WHERE agent_name = ? AND session_id = ?
|
|
9370
9855
|
`
|
|
9371
|
-
).run(
|
|
9856
|
+
).run(normalizedReference.agentName, normalizedReference.sessionId);
|
|
9372
9857
|
});
|
|
9373
9858
|
}
|
|
9374
9859
|
var DASHBOARD_RECENT_LIMIT = 10;
|
|
@@ -9376,7 +9861,7 @@ function getTotalTokens(stats) {
|
|
|
9376
9861
|
return stats.total_tokens ?? stats.total_input_tokens + stats.total_output_tokens;
|
|
9377
9862
|
}
|
|
9378
9863
|
function getSessionAgentName(session) {
|
|
9379
|
-
return session
|
|
9864
|
+
return getSessionAgentKey(session);
|
|
9380
9865
|
}
|
|
9381
9866
|
function getSessionActivityTime(session) {
|
|
9382
9867
|
return session.time_updated ?? session.time_created;
|
|
@@ -9510,7 +9995,10 @@ function buildDashboard(sessions, options) {
|
|
|
9510
9995
|
const modelDistribution = [...modelAgg.entries()].map(([model, { tokens, sessions: count }]) => ({ model, tokens, sessions: count })).sort((a, b) => b.tokens - a.tokens);
|
|
9511
9996
|
const recentSessions = recentCandidates.map(({ session }) => {
|
|
9512
9997
|
const agentKey = getSessionAgentName(session);
|
|
9513
|
-
return {
|
|
9998
|
+
return {
|
|
9999
|
+
reference: { agentName: agentKey, sessionId: session.id },
|
|
10000
|
+
session
|
|
10001
|
+
};
|
|
9514
10002
|
});
|
|
9515
10003
|
return {
|
|
9516
10004
|
totals: {
|
|
@@ -9528,6 +10016,58 @@ function buildDashboard(sessions, options) {
|
|
|
9528
10016
|
recentSessions
|
|
9529
10017
|
};
|
|
9530
10018
|
}
|
|
10019
|
+
function getProjectGroupKey(identityKind, identityKey) {
|
|
10020
|
+
return `${identityKind}:${identityKey}`;
|
|
10021
|
+
}
|
|
10022
|
+
function emptyMetrics() {
|
|
10023
|
+
return { messages: 0, tokens: 0, cost: 0, hasEstimatedCost: false, agentStats: /* @__PURE__ */ new Map() };
|
|
10024
|
+
}
|
|
10025
|
+
function attachProjectMetrics(projects, sessions) {
|
|
10026
|
+
const metrics = /* @__PURE__ */ new Map();
|
|
10027
|
+
for (const session of sessions) {
|
|
10028
|
+
const identity = session.project_identity;
|
|
10029
|
+
if (!identity) continue;
|
|
10030
|
+
const key = getProjectGroupKey(identity.kind, identity.key);
|
|
10031
|
+
let current = metrics.get(key);
|
|
10032
|
+
if (!current) {
|
|
10033
|
+
current = emptyMetrics();
|
|
10034
|
+
metrics.set(key, current);
|
|
10035
|
+
}
|
|
10036
|
+
const tokens = getTotalTokens(session.stats);
|
|
10037
|
+
const cost = session.stats.total_cost ?? 0;
|
|
10038
|
+
current.messages += session.stats.message_count;
|
|
10039
|
+
current.tokens += tokens;
|
|
10040
|
+
current.cost += cost;
|
|
10041
|
+
if (session.stats.cost_source === "estimated") current.hasEstimatedCost = true;
|
|
10042
|
+
const agentName = getSessionAgentName(session);
|
|
10043
|
+
const agent = current.agentStats.get(agentName);
|
|
10044
|
+
if (agent) {
|
|
10045
|
+
agent.sessions += 1;
|
|
10046
|
+
agent.messages += session.stats.message_count;
|
|
10047
|
+
agent.tokens += tokens;
|
|
10048
|
+
agent.cost += cost;
|
|
10049
|
+
} else {
|
|
10050
|
+
current.agentStats.set(agentName, {
|
|
10051
|
+
name: agentName,
|
|
10052
|
+
sessions: 1,
|
|
10053
|
+
messages: session.stats.message_count,
|
|
10054
|
+
tokens,
|
|
10055
|
+
cost
|
|
10056
|
+
});
|
|
10057
|
+
}
|
|
10058
|
+
}
|
|
10059
|
+
return projects.map((project) => {
|
|
10060
|
+
const metric = metrics.get(getProjectGroupKey(project.identityKind, project.identityKey));
|
|
10061
|
+
return {
|
|
10062
|
+
...project,
|
|
10063
|
+
messages: metric?.messages ?? 0,
|
|
10064
|
+
tokens: metric?.tokens ?? 0,
|
|
10065
|
+
cost: metric?.cost ?? 0,
|
|
10066
|
+
cost_source: metric && metric.cost > 0 ? metric.hasEstimatedCost ? "estimated" : "recorded" : void 0,
|
|
10067
|
+
agentStats: [...metric?.agentStats.values() ?? []].sort((a, b) => b.sessions - a.sessions)
|
|
10068
|
+
};
|
|
10069
|
+
});
|
|
10070
|
+
}
|
|
9531
10071
|
function executeSessionSearch(query, options, snapshot) {
|
|
9532
10072
|
const merged = mergeSearchQueryOptions(query, options);
|
|
9533
10073
|
if (!needsIndexedSearch(merged.text, merged.options)) {
|
|
@@ -9570,15 +10110,49 @@ function matchesSessionSearchFilters(agentName, session, options, projectScope =
|
|
|
9570
10110
|
if (options.to != null && activity > options.to) return false;
|
|
9571
10111
|
return matchesRecentSearchFilters(session, options, projectScope);
|
|
9572
10112
|
}
|
|
10113
|
+
function sessionReferenceKey2(agentName, sessionId) {
|
|
10114
|
+
return `${agentName}\0${sessionId}`;
|
|
10115
|
+
}
|
|
10116
|
+
function filterSessionSearchCandidates(candidates, options) {
|
|
10117
|
+
const projectScope = options.cwd ? createProjectScopeMatcher(options.cwd) : null;
|
|
10118
|
+
const headMatches = candidates.filter(
|
|
10119
|
+
(candidate) => matchesSessionSearchFilters(
|
|
10120
|
+
candidate.reference.agentName,
|
|
10121
|
+
candidate.session,
|
|
10122
|
+
options,
|
|
10123
|
+
projectScope
|
|
10124
|
+
)
|
|
10125
|
+
);
|
|
10126
|
+
if (!options.file && !options.fileKind && !options.tools?.length) return headMatches;
|
|
10127
|
+
const indexedMatches = filterIndexedSessionReferences(
|
|
10128
|
+
headMatches.map((candidate) => ({
|
|
10129
|
+
agentName: candidate.reference.agentName,
|
|
10130
|
+
sessionId: candidate.reference.sessionId
|
|
10131
|
+
})),
|
|
10132
|
+
{
|
|
10133
|
+
file: options.file,
|
|
10134
|
+
fileKind: options.fileKind,
|
|
10135
|
+
tools: options.tools
|
|
10136
|
+
}
|
|
10137
|
+
);
|
|
10138
|
+
return headMatches.filter(
|
|
10139
|
+
(candidate) => indexedMatches.has(
|
|
10140
|
+
sessionReferenceKey2(candidate.reference.agentName, candidate.reference.sessionId)
|
|
10141
|
+
)
|
|
10142
|
+
);
|
|
10143
|
+
}
|
|
9573
10144
|
function searchRecentSessions(snapshot, options) {
|
|
9574
10145
|
const projectScope = options.cwd ? createProjectScopeMatcher(options.cwd) : null;
|
|
9575
10146
|
const entries = options.agent ? [[options.agent, snapshot.byAgent[options.agent] ?? []]] : Object.entries(snapshot.byAgent);
|
|
9576
10147
|
return entries.flatMap(
|
|
9577
|
-
([agentName, sessions]) => sessions.filter((session) => matchesSessionSearchFilters(agentName, session, options, projectScope)).map((session) => ({
|
|
10148
|
+
([agentName, sessions]) => sessions.filter((session) => matchesSessionSearchFilters(agentName, session, options, projectScope)).map((session) => ({
|
|
10149
|
+
reference: { agentName, sessionId: session.id },
|
|
10150
|
+
session
|
|
10151
|
+
}))
|
|
9578
10152
|
).sort(
|
|
9579
10153
|
(a, b) => (b.session.time_updated ?? b.session.time_created) - (a.session.time_updated ?? a.session.time_created)
|
|
9580
|
-
).slice(0, options.limit ?? 50).map(({
|
|
9581
|
-
|
|
10154
|
+
).slice(0, options.limit ?? 50).map(({ reference, session }) => ({
|
|
10155
|
+
reference,
|
|
9582
10156
|
session,
|
|
9583
10157
|
snippet: `Recent session \xB7 ${session.directory}`,
|
|
9584
10158
|
matchType: "recent"
|
|
@@ -9591,7 +10165,7 @@ function mergeSearchResultSources(results, limit) {
|
|
|
9591
10165
|
const seen = /* @__PURE__ */ new Set();
|
|
9592
10166
|
const merged = [];
|
|
9593
10167
|
for (const result of results) {
|
|
9594
|
-
const key =
|
|
10168
|
+
const key = sessionReferenceKey2(result.reference.agentName, result.reference.sessionId);
|
|
9595
10169
|
if (seen.has(key)) continue;
|
|
9596
10170
|
seen.add(key);
|
|
9597
10171
|
merged.push(result);
|
|
@@ -9599,133 +10173,83 @@ function mergeSearchResultSources(results, limit) {
|
|
|
9599
10173
|
}
|
|
9600
10174
|
return merged;
|
|
9601
10175
|
}
|
|
9602
|
-
function canSkipSessionsSearch(fileQuery, textQuery
|
|
9603
|
-
return Boolean(
|
|
9604
|
-
fileQuery && !textQuery && !options.tools?.length && !options.tags?.length && options.from == null && options.to == null
|
|
9605
|
-
);
|
|
10176
|
+
function canSkipSessionsSearch(fileQuery, textQuery) {
|
|
10177
|
+
return Boolean(fileQuery && !textQuery);
|
|
9606
10178
|
}
|
|
9607
10179
|
function searchIndexedSessions(query, textQuery, parsed, options) {
|
|
9608
10180
|
const fileQuery = deriveFileQuery(query, parsed, options);
|
|
9609
10181
|
const fileResults = fileQuery ? searchFileActivitySessions(fileQuery, options) : [];
|
|
9610
|
-
const sessionResults = canSkipSessionsSearch(fileQuery, textQuery
|
|
9611
|
-
|
|
10182
|
+
const sessionResults = canSkipSessionsSearch(fileQuery, textQuery) ? [] : searchSessions(query, options);
|
|
10183
|
+
const textMatchReferences = textQuery && options.file ? new Set(
|
|
10184
|
+
sessionResults.map(
|
|
10185
|
+
(result) => sessionReferenceKey2(result.reference.agentName, result.reference.sessionId)
|
|
10186
|
+
)
|
|
10187
|
+
) : null;
|
|
10188
|
+
const matchingFileResults = textMatchReferences ? fileResults.filter(
|
|
10189
|
+
(result) => textMatchReferences.has(
|
|
10190
|
+
sessionReferenceKey2(result.reference.agentName, result.reference.sessionId)
|
|
10191
|
+
)
|
|
10192
|
+
) : fileResults;
|
|
10193
|
+
return mergeSearchResultSources([...matchingFileResults, ...sessionResults], options.limit ?? 50);
|
|
9612
10194
|
}
|
|
9613
10195
|
|
|
9614
10196
|
export {
|
|
10197
|
+
normalizeSessionReference,
|
|
10198
|
+
formatSessionReference,
|
|
10199
|
+
getSessionAgentKey,
|
|
10200
|
+
mergeSortedSessions,
|
|
9615
10201
|
registerAgent,
|
|
9616
10202
|
createRegisteredAgents,
|
|
9617
10203
|
getRegisteredAgents,
|
|
9618
10204
|
getAgentInfoMap,
|
|
9619
|
-
getAgentByName,
|
|
9620
10205
|
setCoreDiagnostics,
|
|
9621
|
-
|
|
9622
|
-
skippedSession,
|
|
9623
|
-
filteredSession,
|
|
9624
|
-
getParsedSession,
|
|
9625
|
-
matchesScanWindow,
|
|
10206
|
+
diffSessionSources,
|
|
9626
10207
|
BaseAgent,
|
|
9627
10208
|
FileSystemSessionSource,
|
|
9628
|
-
DatabaseSessionSource,
|
|
9629
|
-
firstExisting,
|
|
9630
|
-
resolveProviderRoots,
|
|
9631
|
-
getCursorDataPath,
|
|
9632
|
-
parseJsonlLines,
|
|
9633
|
-
readJsonlFile,
|
|
9634
|
-
cleanDisplayText,
|
|
9635
|
-
firstVisibleLine,
|
|
9636
|
-
normalizeTitleText,
|
|
9637
|
-
basenameTitle,
|
|
9638
|
-
resolveSessionTitle,
|
|
9639
|
-
cleanInternalText,
|
|
9640
|
-
cleanMessagePart,
|
|
9641
|
-
cleanMessageParts,
|
|
9642
|
-
cleanParsedMessage,
|
|
9643
|
-
cleanParsedMessages,
|
|
9644
|
-
firstUserMessageTitle,
|
|
9645
|
-
getPricingRegistry,
|
|
9646
|
-
hasBillablePricing,
|
|
9647
10209
|
refreshPricingCache,
|
|
9648
|
-
pricingResolver,
|
|
9649
|
-
estimateCostForTokens,
|
|
9650
|
-
applyMessageCost,
|
|
9651
|
-
applyMessageCosts,
|
|
9652
|
-
withEstimatedSessionCost,
|
|
9653
|
-
estimateTokenCost,
|
|
9654
|
-
asRecord,
|
|
9655
|
-
asString,
|
|
9656
|
-
asNumber,
|
|
9657
|
-
asArray,
|
|
9658
|
-
reportFieldMismatch,
|
|
9659
|
-
openDbReadOnly,
|
|
9660
|
-
openDb,
|
|
9661
|
-
isSqliteAvailable,
|
|
9662
10210
|
perf,
|
|
9663
|
-
fallbackDisplayName,
|
|
9664
|
-
realFs,
|
|
9665
10211
|
isProjectIdentityKind,
|
|
9666
|
-
getProjectIdentityKey,
|
|
9667
10212
|
matchesProjectIdentity,
|
|
9668
|
-
normalizeGitRemote,
|
|
9669
|
-
clearIdentityCache,
|
|
9670
|
-
computeIdentity,
|
|
9671
|
-
buildProjectGroups,
|
|
9672
10213
|
createProjectScopeMatcher,
|
|
9673
10214
|
matchesProjectScope,
|
|
9674
|
-
filterSessionsByProjectScope,
|
|
9675
10215
|
getSmartTagSourceTimestamp,
|
|
9676
10216
|
classifySessionTags,
|
|
9677
|
-
extractFileActivityOccurrences,
|
|
9678
|
-
summarizeFileActivity,
|
|
9679
|
-
extractSessionFileActivity,
|
|
9680
|
-
setFtsIntegrityCheckedPath,
|
|
9681
10217
|
getCachePath2,
|
|
9682
|
-
parseSearchQuery,
|
|
9683
10218
|
syncSessionSearchIndex,
|
|
9684
10219
|
syncSessionSearchIndexChanges,
|
|
9685
10220
|
mergeSearchQueryOptions,
|
|
9686
|
-
searchSessions,
|
|
9687
10221
|
listFileActivity,
|
|
9688
|
-
listSessionFileActivity,
|
|
9689
|
-
searchFileActivitySessions,
|
|
9690
10222
|
loadCachedSessions,
|
|
9691
10223
|
isAgentCacheInitialized,
|
|
9692
10224
|
markAgentCacheInitialized,
|
|
9693
10225
|
getAgentLastFullSyncAt,
|
|
9694
10226
|
markAgentFullSyncCompleted,
|
|
9695
|
-
loadCachedSessionDataEntry,
|
|
9696
|
-
loadCachedSessionData,
|
|
9697
10227
|
saveCachedSessions,
|
|
9698
10228
|
saveCachedSessionChanges,
|
|
9699
10229
|
clearCache,
|
|
9700
|
-
getCacheInfo,
|
|
9701
|
-
listCachedProjectGroups,
|
|
9702
10230
|
attachMissingProjectIdentities,
|
|
9703
10231
|
buildAgentCacheMeta,
|
|
9704
10232
|
sessionSignature,
|
|
9705
10233
|
sortSessions,
|
|
9706
10234
|
computeSessionDiff,
|
|
9707
|
-
filterSessions,
|
|
9708
10235
|
ensureSessionTagsSync,
|
|
9709
10236
|
scanSessions,
|
|
9710
|
-
|
|
10237
|
+
materializeSessionDetailResponse,
|
|
10238
|
+
listCachedProjectGroups,
|
|
9711
10239
|
StateStorageUnavailableError,
|
|
9712
10240
|
listBookmarks,
|
|
9713
10241
|
upsertBookmark,
|
|
9714
10242
|
importBookmarks,
|
|
9715
10243
|
deleteBookmark,
|
|
9716
|
-
|
|
9717
|
-
normalizeSessionAlias,
|
|
10244
|
+
SessionAliasValidationError,
|
|
9718
10245
|
listSessionAliases,
|
|
9719
10246
|
upsertSessionAlias,
|
|
9720
10247
|
deleteSessionAlias,
|
|
9721
|
-
DASHBOARD_RECENT_LIMIT,
|
|
9722
|
-
getTotalTokens,
|
|
9723
|
-
getSessionAgentName,
|
|
9724
10248
|
getSessionActivityTime,
|
|
9725
|
-
toLocalDateKey,
|
|
9726
10249
|
startOfLocalDay,
|
|
9727
10250
|
buildDashboard,
|
|
10251
|
+
attachProjectMetrics,
|
|
9728
10252
|
executeSessionSearch,
|
|
9729
|
-
|
|
10253
|
+
filterSessionSearchCandidates
|
|
9730
10254
|
};
|
|
9731
|
-
//# sourceMappingURL=chunk-
|
|
10255
|
+
//# sourceMappingURL=chunk-7APNDHQ6.js.map
|