@phnx-labs/agents-cli 1.22.116 → 1.22.118
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 +247 -0
- package/dist/bootstrap.js +4 -11
- 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.d.ts +18 -0
- package/dist/commands/exec.js +104 -12
- 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-term.d.ts +4 -3
- package/dist/commands/setup-term.js +5 -4
- package/dist/commands/setup.js +11 -11
- 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/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 +6 -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-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 +0 -11
- 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/harness-auth-capabilities.js +0 -1
- 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/hosts/remote-cmd.js +1 -0
- 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.js +9 -7
- package/dist/lib/installations/update.js +21 -15
- package/dist/lib/installations/versions.js +12 -1
- 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/placement.d.ts +2 -0
- package/dist/lib/placement.js +5 -0
- package/dist/lib/profiles.js +0 -2
- 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/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/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 +8 -12
- 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 +10 -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
|
@@ -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
|
|
@@ -142,7 +142,6 @@ function truncateNormalizedToolOutput(output, maxChars) {
|
|
|
142
142
|
const TRANSCRIPT_PARSERS = {
|
|
143
143
|
claude: (filePath, opts) => parseClaude(filePath, opts),
|
|
144
144
|
codex: (filePath) => parseCodex(filePath),
|
|
145
|
-
gemini: (filePath) => parseGemini(filePath),
|
|
146
145
|
antigravity: (filePath) => parseAntigravity(filePath),
|
|
147
146
|
// Cloud-captured opencode sessions are normalized JSONL (produced by the
|
|
148
147
|
// factory at capture time), NOT the local `opencode.db#<session>` SQLite
|
|
@@ -183,14 +182,10 @@ export function detectAgent(filePath) {
|
|
|
183
182
|
return 'claude';
|
|
184
183
|
if (filePath.includes('/.codex/') || filePath.includes('\\.codex\\'))
|
|
185
184
|
return 'codex';
|
|
186
|
-
// Antigravity lives under ~/.gemini/antigravity-cli/conversations/<uuid>.db
|
|
187
|
-
// it must be matched BEFORE the generic /.gemini/ check below or it would be
|
|
188
|
-
// misdetected as Gemini.
|
|
185
|
+
// Antigravity lives under ~/.gemini/antigravity-cli/conversations/<uuid>.db.
|
|
189
186
|
if ((filePath.includes('/antigravity-cli/conversations/') || filePath.includes('\\antigravity-cli\\conversations\\'))
|
|
190
187
|
&& filePath.endsWith('.db'))
|
|
191
188
|
return 'antigravity';
|
|
192
|
-
if (filePath.includes('/.gemini/') || filePath.includes('\\.gemini\\'))
|
|
193
|
-
return 'gemini';
|
|
194
189
|
if (filePath.includes('/.grok/') || filePath.includes('\\.grok\\'))
|
|
195
190
|
return 'grok';
|
|
196
191
|
if (filePath.includes('/.rush/') || filePath.includes('\\.rush\\'))
|
|
@@ -216,9 +211,6 @@ export function detectAgent(filePath) {
|
|
|
216
211
|
return cloudMatch[1];
|
|
217
212
|
if (filePath.includes('opencode.db'))
|
|
218
213
|
return 'opencode';
|
|
219
|
-
// Try file extension + content heuristic
|
|
220
|
-
if (filePath.endsWith('.json'))
|
|
221
|
-
return 'gemini';
|
|
222
214
|
return null;
|
|
223
215
|
}
|
|
224
216
|
/**
|
|
@@ -1051,135 +1043,6 @@ export function parseCodexContent(content) {
|
|
|
1051
1043
|
}
|
|
1052
1044
|
return events;
|
|
1053
1045
|
}
|
|
1054
|
-
// ---------------------------------------------------------------------------
|
|
1055
|
-
// Gemini parser
|
|
1056
|
-
// ---------------------------------------------------------------------------
|
|
1057
|
-
/** Parse a Gemini JSON session file into normalized events. */
|
|
1058
|
-
export function parseGemini(filePath) {
|
|
1059
|
-
const content = safeReadSessionFile(filePath);
|
|
1060
|
-
let session;
|
|
1061
|
-
try {
|
|
1062
|
-
session = JSON.parse(content);
|
|
1063
|
-
}
|
|
1064
|
-
catch {
|
|
1065
|
-
/* Gemini session file is not valid JSON */
|
|
1066
|
-
throw new Error(`Failed to parse Gemini session: ${filePath}`);
|
|
1067
|
-
}
|
|
1068
|
-
const messages = session.messages || [];
|
|
1069
|
-
const events = [];
|
|
1070
|
-
events.push({
|
|
1071
|
-
type: 'init',
|
|
1072
|
-
agent: 'gemini',
|
|
1073
|
-
timestamp: session.startTime || new Date().toISOString(),
|
|
1074
|
-
content: `Gemini session ${session.sessionId || ''}`.trim(),
|
|
1075
|
-
});
|
|
1076
|
-
for (const msg of messages) {
|
|
1077
|
-
const timestamp = msg.timestamp || session.startTime || new Date().toISOString();
|
|
1078
|
-
if (msg.type === 'user') {
|
|
1079
|
-
const text = extractGeminiContent(msg.content);
|
|
1080
|
-
if (text) {
|
|
1081
|
-
events.push({
|
|
1082
|
-
type: 'message',
|
|
1083
|
-
agent: 'gemini',
|
|
1084
|
-
timestamp,
|
|
1085
|
-
role: 'user',
|
|
1086
|
-
content: text,
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
else if (msg.type === 'gemini') {
|
|
1091
|
-
// Reasoning thoughts
|
|
1092
|
-
if (Array.isArray(msg.thoughts)) {
|
|
1093
|
-
for (const thought of msg.thoughts) {
|
|
1094
|
-
const text = thought.description || thought.subject || '';
|
|
1095
|
-
if (text.trim()) {
|
|
1096
|
-
const subject = thought.subject ? `**${thought.subject}**: ` : '';
|
|
1097
|
-
events.push({
|
|
1098
|
-
type: 'thinking',
|
|
1099
|
-
agent: 'gemini',
|
|
1100
|
-
timestamp: thought.timestamp || timestamp,
|
|
1101
|
-
content: `${subject}${thought.description || ''}`.trim(),
|
|
1102
|
-
});
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
// Assistant text
|
|
1107
|
-
const text = extractGeminiContent(msg.content);
|
|
1108
|
-
if (text) {
|
|
1109
|
-
events.push({
|
|
1110
|
-
type: 'message',
|
|
1111
|
-
agent: 'gemini',
|
|
1112
|
-
timestamp,
|
|
1113
|
-
role: 'assistant',
|
|
1114
|
-
content: text,
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
// Tool calls (Gemini inlines call + result on the same message)
|
|
1118
|
-
if (Array.isArray(msg.toolCalls)) {
|
|
1119
|
-
for (const tc of msg.toolCalls) {
|
|
1120
|
-
const toolName = tc.name || 'unknown';
|
|
1121
|
-
const args = tc.args || {};
|
|
1122
|
-
events.push({
|
|
1123
|
-
type: 'tool_use',
|
|
1124
|
-
agent: 'gemini',
|
|
1125
|
-
timestamp: tc.timestamp || timestamp,
|
|
1126
|
-
tool: toolName,
|
|
1127
|
-
callId: typeof tc.id === 'string' ? tc.id : undefined,
|
|
1128
|
-
args,
|
|
1129
|
-
command: ['run_shell_command', 'shell', 'bash'].includes(toolName) ? args.command : undefined,
|
|
1130
|
-
path: args.file_path || args.path || undefined,
|
|
1131
|
-
});
|
|
1132
|
-
// Inline result
|
|
1133
|
-
if (tc.result || tc.status) {
|
|
1134
|
-
let output = '';
|
|
1135
|
-
if (Array.isArray(tc.result)) {
|
|
1136
|
-
for (const r of tc.result) {
|
|
1137
|
-
const resp = r?.functionResponse?.response;
|
|
1138
|
-
if (resp?.output) {
|
|
1139
|
-
output += String(resp.output);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else if (typeof tc.result === 'string') {
|
|
1144
|
-
output = tc.result;
|
|
1145
|
-
}
|
|
1146
|
-
events.push({
|
|
1147
|
-
type: 'tool_result',
|
|
1148
|
-
agent: 'gemini',
|
|
1149
|
-
timestamp: tc.timestamp || timestamp,
|
|
1150
|
-
tool: toolName,
|
|
1151
|
-
callId: typeof tc.id === 'string' ? tc.id : undefined,
|
|
1152
|
-
success: tc.status === 'success',
|
|
1153
|
-
output,
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
return events;
|
|
1161
|
-
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Extract text content from Gemini's content field,
|
|
1164
|
-
* which can be a string or an array of {text: string} parts.
|
|
1165
|
-
*/
|
|
1166
|
-
function extractGeminiContent(content) {
|
|
1167
|
-
if (typeof content === 'string')
|
|
1168
|
-
return content.trim();
|
|
1169
|
-
if (Array.isArray(content)) {
|
|
1170
|
-
return content
|
|
1171
|
-
.map((part) => {
|
|
1172
|
-
if (typeof part === 'string')
|
|
1173
|
-
return part;
|
|
1174
|
-
if (typeof part?.text === 'string')
|
|
1175
|
-
return part.text;
|
|
1176
|
-
return '';
|
|
1177
|
-
})
|
|
1178
|
-
.join('\n')
|
|
1179
|
-
.trim();
|
|
1180
|
-
}
|
|
1181
|
-
return '';
|
|
1182
|
-
}
|
|
1183
1046
|
/** Read a base-128 varint from `b` at offset `i`; returns [value, nextOffset]. */
|
|
1184
1047
|
function readVarint(b, i) {
|
|
1185
1048
|
let shift = 0;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* straight off `agents sessions --active --json` instead (issue #741).
|
|
9
9
|
*/
|
|
10
10
|
/** Agents whose transcript formats report per-turn output-token usage. */
|
|
11
|
-
type ThroughputAgent = 'claude' | 'codex'
|
|
11
|
+
type ThroughputAgent = 'claude' | 'codex';
|
|
12
12
|
/**
|
|
13
13
|
* Output-token throughput (tokens/sec) over the last `windowSec` seconds.
|
|
14
14
|
*
|
|
@@ -22,8 +22,6 @@ type ThroughputAgent = 'claude' | 'codex' | 'gemini';
|
|
|
22
22
|
* - Codex: JSONL. Each token_count event is `{type: 'event_msg', timestamp,
|
|
23
23
|
* payload: {type: 'token_count', info: {last_token_usage: {output_tokens,
|
|
24
24
|
* reasoning_output_tokens}}}}`. `last_token_usage` is per-turn (not cumulative).
|
|
25
|
-
* - Gemini: single JSON object. `{messages: [{type: 'gemini', timestamp,
|
|
26
|
-
* tokens: {output, thoughts}}]}`. Caller must pass the whole file.
|
|
27
25
|
*/
|
|
28
26
|
export declare function computeTokPerSec(sessionContent: string, agent: ThroughputAgent, windowSec?: number, now?: number): number;
|
|
29
27
|
export {};
|
|
@@ -22,30 +22,10 @@ const DEFAULT_THROUGHPUT_WINDOW_SEC = 60;
|
|
|
22
22
|
* - Codex: JSONL. Each token_count event is `{type: 'event_msg', timestamp,
|
|
23
23
|
* payload: {type: 'token_count', info: {last_token_usage: {output_tokens,
|
|
24
24
|
* reasoning_output_tokens}}}}`. `last_token_usage` is per-turn (not cumulative).
|
|
25
|
-
* - Gemini: single JSON object. `{messages: [{type: 'gemini', timestamp,
|
|
26
|
-
* tokens: {output, thoughts}}]}`. Caller must pass the whole file.
|
|
27
25
|
*/
|
|
28
26
|
export function computeTokPerSec(sessionContent, agent, windowSec = DEFAULT_THROUGHPUT_WINDOW_SEC, now = Date.now()) {
|
|
29
27
|
const cutoff = now - windowSec * 1000;
|
|
30
28
|
let total = 0;
|
|
31
|
-
if (agent === 'gemini') {
|
|
32
|
-
try {
|
|
33
|
-
const d = JSON.parse(sessionContent);
|
|
34
|
-
const messages = Array.isArray(d?.messages) ? d.messages : [];
|
|
35
|
-
for (const m of messages) {
|
|
36
|
-
if (m?.type !== 'gemini')
|
|
37
|
-
continue;
|
|
38
|
-
const ts = typeof m.timestamp === 'string' ? Date.parse(m.timestamp) : 0;
|
|
39
|
-
if (!ts || ts < cutoff)
|
|
40
|
-
continue;
|
|
41
|
-
const out = typeof m?.tokens?.output === 'number' ? m.tokens.output : 0;
|
|
42
|
-
const thoughts = typeof m?.tokens?.thoughts === 'number' ? m.tokens.thoughts : 0;
|
|
43
|
-
total += out + thoughts;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch { /* malformed gemini file → 0 */ }
|
|
47
|
-
return total / windowSec;
|
|
48
|
-
}
|
|
49
29
|
const lines = sessionContent.split(/\r?\n/);
|
|
50
30
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
51
31
|
const line = lines[i];
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Session data model.
|
|
3
3
|
*
|
|
4
4
|
* Normalized types that unify the different session storage formats used by
|
|
5
|
-
* Claude (JSONL), Codex (JSONL events),
|
|
5
|
+
* Claude (JSONL), Codex (JSONL events), and OpenCode.
|
|
6
6
|
* Everything in the session pipeline -- discovery, parsing, rendering --
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** Agents that store session data on disk and can be discovered by `agents sessions`. */
|
|
10
|
-
export type SessionAgentId = 'claude' | 'codex' | '
|
|
10
|
+
export type SessionAgentId = 'claude' | 'codex' | 'antigravity' | 'opencode' | 'openclaw' | 'rush' | 'hermes' | 'grok' | 'kimi' | 'droid' | 'cursor' | 'muse';
|
|
11
11
|
/** Effective permissions mode used to launch a managed agent session. */
|
|
12
12
|
export type SessionRunMode = 'plan' | 'edit' | 'auto' | 'skip';
|
|
13
13
|
/** All agents with session discovery support, in display order. */
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
* Session data model.
|
|
3
3
|
*
|
|
4
4
|
* Normalized types that unify the different session storage formats used by
|
|
5
|
-
* Claude (JSONL), Codex (JSONL events),
|
|
5
|
+
* Claude (JSONL), Codex (JSONL events), and OpenCode.
|
|
6
6
|
* Everything in the session pipeline -- discovery, parsing, rendering --
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** All agents with session discovery support, in display order. */
|
|
10
|
-
export const SESSION_AGENTS = ['claude', 'codex', '
|
|
10
|
+
export const SESSION_AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'openclaw', 'rush', 'hermes', 'grok', 'kimi', 'droid', 'cursor', 'muse'];
|
|
11
11
|
/**
|
|
12
12
|
* True when `agent` stores session data `agents sessions` can discover (a member
|
|
13
13
|
* of {@link SESSION_AGENTS}). The single predicate every session-index writer
|