@phnx-labs/agents-cli 1.22.43 → 1.22.45

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 (164) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +14 -12
  3. package/dist/bootstrap.js +3 -6
  4. package/dist/cli/command-registry.d.ts +0 -7
  5. package/dist/cli/command-registry.js +1 -21
  6. package/dist/commands/accounts.d.ts +1 -9
  7. package/dist/commands/accounts.js +12 -90
  8. package/dist/commands/apply.d.ts +7 -10
  9. package/dist/commands/apply.js +15 -35
  10. package/dist/commands/artifacts.js +4 -5
  11. package/dist/commands/audit.d.ts +6 -6
  12. package/dist/commands/audit.js +12 -12
  13. package/dist/commands/beta.d.ts +7 -1
  14. package/dist/commands/beta.js +16 -9
  15. package/dist/commands/commands.js +1 -1
  16. package/dist/commands/doctor.js +1 -4
  17. package/dist/commands/events.d.ts +2 -2
  18. package/dist/commands/events.js +6 -5
  19. package/dist/commands/exec.d.ts +1 -1
  20. package/dist/commands/exec.js +8 -4
  21. package/dist/commands/factory.d.ts +1 -1
  22. package/dist/commands/fleet-capture.d.ts +1 -1
  23. package/dist/commands/fleet-capture.js +4 -5
  24. package/dist/commands/harness.js +1 -49
  25. package/dist/commands/hooks.js +1 -1
  26. package/dist/commands/insights.d.ts +1 -1
  27. package/dist/commands/insights.js +85 -156
  28. package/dist/commands/menubar.js +58 -1
  29. package/dist/commands/output.js +2 -2
  30. package/dist/commands/prune.js +5 -4
  31. package/dist/commands/routines.test-fixture.d.ts +86 -0
  32. package/dist/commands/routines.test-fixture.js +345 -0
  33. package/dist/commands/sessions.test-fixture.d.ts +24 -0
  34. package/dist/commands/sessions.test-fixture.js +224 -0
  35. package/dist/commands/setup.js +5 -1
  36. package/dist/commands/share.d.ts +9 -10
  37. package/dist/commands/share.js +68 -50
  38. package/dist/commands/skills.js +1 -1
  39. package/dist/commands/ssh.js +15 -62
  40. package/dist/commands/subagents.js +1 -1
  41. package/dist/commands/view.js +5 -2
  42. package/dist/lib/analytics/mix-commands.d.ts +6 -28
  43. package/dist/lib/analytics/mix-commands.js +14 -40
  44. package/dist/lib/audit/log.d.ts +2 -2
  45. package/dist/lib/audit/log.js +2 -2
  46. package/dist/lib/beta.js +1 -1
  47. package/dist/lib/browser/domain-skills.d.ts +26 -6
  48. package/dist/lib/browser/domain-skills.js +81 -43
  49. package/dist/lib/daemon/daemon.js +11 -0
  50. package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
  51. package/dist/lib/daemon/daemon.test-fixture.js +71 -0
  52. package/dist/lib/daemon-ticks.d.ts +38 -1
  53. package/dist/lib/daemon-ticks.js +52 -5
  54. package/dist/lib/device-config.js +1 -2
  55. package/dist/lib/devices/discovery-policy.d.ts +3 -3
  56. package/dist/lib/devices/discovery-policy.js +3 -3
  57. package/dist/lib/devices/fleet.d.ts +4 -8
  58. package/dist/lib/devices/fleet.js +3 -15
  59. package/dist/lib/devices/health-report.js +3 -3
  60. package/dist/lib/devices/pool.d.ts +0 -6
  61. package/dist/lib/devices/pool.js +0 -6
  62. package/dist/lib/devices/registry.d.ts +0 -15
  63. package/dist/lib/devices/registry.js +0 -9
  64. package/dist/lib/devices/stats-cache.d.ts +12 -0
  65. package/dist/lib/devices/stats-cache.js +24 -5
  66. package/dist/lib/fleet/capture.d.ts +1 -1
  67. package/dist/lib/fleet/manifest.js +1 -1
  68. package/dist/lib/fleet/remote-login.d.ts +2 -2
  69. package/dist/lib/fleet/remote-login.js +3 -4
  70. package/dist/lib/hosts/dispatch.d.ts +2 -0
  71. package/dist/lib/hosts/dispatch.js +8 -1
  72. package/dist/lib/hosts/passthrough.js +0 -1
  73. package/dist/lib/hosts/providers/devices.js +1 -13
  74. package/dist/lib/hosts/ready.d.ts +12 -3
  75. package/dist/lib/hosts/ready.js +27 -12
  76. package/dist/lib/hosts/registry.d.ts +4 -5
  77. package/dist/lib/hosts/registry.js +3 -7
  78. package/dist/lib/installations/versions.js +21 -2
  79. package/dist/lib/menubar/install-menubar.d.ts +109 -0
  80. package/dist/lib/menubar/install-menubar.js +199 -4
  81. package/dist/lib/remote-agents-json.js +1 -7
  82. package/dist/lib/routines-placement.d.ts +1 -1
  83. package/dist/lib/routines-placement.js +1 -1
  84. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  85. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  86. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  87. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  88. package/dist/lib/session/active.d.ts +4 -16
  89. package/dist/lib/session/active.js +2 -6
  90. package/dist/lib/session/db.d.ts +13 -98
  91. package/dist/lib/session/db.js +4 -11
  92. package/dist/lib/session/discover.d.ts +5 -81
  93. package/dist/lib/session/discover.js +5 -5
  94. package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
  95. package/dist/lib/session/remote/remote-bundle.js +61 -0
  96. package/dist/lib/session/remote/remote-list.d.ts +148 -0
  97. package/dist/lib/session/remote/remote-list.js +607 -0
  98. package/dist/lib/session/remote/remote.d.ts +132 -0
  99. package/dist/lib/session/remote/remote.js +314 -0
  100. package/dist/lib/session/remote/watch.d.ts +101 -0
  101. package/dist/lib/session/remote/watch.js +241 -0
  102. package/dist/lib/session/remote-bundle.d.ts +1 -12
  103. package/dist/lib/session/remote-bundle.js +3 -61
  104. package/dist/lib/session/remote-list.d.ts +1 -148
  105. package/dist/lib/session/remote-list.js +3 -607
  106. package/dist/lib/session/remote.d.ts +1 -132
  107. package/dist/lib/session/remote.js +3 -314
  108. package/dist/lib/session/watch.d.ts +1 -101
  109. package/dist/lib/session/watch.js +3 -242
  110. package/dist/lib/share/config.d.ts +12 -2
  111. package/dist/lib/share/config.js +47 -11
  112. package/dist/lib/share/delete.js +1 -1
  113. package/dist/lib/share/publish.js +1 -1
  114. package/dist/lib/share/worker-template.js +2 -2
  115. package/dist/lib/smart-launch.d.ts +4 -4
  116. package/dist/lib/smart-launch.js +8 -18
  117. package/dist/lib/staleness/index.d.ts +3 -1
  118. package/dist/lib/staleness/index.js +21 -1
  119. package/dist/lib/staleness/types.d.ts +11 -0
  120. package/dist/lib/staleness/writers/commands.js +13 -4
  121. package/dist/lib/staleness/writers/hooks.js +3 -1
  122. package/dist/lib/staleness/writers/mcp.d.ts +0 -7
  123. package/dist/lib/staleness/writers/mcp.js +16 -1
  124. package/dist/lib/staleness/writers/rules.js +1 -1
  125. package/dist/lib/staleness/writers/skills.js +3 -1
  126. package/dist/lib/staleness/writers/subagents.d.ts +0 -9
  127. package/dist/lib/staleness/writers/subagents.js +18 -1
  128. package/dist/lib/staleness/writers/types.d.ts +10 -0
  129. package/dist/lib/startup/command-registry.d.ts +8 -1
  130. package/dist/lib/startup/command-registry.js +21 -6
  131. package/dist/lib/startup/root-command.d.ts +18 -1
  132. package/dist/lib/startup/root-command.js +18 -1
  133. package/dist/lib/state.js +7 -0
  134. package/dist/lib/teams/placement-probe.js +27 -12
  135. package/dist/lib/teams/scheduler.d.ts +3 -1
  136. package/dist/lib/types.d.ts +1 -1
  137. package/dist/lib/view-types.d.ts +3 -0
  138. package/package.json +2 -3
  139. package/dist/bin/agents +0 -0
  140. package/dist/commands/auth.d.ts +0 -9
  141. package/dist/commands/auth.js +0 -108
  142. package/dist/commands/org.d.ts +0 -6
  143. package/dist/commands/org.js +0 -175
  144. package/dist/commands/serve.d.ts +0 -10
  145. package/dist/commands/serve.js +0 -68
  146. package/dist/commands/trends.d.ts +0 -10
  147. package/dist/commands/trends.js +0 -12
  148. package/dist/lib/entitlement.d.ts +0 -31
  149. package/dist/lib/entitlement.js +0 -137
  150. package/dist/lib/prix-account.d.ts +0 -158
  151. package/dist/lib/prix-account.js +0 -214
  152. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  153. package/dist/lib/serve/control.d.ts +0 -95
  154. package/dist/lib/serve/control.js +0 -260
  155. package/dist/lib/serve/data.d.ts +0 -81
  156. package/dist/lib/serve/data.js +0 -91
  157. package/dist/lib/serve/page.d.ts +0 -7
  158. package/dist/lib/serve/page.js +0 -140
  159. package/dist/lib/serve/server.d.ts +0 -80
  160. package/dist/lib/serve/server.js +0 -145
  161. package/dist/lib/serve/stream.d.ts +0 -43
  162. package/dist/lib/serve/stream.js +0 -116
  163. package/dist/lib/serve/token.d.ts +0 -35
  164. package/dist/lib/serve/token.js +0 -85
