@phnx-labs/agents-cli 1.22.117 → 1.22.119
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 +301 -0
- package/dist/bootstrap.js +4 -11
- package/dist/commands/accounts.js +7 -4
- package/dist/commands/browser.d.ts +52 -70
- package/dist/commands/browser.js +192 -3562
- package/dist/commands/daemon.d.ts +39 -2
- package/dist/commands/daemon.js +72 -73
- package/dist/commands/events.js +1 -1
- package/dist/commands/exec.js +53 -1
- package/dist/commands/projects.js +92 -7
- package/dist/commands/repo.js +13 -49
- package/dist/commands/route.js +5 -5
- package/dist/commands/routines.js +39 -12
- package/dist/commands/sessions-render.d.ts +1 -1
- package/dist/commands/sessions-render.js +0 -1
- package/dist/commands/sessions.test-fixture.d.ts +0 -1
- package/dist/commands/sessions.test-fixture.js +0 -37
- package/dist/commands/setup-browser.d.ts +17 -12
- package/dist/commands/setup-browser.js +52 -87
- package/dist/commands/setup-preferences.d.ts +0 -17
- package/dist/commands/setup-preferences.js +18 -49
- package/dist/commands/setup.js +9 -9
- package/dist/commands/ssh.js +10 -1
- package/dist/commands/sync.js +6 -44
- package/dist/commands/teams.js +0 -1
- package/dist/commands/update.d.ts +19 -0
- package/dist/commands/update.js +34 -6
- package/dist/commands/view.d.ts +12 -1
- package/dist/commands/view.js +47 -2
- package/dist/index.js +107 -26
- package/dist/lib/account-capabilities.js +0 -2
- package/dist/lib/account-catalog.d.ts +38 -0
- package/dist/lib/account-catalog.js +110 -26
- package/dist/lib/account-provider-registry.js +1 -1
- package/dist/lib/account-registry.d.ts +1 -1
- package/dist/lib/account-registry.js +36 -2
- package/dist/lib/accounting/rotate.js +22 -8
- package/dist/lib/accounting/usage-ingest.d.ts +6 -1
- package/dist/lib/accounting/usage-ingest.js +115 -45
- package/dist/lib/accounting/usage-sync.d.ts +134 -31
- package/dist/lib/accounting/usage-sync.js +227 -66
- package/dist/lib/accounting/usage.d.ts +16 -3
- package/dist/lib/accounting/usage.js +26 -8
- package/dist/lib/accounts/add.d.ts +3 -1
- package/dist/lib/accounts/add.js +24 -13
- package/dist/lib/accounts/slots.d.ts +8 -0
- package/dist/lib/accounts/slots.js +17 -0
- package/dist/lib/add-dir.js +0 -1
- package/dist/lib/agent-cli-commands.js +0 -1
- package/dist/lib/agent-spec/agents.d.ts +6 -7
- package/dist/lib/agent-spec/agents.js +7 -60
- package/dist/lib/auth-health.d.ts +69 -1
- package/dist/lib/auth-health.js +133 -4
- package/dist/lib/browser/context.d.ts +82 -0
- package/dist/lib/browser/context.js +88 -0
- package/dist/lib/browser/paths.d.ts +25 -0
- package/dist/lib/browser/paths.js +51 -0
- package/dist/lib/browser/record.d.ts +38 -0
- package/dist/lib/browser/record.js +57 -0
- package/dist/lib/browser/sessions-list.js +1 -2
- package/dist/lib/browser-client.d.ts +158 -0
- package/dist/lib/browser-client.js +234 -0
- package/dist/lib/channels/owner-sink.d.ts +1 -18
- package/dist/lib/channels/owner-sink.js +29 -82
- package/dist/lib/channels/providers/rush.d.ts +7 -7
- package/dist/lib/channels/providers/rush.js +127 -57
- package/dist/lib/claude-account-token.d.ts +7 -1
- package/dist/lib/claude-account-token.js +26 -4
- package/dist/lib/claude-statusline.d.ts +1 -1
- package/dist/lib/claude-statusline.js +15 -3
- package/dist/lib/commands.js +1 -1
- package/dist/lib/crabbox/lease.js +1 -1
- package/dist/lib/crabbox/runtimes.js +1 -3
- package/dist/lib/daemon/account-state-daemon-service.js +1 -0
- package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
- package/dist/lib/daemon/auth-sync-service.js +37 -43
- package/dist/lib/daemon/daemon.d.ts +25 -14
- package/dist/lib/daemon/daemon.js +92 -154
- package/dist/lib/daemon/runner.d.ts +2 -2
- package/dist/lib/daemon/runner.js +32 -9
- package/dist/lib/daemon/self-update-service.d.ts +35 -34
- package/dist/lib/daemon/self-update-service.js +40 -36
- package/dist/lib/daemon/service.d.ts +12 -8
- package/dist/lib/daemon/supervisor.d.ts +65 -54
- package/dist/lib/daemon/supervisor.js +136 -171
- package/dist/lib/daemon/testdata/process-view-start.js +5 -2
- package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
- package/dist/lib/daemon/usage-sync-service.js +46 -78
- package/dist/lib/daemon-health.d.ts +22 -5
- package/dist/lib/daemon-health.js +47 -3
- package/dist/lib/daemon-services.d.ts +1 -1
- package/dist/lib/daemon-services.js +0 -10
- package/dist/lib/daemon-ticks.js +4 -2
- package/dist/lib/devices/doctor-findings.js +8 -11
- package/dist/lib/devices/harness-inventory.js +14 -5
- package/dist/lib/exec-account-home.js +5 -0
- package/dist/lib/exec.js +10 -13
- package/dist/lib/feed/events.js +0 -1
- package/dist/lib/feed/tool-activity.js +5 -2
- package/dist/lib/fleet-shared-state.d.ts +45 -0
- package/dist/lib/fleet-shared-state.js +78 -9
- package/dist/lib/fs-atomic.js +25 -0
- package/dist/lib/github/pr-mergeable.d.ts +4 -2
- package/dist/lib/github/pr-mergeable.js +5 -3
- package/dist/lib/github/project-prs.d.ts +58 -3
- package/dist/lib/github/project-prs.js +205 -7
- package/dist/lib/harness/adapters/cursor.js +14 -4
- package/dist/lib/harness-auth-capabilities.d.ts +4 -3
- package/dist/lib/harness-auth-capabilities.js +1 -2
- package/dist/lib/helper-versions.js +1 -1
- package/dist/lib/hooks/cache.d.ts +7 -0
- package/dist/lib/hooks/cache.js +27 -2
- package/dist/lib/hooks/install.js +7 -1
- package/dist/lib/hosts/ready.d.ts +38 -8
- package/dist/lib/hosts/ready.js +62 -14
- package/dist/lib/installations/active-check.d.ts +27 -0
- package/dist/lib/installations/active-check.js +61 -0
- package/dist/lib/installations/migrate.js +6 -3
- package/dist/lib/installations/shims.d.ts +1 -1
- package/dist/lib/installations/shims.js +32 -12
- package/dist/lib/installations/store.d.ts +9 -0
- package/dist/lib/installations/store.js +24 -0
- package/dist/lib/installations/update.js +21 -15
- package/dist/lib/installations/versions.js +15 -3
- package/dist/lib/manifest.js +1 -1
- package/dist/lib/models.js +0 -2
- package/dist/lib/open-url.js +28 -54
- package/dist/lib/profiles.js +0 -2
- package/dist/lib/projects.d.ts +16 -0
- package/dist/lib/projects.js +31 -0
- package/dist/lib/refresh.js +1 -1
- package/dist/lib/routine-readiness.d.ts +35 -0
- package/dist/lib/routine-readiness.js +74 -16
- package/dist/lib/rules/compile.js +6 -0
- package/dist/lib/sandbox.js +0 -2
- package/dist/lib/secrets-policy.d.ts +89 -11
- package/dist/lib/secrets-policy.js +175 -28
- package/dist/lib/session/active.d.ts +17 -6
- package/dist/lib/session/active.js +105 -49
- package/dist/lib/session/db.d.ts +1 -1
- package/dist/lib/session/db.js +65 -18
- package/dist/lib/session/discover.js +1 -283
- package/dist/lib/session/mirror.d.ts +4 -3
- package/dist/lib/session/mirror.js +4 -3
- package/dist/lib/session/parse.d.ts +0 -2
- package/dist/lib/session/parse.js +1 -138
- package/dist/lib/session/pid-registry.d.ts +6 -3
- package/dist/lib/session/pid-registry.js +18 -8
- package/dist/lib/session/remote/watch.d.ts +1 -1
- package/dist/lib/session/remote/watch.js +7 -3
- package/dist/lib/session/throughput.d.ts +1 -3
- package/dist/lib/session/throughput.js +0 -20
- package/dist/lib/session/types.d.ts +2 -2
- package/dist/lib/session/types.js +2 -2
- package/dist/lib/sessions-client.d.ts +60 -1
- package/dist/lib/sessions-client.js +227 -4
- package/dist/lib/setup-tool-status.js +5 -10
- package/dist/lib/signin-badge.d.ts +3 -2
- package/dist/lib/signin-badge.js +6 -3
- package/dist/lib/smart-launch.js +4 -1
- package/dist/lib/staleness/detectors/commands.d.ts +1 -1
- package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
- package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
- package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
- package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
- package/dist/lib/staleness/detectors/rules.d.ts +1 -1
- package/dist/lib/staleness/detectors/skills.d.ts +1 -1
- package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
- package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
- package/dist/lib/staleness/writers/commands.d.ts +1 -1
- package/dist/lib/staleness/writers/hooks.d.ts +1 -1
- package/dist/lib/staleness/writers/mcp.d.ts +1 -1
- package/dist/lib/staleness/writers/permissions.d.ts +1 -1
- package/dist/lib/staleness/writers/plugins.d.ts +1 -1
- package/dist/lib/staleness/writers/rules.d.ts +1 -1
- package/dist/lib/staleness/writers/skills.d.ts +1 -1
- package/dist/lib/staleness/writers/sources.js +5 -0
- package/dist/lib/staleness/writers/subagents.d.ts +1 -1
- package/dist/lib/staleness/writers/workflows.d.ts +1 -1
- package/dist/lib/state.d.ts +6 -4
- package/dist/lib/state.js +7 -17
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +1 -136
- package/dist/lib/teams/placement-probe.js +50 -15
- package/dist/lib/teams/scheduler.d.ts +7 -0
- package/dist/lib/types.d.ts +16 -1
- package/dist/lib/types.js +1 -1
- package/dist/lib/view-types.d.ts +32 -0
- package/dist/lib/watchdog/read.js +0 -1
- package/dist/lib/watchdog/rotate.js +1 -2
- package/dist/lib/watchdog/runner.js +2 -2
- package/dist/lib/watchdog/watchdog.d.ts +1 -1
- package/dist/lib/watchdog/watchdogTail.js +0 -31
- package/package.json +1 -1
- package/scripts/postinstall.js +8 -8
- package/dist/browser.d.ts +0 -2
- package/dist/browser.js +0 -17
- package/dist/commands/browser-picker.d.ts +0 -18
- package/dist/commands/browser-picker.js +0 -97
- package/dist/lib/browser/arc-discovery.d.ts +0 -52
- package/dist/lib/browser/arc-discovery.js +0 -197
- package/dist/lib/browser/arc-dom.d.ts +0 -14
- package/dist/lib/browser/arc-dom.js +0 -121
- package/dist/lib/browser/caller-identity.d.ts +0 -40
- package/dist/lib/browser/caller-identity.js +0 -175
- package/dist/lib/browser/cdp.d.ts +0 -51
- package/dist/lib/browser/cdp.js +0 -254
- package/dist/lib/browser/chrome.d.ts +0 -141
- package/dist/lib/browser/chrome.js +0 -704
- package/dist/lib/browser/chromium-discovery.d.ts +0 -28
- package/dist/lib/browser/chromium-discovery.js +0 -96
- package/dist/lib/browser/devices.d.ts +0 -34
- package/dist/lib/browser/devices.js +0 -61
- package/dist/lib/browser/domain-skills.d.ts +0 -71
- package/dist/lib/browser/domain-skills.js +0 -195
- package/dist/lib/browser/drivers/arc.d.ts +0 -40
- package/dist/lib/browser/drivers/arc.js +0 -276
- package/dist/lib/browser/drivers/firefox.d.ts +0 -99
- package/dist/lib/browser/drivers/firefox.js +0 -377
- package/dist/lib/browser/drivers/local.d.ts +0 -55
- package/dist/lib/browser/drivers/local.js +0 -266
- package/dist/lib/browser/drivers/ssh.d.ts +0 -120
- package/dist/lib/browser/drivers/ssh.js +0 -467
- package/dist/lib/browser/editor.d.ts +0 -3
- package/dist/lib/browser/editor.js +0 -50
- package/dist/lib/browser/ffmpeg.d.ts +0 -12
- package/dist/lib/browser/ffmpeg.js +0 -184
- package/dist/lib/browser/firefox-discovery.d.ts +0 -69
- package/dist/lib/browser/firefox-discovery.js +0 -162
- package/dist/lib/browser/har.d.ts +0 -85
- package/dist/lib/browser/har.js +0 -77
- package/dist/lib/browser/hygiene.d.ts +0 -97
- package/dist/lib/browser/hygiene.js +0 -153
- package/dist/lib/browser/index.d.ts +0 -5
- package/dist/lib/browser/index.js +0 -5
- package/dist/lib/browser/input.d.ts +0 -7
- package/dist/lib/browser/input.js +0 -92
- package/dist/lib/browser/ipc.d.ts +0 -204
- package/dist/lib/browser/ipc.js +0 -1453
- package/dist/lib/browser/login-detection.d.ts +0 -87
- package/dist/lib/browser/login-detection.js +0 -274
- package/dist/lib/browser/profiles.d.ts +0 -422
- package/dist/lib/browser/profiles.js +0 -1157
- package/dist/lib/browser/refs.d.ts +0 -89
- package/dist/lib/browser/refs.js +0 -191
- package/dist/lib/browser/registry.d.ts +0 -70
- package/dist/lib/browser/registry.js +0 -252
- package/dist/lib/browser/remote-control.d.ts +0 -59
- package/dist/lib/browser/remote-control.js +0 -85
- package/dist/lib/browser/resolve-target.d.ts +0 -66
- package/dist/lib/browser/resolve-target.js +0 -264
- package/dist/lib/browser/runtime-state.d.ts +0 -291
- package/dist/lib/browser/runtime-state.js +0 -584
- package/dist/lib/browser/secret-ref.d.ts +0 -10
- package/dist/lib/browser/secret-ref.js +0 -14
- package/dist/lib/browser/service.d.ts +0 -812
- package/dist/lib/browser/service.js +0 -4693
- package/dist/lib/browser/stream.d.ts +0 -17
- package/dist/lib/browser/stream.js +0 -72
- package/dist/lib/browser/task-index.d.ts +0 -78
- package/dist/lib/browser/task-index.js +0 -199
- package/dist/lib/browser/types.d.ts +0 -647
- package/dist/lib/browser/types.js +0 -61
- package/dist/lib/browser/upload.d.ts +0 -29
- package/dist/lib/browser/upload.js +0 -298
- package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
- package/dist/lib/daemon/browser-ipc-service.js +0 -46
- package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
- package/dist/lib/daemon/browser-task-reap-service.js +0 -26
- package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
- package/dist/lib/fleet-shared-repo-sync.js +0 -490
package/dist/lib/session/db.js
CHANGED
|
@@ -29,7 +29,7 @@ const DB_PATH = getSessionsDbPath();
|
|
|
29
29
|
/** Current schema version; bumped when migrations are added. Exported so tests
|
|
30
30
|
* assert against the constant instead of hardcoding a number that every bump
|
|
31
31
|
* then has to chase (docs/sessions.md calls the constant the source of truth). */
|
|
32
|
-
export const SCHEMA_VERSION =
|
|
32
|
+
export const SCHEMA_VERSION = 51;
|
|
33
33
|
/**
|
|
34
34
|
* Bump to force the content extractor (assistant-answer text, alongside the
|
|
35
35
|
* user-prompt text every harness already accumulates) to re-derive on every
|
|
@@ -182,6 +182,10 @@ CREATE INDEX IF NOT EXISTS idx_sessions_short_id ON sessions(short_id);
|
|
|
182
182
|
-- v17 guarantees the machine column exists (same pattern as last_activity);
|
|
183
183
|
-- idx_sessions_mirror_synced likewise waits for migration v46's column add.
|
|
184
184
|
|
|
185
|
+
-- A row sits at the rowid of the sessions row it describes (v51) and is
|
|
186
|
+
-- addressed by that rowid, never by session_id: an UNINDEXED FTS5 column cannot
|
|
187
|
+
-- be seeked, so a session_id predicate scans every row's content — the same
|
|
188
|
+
-- rule tool_call_text follows against tool_calls.rowid (v36).
|
|
185
189
|
CREATE VIRTUAL TABLE IF NOT EXISTS session_text USING fts5(
|
|
186
190
|
session_id UNINDEXED,
|
|
187
191
|
label,
|
|
@@ -1411,6 +1415,39 @@ function migrateSchema(db, fromVersion) {
|
|
|
1411
1415
|
if (!cols.has('generated_title_at'))
|
|
1412
1416
|
db.exec(`ALTER TABLE sessions ADD COLUMN generated_title_at INTEGER`);
|
|
1413
1417
|
}
|
|
1418
|
+
if (fromVersion < 51) {
|
|
1419
|
+
// v50 -> v51: session_text rows move to the rowid of the sessions row they
|
|
1420
|
+
// describe (PHNX-4154), the repair v36 made to tool_call_text. The only key
|
|
1421
|
+
// was the UNINDEXED session_id, so every delete and content read was a full
|
|
1422
|
+
// scan of the FTS content — 669 MB and 0.2-0.5 s per statement on a real
|
|
1423
|
+
// box — and the mirror ingest issues one per mirrored row (2,298 there)
|
|
1424
|
+
// inside a daemon tick: the event loop froze for minutes, every service
|
|
1425
|
+
// blew its deadline, and browser verbs timed out behind it.
|
|
1426
|
+
//
|
|
1427
|
+
// Same shape as v42: rename, recreate, copy back keyed by sessions.rowid,
|
|
1428
|
+
// drop — search never blacks out. OR REPLACE keeps the newest of any
|
|
1429
|
+
// duplicate session_id rows. A text row whose session is gone has no rowid
|
|
1430
|
+
// to sit at and is dropped; nothing could reach it anyway.
|
|
1431
|
+
db.exec(`ALTER TABLE session_text RENAME TO session_text_v50`);
|
|
1432
|
+
db.exec(`
|
|
1433
|
+
CREATE VIRTUAL TABLE session_text USING fts5(
|
|
1434
|
+
session_id UNINDEXED,
|
|
1435
|
+
label,
|
|
1436
|
+
topic,
|
|
1437
|
+
project,
|
|
1438
|
+
content,
|
|
1439
|
+
assistant,
|
|
1440
|
+
tokenize = 'unicode61 remove_diacritics 2'
|
|
1441
|
+
);
|
|
1442
|
+
`);
|
|
1443
|
+
db.exec(`
|
|
1444
|
+
INSERT OR REPLACE INTO session_text (rowid, session_id, label, topic, project, content, assistant)
|
|
1445
|
+
SELECT s.rowid, t.session_id, t.label, t.topic, t.project, t.content, t.assistant
|
|
1446
|
+
FROM session_text_v50 t JOIN sessions s ON s.id = t.session_id
|
|
1447
|
+
ORDER BY t.rowid
|
|
1448
|
+
`);
|
|
1449
|
+
db.exec(`DROP TABLE session_text_v50`);
|
|
1450
|
+
}
|
|
1414
1451
|
}
|
|
1415
1452
|
/**
|
|
1416
1453
|
* Stamp `account_key` / `account_org` / `account` on every Claude row from its
|
|
@@ -2292,8 +2329,15 @@ function enrichCachedSessionMeta(meta) {
|
|
|
2292
2329
|
return meta;
|
|
2293
2330
|
}
|
|
2294
2331
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2332
|
+
/**
|
|
2333
|
+
* The one way to address a session's FTS row: its rowid is the sessions rowid
|
|
2334
|
+
* (schema comment on session_text). A `session_id = ?` predicate scans the whole
|
|
2335
|
+
* index instead — the PHNX-4154 daemon freeze.
|
|
2336
|
+
*/
|
|
2337
|
+
const SESSION_TEXT_ROWID = `(SELECT rowid FROM sessions WHERE id = ?)`;
|
|
2338
|
+
const deleteTextStmt = (db) => db.prepare(`DELETE FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
2339
|
+
// Binds the session id twice: once for the rowid lookup, once for the column.
|
|
2340
|
+
const insertTextStmt = (db) => db.prepare(`INSERT INTO session_text (rowid, session_id, label, topic, project, content, assistant) VALUES (${SESSION_TEXT_ROWID}, ?, ?, ?, ?, ?, ?)`);
|
|
2297
2341
|
// Read back the label the upsert actually stored (which may be the preserved
|
|
2298
2342
|
// one, not the incoming blank) so the FTS label column stays consistent with
|
|
2299
2343
|
// sessions.label after a bare rescan.
|
|
@@ -2406,7 +2450,7 @@ export function upsertSession(meta, content, scan, assistantContent = '') {
|
|
|
2406
2450
|
const txn = db.transaction(() => {
|
|
2407
2451
|
upsert.run(row);
|
|
2408
2452
|
delText.run(meta.id);
|
|
2409
|
-
insText.run(meta.id,
|
|
2453
|
+
insText.run(meta.id, meta.id,
|
|
2410
2454
|
// Use the label the upsert actually stored (preserve-non-empty rule),
|
|
2411
2455
|
// not the raw incoming one, so FTS label ranking survives a bare rescan.
|
|
2412
2456
|
storedFtsLabel(readLabel, meta.id), meta.topic ?? '', meta.project ?? '', content ?? '', assistantContent ?? '');
|
|
@@ -2427,8 +2471,10 @@ function reconcileCodexFileOwners(db, metas) {
|
|
|
2427
2471
|
for (const row of rows) {
|
|
2428
2472
|
if (owners.get(row.file_path) === row.id)
|
|
2429
2473
|
continue;
|
|
2474
|
+
// The text row is keyed by the sessions rowid, so it goes before the row.
|
|
2475
|
+
db.prepare(`DELETE FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`).run(row.id);
|
|
2430
2476
|
db.prepare('DELETE FROM sessions WHERE id = ?').run(row.id);
|
|
2431
|
-
for (const table of ['
|
|
2477
|
+
for (const table of ['session_preview_cache', 'session_summaries', 'session_insights', 'session_topics', 'session_phenotypes', 'session_resource_usage', 'resource_scan_ledger']) {
|
|
2432
2478
|
db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(row.id);
|
|
2433
2479
|
}
|
|
2434
2480
|
// The tool ledger has a unique file path too; release the wrong binding
|
|
@@ -2671,7 +2717,7 @@ export function upsertSessionsBatch(entries) {
|
|
|
2671
2717
|
};
|
|
2672
2718
|
upsert.run(row);
|
|
2673
2719
|
delText.run(meta.id);
|
|
2674
|
-
insText.run(meta.id,
|
|
2720
|
+
insText.run(meta.id, meta.id,
|
|
2675
2721
|
// Mirror upsertSession: index the label the upsert actually stored
|
|
2676
2722
|
// (preserve-non-empty rule), not the raw incoming one.
|
|
2677
2723
|
storedFtsLabel(readLabel, meta.id), meta.topic ?? '', meta.project ?? '', content ?? '', assistantContent ?? '');
|
|
@@ -2746,7 +2792,7 @@ export function syncLabels(labelMap) {
|
|
|
2746
2792
|
if (updates.length === 0)
|
|
2747
2793
|
return 0;
|
|
2748
2794
|
const updSessions = db.prepare(`UPDATE sessions SET label = ? WHERE id = ?`);
|
|
2749
|
-
const updFts = db.prepare(`UPDATE session_text SET label = ? WHERE
|
|
2795
|
+
const updFts = db.prepare(`UPDATE session_text SET label = ? WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
2750
2796
|
const txn = db.transaction((items) => {
|
|
2751
2797
|
for (const { id, label } of items) {
|
|
2752
2798
|
updSessions.run(label, id);
|
|
@@ -2797,7 +2843,7 @@ export function seedLabelsFromNames(nameMap) {
|
|
|
2797
2843
|
if (updates.length === 0)
|
|
2798
2844
|
return 0;
|
|
2799
2845
|
const updSessions = db.prepare(`UPDATE sessions SET label = ? WHERE id = ?`);
|
|
2800
|
-
const updFts = db.prepare(`UPDATE session_text SET label = ? WHERE
|
|
2846
|
+
const updFts = db.prepare(`UPDATE session_text SET label = ? WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
2801
2847
|
const txn = db.transaction((items) => {
|
|
2802
2848
|
for (const { id, label } of items) {
|
|
2803
2849
|
updSessions.run(label, id);
|
|
@@ -2839,7 +2885,7 @@ export function syncTopics(topicMap) {
|
|
|
2839
2885
|
if (updates.length === 0)
|
|
2840
2886
|
return 0;
|
|
2841
2887
|
const updSessions = db.prepare(`UPDATE sessions SET topic = ? WHERE id = ?`);
|
|
2842
|
-
const updFts = db.prepare(`UPDATE session_text SET topic = ? WHERE
|
|
2888
|
+
const updFts = db.prepare(`UPDATE session_text SET topic = ? WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
2843
2889
|
const txn = db.transaction((items) => {
|
|
2844
2890
|
for (const { id, topic } of items) {
|
|
2845
2891
|
updSessions.run(topic, id);
|
|
@@ -3231,7 +3277,7 @@ export function querySessions(options = {}) {
|
|
|
3231
3277
|
const missingIds = new Set(missing.map(r => r.id));
|
|
3232
3278
|
const phantomIds = new Set();
|
|
3233
3279
|
if (missing.length > 0) {
|
|
3234
|
-
const readContent = db.prepare(`SELECT content FROM session_text WHERE
|
|
3280
|
+
const readContent = db.prepare(`SELECT content FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
3235
3281
|
const markArchived = db.prepare(`UPDATE sessions SET archived_at = ? WHERE id = ? AND archived_at IS NULL`);
|
|
3236
3282
|
const now = Date.now();
|
|
3237
3283
|
const classify = db.transaction(() => {
|
|
@@ -3706,7 +3752,7 @@ export function writeRemotePreviewCacheFailure(device, sessionId, reason, backof
|
|
|
3706
3752
|
* when no row exists; an empty string when a row exists but carried no content.
|
|
3707
3753
|
*/
|
|
3708
3754
|
export function readSessionContent(id) {
|
|
3709
|
-
const row = getDB().prepare(`SELECT content FROM session_text WHERE
|
|
3755
|
+
const row = getDB().prepare(`SELECT content FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`).get(id);
|
|
3710
3756
|
return row?.content;
|
|
3711
3757
|
}
|
|
3712
3758
|
/**
|
|
@@ -3964,11 +4010,11 @@ export function upsertMirrorSession(row, source, syncedAt) {
|
|
|
3964
4010
|
if (result.changes === 0)
|
|
3965
4011
|
return false;
|
|
3966
4012
|
// Keep the mirror row searchable by topic + first user turn, like a local row.
|
|
3967
|
-
db.prepare(`DELETE FROM session_text WHERE
|
|
4013
|
+
db.prepare(`DELETE FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`).run(row.id);
|
|
3968
4014
|
db.prepare(`
|
|
3969
|
-
INSERT INTO session_text (session_id, label, topic, project, content, assistant)
|
|
3970
|
-
VALUES (?, ?, ?, '', ?, '')
|
|
3971
|
-
`).run(row.id, row.label ?? '', row.topic ?? '', row.firstUser ?? '');
|
|
4015
|
+
INSERT INTO session_text (rowid, session_id, label, topic, project, content, assistant)
|
|
4016
|
+
VALUES (${SESSION_TEXT_ROWID}, ?, ?, ?, '', ?, '')
|
|
4017
|
+
`).run(row.id, row.id, row.label ?? '', row.topic ?? '', row.firstUser ?? '');
|
|
3972
4018
|
// Carry the peer's daemon-computed summary into the same session_summaries
|
|
3973
4019
|
// cache the local merge reads (PHNX-3939), so a peer session renders its
|
|
3974
4020
|
// goal/checkpoints/checklist inline with no transcript. Stamp is null: the
|
|
@@ -4072,14 +4118,15 @@ export function pruneMirrorSessions(cutoffMs) {
|
|
|
4072
4118
|
if (stale.length === 0)
|
|
4073
4119
|
return 0;
|
|
4074
4120
|
const delRow = db.prepare(`DELETE FROM sessions WHERE id = ?`);
|
|
4075
|
-
const delText = db.prepare(`DELETE FROM session_text WHERE
|
|
4121
|
+
const delText = db.prepare(`DELETE FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
4076
4122
|
const delPreview = db.prepare(`DELETE FROM session_preview_cache WHERE session_id = ?`);
|
|
4077
4123
|
const delSummary = db.prepare(`DELETE FROM session_summaries WHERE session_id = ?`);
|
|
4078
4124
|
const delTimeline = db.prepare(`DELETE FROM session_timelines WHERE session_id = ?`);
|
|
4079
4125
|
const txn = db.transaction(() => {
|
|
4080
4126
|
for (const { id } of stale) {
|
|
4081
|
-
|
|
4127
|
+
// Text first: its rowid lookup needs the sessions row still present.
|
|
4082
4128
|
delText.run(id);
|
|
4129
|
+
delRow.run(id);
|
|
4083
4130
|
delPreview.run(id);
|
|
4084
4131
|
delSummary.run(id);
|
|
4085
4132
|
delTimeline.run(id);
|
|
@@ -4461,7 +4508,7 @@ export function topSessionsByCost(n, options = {}) {
|
|
|
4461
4508
|
// still excluded. archived_at is the persisted signal; stamp it here too (once)
|
|
4462
4509
|
// so a session first surfaced through the cost rollup reports `archived`
|
|
4463
4510
|
// consistently with the listing path.
|
|
4464
|
-
const readContent = db.prepare(`SELECT content FROM session_text WHERE
|
|
4511
|
+
const readContent = db.prepare(`SELECT content FROM session_text WHERE rowid = ${SESSION_TEXT_ROWID}`);
|
|
4465
4512
|
const markArchived = db.prepare(`UPDATE sessions SET archived_at = ? WHERE id = ? AND archived_at IS NULL`);
|
|
4466
4513
|
const now = Date.now();
|
|
4467
4514
|
const toStamp = [];
|
|
@@ -425,7 +425,6 @@ function dispatchAgentScan(agent, onProgress) {
|
|
|
425
425
|
switch (agent) {
|
|
426
426
|
case 'claude': return scanClaudeIncremental(onProgress);
|
|
427
427
|
case 'codex': return scanCodexIncremental(onProgress);
|
|
428
|
-
case 'gemini': return scanGeminiIncremental(onProgress);
|
|
429
428
|
case 'antigravity': return scanAntigravityIncremental(onProgress);
|
|
430
429
|
case 'opencode': return scanOpenCodeIncremental(onProgress);
|
|
431
430
|
case 'openclaw': return scanOpenClawIncremental(onProgress);
|
|
@@ -948,7 +947,6 @@ export function getAgentSessionDirs(agent, subdir) {
|
|
|
948
947
|
const SESSION_ROOT_SPECS = [
|
|
949
948
|
{ agent: 'claude', subdir: 'projects' },
|
|
950
949
|
{ agent: 'codex', subdir: 'sessions' },
|
|
951
|
-
{ agent: 'gemini', subdir: 'tmp' },
|
|
952
950
|
{ agent: 'antigravity', subdir: 'conversations' },
|
|
953
951
|
{ agent: 'droid', subdir: 'sessions' },
|
|
954
952
|
{ agent: 'kimi', subdir: 'sessions' },
|
|
@@ -1059,7 +1057,7 @@ async function scanRoutineArchivesIncremental(agent, onProgress) {
|
|
|
1059
1057
|
const subdir = sessionRootSubdir(agent);
|
|
1060
1058
|
if (!subdir)
|
|
1061
1059
|
return;
|
|
1062
|
-
const ext =
|
|
1060
|
+
const ext = '.jsonl';
|
|
1063
1061
|
const prestat = [];
|
|
1064
1062
|
for (const sessionsDir of getRoutineArchiveSessionDirs(agent, subdir)) {
|
|
1065
1063
|
for (const f of walkForFilesWithStat(sessionsDir, ext, 100_000)) {
|
|
@@ -1723,254 +1721,6 @@ function pickLatestCodexTimestamp(metaTimestamp, filePath) {
|
|
|
1723
1721
|
return candidates.reduce((best, cur) => (cur > best ? cur : best));
|
|
1724
1722
|
}
|
|
1725
1723
|
// ---------------------------------------------------------------------------
|
|
1726
|
-
// Gemini
|
|
1727
|
-
// ---------------------------------------------------------------------------
|
|
1728
|
-
/** Incrementally re-scan changed Gemini session files and upsert into the DB. */
|
|
1729
|
-
async function scanGeminiIncremental(onProgress) {
|
|
1730
|
-
const currentVersion = await getCurrentAgentVersion('gemini');
|
|
1731
|
-
const projectMap = buildGeminiProjectMap();
|
|
1732
|
-
// Each `<tmpDir>/<hashDir>/chats` is a leaf dir of Gemini transcripts. The
|
|
1733
|
-
// FIRST tmp root is the live `~/.gemini/tmp` — its chats dirs are live roots;
|
|
1734
|
-
// version-home + backup roots are immutable and short-circuit when unchanged.
|
|
1735
|
-
const tmpRoots = getAgentSessionDirs('gemini', 'tmp');
|
|
1736
|
-
const leafDirs = [];
|
|
1737
|
-
const seenLeaf = new Set();
|
|
1738
|
-
tmpRoots.forEach((tmpDir, rootIdx) => {
|
|
1739
|
-
const isLiveRoot = rootIdx === 0;
|
|
1740
|
-
let hashDirs;
|
|
1741
|
-
try {
|
|
1742
|
-
hashDirs = fs.readdirSync(tmpDir);
|
|
1743
|
-
}
|
|
1744
|
-
catch {
|
|
1745
|
-
return;
|
|
1746
|
-
}
|
|
1747
|
-
for (const hashDir of hashDirs) {
|
|
1748
|
-
const chatsDir = path.join(tmpDir, hashDir, 'chats');
|
|
1749
|
-
if (!fs.existsSync(chatsDir))
|
|
1750
|
-
continue;
|
|
1751
|
-
const key = safeRealpathSync(chatsDir) || chatsDir;
|
|
1752
|
-
if (seenLeaf.has(key))
|
|
1753
|
-
continue;
|
|
1754
|
-
seenLeaf.add(key);
|
|
1755
|
-
leafDirs.push({ dirPath: chatsDir, isLiveRoot });
|
|
1756
|
-
}
|
|
1757
|
-
});
|
|
1758
|
-
const { changed } = collectChangedFilesInLeafDirs(leafDirs, '.json');
|
|
1759
|
-
const changedByPath = new Map(changed.map(c => [c.filePath, c.scan]));
|
|
1760
|
-
if (changedByPath.size === 0)
|
|
1761
|
-
return;
|
|
1762
|
-
onProgress?.({ agent: 'gemini', parsed: 0, total: changedByPath.size });
|
|
1763
|
-
const entries = [];
|
|
1764
|
-
const touched = [];
|
|
1765
|
-
const seen = new Set();
|
|
1766
|
-
let parsed = 0;
|
|
1767
|
-
for (const { filePath, scan } of changed) {
|
|
1768
|
-
// The hashDir is the directory two levels up: <hashDir>/chats/<file>.json.
|
|
1769
|
-
const hashDir = path.basename(path.dirname(path.dirname(filePath)));
|
|
1770
|
-
try {
|
|
1771
|
-
const result = readGeminiMeta(filePath, hashDir, projectMap, currentVersion);
|
|
1772
|
-
if (result && !seen.has(result.meta.id)) {
|
|
1773
|
-
seen.add(result.meta.id);
|
|
1774
|
-
entries.push({ meta: result.meta, content: result.content, assistantContent: result.assistantContent, scan });
|
|
1775
|
-
}
|
|
1776
|
-
else {
|
|
1777
|
-
// Gemini file without a sessionId — record scan so we don't re-parse it next run.
|
|
1778
|
-
touched.push({ filePath, scan });
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
catch {
|
|
1782
|
-
touched.push({ filePath, scan });
|
|
1783
|
-
}
|
|
1784
|
-
parsed++;
|
|
1785
|
-
onProgress?.({ agent: 'gemini', parsed, total: changedByPath.size });
|
|
1786
|
-
}
|
|
1787
|
-
upsertSessionsBatch(entries);
|
|
1788
|
-
recordScans(touched);
|
|
1789
|
-
}
|
|
1790
|
-
/** Parse a single Gemini JSON session file to extract session metadata. */
|
|
1791
|
-
function readGeminiMeta(filePath, hashDir, projectMap, currentVersion) {
|
|
1792
|
-
let session;
|
|
1793
|
-
try {
|
|
1794
|
-
session = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1795
|
-
}
|
|
1796
|
-
catch {
|
|
1797
|
-
return null;
|
|
1798
|
-
}
|
|
1799
|
-
const sessionId = typeof session.sessionId === 'string' ? session.sessionId : '';
|
|
1800
|
-
const startTime = typeof session.startTime === 'string' ? session.startTime : '';
|
|
1801
|
-
const projectHash = typeof session.projectHash === 'string' ? session.projectHash : '';
|
|
1802
|
-
const embeddedVersion = typeof session.version === 'string'
|
|
1803
|
-
? session.version
|
|
1804
|
-
: typeof session.cliVersion === 'string'
|
|
1805
|
-
? session.cliVersion
|
|
1806
|
-
: undefined;
|
|
1807
|
-
if (!sessionId)
|
|
1808
|
-
return null;
|
|
1809
|
-
const projectInfo = projectMap.get(projectHash || hashDir);
|
|
1810
|
-
const project = projectInfo?.name || hashDir.slice(0, 12);
|
|
1811
|
-
const cwd = projectInfo?.path ? normalizeCwd(projectInfo.path) : undefined;
|
|
1812
|
-
const stat = safeStatSync(filePath);
|
|
1813
|
-
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
1814
|
-
const sessionModel = typeof session.model === 'string' ? session.model : undefined;
|
|
1815
|
-
let topic;
|
|
1816
|
-
let messageCount = 0;
|
|
1817
|
-
let tokenCount = 0;
|
|
1818
|
-
let outputTokens = 0;
|
|
1819
|
-
let inputTokens = 0;
|
|
1820
|
-
let cacheReadTokens = 0;
|
|
1821
|
-
let sawTokenCount = false;
|
|
1822
|
-
let costUsd = 0;
|
|
1823
|
-
let costUsdNoCache = 0;
|
|
1824
|
-
let sawCost = false;
|
|
1825
|
-
let firstTsMs;
|
|
1826
|
-
let lastTsMs;
|
|
1827
|
-
const userTexts = [];
|
|
1828
|
-
const assistantTexts = [];
|
|
1829
|
-
for (const message of messages) {
|
|
1830
|
-
if (message.type === 'user') {
|
|
1831
|
-
const text = extractGeminiMessageText(message.content);
|
|
1832
|
-
if (text) {
|
|
1833
|
-
messageCount++;
|
|
1834
|
-
userTexts.push(text);
|
|
1835
|
-
if (!topic)
|
|
1836
|
-
topic = extractSessionTopic(text);
|
|
1837
|
-
}
|
|
1838
|
-
}
|
|
1839
|
-
else if (message.type === 'gemini') {
|
|
1840
|
-
const text = extractGeminiMessageText(message.content);
|
|
1841
|
-
if (text) {
|
|
1842
|
-
messageCount++;
|
|
1843
|
-
assistantTexts.push(text);
|
|
1844
|
-
}
|
|
1845
|
-
}
|
|
1846
|
-
// Duration: messages carry a `timestamp` on most Gemini CLI versions.
|
|
1847
|
-
const tsRaw = message.timestamp ?? message.time;
|
|
1848
|
-
if (typeof tsRaw === 'string' || typeof tsRaw === 'number') {
|
|
1849
|
-
const ms = new Date(tsRaw).getTime();
|
|
1850
|
-
if (!Number.isNaN(ms)) {
|
|
1851
|
-
if (firstTsMs === undefined || ms < firstTsMs)
|
|
1852
|
-
firstTsMs = ms;
|
|
1853
|
-
if (lastTsMs === undefined || ms > lastTsMs)
|
|
1854
|
-
lastTsMs = ms;
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
const total = getGeminiTokenCount(message.tokens);
|
|
1858
|
-
if (total !== null) {
|
|
1859
|
-
tokenCount += total;
|
|
1860
|
-
sawTokenCount = true;
|
|
1861
|
-
}
|
|
1862
|
-
// Output tokens: sum directional generation fields per message (output +
|
|
1863
|
-
// thoughts + tool), mirroring the cost path — never `tokens.total`, which
|
|
1864
|
-
// may be cumulative and would double-count when summed.
|
|
1865
|
-
const gtk = message.tokens;
|
|
1866
|
-
if (gtk && typeof gtk === 'object') {
|
|
1867
|
-
outputTokens +=
|
|
1868
|
-
(typeof gtk.output === 'number' ? gtk.output : 0) +
|
|
1869
|
-
(typeof gtk.thoughts === 'number' ? gtk.thoughts : 0) +
|
|
1870
|
-
(typeof gtk.tool === 'number' ? gtk.tool : 0);
|
|
1871
|
-
// Burn split (RUSH-2287): Gemini has uncached input + cache-read, no cache-write.
|
|
1872
|
-
if (typeof gtk.input === 'number')
|
|
1873
|
-
inputTokens += gtk.input;
|
|
1874
|
-
if (typeof gtk.cached === 'number')
|
|
1875
|
-
cacheReadTokens += gtk.cached;
|
|
1876
|
-
}
|
|
1877
|
-
// Per-message cost: directional tokens × this message's model price.
|
|
1878
|
-
const msgModel = (typeof message.model === 'string' ? message.model : undefined) || sessionModel;
|
|
1879
|
-
const tk = message.tokens;
|
|
1880
|
-
if (msgModel && tk && typeof tk === 'object') {
|
|
1881
|
-
const usage = {
|
|
1882
|
-
model: msgModel,
|
|
1883
|
-
inputTokens: typeof tk.input === 'number' ? tk.input : undefined,
|
|
1884
|
-
outputTokens: (typeof tk.output === 'number' ? tk.output : 0) +
|
|
1885
|
-
(typeof tk.thoughts === 'number' ? tk.thoughts : 0) +
|
|
1886
|
-
(typeof tk.tool === 'number' ? tk.tool : 0),
|
|
1887
|
-
cacheReadTokens: typeof tk.cached === 'number' ? tk.cached : undefined,
|
|
1888
|
-
};
|
|
1889
|
-
const c = costOfUsage(usage);
|
|
1890
|
-
if (c > 0) {
|
|
1891
|
-
costUsd += c;
|
|
1892
|
-
costUsdNoCache += costOfUsageNoCache(usage);
|
|
1893
|
-
sawCost = true;
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
|
-
}
|
|
1897
|
-
const durationMs = firstTsMs !== undefined && lastTsMs !== undefined && lastTsMs > firstTsMs
|
|
1898
|
-
? lastTsMs - firstTsMs
|
|
1899
|
-
: undefined;
|
|
1900
|
-
const meta = {
|
|
1901
|
-
id: sessionId,
|
|
1902
|
-
shortId: deriveShortId(sessionId),
|
|
1903
|
-
agent: 'gemini',
|
|
1904
|
-
timestamp: startTime || (stat ? stat.mtime.toISOString() : new Date().toISOString()),
|
|
1905
|
-
lastActivity: lastTsMs !== undefined ? new Date(lastTsMs).toISOString() : undefined,
|
|
1906
|
-
project,
|
|
1907
|
-
cwd,
|
|
1908
|
-
filePath,
|
|
1909
|
-
version: resolveSessionVersion('gemini', filePath, embeddedVersion, currentVersion),
|
|
1910
|
-
model: sessionModel,
|
|
1911
|
-
topic,
|
|
1912
|
-
firstUserMessage: userTexts.map(cleanFirstUserMessage).find((text) => !!text),
|
|
1913
|
-
messageCount,
|
|
1914
|
-
tokenCount: sawTokenCount ? tokenCount : undefined,
|
|
1915
|
-
outputTokens: sawTokenCount ? outputTokens : undefined,
|
|
1916
|
-
inputTokens: sawTokenCount ? inputTokens : undefined,
|
|
1917
|
-
cacheReadTokens: sawTokenCount ? cacheReadTokens : undefined,
|
|
1918
|
-
costUsd: sawCost ? costUsd : undefined,
|
|
1919
|
-
costUsdNoCache: sawCost ? costUsdNoCache : undefined,
|
|
1920
|
-
durationMs,
|
|
1921
|
-
};
|
|
1922
|
-
return { meta, content: userTexts.join('\n'), assistantContent: assistantTexts.join('\n') };
|
|
1923
|
-
}
|
|
1924
|
-
/** Build a hash-to-project mapping from Gemini's projects.json and history directories. */
|
|
1925
|
-
function buildGeminiProjectMap() {
|
|
1926
|
-
const map = new Map();
|
|
1927
|
-
const projectsJsonPath = path.join(HOME, '.gemini', 'projects.json');
|
|
1928
|
-
if (fs.existsSync(projectsJsonPath)) {
|
|
1929
|
-
try {
|
|
1930
|
-
const data = JSON.parse(fs.readFileSync(projectsJsonPath, 'utf-8'));
|
|
1931
|
-
const projects = data.projects;
|
|
1932
|
-
if (typeof projects === 'object' && projects !== null) {
|
|
1933
|
-
if (Array.isArray(projects)) {
|
|
1934
|
-
for (const p of projects) {
|
|
1935
|
-
if (typeof p === 'string') {
|
|
1936
|
-
const hash = sha256(p);
|
|
1937
|
-
map.set(hash, { name: path.basename(p), path: p });
|
|
1938
|
-
map.set(p, { name: path.basename(p), path: p });
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
}
|
|
1942
|
-
else {
|
|
1943
|
-
for (const [p, name] of Object.entries(projects)) {
|
|
1944
|
-
const hash = sha256(p);
|
|
1945
|
-
map.set(hash, { name: String(name), path: p });
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
catch { /* projects.json missing or malformed */ }
|
|
1951
|
-
}
|
|
1952
|
-
const historyDir = path.join(HOME, '.gemini', 'history');
|
|
1953
|
-
if (fs.existsSync(historyDir)) {
|
|
1954
|
-
try {
|
|
1955
|
-
for (const name of fs.readdirSync(historyDir)) {
|
|
1956
|
-
const rootFile = path.join(historyDir, name, '.project_root');
|
|
1957
|
-
if (fs.existsSync(rootFile)) {
|
|
1958
|
-
try {
|
|
1959
|
-
const projectPath = fs.readFileSync(rootFile, 'utf-8').trim();
|
|
1960
|
-
if (projectPath) {
|
|
1961
|
-
const hash = sha256(projectPath);
|
|
1962
|
-
map.set(hash, { name, path: projectPath });
|
|
1963
|
-
}
|
|
1964
|
-
}
|
|
1965
|
-
catch { /* history entry unreadable */ }
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
}
|
|
1969
|
-
catch { /* history entry unreadable */ }
|
|
1970
|
-
}
|
|
1971
|
-
return map;
|
|
1972
|
-
}
|
|
1973
|
-
// ---------------------------------------------------------------------------
|
|
1974
1724
|
// Antigravity
|
|
1975
1725
|
//
|
|
1976
1726
|
// Antigravity stores one SQLite DB per conversation at
|
|
@@ -4458,38 +4208,6 @@ function getCodexTokenCount(totalTokenUsage) {
|
|
|
4458
4208
|
totalTokenUsage.reasoning_output_tokens,
|
|
4459
4209
|
]);
|
|
4460
4210
|
}
|
|
4461
|
-
/** Extract text from a Gemini message content field (string or array of parts). */
|
|
4462
|
-
function extractGeminiMessageText(content) {
|
|
4463
|
-
if (typeof content === 'string')
|
|
4464
|
-
return content.trim();
|
|
4465
|
-
if (Array.isArray(content)) {
|
|
4466
|
-
return content
|
|
4467
|
-
.map((part) => {
|
|
4468
|
-
if (typeof part === 'string')
|
|
4469
|
-
return part;
|
|
4470
|
-
if (typeof part?.text === 'string')
|
|
4471
|
-
return part.text;
|
|
4472
|
-
return '';
|
|
4473
|
-
})
|
|
4474
|
-
.join('\n')
|
|
4475
|
-
.trim();
|
|
4476
|
-
}
|
|
4477
|
-
return '';
|
|
4478
|
-
}
|
|
4479
|
-
/** Extract the total token count from a Gemini message's tokens object. */
|
|
4480
|
-
function getGeminiTokenCount(tokens) {
|
|
4481
|
-
if (!tokens || typeof tokens !== 'object')
|
|
4482
|
-
return null;
|
|
4483
|
-
if (typeof tokens.total === 'number')
|
|
4484
|
-
return tokens.total;
|
|
4485
|
-
return sumKnownNumbers([
|
|
4486
|
-
tokens.input,
|
|
4487
|
-
tokens.output,
|
|
4488
|
-
tokens.cached,
|
|
4489
|
-
tokens.thoughts,
|
|
4490
|
-
tokens.tool,
|
|
4491
|
-
]);
|
|
4492
|
-
}
|
|
4493
4211
|
/** Sum all numeric values in an array, returning null if none are valid numbers. */
|
|
4494
4212
|
function sumKnownNumbers(values) {
|
|
4495
4213
|
let total = 0;
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* used to render as a bare `[host/<peer>]` row with no topic, and its preview
|
|
6
6
|
* pane fetched the peer's digest LIVE over SSH per row — slow, and blank when
|
|
7
7
|
* the peer is asleep. This module mirrors each box's own session digests into
|
|
8
|
-
* its
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* its own `~/.agents/devices/<device>/daemon-state.json`, which the usage-sync
|
|
9
|
+
* SSH exchange (`accounting/usage-sync.ts`, PHNX-4116) carries to every peer:
|
|
10
|
+
* a headed box's tick sends its envelope and stores each peer's reply, so both
|
|
11
|
+
* directions land with no operator step and nothing rides the user repo. The consuming device folds peer
|
|
11
12
|
* digests into its local `sessions` index as mirror rows, so the picker/list/
|
|
12
13
|
* focus read a LOCAL row instead of dialing the peer. No transcript is shipped —
|
|
13
14
|
* only topic/label, the peer's daemon-generated title (PHNX-3797), a
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* used to render as a bare `[host/<peer>]` row with no topic, and its preview
|
|
6
6
|
* pane fetched the peer's digest LIVE over SSH per row — slow, and blank when
|
|
7
7
|
* the peer is asleep. This module mirrors each box's own session digests into
|
|
8
|
-
* its
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* its own `~/.agents/devices/<device>/daemon-state.json`, which the usage-sync
|
|
9
|
+
* SSH exchange (`accounting/usage-sync.ts`, PHNX-4116) carries to every peer:
|
|
10
|
+
* a headed box's tick sends its envelope and stores each peer's reply, so both
|
|
11
|
+
* directions land with no operator step and nothing rides the user repo. The consuming device folds peer
|
|
11
12
|
* digests into its local `sessions` index as mirror rows, so the picker/list/
|
|
12
13
|
* focus read a LOCAL row instead of dialing the peer. No transcript is shipped —
|
|
13
14
|
* only topic/label, the peer's daemon-generated title (PHNX-3797), a
|
|
@@ -100,8 +100,6 @@ export declare function parseCodexItemsContent(content: string): SessionEvent[];
|
|
|
100
100
|
* chunk without re-reading the whole file.
|
|
101
101
|
*/
|
|
102
102
|
export declare function parseCodexContent(content: string): SessionEvent[];
|
|
103
|
-
/** Parse a Gemini JSON session file into normalized events. */
|
|
104
|
-
export declare function parseGemini(filePath: string): SessionEvent[];
|
|
105
103
|
/**
|
|
106
104
|
* Parse an Antigravity conversation SQLite DB into normalized tool_use events.
|
|
107
105
|
* Deduped by the tool-call id so each tool appears once (Antigravity writes a
|