@phnx-labs/agents-cli 1.22.117 → 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.
Files changed (248) hide show
  1. package/CHANGELOG.md +235 -0
  2. package/dist/bootstrap.js +4 -11
  3. package/dist/commands/browser.d.ts +52 -70
  4. package/dist/commands/browser.js +192 -3562
  5. package/dist/commands/daemon.d.ts +39 -2
  6. package/dist/commands/daemon.js +72 -73
  7. package/dist/commands/events.js +1 -1
  8. package/dist/commands/exec.js +53 -1
  9. package/dist/commands/repo.js +13 -49
  10. package/dist/commands/route.js +5 -5
  11. package/dist/commands/routines.js +39 -12
  12. package/dist/commands/sessions-render.d.ts +1 -1
  13. package/dist/commands/sessions-render.js +0 -1
  14. package/dist/commands/sessions.test-fixture.d.ts +0 -1
  15. package/dist/commands/sessions.test-fixture.js +0 -37
  16. package/dist/commands/setup-browser.d.ts +17 -12
  17. package/dist/commands/setup-browser.js +52 -87
  18. package/dist/commands/setup-preferences.d.ts +0 -17
  19. package/dist/commands/setup-preferences.js +18 -49
  20. package/dist/commands/setup.js +9 -9
  21. package/dist/commands/ssh.js +10 -1
  22. package/dist/commands/sync.js +6 -44
  23. package/dist/commands/teams.js +0 -1
  24. package/dist/commands/update.d.ts +19 -0
  25. package/dist/commands/update.js +34 -6
  26. package/dist/commands/view.d.ts +12 -1
  27. package/dist/commands/view.js +47 -2
  28. package/dist/index.js +107 -26
  29. package/dist/lib/account-capabilities.js +0 -2
  30. package/dist/lib/account-catalog.d.ts +38 -0
  31. package/dist/lib/account-catalog.js +110 -26
  32. package/dist/lib/account-provider-registry.js +1 -1
  33. package/dist/lib/account-registry.d.ts +1 -1
  34. package/dist/lib/account-registry.js +36 -2
  35. package/dist/lib/accounting/rotate.js +22 -8
  36. package/dist/lib/accounting/usage-ingest.d.ts +6 -1
  37. package/dist/lib/accounting/usage-ingest.js +115 -45
  38. package/dist/lib/accounting/usage-sync.d.ts +134 -31
  39. package/dist/lib/accounting/usage-sync.js +227 -66
  40. package/dist/lib/accounting/usage.d.ts +16 -3
  41. package/dist/lib/accounting/usage.js +26 -8
  42. package/dist/lib/accounts/slots.d.ts +8 -0
  43. package/dist/lib/accounts/slots.js +17 -0
  44. package/dist/lib/add-dir.js +0 -1
  45. package/dist/lib/agent-cli-commands.js +0 -1
  46. package/dist/lib/agent-spec/agents.d.ts +6 -7
  47. package/dist/lib/agent-spec/agents.js +6 -60
  48. package/dist/lib/auth-health.d.ts +69 -1
  49. package/dist/lib/auth-health.js +133 -4
  50. package/dist/lib/browser/context.d.ts +82 -0
  51. package/dist/lib/browser/context.js +88 -0
  52. package/dist/lib/browser/paths.d.ts +25 -0
  53. package/dist/lib/browser/paths.js +51 -0
  54. package/dist/lib/browser/record.d.ts +38 -0
  55. package/dist/lib/browser/record.js +57 -0
  56. package/dist/lib/browser/sessions-list.js +1 -2
  57. package/dist/lib/browser-client.d.ts +158 -0
  58. package/dist/lib/browser-client.js +234 -0
  59. package/dist/lib/channels/owner-sink.d.ts +1 -18
  60. package/dist/lib/channels/owner-sink.js +29 -82
  61. package/dist/lib/channels/providers/rush.d.ts +7 -7
  62. package/dist/lib/channels/providers/rush.js +127 -57
  63. package/dist/lib/claude-statusline.d.ts +1 -1
  64. package/dist/lib/claude-statusline.js +15 -3
  65. package/dist/lib/commands.js +1 -1
  66. package/dist/lib/crabbox/lease.js +1 -1
  67. package/dist/lib/crabbox/runtimes.js +1 -3
  68. package/dist/lib/daemon/account-state-daemon-service.js +1 -0
  69. package/dist/lib/daemon/auth-sync-service.d.ts +21 -18
  70. package/dist/lib/daemon/auth-sync-service.js +37 -43
  71. package/dist/lib/daemon/daemon.d.ts +25 -14
  72. package/dist/lib/daemon/daemon.js +92 -154
  73. package/dist/lib/daemon/runner.d.ts +2 -2
  74. package/dist/lib/daemon/runner.js +32 -9
  75. package/dist/lib/daemon/self-update-service.d.ts +35 -34
  76. package/dist/lib/daemon/self-update-service.js +40 -36
  77. package/dist/lib/daemon/service.d.ts +12 -8
  78. package/dist/lib/daemon/supervisor.d.ts +65 -54
  79. package/dist/lib/daemon/supervisor.js +136 -171
  80. package/dist/lib/daemon/testdata/process-view-start.js +5 -2
  81. package/dist/lib/daemon/usage-sync-service.d.ts +21 -30
  82. package/dist/lib/daemon/usage-sync-service.js +46 -78
  83. package/dist/lib/daemon-health.d.ts +22 -5
  84. package/dist/lib/daemon-health.js +47 -3
  85. package/dist/lib/daemon-services.d.ts +1 -1
  86. package/dist/lib/daemon-services.js +0 -10
  87. package/dist/lib/daemon-ticks.js +4 -2
  88. package/dist/lib/devices/doctor-findings.js +8 -11
  89. package/dist/lib/devices/harness-inventory.js +14 -5
  90. package/dist/lib/exec-account-home.js +5 -0
  91. package/dist/lib/exec.js +0 -11
  92. package/dist/lib/feed/events.js +0 -1
  93. package/dist/lib/feed/tool-activity.js +5 -2
  94. package/dist/lib/fleet-shared-state.d.ts +45 -0
  95. package/dist/lib/fleet-shared-state.js +78 -9
  96. package/dist/lib/fs-atomic.js +25 -0
  97. package/dist/lib/harness-auth-capabilities.js +0 -1
  98. package/dist/lib/helper-versions.js +1 -1
  99. package/dist/lib/hooks/cache.d.ts +7 -0
  100. package/dist/lib/hooks/cache.js +27 -2
  101. package/dist/lib/hooks/install.js +7 -1
  102. package/dist/lib/hosts/ready.d.ts +38 -8
  103. package/dist/lib/hosts/ready.js +62 -14
  104. package/dist/lib/installations/active-check.d.ts +27 -0
  105. package/dist/lib/installations/active-check.js +61 -0
  106. package/dist/lib/installations/migrate.js +6 -3
  107. package/dist/lib/installations/shims.js +9 -7
  108. package/dist/lib/installations/update.js +21 -15
  109. package/dist/lib/installations/versions.js +12 -1
  110. package/dist/lib/manifest.js +1 -1
  111. package/dist/lib/models.js +0 -2
  112. package/dist/lib/open-url.js +28 -54
  113. package/dist/lib/profiles.js +0 -2
  114. package/dist/lib/refresh.js +1 -1
  115. package/dist/lib/routine-readiness.d.ts +35 -0
  116. package/dist/lib/routine-readiness.js +74 -16
  117. package/dist/lib/rules/compile.js +6 -0
  118. package/dist/lib/sandbox.js +0 -2
  119. package/dist/lib/secrets-policy.d.ts +89 -11
  120. package/dist/lib/secrets-policy.js +175 -28
  121. package/dist/lib/session/db.d.ts +1 -1
  122. package/dist/lib/session/db.js +65 -18
  123. package/dist/lib/session/discover.js +1 -283
  124. package/dist/lib/session/mirror.d.ts +4 -3
  125. package/dist/lib/session/mirror.js +4 -3
  126. package/dist/lib/session/parse.d.ts +0 -2
  127. package/dist/lib/session/parse.js +1 -138
  128. package/dist/lib/session/throughput.d.ts +1 -3
  129. package/dist/lib/session/throughput.js +0 -20
  130. package/dist/lib/session/types.d.ts +2 -2
  131. package/dist/lib/session/types.js +2 -2
  132. package/dist/lib/sessions-client.d.ts +60 -1
  133. package/dist/lib/sessions-client.js +227 -4
  134. package/dist/lib/setup-tool-status.js +5 -10
  135. package/dist/lib/signin-badge.d.ts +3 -2
  136. package/dist/lib/signin-badge.js +6 -3
  137. package/dist/lib/smart-launch.js +4 -1
  138. package/dist/lib/staleness/detectors/commands.d.ts +1 -1
  139. package/dist/lib/staleness/detectors/hooks.d.ts +1 -1
  140. package/dist/lib/staleness/detectors/mcp.d.ts +1 -1
  141. package/dist/lib/staleness/detectors/permissions.d.ts +1 -1
  142. package/dist/lib/staleness/detectors/plugins.d.ts +1 -1
  143. package/dist/lib/staleness/detectors/rules.d.ts +1 -1
  144. package/dist/lib/staleness/detectors/skills.d.ts +1 -1
  145. package/dist/lib/staleness/detectors/subagents.d.ts +1 -1
  146. package/dist/lib/staleness/detectors/workflows.d.ts +1 -1
  147. package/dist/lib/staleness/writers/commands.d.ts +1 -1
  148. package/dist/lib/staleness/writers/hooks.d.ts +1 -1
  149. package/dist/lib/staleness/writers/mcp.d.ts +1 -1
  150. package/dist/lib/staleness/writers/permissions.d.ts +1 -1
  151. package/dist/lib/staleness/writers/plugins.d.ts +1 -1
  152. package/dist/lib/staleness/writers/rules.d.ts +1 -1
  153. package/dist/lib/staleness/writers/skills.d.ts +1 -1
  154. package/dist/lib/staleness/writers/sources.js +5 -0
  155. package/dist/lib/staleness/writers/subagents.d.ts +1 -1
  156. package/dist/lib/staleness/writers/workflows.d.ts +1 -1
  157. package/dist/lib/state.d.ts +6 -4
  158. package/dist/lib/state.js +7 -17
  159. package/dist/lib/teams/parsers.d.ts +1 -1
  160. package/dist/lib/teams/parsers.js +1 -136
  161. package/dist/lib/teams/placement-probe.js +50 -15
  162. package/dist/lib/teams/scheduler.d.ts +7 -0
  163. package/dist/lib/types.d.ts +10 -1
  164. package/dist/lib/types.js +1 -1
  165. package/dist/lib/view-types.d.ts +32 -0
  166. package/dist/lib/watchdog/read.js +0 -1
  167. package/dist/lib/watchdog/rotate.js +1 -2
  168. package/dist/lib/watchdog/runner.js +2 -2
  169. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  170. package/dist/lib/watchdog/watchdogTail.js +0 -31
  171. package/package.json +1 -1
  172. package/scripts/postinstall.js +8 -8
  173. package/dist/browser.d.ts +0 -2
  174. package/dist/browser.js +0 -17
  175. package/dist/commands/browser-picker.d.ts +0 -18
  176. package/dist/commands/browser-picker.js +0 -97
  177. package/dist/lib/browser/arc-discovery.d.ts +0 -52
  178. package/dist/lib/browser/arc-discovery.js +0 -197
  179. package/dist/lib/browser/arc-dom.d.ts +0 -14
  180. package/dist/lib/browser/arc-dom.js +0 -121
  181. package/dist/lib/browser/caller-identity.d.ts +0 -40
  182. package/dist/lib/browser/caller-identity.js +0 -175
  183. package/dist/lib/browser/cdp.d.ts +0 -51
  184. package/dist/lib/browser/cdp.js +0 -254
  185. package/dist/lib/browser/chrome.d.ts +0 -141
  186. package/dist/lib/browser/chrome.js +0 -704
  187. package/dist/lib/browser/chromium-discovery.d.ts +0 -28
  188. package/dist/lib/browser/chromium-discovery.js +0 -96
  189. package/dist/lib/browser/devices.d.ts +0 -34
  190. package/dist/lib/browser/devices.js +0 -61
  191. package/dist/lib/browser/domain-skills.d.ts +0 -71
  192. package/dist/lib/browser/domain-skills.js +0 -195
  193. package/dist/lib/browser/drivers/arc.d.ts +0 -40
  194. package/dist/lib/browser/drivers/arc.js +0 -276
  195. package/dist/lib/browser/drivers/firefox.d.ts +0 -99
  196. package/dist/lib/browser/drivers/firefox.js +0 -377
  197. package/dist/lib/browser/drivers/local.d.ts +0 -55
  198. package/dist/lib/browser/drivers/local.js +0 -266
  199. package/dist/lib/browser/drivers/ssh.d.ts +0 -120
  200. package/dist/lib/browser/drivers/ssh.js +0 -467
  201. package/dist/lib/browser/editor.d.ts +0 -3
  202. package/dist/lib/browser/editor.js +0 -50
  203. package/dist/lib/browser/ffmpeg.d.ts +0 -12
  204. package/dist/lib/browser/ffmpeg.js +0 -184
  205. package/dist/lib/browser/firefox-discovery.d.ts +0 -69
  206. package/dist/lib/browser/firefox-discovery.js +0 -162
  207. package/dist/lib/browser/har.d.ts +0 -85
  208. package/dist/lib/browser/har.js +0 -77
  209. package/dist/lib/browser/hygiene.d.ts +0 -97
  210. package/dist/lib/browser/hygiene.js +0 -153
  211. package/dist/lib/browser/index.d.ts +0 -5
  212. package/dist/lib/browser/index.js +0 -5
  213. package/dist/lib/browser/input.d.ts +0 -7
  214. package/dist/lib/browser/input.js +0 -92
  215. package/dist/lib/browser/ipc.d.ts +0 -204
  216. package/dist/lib/browser/ipc.js +0 -1453
  217. package/dist/lib/browser/login-detection.d.ts +0 -87
  218. package/dist/lib/browser/login-detection.js +0 -274
  219. package/dist/lib/browser/profiles.d.ts +0 -422
  220. package/dist/lib/browser/profiles.js +0 -1157
  221. package/dist/lib/browser/refs.d.ts +0 -89
  222. package/dist/lib/browser/refs.js +0 -191
  223. package/dist/lib/browser/registry.d.ts +0 -70
  224. package/dist/lib/browser/registry.js +0 -252
  225. package/dist/lib/browser/remote-control.d.ts +0 -59
  226. package/dist/lib/browser/remote-control.js +0 -85
  227. package/dist/lib/browser/resolve-target.d.ts +0 -66
  228. package/dist/lib/browser/resolve-target.js +0 -264
  229. package/dist/lib/browser/runtime-state.d.ts +0 -291
  230. package/dist/lib/browser/runtime-state.js +0 -584
  231. package/dist/lib/browser/secret-ref.d.ts +0 -10
  232. package/dist/lib/browser/secret-ref.js +0 -14
  233. package/dist/lib/browser/service.d.ts +0 -812
  234. package/dist/lib/browser/service.js +0 -4693
  235. package/dist/lib/browser/stream.d.ts +0 -17
  236. package/dist/lib/browser/stream.js +0 -72
  237. package/dist/lib/browser/task-index.d.ts +0 -78
  238. package/dist/lib/browser/task-index.js +0 -199
  239. package/dist/lib/browser/types.d.ts +0 -647
  240. package/dist/lib/browser/types.js +0 -61
  241. package/dist/lib/browser/upload.d.ts +0 -29
  242. package/dist/lib/browser/upload.js +0 -298
  243. package/dist/lib/daemon/browser-ipc-service.d.ts +0 -20
  244. package/dist/lib/daemon/browser-ipc-service.js +0 -46
  245. package/dist/lib/daemon/browser-task-reap-service.d.ts +0 -14
  246. package/dist/lib/daemon/browser-task-reap-service.js +0 -26
  247. package/dist/lib/fleet-shared-repo-sync.d.ts +0 -55
  248. 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 = agent === 'gemini' ? '.json' : '.jsonl';
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 conflict-free `~/.agents/devices/<device>/daemon-state.json`, which the
9
- * daemon's existing bounded Git transport (`fleet-shared-repo-sync.ts`) already
10
- * delivers fleet-wide with no operator step. The consuming device folds peer
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 conflict-free `~/.agents/devices/<device>/daemon-state.json`, which the
9
- * daemon's existing bounded Git transport (`fleet-shared-repo-sync.ts`) already
10
- * delivers fleet-wide with no operator step. The consuming device folds peer
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, so
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' | 'gemini';
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), Gemini (single JSON), and OpenCode.
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' | 'gemini' | 'antigravity' | 'opencode' | 'openclaw' | 'rush' | 'hermes' | 'grok' | 'kimi' | 'droid' | 'cursor' | 'muse';
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), Gemini (single JSON), and OpenCode.
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', 'gemini', 'antigravity', 'opencode', 'openclaw', 'rush', 'hermes', 'grok', 'kimi', 'droid', 'cursor', 'muse'];
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