@swarmify/agents-cli 1.10.3 → 1.11.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/CHANGELOG.md +18 -0
- package/README.md +71 -0
- package/dist/commands/__tests__/sessions.test.d.ts +2 -0
- package/dist/commands/__tests__/sessions.test.d.ts.map +1 -0
- package/dist/commands/__tests__/sessions.test.js +436 -0
- package/dist/commands/__tests__/sessions.test.js.map +1 -0
- package/dist/commands/commands.d.ts.map +1 -1
- package/dist/commands/commands.js +79 -67
- package/dist/commands/commands.js.map +1 -1
- package/dist/commands/daemon.js +1 -1
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +11 -1
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/hooks.d.ts.map +1 -1
- package/dist/commands/hooks.js +72 -60
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +134 -57
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/packages.d.ts.map +1 -1
- package/dist/commands/packages.js +92 -42
- package/dist/commands/packages.js.map +1 -1
- package/dist/commands/permissions.d.ts.map +1 -1
- package/dist/commands/permissions.js +108 -81
- package/dist/commands/permissions.js.map +1 -1
- package/dist/commands/plugins.js +8 -8
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/pty.d.ts +20 -0
- package/dist/commands/pty.d.ts.map +1 -0
- package/dist/commands/pty.js +280 -0
- package/dist/commands/pty.js.map +1 -0
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +27 -27
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +4 -2
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/routines.d.ts.map +1 -1
- package/dist/commands/routines.js +14 -10
- package/dist/commands/routines.js.map +1 -1
- package/dist/commands/rules.d.ts.map +1 -1
- package/dist/commands/rules.js +70 -61
- package/dist/commands/rules.js.map +1 -1
- package/dist/commands/sessions.d.ts.map +1 -1
- package/dist/commands/sessions.js +369 -44
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/sessions.test.d.ts +2 -0
- package/dist/commands/sessions.test.d.ts.map +1 -0
- package/dist/commands/sessions.test.js +53 -0
- package/dist/commands/sessions.test.js.map +1 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +75 -57
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/subagents.d.ts.map +1 -1
- package/dist/commands/subagents.js +10 -4
- package/dist/commands/subagents.js.map +1 -1
- package/dist/commands/utils.d.ts +16 -0
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +48 -0
- package/dist/commands/utils.js.map +1 -1
- package/dist/commands/versions.d.ts.map +1 -1
- package/dist/commands/versions.js +148 -43
- package/dist/commands/versions.js.map +1 -1
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +109 -45
- package/dist/commands/view.js.map +1 -1
- package/dist/index.js +66 -42
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/exec.test.js +34 -1
- package/dist/lib/__tests__/exec.test.js.map +1 -1
- package/dist/lib/agents.d.ts +23 -0
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +212 -16
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/daemon.js +15 -7
- package/dist/lib/daemon.js.map +1 -1
- package/dist/lib/exec.d.ts +3 -0
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +26 -0
- package/dist/lib/exec.js.map +1 -1
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +11 -1
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/pty-client.d.ts +22 -0
- package/dist/lib/pty-client.d.ts.map +1 -0
- package/dist/lib/pty-client.js +181 -0
- package/dist/lib/pty-client.js.map +1 -0
- package/dist/lib/pty-server.d.ts +16 -0
- package/dist/lib/pty-server.d.ts.map +1 -0
- package/dist/lib/pty-server.js +422 -0
- package/dist/lib/pty-server.js.map +1 -0
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/registry.js +8 -0
- package/dist/lib/registry.js.map +1 -1
- package/dist/lib/resources.d.ts +2 -0
- package/dist/lib/resources.d.ts.map +1 -1
- package/dist/lib/resources.js +7 -7
- package/dist/lib/resources.js.map +1 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +13 -9
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/sandbox.js +1 -1
- package/dist/lib/sandbox.js.map +1 -1
- package/dist/lib/session/discover.d.ts +18 -0
- package/dist/lib/session/discover.d.ts.map +1 -1
- package/dist/lib/session/discover.js +405 -167
- package/dist/lib/session/discover.js.map +1 -1
- package/dist/lib/session/parse.d.ts.map +1 -1
- package/dist/lib/session/parse.js +8 -3
- package/dist/lib/session/parse.js.map +1 -1
- package/dist/lib/session/prompt.d.ts +3 -0
- package/dist/lib/session/prompt.d.ts.map +1 -0
- package/dist/lib/session/prompt.js +40 -0
- package/dist/lib/session/prompt.js.map +1 -0
- package/dist/lib/session/render.d.ts.map +1 -1
- package/dist/lib/session/render.js +6 -37
- package/dist/lib/session/render.js.map +1 -1
- package/dist/lib/session/types.d.ts +1 -0
- package/dist/lib/session/types.d.ts.map +1 -1
- package/dist/lib/shims.d.ts.map +1 -1
- package/dist/lib/shims.js +6 -1
- package/dist/lib/shims.js.map +1 -1
- package/dist/lib/state.d.ts +0 -1
- package/dist/lib/state.d.ts.map +1 -1
- package/dist/lib/state.js +3 -9
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/types.d.ts +3 -5
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/usage.d.ts +30 -0
- package/dist/lib/usage.d.ts.map +1 -0
- package/dist/lib/usage.js +428 -0
- package/dist/lib/usage.js.map +1 -0
- package/dist/lib/versions.d.ts +28 -0
- package/dist/lib/versions.d.ts.map +1 -1
- package/dist/lib/versions.js +200 -9
- package/dist/lib/versions.js.map +1 -1
- package/package.json +3 -1
- package/dist/commands/cron.d.ts +0 -3
- package/dist/commands/cron.d.ts.map +0 -1
- package/dist/commands/cron.js +0 -457
- package/dist/commands/cron.js.map +0 -1
- package/dist/lib/cron.d.ts +0 -70
- package/dist/lib/cron.d.ts.map +0 -1
- package/dist/lib/cron.js +0 -325
- package/dist/lib/cron.js.map +0 -1
- package/dist/lib/drive-server.d.ts +0 -9
- package/dist/lib/drive-server.d.ts.map +0 -1
- package/dist/lib/drive-server.js +0 -217
- package/dist/lib/drive-server.js.map +0 -1
- package/dist/lib/drives.d.ts +0 -34
- package/dist/lib/drives.d.ts.map +0 -1
- package/dist/lib/drives.js +0 -267
- package/dist/lib/drives.js.map +0 -1
|
@@ -5,6 +5,7 @@ import * as crypto from 'crypto';
|
|
|
5
5
|
import * as readline from 'readline';
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
7
|
import { SESSION_AGENTS } from './types.js';
|
|
8
|
+
import { extractSessionTopic } from './prompt.js';
|
|
8
9
|
const HOME = os.homedir();
|
|
9
10
|
const AGENTS_DIR = path.join(HOME, '.agents');
|
|
10
11
|
const SESSIONS_DIR = path.join(AGENTS_DIR, 'sessions');
|
|
@@ -30,18 +31,31 @@ export async function discoverSessions(options) {
|
|
|
30
31
|
// Merge with persistent index (preserves sessions whose files were removed)
|
|
31
32
|
const index = loadIndex();
|
|
32
33
|
const liveIds = new Set(sessions.map(s => s.id));
|
|
34
|
+
const agentFilter = new Set(agents);
|
|
35
|
+
// Add matching index entries to display results
|
|
33
36
|
index.forEach((entry, id) => {
|
|
34
|
-
if (!liveIds.has(id)) {
|
|
37
|
+
if (!liveIds.has(id) && agentFilter.has(entry.agent)) {
|
|
35
38
|
sessions.push(entry);
|
|
36
39
|
}
|
|
37
40
|
});
|
|
38
|
-
// Persist
|
|
39
|
-
|
|
41
|
+
// Persist: merge live sessions into full index (don't drop unqueried agents)
|
|
42
|
+
const toSave = new Map(index);
|
|
43
|
+
for (const s of sessions) {
|
|
44
|
+
toSave.set(s.id, s);
|
|
45
|
+
}
|
|
46
|
+
saveIndex([...toSave.values()]);
|
|
47
|
+
const projectQuery = options?.project?.trim();
|
|
40
48
|
// Filter by project (case-insensitive substring match)
|
|
41
|
-
if (
|
|
42
|
-
const query =
|
|
49
|
+
if (projectQuery) {
|
|
50
|
+
const query = projectQuery.toLowerCase();
|
|
43
51
|
sessions = sessions.filter(s => s.project?.toLowerCase().includes(query));
|
|
44
52
|
}
|
|
53
|
+
// An explicit project search should scan across directories instead of
|
|
54
|
+
// intersecting with the default cwd-only scope.
|
|
55
|
+
if (!options?.all && !projectQuery) {
|
|
56
|
+
const currentDir = normalizeCwd(options?.cwd || process.cwd());
|
|
57
|
+
sessions = sessions.filter(s => normalizeCwd(s.cwd) === currentDir);
|
|
58
|
+
}
|
|
45
59
|
// Sort by timestamp descending
|
|
46
60
|
sessions.sort((a, b) => {
|
|
47
61
|
const ta = new Date(a.timestamp).getTime() || 0;
|
|
@@ -50,17 +64,23 @@ export async function discoverSessions(options) {
|
|
|
50
64
|
});
|
|
51
65
|
return sessions.slice(0, limit);
|
|
52
66
|
}
|
|
67
|
+
function normalizeCwd(cwd) {
|
|
68
|
+
if (!cwd)
|
|
69
|
+
return '';
|
|
70
|
+
const resolved = path.resolve(cwd);
|
|
71
|
+
return safeRealpathSync(resolved) || resolved;
|
|
72
|
+
}
|
|
53
73
|
/**
|
|
54
74
|
* Resolve a session by full or short ID from the full index.
|
|
55
75
|
*/
|
|
56
76
|
export function resolveSessionById(sessions, idQuery) {
|
|
57
77
|
const query = idQuery.toLowerCase();
|
|
58
|
-
// Exact match first
|
|
59
|
-
const exact = sessions.filter(s => s.id.toLowerCase() === query);
|
|
78
|
+
// Exact match first (full id or shortId)
|
|
79
|
+
const exact = sessions.filter(s => s.id.toLowerCase() === query || s.shortId.toLowerCase() === query);
|
|
60
80
|
if (exact.length > 0)
|
|
61
81
|
return exact;
|
|
62
|
-
// Prefix match
|
|
63
|
-
return sessions.filter(s => s.id.toLowerCase().startsWith(query));
|
|
82
|
+
// Prefix match (against both id and shortId)
|
|
83
|
+
return sessions.filter(s => s.id.toLowerCase().startsWith(query) || s.shortId.toLowerCase().startsWith(query));
|
|
64
84
|
}
|
|
65
85
|
// ---------------------------------------------------------------------------
|
|
66
86
|
// Persistent session index
|
|
@@ -79,10 +99,12 @@ function loadIndex() {
|
|
|
79
99
|
if (entry.id)
|
|
80
100
|
map.set(entry.id, entry);
|
|
81
101
|
}
|
|
82
|
-
catch { }
|
|
102
|
+
catch { /* malformed index entry, skip */ }
|
|
83
103
|
}
|
|
84
104
|
}
|
|
85
|
-
catch {
|
|
105
|
+
catch (err) {
|
|
106
|
+
console.error(`Warning: Could not load session cache (${err.message}). Rebuilding...`);
|
|
107
|
+
}
|
|
86
108
|
return map;
|
|
87
109
|
}
|
|
88
110
|
function saveIndex(sessions) {
|
|
@@ -95,22 +117,13 @@ function saveIndex(sessions) {
|
|
|
95
117
|
if (seen.has(s.id))
|
|
96
118
|
continue;
|
|
97
119
|
seen.add(s.id);
|
|
98
|
-
lines.push(JSON.stringify(
|
|
99
|
-
id: s.id,
|
|
100
|
-
shortId: s.shortId,
|
|
101
|
-
agent: s.agent,
|
|
102
|
-
timestamp: s.timestamp,
|
|
103
|
-
project: s.project,
|
|
104
|
-
cwd: s.cwd,
|
|
105
|
-
filePath: s.filePath,
|
|
106
|
-
gitBranch: s.gitBranch,
|
|
107
|
-
version: s.version,
|
|
108
|
-
account: s.account,
|
|
109
|
-
}));
|
|
120
|
+
lines.push(JSON.stringify(s));
|
|
110
121
|
}
|
|
111
122
|
fs.writeFileSync(INDEX_PATH, lines.join('\n') + '\n', 'utf-8');
|
|
112
123
|
}
|
|
113
|
-
catch {
|
|
124
|
+
catch (err) {
|
|
125
|
+
console.error(`Warning: Could not save session cache: ${err.message}`);
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
// ---------------------------------------------------------------------------
|
|
116
129
|
// Multi-version directory scanning
|
|
@@ -124,7 +137,7 @@ function saveIndex(sessions) {
|
|
|
124
137
|
* @param subdir - Subdirectory within the agent's config dir where sessions live
|
|
125
138
|
* (e.g., 'projects' for Claude, 'sessions' for Codex, 'tmp' for Gemini)
|
|
126
139
|
*/
|
|
127
|
-
function getAgentSessionDirs(agent, subdir) {
|
|
140
|
+
export function getAgentSessionDirs(agent, subdir) {
|
|
128
141
|
const resolved = new Set();
|
|
129
142
|
const dirs = [];
|
|
130
143
|
function addDir(dir) {
|
|
@@ -147,7 +160,7 @@ function getAgentSessionDirs(agent, subdir) {
|
|
|
147
160
|
addDir(path.join(versionsBase, version, 'home', `.${agent}`, subdir));
|
|
148
161
|
}
|
|
149
162
|
}
|
|
150
|
-
catch { }
|
|
163
|
+
catch { /* dir unreadable or missing */ }
|
|
151
164
|
}
|
|
152
165
|
// 3. Backups (from before version management was enabled)
|
|
153
166
|
const backupsBase = path.join(AGENTS_DIR, 'backups', agent);
|
|
@@ -157,7 +170,7 @@ function getAgentSessionDirs(agent, subdir) {
|
|
|
157
170
|
addDir(path.join(backupsBase, ts, subdir));
|
|
158
171
|
}
|
|
159
172
|
}
|
|
160
|
-
catch { }
|
|
173
|
+
catch { /* dir unreadable or missing */ }
|
|
161
174
|
}
|
|
162
175
|
return dirs;
|
|
163
176
|
}
|
|
@@ -180,7 +193,7 @@ function getClaudeAccount() {
|
|
|
180
193
|
candidates.push(path.join(versionsBase, version, 'home', '.claude.json'));
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
|
-
catch { }
|
|
196
|
+
catch { /* versions dir unreadable */ }
|
|
184
197
|
}
|
|
185
198
|
for (const candidate of candidates) {
|
|
186
199
|
try {
|
|
@@ -193,7 +206,7 @@ function getClaudeAccount() {
|
|
|
193
206
|
return name;
|
|
194
207
|
}
|
|
195
208
|
}
|
|
196
|
-
catch { }
|
|
209
|
+
catch { /* auth file unreadable or malformed */ }
|
|
197
210
|
}
|
|
198
211
|
cachedClaudeAccount = '';
|
|
199
212
|
return undefined;
|
|
@@ -205,6 +218,7 @@ async function discoverClaudeSessions() {
|
|
|
205
218
|
const sessions = [];
|
|
206
219
|
const seen = new Set();
|
|
207
220
|
const account = getClaudeAccount();
|
|
221
|
+
let skipped = 0;
|
|
208
222
|
for (const projectsDir of getAgentSessionDirs('claude', 'projects')) {
|
|
209
223
|
let projectDirs;
|
|
210
224
|
try {
|
|
@@ -236,48 +250,36 @@ async function discoverClaudeSessions() {
|
|
|
236
250
|
if (meta)
|
|
237
251
|
sessions.push(meta);
|
|
238
252
|
}
|
|
239
|
-
catch {
|
|
253
|
+
catch {
|
|
254
|
+
skipped++;
|
|
255
|
+
}
|
|
240
256
|
}
|
|
241
257
|
}
|
|
242
258
|
}
|
|
259
|
+
if (skipped > 0 && process.env.AGENTS_DEBUG) {
|
|
260
|
+
console.error(`[debug] Skipped ${skipped} unreadable Claude session(s)`);
|
|
261
|
+
}
|
|
243
262
|
return sessions;
|
|
244
263
|
}
|
|
245
264
|
async function readClaudeMeta(filePath, sessionId, account) {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
// Look for first user or assistant line with timestamp/cwd
|
|
265
|
-
if ((parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
|
|
266
|
-
const cwd = parsed.cwd || '';
|
|
267
|
-
return {
|
|
268
|
-
id: sessionId,
|
|
269
|
-
shortId: sessionId.slice(0, 8),
|
|
270
|
-
agent: 'claude',
|
|
271
|
-
timestamp: parsed.timestamp,
|
|
272
|
-
project: cwd ? path.basename(cwd) : undefined,
|
|
273
|
-
cwd,
|
|
274
|
-
filePath,
|
|
275
|
-
gitBranch: parsed.gitBranch || undefined,
|
|
276
|
-
version: parsed.version || undefined,
|
|
277
|
-
account,
|
|
278
|
-
topic,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
265
|
+
const scan = await scanClaudeSession(filePath);
|
|
266
|
+
if (scan.timestamp) {
|
|
267
|
+
const cwd = scan.cwd || '';
|
|
268
|
+
return {
|
|
269
|
+
id: sessionId,
|
|
270
|
+
shortId: sessionId.slice(0, 8),
|
|
271
|
+
agent: 'claude',
|
|
272
|
+
timestamp: scan.timestamp,
|
|
273
|
+
project: cwd ? path.basename(cwd) : undefined,
|
|
274
|
+
cwd,
|
|
275
|
+
filePath,
|
|
276
|
+
gitBranch: scan.gitBranch,
|
|
277
|
+
version: scan.version,
|
|
278
|
+
account,
|
|
279
|
+
topic: scan.topic,
|
|
280
|
+
messageCount: scan.messageCount,
|
|
281
|
+
tokenCount: scan.tokenCount,
|
|
282
|
+
};
|
|
281
283
|
}
|
|
282
284
|
// Fallback: use file mtime
|
|
283
285
|
const stat = safeStatSync(filePath);
|
|
@@ -288,6 +290,9 @@ async function readClaudeMeta(filePath, sessionId, account) {
|
|
|
288
290
|
timestamp: stat ? stat.mtime.toISOString() : new Date().toISOString(),
|
|
289
291
|
filePath,
|
|
290
292
|
account,
|
|
293
|
+
messageCount: scan.messageCount,
|
|
294
|
+
tokenCount: scan.tokenCount,
|
|
295
|
+
topic: scan.topic,
|
|
291
296
|
};
|
|
292
297
|
}
|
|
293
298
|
// ---------------------------------------------------------------------------
|
|
@@ -308,7 +313,7 @@ function getCodexAccount() {
|
|
|
308
313
|
candidates.push(path.join(versionsBase, version, 'home', '.codex', 'auth.json'));
|
|
309
314
|
}
|
|
310
315
|
}
|
|
311
|
-
catch { }
|
|
316
|
+
catch { /* versions dir unreadable */ }
|
|
312
317
|
}
|
|
313
318
|
for (const candidate of candidates) {
|
|
314
319
|
try {
|
|
@@ -328,7 +333,7 @@ function getCodexAccount() {
|
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
335
|
}
|
|
331
|
-
catch { }
|
|
336
|
+
catch { /* auth file or JWT malformed */ }
|
|
332
337
|
}
|
|
333
338
|
cachedCodexAccount = '';
|
|
334
339
|
return undefined;
|
|
@@ -340,6 +345,7 @@ async function discoverCodexSessions() {
|
|
|
340
345
|
const sessions = [];
|
|
341
346
|
const seen = new Set();
|
|
342
347
|
const account = getCodexAccount();
|
|
348
|
+
let skipped = 0;
|
|
343
349
|
for (const sessionsDir of getAgentSessionDirs('codex', 'sessions')) {
|
|
344
350
|
const jsonlFiles = walkForFiles(sessionsDir, '.jsonl', 200);
|
|
345
351
|
for (const filePath of jsonlFiles) {
|
|
@@ -350,56 +356,35 @@ async function discoverCodexSessions() {
|
|
|
350
356
|
sessions.push(meta);
|
|
351
357
|
}
|
|
352
358
|
}
|
|
353
|
-
catch {
|
|
359
|
+
catch {
|
|
360
|
+
skipped++;
|
|
361
|
+
}
|
|
354
362
|
}
|
|
355
363
|
}
|
|
364
|
+
if (skipped > 0 && process.env.AGENTS_DEBUG) {
|
|
365
|
+
console.error(`[debug] Skipped ${skipped} unreadable Codex session(s)`);
|
|
366
|
+
}
|
|
356
367
|
return sessions;
|
|
357
368
|
}
|
|
358
369
|
async function readCodexMeta(filePath, account) {
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
return null;
|
|
362
|
-
let parsed;
|
|
363
|
-
try {
|
|
364
|
-
parsed = JSON.parse(lines[0]);
|
|
365
|
-
}
|
|
366
|
-
catch {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
if (parsed.type !== 'session_meta')
|
|
370
|
-
return null;
|
|
371
|
-
const payload = parsed.payload || {};
|
|
372
|
-
const sessionId = payload.id || '';
|
|
370
|
+
const scan = await scanCodexSession(filePath);
|
|
371
|
+
const sessionId = scan.sessionId || '';
|
|
373
372
|
if (!sessionId)
|
|
374
373
|
return null;
|
|
375
|
-
|
|
376
|
-
let topic;
|
|
377
|
-
for (let i = 1; i < lines.length; i++) {
|
|
378
|
-
try {
|
|
379
|
-
const ev = JSON.parse(lines[i]);
|
|
380
|
-
if (ev.type === 'message' && ev.role === 'user' && ev.content) {
|
|
381
|
-
const text = typeof ev.content === 'string' ? ev.content
|
|
382
|
-
: Array.isArray(ev.content) ? ev.content.find((b) => b.type === 'input_text')?.text : undefined;
|
|
383
|
-
if (text) {
|
|
384
|
-
topic = extractTopic(text);
|
|
385
|
-
break;
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
catch { }
|
|
390
|
-
}
|
|
391
|
-
const cwd = payload.cwd || '';
|
|
374
|
+
const cwd = scan.cwd || '';
|
|
392
375
|
return {
|
|
393
376
|
id: sessionId,
|
|
394
377
|
shortId: sessionId.slice(0, 8),
|
|
395
378
|
agent: 'codex',
|
|
396
|
-
timestamp:
|
|
379
|
+
timestamp: scan.timestamp || new Date().toISOString(),
|
|
397
380
|
project: cwd ? path.basename(cwd) : undefined,
|
|
398
381
|
cwd,
|
|
399
382
|
filePath,
|
|
400
|
-
gitBranch:
|
|
401
|
-
version:
|
|
402
|
-
topic,
|
|
383
|
+
gitBranch: scan.gitBranch,
|
|
384
|
+
version: scan.version,
|
|
385
|
+
topic: scan.topic,
|
|
386
|
+
messageCount: scan.messageCount,
|
|
387
|
+
tokenCount: scan.tokenCount,
|
|
403
388
|
account,
|
|
404
389
|
};
|
|
405
390
|
}
|
|
@@ -410,6 +395,7 @@ async function discoverGeminiSessions() {
|
|
|
410
395
|
const projectMap = buildGeminiProjectMap();
|
|
411
396
|
const sessions = [];
|
|
412
397
|
const seen = new Set();
|
|
398
|
+
let skipped = 0;
|
|
413
399
|
for (const tmpDir of getAgentSessionDirs('gemini', 'tmp')) {
|
|
414
400
|
let hashDirs;
|
|
415
401
|
try {
|
|
@@ -438,23 +424,28 @@ async function discoverGeminiSessions() {
|
|
|
438
424
|
sessions.push(meta);
|
|
439
425
|
}
|
|
440
426
|
}
|
|
441
|
-
catch {
|
|
427
|
+
catch {
|
|
428
|
+
skipped++;
|
|
429
|
+
}
|
|
442
430
|
}
|
|
443
431
|
}
|
|
444
432
|
}
|
|
433
|
+
if (skipped > 0 && process.env.AGENTS_DEBUG) {
|
|
434
|
+
console.error(`[debug] Skipped ${skipped} unreadable Gemini session(s)`);
|
|
435
|
+
}
|
|
445
436
|
return sessions;
|
|
446
437
|
}
|
|
447
438
|
function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const sessionId =
|
|
456
|
-
const startTime =
|
|
457
|
-
const projectHash =
|
|
439
|
+
let session;
|
|
440
|
+
try {
|
|
441
|
+
session = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
const sessionId = typeof session.sessionId === 'string' ? session.sessionId : '';
|
|
447
|
+
const startTime = typeof session.startTime === 'string' ? session.startTime : '';
|
|
448
|
+
const projectHash = typeof session.projectHash === 'string' ? session.projectHash : '';
|
|
458
449
|
if (!sessionId)
|
|
459
450
|
return null;
|
|
460
451
|
// Resolve project name from hash
|
|
@@ -462,11 +453,31 @@ function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
|
462
453
|
const project = projectInfo?.name || hashDir.slice(0, 12);
|
|
463
454
|
const cwd = projectInfo?.path;
|
|
464
455
|
const stat = safeStatSync(filePath);
|
|
465
|
-
|
|
456
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
466
457
|
let topic;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
458
|
+
let messageCount = 0;
|
|
459
|
+
let tokenCount = 0;
|
|
460
|
+
let sawTokenCount = false;
|
|
461
|
+
for (const message of messages) {
|
|
462
|
+
if (message.type === 'user') {
|
|
463
|
+
const text = extractGeminiMessageText(message.content);
|
|
464
|
+
if (text) {
|
|
465
|
+
messageCount++;
|
|
466
|
+
if (!topic)
|
|
467
|
+
topic = extractSessionTopic(text);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else if (message.type === 'gemini') {
|
|
471
|
+
if (extractGeminiMessageText(message.content)) {
|
|
472
|
+
messageCount++;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const total = getGeminiTokenCount(message.tokens);
|
|
476
|
+
if (total !== null) {
|
|
477
|
+
tokenCount += total;
|
|
478
|
+
sawTokenCount = true;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
470
481
|
return {
|
|
471
482
|
id: sessionId,
|
|
472
483
|
shortId: sessionId.slice(0, 8),
|
|
@@ -476,6 +487,8 @@ function readGeminiMeta(filePath, hashDir, projectMap) {
|
|
|
476
487
|
cwd,
|
|
477
488
|
filePath,
|
|
478
489
|
topic,
|
|
490
|
+
messageCount,
|
|
491
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
479
492
|
};
|
|
480
493
|
}
|
|
481
494
|
function buildGeminiProjectMap() {
|
|
@@ -507,9 +520,7 @@ function buildGeminiProjectMap() {
|
|
|
507
520
|
}
|
|
508
521
|
}
|
|
509
522
|
}
|
|
510
|
-
catch {
|
|
511
|
-
// Ignore parse errors
|
|
512
|
-
}
|
|
523
|
+
catch { /* projects.json missing or malformed */ }
|
|
513
524
|
// Also check ~/.gemini/history/*/.project_root for additional mappings
|
|
514
525
|
const historyDir = path.join(HOME, '.gemini', 'history');
|
|
515
526
|
if (fs.existsSync(historyDir)) {
|
|
@@ -524,15 +535,11 @@ function buildGeminiProjectMap() {
|
|
|
524
535
|
map.set(hash, { name, path: projectPath });
|
|
525
536
|
}
|
|
526
537
|
}
|
|
527
|
-
catch {
|
|
528
|
-
// Skip
|
|
529
|
-
}
|
|
538
|
+
catch { /* history entry unreadable */ }
|
|
530
539
|
}
|
|
531
540
|
}
|
|
532
541
|
}
|
|
533
|
-
catch {
|
|
534
|
-
// Skip
|
|
535
|
-
}
|
|
542
|
+
catch { /* history entry unreadable */ }
|
|
536
543
|
}
|
|
537
544
|
return map;
|
|
538
545
|
}
|
|
@@ -554,7 +561,7 @@ function getOpenCodeAccount() {
|
|
|
554
561
|
}
|
|
555
562
|
}
|
|
556
563
|
}
|
|
557
|
-
catch { }
|
|
564
|
+
catch { /* sqlite3 unavailable or DB locked */ }
|
|
558
565
|
cachedOpenCodeAccount = '';
|
|
559
566
|
return undefined;
|
|
560
567
|
}
|
|
@@ -563,8 +570,8 @@ async function discoverOpenCodeSessions() {
|
|
|
563
570
|
return [];
|
|
564
571
|
const account = getOpenCodeAccount();
|
|
565
572
|
try {
|
|
566
|
-
// Query sessions
|
|
567
|
-
//
|
|
573
|
+
// Query sessions. time_created is millisecond epoch. Limit to 200 most recent.
|
|
574
|
+
// Use session.title as topic (OpenCode auto-generates good titles).
|
|
568
575
|
const query = `
|
|
569
576
|
SELECT
|
|
570
577
|
s.id,
|
|
@@ -572,41 +579,43 @@ async function discoverOpenCodeSessions() {
|
|
|
572
579
|
s.directory,
|
|
573
580
|
s.version,
|
|
574
581
|
s.time_created,
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
JOIN part p ON p.message_id = m.id AND p.session_id = m.session_id
|
|
579
|
-
WHERE m.session_id = s.id
|
|
580
|
-
AND json_extract(m.data, '$.role') = 'user'
|
|
581
|
-
AND json_extract(p.data, '$.type') = 'text'
|
|
582
|
-
ORDER BY m.time_created ASC
|
|
583
|
-
LIMIT 1) AS first_user_text
|
|
582
|
+
COALESCE(stats.message_count, 0),
|
|
583
|
+
stats.token_count,
|
|
584
|
+
COALESCE(stats.has_token_data, 0)
|
|
584
585
|
FROM session s
|
|
586
|
+
LEFT JOIN (
|
|
587
|
+
SELECT
|
|
588
|
+
session_id,
|
|
589
|
+
COUNT(*) AS message_count,
|
|
590
|
+
SUM(
|
|
591
|
+
COALESCE(json_extract(data, '$.tokens.input'), 0) +
|
|
592
|
+
COALESCE(json_extract(data, '$.tokens.output'), 0) +
|
|
593
|
+
COALESCE(json_extract(data, '$.tokens.reasoning'), 0) +
|
|
594
|
+
COALESCE(json_extract(data, '$.tokens.cache.read'), 0) +
|
|
595
|
+
COALESCE(json_extract(data, '$.tokens.cache.write'), 0)
|
|
596
|
+
) AS token_count,
|
|
597
|
+
MAX(CASE WHEN json_type(data, '$.tokens') IS NOT NULL THEN 1 ELSE 0 END) AS has_token_data
|
|
598
|
+
FROM message
|
|
599
|
+
GROUP BY session_id
|
|
600
|
+
) stats ON stats.session_id = s.id
|
|
585
601
|
WHERE s.parent_id IS NULL
|
|
586
|
-
ORDER BY
|
|
602
|
+
ORDER BY time_created DESC
|
|
587
603
|
LIMIT 200;
|
|
588
604
|
`.replace(/\n/g, ' ');
|
|
589
|
-
const out = execSync(`sqlite3 -separator '|||' "${OPENCODE_DB}"
|
|
605
|
+
const out = execSync(`sqlite3 -separator '|||' "${OPENCODE_DB}"`, { encoding: 'utf-8', input: query, stdio: ['pipe', 'pipe', 'ignore'], timeout: 5000 });
|
|
590
606
|
const sessions = [];
|
|
591
607
|
for (const line of out.split('\n')) {
|
|
592
608
|
if (!line.trim())
|
|
593
609
|
continue;
|
|
594
|
-
const [id, title, directory, version, timeCreatedStr,
|
|
610
|
+
const [id, title, directory, version, timeCreatedStr, messageCountStr, tokenCountStr, hasTokenDataStr] = line.split('|||');
|
|
595
611
|
if (!id)
|
|
596
612
|
continue;
|
|
597
613
|
const timeCreated = parseInt(timeCreatedStr, 10);
|
|
614
|
+
const messageCount = parseInt(messageCountStr, 10);
|
|
615
|
+
const tokenCount = parseInt(tokenCountStr, 10);
|
|
616
|
+
const hasTokenData = hasTokenDataStr === '1';
|
|
598
617
|
const timestamp = isNaN(timeCreated) ? new Date().toISOString() : new Date(timeCreated).toISOString();
|
|
599
|
-
|
|
600
|
-
let topic = title || undefined;
|
|
601
|
-
if (firstUserText) {
|
|
602
|
-
try {
|
|
603
|
-
const partData = JSON.parse(firstUserText);
|
|
604
|
-
if (partData.text) {
|
|
605
|
-
topic = extractTopic(partData.text);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
catch { }
|
|
609
|
-
}
|
|
618
|
+
const topic = title || undefined;
|
|
610
619
|
sessions.push({
|
|
611
620
|
id,
|
|
612
621
|
shortId: id.replace(/^ses_/, '').slice(0, 8),
|
|
@@ -618,11 +627,16 @@ async function discoverOpenCodeSessions() {
|
|
|
618
627
|
version: version || undefined,
|
|
619
628
|
account,
|
|
620
629
|
topic,
|
|
630
|
+
messageCount: Number.isNaN(messageCount) ? undefined : messageCount,
|
|
631
|
+
tokenCount: hasTokenData && !Number.isNaN(tokenCount) ? tokenCount : undefined,
|
|
621
632
|
});
|
|
622
633
|
}
|
|
623
634
|
return sessions;
|
|
624
635
|
}
|
|
625
|
-
catch {
|
|
636
|
+
catch (err) {
|
|
637
|
+
if (process.stderr.isTTY) {
|
|
638
|
+
console.error(`Warning: Could not query OpenCode sessions: ${err.message}`);
|
|
639
|
+
}
|
|
626
640
|
return [];
|
|
627
641
|
}
|
|
628
642
|
}
|
|
@@ -711,10 +725,146 @@ async function discoverOpenClawSessions() {
|
|
|
711
725
|
}
|
|
712
726
|
return sessions;
|
|
713
727
|
}
|
|
728
|
+
async function scanClaudeSession(filePath) {
|
|
729
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
730
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
731
|
+
let timestamp;
|
|
732
|
+
let cwd;
|
|
733
|
+
let gitBranch;
|
|
734
|
+
let version;
|
|
735
|
+
let topic;
|
|
736
|
+
let messageCount = 0;
|
|
737
|
+
let tokenCount = 0;
|
|
738
|
+
let sawTokenCount = false;
|
|
739
|
+
const seenAssistantIds = new Set();
|
|
740
|
+
try {
|
|
741
|
+
for await (const line of rl) {
|
|
742
|
+
if (!line.trim())
|
|
743
|
+
continue;
|
|
744
|
+
let parsed;
|
|
745
|
+
try {
|
|
746
|
+
parsed = JSON.parse(line);
|
|
747
|
+
}
|
|
748
|
+
catch {
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
751
|
+
if (!timestamp && (parsed.type === 'user' || parsed.type === 'assistant') && parsed.timestamp) {
|
|
752
|
+
timestamp = parsed.timestamp;
|
|
753
|
+
cwd = parsed.cwd || '';
|
|
754
|
+
gitBranch = parsed.gitBranch || undefined;
|
|
755
|
+
version = parsed.version || undefined;
|
|
756
|
+
}
|
|
757
|
+
if (parsed.type === 'user') {
|
|
758
|
+
const text = extractClaudeUserText(parsed);
|
|
759
|
+
if (text) {
|
|
760
|
+
messageCount++;
|
|
761
|
+
if (!topic)
|
|
762
|
+
topic = extractSessionTopic(text);
|
|
763
|
+
}
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (parsed.type !== 'assistant')
|
|
767
|
+
continue;
|
|
768
|
+
const assistantId = typeof parsed.message?.id === 'string'
|
|
769
|
+
? parsed.message.id
|
|
770
|
+
: typeof parsed.uuid === 'string'
|
|
771
|
+
? parsed.uuid
|
|
772
|
+
: undefined;
|
|
773
|
+
const logicalId = assistantId || `${parsed.timestamp || ''}:${seenAssistantIds.size}`;
|
|
774
|
+
if (seenAssistantIds.has(logicalId))
|
|
775
|
+
continue;
|
|
776
|
+
seenAssistantIds.add(logicalId);
|
|
777
|
+
messageCount++;
|
|
778
|
+
const usage = getClaudeUsageTotal(parsed.message?.usage || parsed.usage);
|
|
779
|
+
if (usage !== null) {
|
|
780
|
+
tokenCount += usage;
|
|
781
|
+
sawTokenCount = true;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
finally {
|
|
786
|
+
rl.close();
|
|
787
|
+
stream.destroy();
|
|
788
|
+
}
|
|
789
|
+
return {
|
|
790
|
+
timestamp,
|
|
791
|
+
cwd,
|
|
792
|
+
gitBranch,
|
|
793
|
+
version,
|
|
794
|
+
topic,
|
|
795
|
+
messageCount,
|
|
796
|
+
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
async function scanCodexSession(filePath) {
|
|
800
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
801
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
802
|
+
let sessionId;
|
|
803
|
+
let timestamp;
|
|
804
|
+
let cwd;
|
|
805
|
+
let gitBranch;
|
|
806
|
+
let version;
|
|
807
|
+
let topic;
|
|
808
|
+
let messageCount = 0;
|
|
809
|
+
let tokenCount;
|
|
810
|
+
try {
|
|
811
|
+
for await (const line of rl) {
|
|
812
|
+
if (!line.trim())
|
|
813
|
+
continue;
|
|
814
|
+
let parsed;
|
|
815
|
+
try {
|
|
816
|
+
parsed = JSON.parse(line);
|
|
817
|
+
}
|
|
818
|
+
catch {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (parsed.type === 'session_meta') {
|
|
822
|
+
const payload = parsed.payload || {};
|
|
823
|
+
sessionId = payload.id || sessionId;
|
|
824
|
+
timestamp = payload.timestamp || parsed.timestamp || timestamp;
|
|
825
|
+
cwd = payload.cwd || cwd;
|
|
826
|
+
gitBranch = payload.git?.branch || gitBranch;
|
|
827
|
+
version = payload.version || version;
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (parsed.type === 'response_item' && parsed.payload?.type === 'message') {
|
|
831
|
+
const role = parsed.payload.role === 'user' || parsed.payload.role === 'developer'
|
|
832
|
+
? 'user'
|
|
833
|
+
: 'assistant';
|
|
834
|
+
const text = extractCodexMessageText(parsed.payload.content, role);
|
|
835
|
+
if (!text)
|
|
836
|
+
continue;
|
|
837
|
+
messageCount++;
|
|
838
|
+
if (role === 'user' && !topic)
|
|
839
|
+
topic = extractSessionTopic(text);
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
if (parsed.type === 'event_msg' && parsed.payload?.type === 'token_count') {
|
|
843
|
+
const total = getCodexTokenCount(parsed.payload.info?.total_token_usage);
|
|
844
|
+
if (total !== null)
|
|
845
|
+
tokenCount = total;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
finally {
|
|
850
|
+
rl.close();
|
|
851
|
+
stream.destroy();
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
sessionId,
|
|
855
|
+
timestamp,
|
|
856
|
+
cwd,
|
|
857
|
+
gitBranch,
|
|
858
|
+
version,
|
|
859
|
+
topic,
|
|
860
|
+
messageCount,
|
|
861
|
+
tokenCount,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
714
864
|
// ---------------------------------------------------------------------------
|
|
715
865
|
// Utilities
|
|
716
866
|
// ---------------------------------------------------------------------------
|
|
717
|
-
function readFirstLines(filePath, maxLines) {
|
|
867
|
+
export function readFirstLines(filePath, maxLines) {
|
|
718
868
|
return new Promise((resolve) => {
|
|
719
869
|
const lines = [];
|
|
720
870
|
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
@@ -736,7 +886,7 @@ function readFirstLines(filePath, maxLines) {
|
|
|
736
886
|
* Walk a directory recursively for files with a given extension.
|
|
737
887
|
* Returns at most `limit` files, sorted by mtime descending.
|
|
738
888
|
*/
|
|
739
|
-
function walkForFiles(dir, ext, limit) {
|
|
889
|
+
export function walkForFiles(dir, ext, limit) {
|
|
740
890
|
const results = [];
|
|
741
891
|
function walk(d, depth) {
|
|
742
892
|
if (depth > 5)
|
|
@@ -766,11 +916,6 @@ function walkForFiles(dir, ext, limit) {
|
|
|
766
916
|
results.sort((a, b) => b.mtime - a.mtime);
|
|
767
917
|
return results.slice(0, limit).map(r => r.path);
|
|
768
918
|
}
|
|
769
|
-
function extractJsonField(text, field) {
|
|
770
|
-
const re = new RegExp(`"${field}"\\s*:\\s*"([^"]*)"`, 'i');
|
|
771
|
-
const match = text.match(re);
|
|
772
|
-
return match ? match[1] : '';
|
|
773
|
-
}
|
|
774
919
|
function sha256(input) {
|
|
775
920
|
return crypto.createHash('sha256').update(input).digest('hex');
|
|
776
921
|
}
|
|
@@ -790,11 +935,104 @@ function safeRealpathSync(p) {
|
|
|
790
935
|
return null;
|
|
791
936
|
}
|
|
792
937
|
}
|
|
793
|
-
function
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
938
|
+
function extractClaudeUserText(parsed) {
|
|
939
|
+
if (parsed.isMeta === true)
|
|
940
|
+
return undefined;
|
|
941
|
+
const content = parsed.message?.content;
|
|
942
|
+
if (typeof content === 'string') {
|
|
943
|
+
const text = content.trim();
|
|
944
|
+
return isLocalCommandMessage(text) ? undefined : text || undefined;
|
|
945
|
+
}
|
|
946
|
+
if (!Array.isArray(content))
|
|
947
|
+
return undefined;
|
|
948
|
+
const text = content
|
|
949
|
+
.filter((block) => block.type === 'text')
|
|
950
|
+
.map((block) => String(block.text || '').trim())
|
|
951
|
+
.find((value) => value && !value.startsWith('[Request interrupted'));
|
|
952
|
+
if (!text || isLocalCommandMessage(text))
|
|
953
|
+
return undefined;
|
|
954
|
+
return text;
|
|
955
|
+
}
|
|
956
|
+
function isLocalCommandMessage(text) {
|
|
957
|
+
return /<local-command-caveat>|<bash-(input|stdout|stderr)>/i.test(text);
|
|
958
|
+
}
|
|
959
|
+
function getClaudeUsageTotal(usage) {
|
|
960
|
+
if (!usage || typeof usage !== 'object')
|
|
961
|
+
return null;
|
|
962
|
+
return sumKnownNumbers([
|
|
963
|
+
usage.input_tokens,
|
|
964
|
+
usage.output_tokens,
|
|
965
|
+
usage.cache_creation_input_tokens,
|
|
966
|
+
usage.cache_read_input_tokens,
|
|
967
|
+
]);
|
|
968
|
+
}
|
|
969
|
+
function extractCodexMessageText(contentBlocks, role) {
|
|
970
|
+
if (!Array.isArray(contentBlocks))
|
|
971
|
+
return undefined;
|
|
972
|
+
const matches = role === 'user'
|
|
973
|
+
? contentBlocks.filter((block) => block.type === 'input_text')
|
|
974
|
+
: contentBlocks.filter((block) => block.type === 'output_text');
|
|
975
|
+
const text = matches
|
|
976
|
+
.map((block) => String(block.text || '').trim())
|
|
977
|
+
.find((value) => {
|
|
978
|
+
if (!value)
|
|
979
|
+
return false;
|
|
980
|
+
if (role === 'user' && (value.length >= 2000 || value.includes('<permissions instructions>') || value.startsWith('# AGENTS.md instructions'))) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
return true;
|
|
984
|
+
});
|
|
985
|
+
return text || undefined;
|
|
986
|
+
}
|
|
987
|
+
function getCodexTokenCount(totalTokenUsage) {
|
|
988
|
+
if (!totalTokenUsage || typeof totalTokenUsage !== 'object')
|
|
989
|
+
return null;
|
|
990
|
+
return sumKnownNumbers([
|
|
991
|
+
totalTokenUsage.input_tokens,
|
|
992
|
+
totalTokenUsage.cached_input_tokens,
|
|
993
|
+
totalTokenUsage.output_tokens,
|
|
994
|
+
totalTokenUsage.reasoning_output_tokens,
|
|
995
|
+
]);
|
|
996
|
+
}
|
|
997
|
+
function extractGeminiMessageText(content) {
|
|
998
|
+
if (typeof content === 'string')
|
|
999
|
+
return content.trim();
|
|
1000
|
+
if (Array.isArray(content)) {
|
|
1001
|
+
return content
|
|
1002
|
+
.map((part) => {
|
|
1003
|
+
if (typeof part === 'string')
|
|
1004
|
+
return part;
|
|
1005
|
+
if (typeof part?.text === 'string')
|
|
1006
|
+
return part.text;
|
|
1007
|
+
return '';
|
|
1008
|
+
})
|
|
1009
|
+
.join('\n')
|
|
1010
|
+
.trim();
|
|
1011
|
+
}
|
|
1012
|
+
return '';
|
|
1013
|
+
}
|
|
1014
|
+
function getGeminiTokenCount(tokens) {
|
|
1015
|
+
if (!tokens || typeof tokens !== 'object')
|
|
1016
|
+
return null;
|
|
1017
|
+
if (typeof tokens.total === 'number')
|
|
1018
|
+
return tokens.total;
|
|
1019
|
+
return sumKnownNumbers([
|
|
1020
|
+
tokens.input,
|
|
1021
|
+
tokens.output,
|
|
1022
|
+
tokens.cached,
|
|
1023
|
+
tokens.thoughts,
|
|
1024
|
+
tokens.tool,
|
|
1025
|
+
]);
|
|
1026
|
+
}
|
|
1027
|
+
function sumKnownNumbers(values) {
|
|
1028
|
+
let total = 0;
|
|
1029
|
+
let found = false;
|
|
1030
|
+
for (const value of values) {
|
|
1031
|
+
if (typeof value !== 'number' || Number.isNaN(value))
|
|
1032
|
+
continue;
|
|
1033
|
+
total += value;
|
|
1034
|
+
found = true;
|
|
1035
|
+
}
|
|
1036
|
+
return found ? total : null;
|
|
799
1037
|
}
|
|
800
1038
|
//# sourceMappingURL=discover.js.map
|