@@ -59,7 +59,7 @@ export declare function attributedSetLostPids(prev: Set<number>, next: Set<numbe
59
59
  * the same pid-reuse guard {@link isPidAlive} uses elsewhere.
60
60
  */
61
61
  export declare function filterCachedUnattributed(sessions: ActiveSession[], attributed: Set<number>, alive: (pid: number, startedAtMs?: number) => boolean): ActiveSession[];
62
- export type ActiveContext = 'terminal' | 'teams' | 'cloud' | 'headless';
62
+ type ActiveContext = 'terminal' | 'teams' | 'cloud' | 'headless';
63
63
  /** The SessionMeta fields the live-row backfill reads — the enrichment a running process cannot report. */
64
64
  export type BackfillMeta = Pick<SessionMeta, 'version' | 'timestamp' | 'label' | 'ticketId' | 'prUrl' | 'prNumber' | 'origin' | 'routineName'>;
65
65
  export declare function backfillActiveRowsFromMeta(sessions: ActiveSession[], metaById: Map<string, BackfillMeta>): void;
@@ -335,7 +335,7 @@ export interface ActiveSession {
335
335
  paneId?: string;
336
336
  }
337
337
  export declare function activeStatusFromCloudStatus(status: CloudTaskStatus): ActiveStatus;
338
- export interface ActiveQueryOptions {
338
+ interface ActiveQueryOptions {
339
339
  /** Skip the `ps` scan for ad-hoc headless agents. */
340
340
  skipHeadless?: boolean;
341
341
  /**
@@ -359,14 +359,6 @@ export interface ActiveQueryOptions {
359
359
  export declare class TmuxDiscoveryDegradedError extends Error {
360
360
  constructor(message: string);
361
361
  }
362
- /**
363
- * A live process can only borrow an indexed session file if that transcript
364
- * has been touched recently enough to plausibly belong to the process. This is
365
- * deliberately wider than ACTIVE_MTIME_WINDOW_MS: an inactive-but-live CLI can
366
- * be idle for longer than 2 minutes, but it must not attach to a weeks-old
367
- * transcript just because a GUI app service with the same basename is alive.
368
- */
369
- export declare const ACTIVE_SESSION_STALE_MS: number;
370
362
  /**
371
363
  * A session whose transcript hasn't been written in this long is ABANDONED /
372
364
  * dangling — the framework can no longer treat it as live work, whether its PID
@@ -420,8 +412,6 @@ export declare function resolveNamesToSessionIds(sessionNames: string[], deps: {
420
412
  * the start time can't be read, we keep the existence answer.
421
413
  */
422
414
  export declare function isPidAlive(pid: number, startedAtMs?: number): boolean;
423
- /** Test seam: drop the Claude session-file path memo. */
424
- export declare function clearClaudeSessionFileCacheForTest(): void;
425
415
  /**
426
416
  * Resolve a Claude transcript for `cwd` across the given project roots, newest
427
417
  * mtime winning when the same id/cwd resolves in more than one version home
@@ -553,8 +543,6 @@ export declare function listTeamsActive(opts?: {
553
543
  }): Promise<ActiveSession[]>;
554
544
  /** Live editor-terminal agents across every IDE window. */
555
545
  export declare function listTerminalsActive(): Promise<ActiveSession[]>;
556
- /** Cloud tasks still in a non-terminal state. `tasks.db` may not exist; that's fine. */
557
- export declare function listCloudActive(): ActiveSession[];
558
546
  interface ProcRow {
559
547
  pid: number;
560
548
  ppid: number;
@@ -628,7 +616,7 @@ export declare function foldSubordinateAgents(candidates: AgentCandidate[], ppid
628
616
  */
629
617
  export declare function listUnattributedActive(attributed: Set<number>): Promise<ActiveSession[]>;
630
618
  /** One tmux pane's resolved agent identity for the authoritative source. */
631
- export interface PaneIdentity {
619
+ interface PaneIdentity {
632
620
  agent: string;
633
621
  /** Exact session id when resolvable (launch registry, or the hook join). */
634
622
  sessionId?: string;
@@ -679,7 +667,7 @@ export declare function listTmuxAgentSessions(): Promise<ActiveSession[]>;
679
667
  */
680
668
  export declare function getActiveSessions(opts?: ActiveQueryOptions): Promise<ActiveSession[]>;
681
669
  /** Local discovery sources probed by {@link describeActiveDiscoveryHealth}. */
682
- export interface ActiveDiscoveryHealth {
670
+ interface ActiveDiscoveryHealth {
683
671
  /** Sources that failed on the probe (currently only `'tmux'`). Empty = nothing degraded locally. */
684
672
  degradedSources: string[];
685
673
  }
@@ -262,7 +262,7 @@ export class TmuxDiscoveryDegradedError extends Error {
262
262
  * be idle for longer than 2 minutes, but it must not attach to a weeks-old
263
263
  * transcript just because a GUI app service with the same basename is alive.
264
264
  */
265
- export const ACTIVE_SESSION_STALE_MS = 24 * 60 * 60_000;
265
+ const ACTIVE_SESSION_STALE_MS = 24 * 60 * 60_000;
266
266
  /**
267
267
  * A session whose transcript hasn't been written in this long is ABANDONED /
268
268
  * dangling — the framework can no longer treat it as live work, whether its PID
@@ -541,10 +541,6 @@ function claudeProjectDirName(cwd) {
541
541
  */
542
542
  const CLAUDE_SESSION_FILE_CACHE_MAX = 256;
543
543
  const claudeSessionFileCache = new Map();
544
- /** Test seam: drop the Claude session-file path memo. */
545
- export function clearClaudeSessionFileCacheForTest() {
546
- claudeSessionFileCache.clear();
547
- }
548
544
  /**
549
545
  * Locate the active Claude session file for a process. If we know the session
550
546
  * UUID (from terminal env or team parent), prefer the exact match. Otherwise
@@ -1094,7 +1090,7 @@ export async function listTerminalsActive() {
1094
1090
  });
1095
1091
  }
1096
1092
  /** Cloud tasks still in a non-terminal state. `tasks.db` may not exist; that's fine. */
1097
- export function listCloudActive() {
1093
+ function listCloudActive() {
1098
1094
  let tasks;
1099
1095
  try {
1100
1096
  tasks = listActiveTasks();
@@ -13,13 +13,6 @@ import { type IndexedToolCall } from './tool-calls.js';
13
13
  * assert against the constant instead of hardcoding a number that every bump
14
14
  * then has to chase (docs/sessions.md calls the constant the source of truth). */
15
15
  export declare const SCHEMA_VERSION = 39;
16
- /**
17
- * Bump to force `agents sessions backfill resources` to re-derive every
18
- * session's skill/slash-command tallies on its next run (resource_scan_ledger
19
- * rows with a lower version are treated as stale — the same mechanism
20
- * TOOL_INDEX_VERSION gives the tool backfill).
21
- */
22
- export declare const RESOURCE_INDEX_VERSION = 1;
23
16
  /**
24
17
  * Bumping this invalidates every cached facet row without touching the schema
25
18
  * version, so a change to the extraction logic (a new metric, a corrected bucket)
@@ -28,67 +21,6 @@ export declare const RESOURCE_INDEX_VERSION = 1;
28
21
  */
29
22
  /** Bump when facet extraction changes so cached rows recompute (stalls-by-model v6). */
30
23
  export declare const INSIGHTS_EXTRACTOR_VERSION = 6;
31
- export declare const PREVIEW_EXTRACTOR_VERSION = 1;
32
- /** Raw row shape returned from the sessions table. */
33
- export interface SessionRow {
34
- id: string;
35
- short_id: string;
36
- agent: string;
37
- origin: string | null;
38
- routine_name: string | null;
39
- routine_run_id: string | null;
40
- version: string | null;
41
- account: string | null;
42
- account_key: string | null;
43
- account_org: string | null;
44
- mode: string | null;
45
- timestamp: string;
46
- last_activity: string | null;
47
- project: string | null;
48
- cwd: string | null;
49
- git_branch: string | null;
50
- topic: string | null;
51
- label: string | null;
52
- message_count: number | null;
53
- token_count: number | null;
54
- output_tokens: number | null;
55
- input_tokens: number | null;
56
- cache_read_tokens: number | null;
57
- cache_write_tokens: number | null;
58
- cost_usd: number | null;
59
- cost_usd_nocache: number | null;
60
- duration_ms: number | null;
61
- model: string | null;
62
- tool_call_count: number | null;
63
- file_path: string;
64
- file_mtime_ms: number | null;
65
- file_size: number | null;
66
- scanned_at: number | null;
67
- is_team_origin: number;
68
- pr_url: string | null;
69
- pr_number: number | null;
70
- worktree_slug: string | null;
71
- ticket_id: string | null;
72
- spawned_team: string | null;
73
- plan: string | null;
74
- machine: string | null;
75
- todos: string | null;
76
- recent_directories_touched: string | null;
77
- linear_project: string | null;
78
- linear_project_url: string | null;
79
- actor: string | null;
80
- initiated_by: string | null;
81
- /** NULL means "not yet computed" (a row scanned before this field existed) — see rowToMeta. */
82
- used_browser: number | null;
83
- used_computer: number | null;
84
- /**
85
- * Epoch ms the transcript file was first confirmed gone while content survived
86
- * (RUSH-2436); NULL = live/never archived. Optional because the scanner upsert
87
- * write-payload never sets it — the INSERT column list omits it, so a rescan
88
- * preserves the sticky stamp; it is written only by querySessions.
89
- */
90
- archived_at?: number | null;
91
- }
92
24
  /** File stat snapshot used to detect changes between scan runs. */
93
25
  export interface ScanStamp {
94
26
  fileMtimeMs: number;
@@ -143,7 +75,7 @@ export interface QueryOptions {
143
75
  export declare function getDB(): Database.Database;
144
76
  /** Close the cached database connection. */
145
77
  export declare function closeDB(): void;
146
- export interface FtsOptimizeResult {
78
+ interface FtsOptimizeResult {
147
79
  table: string;
148
80
  segmentsBefore: number;
149
81
  segmentsAfter: number;
@@ -224,7 +156,7 @@ export declare function getScanStampsForPaths(filePaths: string[]): Map<string,
224
156
  * written by the Claude scan (B-2) and consumed on the next scan to decide
225
157
  * full-vs-incremental and to hydrate the resume.
226
158
  */
227
- export interface ParserStateRow {
159
+ interface ParserStateRow {
228
160
  parserState: string | null;
229
161
  contentText: string | null;
230
162
  fileMtimeMs: number;
@@ -348,7 +280,7 @@ export declare function querySessions(options?: QueryOptions): SessionMeta[];
348
280
  /** Count sessions matching the given filter options. */
349
281
  export declare function countSessions(options?: QueryOptions): number;
350
282
  /** One grouped row in a cost/duration rollup. */
351
- export interface UsageRollupRow {
283
+ interface UsageRollupRow {
352
284
  /**
353
285
  * Grouping key value: the agent id, project name, ISO date (YYYY-MM-DD), or
354
286
  * account identity (`claude:org=<uuid>` / `unattributed:<reason>`).
@@ -454,7 +386,7 @@ export type UsageRollupGroup = 'agent' | 'project' | 'day' | 'account';
454
386
  * Account rotation is NOT done here — that stays on live rate-limit windows
455
387
  * via `--strategy balanced` / rotate.ts.
456
388
  */
457
- export type AffinityGroup = 'machine' | 'agent' | 'machine_agent';
389
+ type AffinityGroup = 'machine' | 'agent' | 'machine_agent';
458
390
  export interface AffinityRow {
459
391
  /** Group key: machine name, agent id, or "machine\\tagent". */
460
392
  key: string;
@@ -540,7 +472,7 @@ export declare function resourceUsageCoverage(): {
540
472
  total: number;
541
473
  };
542
474
  /** Outcome of a resource-usage backfill run. */
543
- export interface ResourceBackfillResult {
475
+ interface ResourceBackfillResult {
544
476
  /** Sessions considered (matched the filter, had a real transcript). */
545
477
  scanned: number;
546
478
  /** Sessions (re)parsed and written this run. */
@@ -585,7 +517,7 @@ export interface TeamSpawner {
585
517
  */
586
518
  export declare function teamSpawners(): Map<string, TeamSpawner>;
587
519
  /** A session with its cost, for the top-N-by-cost listing. */
588
- export interface TopCostSession {
520
+ interface TopCostSession {
589
521
  meta: SessionMeta;
590
522
  costUsd: number;
591
523
  durationMs: number;
@@ -638,7 +570,7 @@ export declare function findSessionsById(idQuery: string, scope?: Pick<QueryOpti
638
570
  */
639
571
  export declare function findSessionsByShortIds(shortIds: string[]): Map<string, SessionMeta>;
640
572
  /** A single full-text search result with ranking score. */
641
- export interface FtsHit {
573
+ interface FtsHit {
642
574
  sessionId: string;
643
575
  score: number;
644
576
  matchedTerms: string[];
@@ -667,11 +599,6 @@ export declare function buildFtsQuery(input: string): {
667
599
  * so "higher = better" is consistent across all tiers.
668
600
  */
669
601
  export declare function ftsSearch(input: string, limit?: number): FtsHit[];
670
- /** Return the total row counts for the sessions and FTS5 tables (diagnostic). */
671
- export declare function getRowCount(): {
672
- sessions: number;
673
- textRows: number;
674
- };
675
602
  /**
676
603
  * Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
677
604
  * it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
@@ -683,14 +610,14 @@ export declare function getRowCount(): {
683
610
  */
684
611
  export declare function updateSessionFilePaths(oldPrefix: string, newPrefix: string): number;
685
612
  /** Per-kind capture tallies for a browser task. Counts only -- never the bytes. */
686
- export interface BrowserCaptureCounts {
613
+ interface BrowserCaptureCounts {
687
614
  screenshot: number;
688
615
  pdf: number;
689
616
  recording: number;
690
617
  download: number;
691
618
  }
692
619
  /** One durable browser-task row. `machine` defaults to this device. */
693
- export interface BrowserSessionRecord {
620
+ interface BrowserSessionRecord {
694
621
  task: string;
695
622
  profile: string;
696
623
  sessionId?: string;
@@ -704,7 +631,7 @@ export interface BrowserSessionRecord {
704
631
  capturesRemote?: string;
705
632
  }
706
633
  /** One durable computer-use invocation row. `machine` defaults to this device. */
707
- export interface ComputerSessionRecord {
634
+ interface ComputerSessionRecord {
708
635
  invocationId: string;
709
636
  sessionId?: string;
710
637
  launchId?: string;
@@ -743,7 +670,7 @@ export declare function recordBrowserSession(record: BrowserSessionRecord): void
743
670
  */
744
671
  export declare function recordComputerSession(record: ComputerSessionRecord): void;
745
672
  /** A stored browser row as read back, with counts rehydrated. */
746
- export interface StoredBrowserSession extends Required<Pick<BrowserSessionRecord, 'task' | 'profile'>> {
673
+ interface StoredBrowserSession extends Required<Pick<BrowserSessionRecord, 'task' | 'profile'>> {
747
674
  sessionId?: string;
748
675
  launchId?: string;
749
676
  actor?: string;
@@ -771,7 +698,7 @@ export declare function listBrowserSessionRecords(profile?: string, opts?: {
771
698
  /** One stored browser task by its (profile, task) key, or null. */
772
699
  export declare function getBrowserSessionRecord(profile: string, task: string): StoredBrowserSession | null;
773
700
  /** A stored computer-use invocation as read back. */
774
- export interface StoredComputerSession {
701
+ interface StoredComputerSession {
775
702
  invocationId: string;
776
703
  sessionId?: string;
777
704
  launchId?: string;
@@ -782,19 +709,6 @@ export interface StoredComputerSession {
782
709
  actionCount: number;
783
710
  taskPreview?: string;
784
711
  }
785
- /**
786
- * Retention for the tool-session tables.
787
- *
788
- * These are the durable answer to a ledger that prunes at 7 days, so they are
789
- * deliberately long-lived — but "durable" is not "unbounded". `computer_sessions`
790
- * takes one row per `agents computer` CLI PROCESS (`COMPUTER_INVOCATION_ID` is
791
- * minted per process), and an agent driving a desktop invokes that hundreds of
792
- * times a day, so an unbounded table would grow without limit and be read in
793
- * full on every listing.
794
- */
795
- export declare const TOOL_SESSION_MAX_AGE_DAYS = 365;
796
- /** Default ceiling on rows one listing will read. */
797
- export declare const TOOL_SESSION_LIST_LIMIT = 2000;
798
712
  /**
799
713
  * Drop tool-session rows past {@link TOOL_SESSION_MAX_AGE_DAYS}.
800
714
  *
@@ -813,3 +727,4 @@ export declare function listComputerSessionRecords(opts?: {
813
727
  limit?: number;
814
728
  startedBeforeMs?: number;
815
729
  }): StoredComputerSession[];
730
+ export {};
@@ -34,7 +34,7 @@ export const SCHEMA_VERSION = 39;
34
34
  * rows with a lower version are treated as stale — the same mechanism
35
35
  * TOOL_INDEX_VERSION gives the tool backfill).
36
36
  */
37
- export const RESOURCE_INDEX_VERSION = 1;
37
+ const RESOURCE_INDEX_VERSION = 1;
38
38
  /**
39
39
  * Canonicalize a file path for use as a scan_ledger key. The same physical
40
40
  * session file is reachable via multiple aliases — `~/.claude/projects/x.jsonl`
@@ -397,7 +397,7 @@ CREATE INDEX IF NOT EXISTS idx_computer_sessions_started ON computer_sessions(st
397
397
  */
398
398
  /** Bump when facet extraction changes so cached rows recompute (stalls-by-model v6). */
399
399
  export const INSIGHTS_EXTRACTOR_VERSION = 6;
400
- export const PREVIEW_EXTRACTOR_VERSION = 1;
400
+ const PREVIEW_EXTRACTOR_VERSION = 1;
401
401
  let dbInstance = null;
402
402
  /**
403
403
  * Apply schema migrations from `fromVersion` → SCHEMA_VERSION. The new
@@ -3365,13 +3365,6 @@ export function ftsSearch(input, limit = 200) {
3365
3365
  hits.sort((a, b) => b.score - a.score);
3366
3366
  return hits.slice(0, limit);
3367
3367
  }
3368
- /** Return the total row counts for the sessions and FTS5 tables (diagnostic). */
3369
- export function getRowCount() {
3370
- const db = getDB();
3371
- const sessions = db.prepare(`SELECT COUNT(*) AS c FROM sessions`).get().c;
3372
- const textRows = db.prepare(`SELECT COUNT(*) AS c FROM session_text`).get().c;
3373
- return { sessions, textRows };
3374
- }
3375
3368
  /**
3376
3369
  * Rewrite file_path for all sessions whose path starts with oldPrefix, replacing
3377
3370
  * it with newPrefix + the unchanged suffix. Also clears the matching scan_ledger
@@ -3520,9 +3513,9 @@ export function getBrowserSessionRecord(profile, task) {
3520
3513
  * times a day, so an unbounded table would grow without limit and be read in
3521
3514
  * full on every listing.
3522
3515
  */
3523
- export const TOOL_SESSION_MAX_AGE_DAYS = 365;
3516
+ const TOOL_SESSION_MAX_AGE_DAYS = 365;
3524
3517
  /** Default ceiling on rows one listing will read. */
3525
- export const TOOL_SESSION_LIST_LIMIT = 2000;
3518
+ const TOOL_SESSION_LIST_LIMIT = 2000;
3526
3519
  /**
3527
3520
  * Drop tool-session rows past {@link TOOL_SESSION_MAX_AGE_DAYS}.
3528
3521
  *
@@ -10,8 +10,6 @@ import type { SessionAgentId, SessionEvent, SessionMeta, TodoProgress } from './
10
10
  export { machineForSessionFile } from './origin-machine.js';
11
11
  import { type ScanStamp } from './db.js';
12
12
  import { ToolCallCollector, type IndexedToolCall, type ToolCallCollectorSnapshot } from './tool-calls.js';
13
- /** One JSONL record may not force an unbounded string allocation. */
14
- export declare const SESSION_JSONL_LINE_MAX_BYTES: number;
15
13
  /** Options controlling which sessions to discover and how to report progress. */
16
14
  export interface DiscoverOptions {
17
15
  agent?: SessionAgentId;
@@ -172,7 +170,7 @@ interface CodexSessionScan {
172
170
  */
173
171
  export declare function discoverSessions(options?: DiscoverOptions): Promise<SessionMeta[]>;
174
172
  /** What one incremental scan actually did. */
175
- export interface IncrementalScanResult {
173
+ interface IncrementalScanResult {
176
174
  /** True when this process won the single-flight claim and ran the scan. */
177
175
  claimed: boolean;
178
176
  /**
@@ -362,7 +360,7 @@ export declare function filterChangedFiles(filePaths: string[]): Array<{
362
360
  scan: ScanStamp;
363
361
  }>;
364
362
  /** A path already stat'd by the walk — mtime is the raw (unfloored) fs value. */
365
- export interface PreStatEntry {
363
+ interface PreStatEntry {
366
364
  filePath: string;
367
365
  fileMtimeMs: number;
368
366
  fileSize: number;
@@ -378,64 +376,13 @@ export declare function filterChangedEntries(entries: PreStatEntry[]): Array<{
378
376
  scan: ScanStamp;
379
377
  }>;
380
378
  export declare function shouldDeferRecentAppend(prev: ScanStamp, current: ScanStamp, nowMs: number, debounceMs?: number): boolean;
381
- /** One leaf directory of transcripts to change-detect, plus its live-root flag. */
382
- export interface LeafDir {
383
- /** Absolute path to the directory that directly holds transcript files. */
384
- dirPath: string;
385
- /**
386
- * True if this dir is under the agent's LIVE `~/.<agent>` root — the only tree
387
- * an agent process appends to live. Every file in such a dir is treated as
388
- * hot (always re-stat'd), so an in-place append is never missed there.
389
- */
390
- isLiveRoot: boolean;
391
- }
392
- /** The changed files a leaf-dir walk surfaced, ready to parse + upsert. */
393
- export interface LeafDirScan {
394
- /** Files whose (mtime, size) changed vs the ledger — the parse set. */
395
- changed: Array<{
396
- filePath: string;
397
- scan: ScanStamp;
398
- }>;
399
- /**
400
- * Every transcript file seen across all leaf dirs (changed or not), in
401
- * live-root-first order, each tagged with whether its dir is a live root.
402
- * Lets a caller restore cross-root, session-id precedence (prefer the live
403
- * copy of a session over a frozen backup copy) independent of which copy
404
- * happened to be flagged "changed" this run.
405
- */
406
- allFiles: Array<{
407
- filePath: string;
408
- isLiveRoot: boolean;
409
- }>;
410
- }
411
- /**
412
- * Walk a set of leaf transcript directories and return the files that changed,
413
- * skipping the per-file `stat` of directories whose (mtime, entry_count) matches
414
- * the dir_ledger.
415
- *
416
- * Per leaf dir:
417
- * - `stat` the dir once and `readdir` it (one cheap syscall) to get the entry
418
- * count and the file list.
419
- * - If the dir matches the dir_ledger (floored mtime AND entry_count), no file
420
- * was created / deleted / renamed since we last walked it. We then stat ONLY
421
- * the hot files (live-root files, or files scanned within HOT_FILE_WINDOW_MS)
422
- * and run just those through the ledger compare — so an in-place append to a
423
- * still-live session is still caught, while immutable backup/version dirs
424
- * collapse to a single dir stat and zero per-file stats.
425
- * - Else (changed dir, or no ledger row) we stat every file (today's full
426
- * walk) and record the fresh dir stamp so the next run can short-circuit.
427
- *
428
- * The kill-switch (`AGENTS_SESSIONS_NO_DIR_LEDGER=1`) forces the full-walk branch
429
- * for every dir and never consults or records the dir_ledger.
430
- */
431
- export declare function collectChangedFilesInLeafDirs(leafDirs: LeafDir[], ext: string): LeafDirScan;
432
379
  /**
433
380
  * Collect all directories to scan for an agent's sessions. Deduplicates by
434
381
  * realpath to avoid double-counting symlinked version homes.
435
382
  */
436
383
  export declare function getAgentSessionDirs(agent: string, subdir: string): string[];
437
384
  /** A session-agent's on-disk watch roots (every version home + backup mirror). */
438
- export interface SessionRoots {
385
+ interface SessionRoots {
439
386
  agent: SessionAgentId;
440
387
  /** Absolute directories that hold this agent's transcripts, existing right now. */
441
388
  dirs: string[];
@@ -492,7 +439,7 @@ export declare function readCodexMeta(filePath: string, resolveAccount?: () => s
492
439
  * both a full parse and a resumable incremental parse (see
493
440
  * {@link scanClaudeSessionIncremental}).
494
441
  */
495
- export interface ClaudeParseState {
442
+ interface ClaudeParseState {
496
443
  timestamp?: string;
497
444
  cwd?: string;
498
445
  gitBranch?: string;
@@ -680,7 +627,7 @@ export declare function __resetClaudeScanBranchCountsForTest(): void;
680
627
  * fold ({@link applyCodexLine}) runs for both a full parse and an incremental
681
628
  * resume. Mirrors {@link ClaudeParseState}.
682
629
  */
683
- export interface CodexParseState {
630
+ interface CodexParseState {
684
631
  sessionId?: string;
685
632
  timestamp?: string;
686
633
  cwd?: string;
@@ -712,18 +659,6 @@ export interface CodexParseState {
712
659
  }
713
660
  /** Zero-value accumulator for a fresh (from-byte-0) Codex parse. */
714
661
  export declare function initCodexParseState(): CodexParseState;
715
- /**
716
- * Fold one parsed Codex line into the accumulator — the exact loop body
717
- * {@link scanCodexSession} used to run inline, extracted verbatim and mutating
718
- * `state.*` in place. `parsed` is the already-`JSON.parse`d line (the
719
- * malformed-line skip happens in the caller, as before).
720
- */
721
- export declare function applyCodexLine(state: CodexParseState, parsed: any): void;
722
- /**
723
- * Build the {@link CodexSessionScan} return object from an accumulator — the
724
- * exact return-building {@link scanCodexSession} used to run inline.
725
- */
726
- export declare function finalizeCodexScan(state: CodexParseState): CodexSessionScan;
727
662
  /**
728
663
  * SERIALIZED continuation blob persisted in `scan_ledger.parser_state` for a
729
664
  * Codex rollout. Carries everything {@link hydrateCodexParseState} needs to
@@ -769,17 +704,6 @@ export interface CodexParserState {
769
704
  * incremental replay equals a full parse.
770
705
  */
771
706
  export declare function serializeCodexParserState(state: CodexParseState, offset: number, jsonlDroppingOversizedLine?: boolean): CodexParserState;
772
- /**
773
- * Rebuild a live {@link CodexParseState} from a persisted continuation so that
774
- * applying the appended lines yields the same accumulator a full parse would.
775
- *
776
- * `userTexts` is rehydrated as a single joined blob from `contentText`: only
777
- * `userTexts.join('\n')` (detectTicket + contentText) and `userTexts.length > 0`
778
- * are ever read downstream, and both are preserved by a one-element array
779
- * holding the joined content. Topic is first-wins and already persisted, so a
780
- * collapsed userTexts never changes it.
781
- */
782
- export declare function hydrateCodexParseState(prior: CodexParserState): CodexParseState;
783
707
  /**
784
708
  * Resume a Codex parse from `fromOffset` bytes into the file, folding only the
785
709
  * newly-appended lines into `prior`. Returns the finalized scan, the next
@@ -55,7 +55,7 @@ const MUSE_SESSIONS_DIR = path.join(HOME, '.local', 'share', 'muse', 'sessions')
55
55
  const OPENCLAW_TTL_MS = 60_000;
56
56
  const ACTIVE_APPEND_RESCAN_DEBOUNCE_MS = 5_000;
57
57
  /** One JSONL record may not force an unbounded string allocation. */
58
- export const SESSION_JSONL_LINE_MAX_BYTES = 1024 * 1024;
58
+ const SESSION_JSONL_LINE_MAX_BYTES = 1024 * 1024;
59
59
  /**
60
60
  * Stream one appended JSONL range, applying only newline-terminated records.
61
61
  * Memory is bounded to one record. An ordinary unterminated tail remains for the
@@ -728,7 +728,7 @@ export function shouldDeferRecentAppend(prev, current, nowMs, debounceMs = ACTIV
728
728
  * The kill-switch (`AGENTS_SESSIONS_NO_DIR_LEDGER=1`) forces the full-walk branch
729
729
  * for every dir and never consults or records the dir_ledger.
730
730
  */
731
- export function collectChangedFilesInLeafDirs(leafDirs, ext) {
731
+ function collectChangedFilesInLeafDirs(leafDirs, ext) {
732
732
  const disabled = dirLedgerDisabled();
733
733
  const dirStamps = disabled ? new Map() : getDirLedgerForPaths(leafDirs.map(d => d.dirPath));
734
734
  const now = Date.now();
@@ -3619,7 +3619,7 @@ export function initCodexParseState() {
3619
3619
  * `state.*` in place. `parsed` is the already-`JSON.parse`d line (the
3620
3620
  * malformed-line skip happens in the caller, as before).
3621
3621
  */
3622
- export function applyCodexLine(state, parsed) {
3622
+ function applyCodexLine(state, parsed) {
3623
3623
  collectCodexToolCalls(state.toolCollector, parsed);
3624
3624
  // PR signal, structurally: a Codex `function_call` whose command is
3625
3625
  // `gh pr create`, then the pull URL from a `function_call_output`.
@@ -3730,7 +3730,7 @@ export function applyCodexLine(state, parsed) {
3730
3730
  * Build the {@link CodexSessionScan} return object from an accumulator — the
3731
3731
  * exact return-building {@link scanCodexSession} used to run inline.
3732
3732
  */
3733
- export function finalizeCodexScan(state) {
3733
+ function finalizeCodexScan(state) {
3734
3734
  // Codex reports one cumulative snapshot: uncached input, cached (cache-read)
3735
3735
  // input, and output+reasoning. It has no cache-write bucket. Derive the burn
3736
3736
  // split and both costs (actual + no-cache) from that final snapshot (RUSH-2287).
@@ -3869,7 +3869,7 @@ export function serializeCodexParserState(state, offset, jsonlDroppingOversizedL
3869
3869
  * holding the joined content. Topic is first-wins and already persisted, so a
3870
3870
  * collapsed userTexts never changes it.
3871
3871
  */
3872
- export function hydrateCodexParseState(prior) {
3872
+ function hydrateCodexParseState(prior) {
3873
3873
  return {
3874
3874
  sessionId: prior.sessionId,
3875
3875
  timestamp: prior.timestamp,
@@ -0,0 +1,12 @@
1
+ import { type ParsedBundle } from '../bundle.js';
2
+ export interface RemotePullResult {
3
+ bundles: ParsedBundle[];
4
+ errors: string[];
5
+ }
6
+ /**
7
+ * Run `agents sessions export …exportArgs --stdout` on each host and parse the
8
+ * streamed bundle. A host that fails (unreachable, remote error, bad output) is
9
+ * collected in `errors` and skipped — one asleep peer never aborts the pull.
10
+ * `exportArgs` must NOT contain --device (the remote export runs for itself only).
11
+ */
12
+ export declare function pullBundlesFromHosts(hosts: string[], exportArgs: string[]): Promise<RemotePullResult>;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Multi-device session transfer over the EXISTING SSH fleet transport
3
+ * (RUSH-1712) — no R2, no daemon. `agents sessions export --device <h>` and
4
+ * `agents sessions import --from-host <h>` both run `agents sessions export
5
+ * … --stdout` ON the peer and stream the bundle back over the same SSH path the
6
+ * cross-machine listing already uses (resolveExplicitTargets + ssh-exec), then
7
+ * either write it (export) or import it (import) locally.
8
+ *
9
+ * This deliberately reuses ssh-exec / resolve-target rather than adding a second
10
+ * transport: the raw form `agents ssh boxA 'agents sessions export --stdout' |
11
+ * agents sessions import -` works with plain export/import; this module is just
12
+ * the one-shot wrapper around it.
13
+ */
14
+ import chalk from 'chalk';
15
+ import { sshExec } from '../../ssh-exec.js';
16
+ import { shellQuote } from '../../ssh-exec.js';
17
+ import { resolveExplicitTargets } from '../../devices/resolve-target.js';
18
+ import { remoteShellFor, buildWindowsAgentsCommand } from '../../hosts/remote-cmd.js';
19
+ import { parseBundle } from '../bundle.js';
20
+ /** Remote export can traverse many sessions; give it a generous ceiling. */
21
+ const REMOTE_EXPORT_TIMEOUT_MS = 300_000;
22
+ /** Build `agents <args>` for the peer's login shell (bash or PowerShell). */
23
+ function remoteAgentsCommand(args, os) {
24
+ if (remoteShellFor(os) === 'powershell') {
25
+ return buildWindowsAgentsCommand({ args });
26
+ }
27
+ const inner = ['agents', ...args].map((t, i) => (i === 0 ? t : shellQuote(t))).join(' ');
28
+ return `bash -lc ${shellQuote(inner)}`;
29
+ }
30
+ /**
31
+ * Run `agents sessions export …exportArgs --stdout` on each host and parse the
32
+ * streamed bundle. A host that fails (unreachable, remote error, bad output) is
33
+ * collected in `errors` and skipped — one asleep peer never aborts the pull.
34
+ * `exportArgs` must NOT contain --device (the remote export runs for itself only).
35
+ */
36
+ export async function pullBundlesFromHosts(hosts, exportArgs) {
37
+ const targets = await resolveExplicitTargets(hosts);
38
+ const bundles = [];
39
+ const errors = [];
40
+ for (const t of targets) {
41
+ const cmd = remoteAgentsCommand(['sessions', 'export', ...exportArgs, '--stdout'], t.os);
42
+ process.stderr.write(chalk.dim(`Pulling sessions from ${t.name}…\n`));
43
+ const res = sshExec(t.target, cmd, { timeoutMs: REMOTE_EXPORT_TIMEOUT_MS, extraSshArgs: t.extraSshArgs });
44
+ if (res.timedOut) {
45
+ errors.push(`${t.name}: timed out after ${Math.round(REMOTE_EXPORT_TIMEOUT_MS / 1000)}s`);
46
+ continue;
47
+ }
48
+ if (res.code !== 0) {
49
+ const tail = res.stderr.trim().split('\n').filter(Boolean).pop();
50
+ errors.push(`${t.name}: remote export failed (${res.code ?? 'ssh error'})${tail ? ': ' + tail : ''}`);
51
+ continue;
52
+ }
53
+ try {
54
+ bundles.push(parseBundle(res.stdout));
55
+ }
56
+ catch (err) {
57
+ errors.push(`${t.name}: ${err.message}`);
58
+ }
59
+ }
60
+ return { bundles, errors };
61
+ }