@phnx-labs/agents-cli 1.22.27 → 1.22.28

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 (87) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +62 -26
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/exec.js +54 -10
  5. package/dist/commands/hosts.js +4 -3
  6. package/dist/commands/perf.d.ts +10 -0
  7. package/dist/commands/perf.js +14 -6
  8. package/dist/commands/projects.js +24 -10
  9. package/dist/commands/routines.js +31 -4
  10. package/dist/commands/run-account-picker.d.ts +37 -0
  11. package/dist/commands/run-account-picker.js +101 -17
  12. package/dist/commands/secrets.js +86 -2
  13. package/dist/commands/sessions.d.ts +26 -6
  14. package/dist/commands/sessions.js +46 -12
  15. package/dist/commands/teams.js +19 -8
  16. package/dist/lib/activity.js +45 -99
  17. package/dist/lib/claude-account-token.js +38 -11
  18. package/dist/lib/cloud/host.js +1 -0
  19. package/dist/lib/devices/resolve-target.d.ts +1 -0
  20. package/dist/lib/devices/resolve-target.js +9 -2
  21. package/dist/lib/events.d.ts +1 -1
  22. package/dist/lib/events.js +2 -2
  23. package/dist/lib/git.d.ts +9 -3
  24. package/dist/lib/git.js +39 -22
  25. package/dist/lib/hooks/profile.d.ts +12 -1
  26. package/dist/lib/hooks/profile.js +6 -1
  27. package/dist/lib/hooks.js +4 -1
  28. package/dist/lib/hosts/dispatch.d.ts +10 -0
  29. package/dist/lib/hosts/dispatch.js +78 -22
  30. package/dist/lib/hosts/logs.js +11 -1
  31. package/dist/lib/hosts/passthrough.d.ts +5 -0
  32. package/dist/lib/hosts/passthrough.js +5 -2
  33. package/dist/lib/hosts/progress.d.ts +10 -0
  34. package/dist/lib/hosts/progress.js +37 -5
  35. package/dist/lib/hosts/ready.d.ts +41 -2
  36. package/dist/lib/hosts/ready.js +100 -12
  37. package/dist/lib/hosts/reconcile.d.ts +2 -1
  38. package/dist/lib/hosts/reconcile.js +13 -5
  39. package/dist/lib/hosts/reconnect.js +5 -3
  40. package/dist/lib/hosts/registry.js +1 -0
  41. package/dist/lib/hosts/tasks.d.ts +1 -0
  42. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  43. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  44. package/dist/lib/menubar/snapshot.js +15 -2
  45. package/dist/lib/models.d.ts +18 -11
  46. package/dist/lib/models.js +19 -15
  47. package/dist/lib/perf/db.js +16 -3
  48. package/dist/lib/perf/types.d.ts +12 -1
  49. package/dist/lib/project-probe.d.ts +22 -3
  50. package/dist/lib/project-probe.js +105 -17
  51. package/dist/lib/project-status.d.ts +9 -0
  52. package/dist/lib/project-status.js +15 -0
  53. package/dist/lib/refresh.js +22 -11
  54. package/dist/lib/rotate.d.ts +22 -0
  55. package/dist/lib/rotate.js +26 -0
  56. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  57. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  58. package/dist/lib/secrets/agent.d.ts +19 -3
  59. package/dist/lib/secrets/agent.js +58 -20
  60. package/dist/lib/secrets/audit.d.ts +1 -1
  61. package/dist/lib/secrets/audit.js +2 -0
  62. package/dist/lib/secrets/bundles.d.ts +3 -3
  63. package/dist/lib/secrets/bundles.js +15 -5
  64. package/dist/lib/secrets/filestore.d.ts +2 -0
  65. package/dist/lib/secrets/filestore.js +4 -0
  66. package/dist/lib/secrets/session-store.d.ts +7 -0
  67. package/dist/lib/secrets/session-store.js +21 -0
  68. package/dist/lib/session/active.d.ts +49 -0
  69. package/dist/lib/session/active.js +175 -11
  70. package/dist/lib/session/bash-command.d.ts +16 -0
  71. package/dist/lib/session/bash-command.js +65 -0
  72. package/dist/lib/session/db.d.ts +4 -0
  73. package/dist/lib/session/db.js +32 -15
  74. package/dist/lib/session/remote-bundle.js +1 -1
  75. package/dist/lib/staleness/detectors/skills.d.ts +2 -0
  76. package/dist/lib/staleness/detectors/skills.js +17 -2
  77. package/dist/lib/staleness/index.d.ts +10 -1
  78. package/dist/lib/staleness/index.js +28 -3
  79. package/dist/lib/teams/agents.d.ts +1 -0
  80. package/dist/lib/teams/agents.js +38 -11
  81. package/dist/lib/teams/remoteWorktree.d.ts +11 -7
  82. package/dist/lib/teams/remoteWorktree.js +29 -27
  83. package/dist/lib/versions.d.ts +1 -0
  84. package/dist/lib/versions.js +40 -22
  85. package/package.json +3 -1
  86. package/dist/lib/exec.bench.d.ts +0 -1
  87. package/dist/lib/exec.bench.js +0 -186
@@ -1,186 +0,0 @@
1
- /**
2
- * Benchmark for the agent-execution hot path: env construction (buildExecEnv),
3
- * argv assembly (buildExecCommand), and process spawn (execAgent -> spawnAgent).
4
- *
5
- * No mocking. Runs against this machine's REAL ~/.agents layout — the actual
6
- * agents.yaml (state.ts:1124 readMeta), the actual installed version homes
7
- * under ~/.agents/.history/versions/<agent>/ (versions.ts:1284
8
- * listInstalledVersions), and, for the spawn group, the actual installed
9
- * `claude` binary. `process.cwd()` during a real `vitest bench` run is this
10
- * package's directory inside whatever checkout invoked it (typically several
11
- * levels deep under a git worktree), so the project-version directory walk in
12
- * versions.ts:2287 getProjectVersion runs its REAL depth here, not a synthetic
13
- * shallow stub.
14
- *
15
- * The dominant, measured cost driver in buildExecEnv turned out to be neither
16
- * of the two caches below on their own -- it's claude-account-token.ts:51
17
- * resolveClaudeSetupToken(), called from exec.ts:424 for EVERY resolved claude
18
- * version (pinned or auto-resolved) whose version home has a signed-in
19
- * account. It is entirely uncached: each call re-derives an AES key via
20
- * `scryptSync` (secrets/filestore.ts:208-210, invoked from
21
- * decryptForFallback at secrets/filestore.ts:230-240) to decrypt the
22
- * file-backed `auth` secrets bundle. Measured standalone (a one-off
23
- * `performance.now()` probe against the real bundle, not part of this
24
- * benchmark file): ~150-170ms per call, first call and every call after —
25
- * scrypt is deliberately memory-hard/slow and nothing memoizes the derived
26
- * key or the decrypted token across calls. A version home with NO signed-in
27
- * account short-circuits at claude-account-token.ts:56-57
28
- * (readClaudeAccountEmail returns null) before ever reaching the decrypt, so
29
- * the SAME code path costs ~0.07ms or ~150ms purely depending on whether that
30
- * specific version is logged in — that split, not warm/cold cache state, is
31
- * the headline finding, so it is what this file isolates directly with two
32
- * explicit version pins discovered from the real ~/.agents/.history/versions/
33
- * layout: `loggedInClaudeVersion` and `loggedOutClaudeVersion`.
34
- *
35
- * Two cache regimes are ALSO benchmarked, because they cost meaningfully
36
- * different amounts on top of the above:
37
- *
38
- * - WARM: state.ts:1124 readMeta()'s mtime-keyed cache and actor.ts:163
39
- * resolveActor()'s process-lifetime cache are both hot. This is the
40
- * steady state inside one long-lived orchestrator process that calls
41
- * buildExecEnv many times (loop.ts:214, teams, runner.ts:720).
42
- * - COLD: both caches invalidated before every sample, matching the FIRST
43
- * buildExecEnv call in a freshly spawned `agents run` process -- the
44
- * common case, since every real CLI invocation is its own process.
45
- * Measured here it costs about the same as warm (readMeta's re-parse of a
46
- * ~5KB agents.yaml and a fresh actor resolve are both sub-millisecond) --
47
- * NOT because the invalidation is a no-op, but because this shell's
48
- * process.env already carries `AGENTS_ACTOR=UNRESOLVED@zion` (inherited
49
- * from the agent harness that launched this session), so actor.ts:149
50
- * `inheritedActor(env)` returns immediately and computeActor() never
51
- * reaches the SSH branch. A genuinely fresh interactive terminal (no
52
- * inherited AGENTS_ACTOR) would instead hit actor.ts:152-154 and, on an
53
- * SSH-connected box (SSH_CONNECTION set), pay actor.ts:62 tailscaleWhois()
54
- * — a real `spawnSync('tailscale', ['whois', ...])` subprocess capped at
55
- * 2s (actor.ts:53 WHOIS_TIMEOUT_MS) — on that literal first call. That
56
- * cost is real but environment-dependent and not reproduced by this bench
57
- * run; it is called out here rather than silently assumed.
58
- *
59
- * The execAgent group spawns the REAL installed `claude` binary (real fork+exec,
60
- * real buildExecEnv/buildExecCommand output) with `passthroughArgs: ['--version']`
61
- * appended. Claude's own arg parsing short-circuits on `--version` before doing
62
- * any network/session work (verified: `claude -p "x" --permission-mode plan
63
- * --version` exits in ~0.2s printing only the version string) — this measures
64
- * exec.ts's own spawn overhead without paying for (or depending on) a real,
65
- * non-deterministic, network-bound agent conversation.
66
- */
67
- import { describe, bench } from 'vitest';
68
- import * as fs from 'node:fs';
69
- import * as path from 'node:path';
70
- import { randomUUID } from 'node:crypto';
71
- import { buildExecEnv, buildExecCommand, execAgent } from './exec.js';
72
- import { resetActorCache } from './actor.js';
73
- import { getUserAgentsDir, getHistoryDir } from './state.js';
74
- import { listInstalledVersions } from './versions.js';
75
- function execOpts(over) {
76
- return { mode: 'plan', effort: 'auto', cwd: process.cwd(), ...over };
77
- }
78
- // Discovered from the REAL version-home layout so this bench runs unmodified
79
- // on any dev box (or degrades gracefully when nothing is installed, e.g. a
80
- // bare CI runner -- this file is not wired into `vitest run`, see
81
- // vitest.config.ts:9, so that only matters for a human running it locally).
82
- const installedClaudeVersions = listInstalledVersions('claude');
83
- const installedCodex = listInstalledVersions('codex').at(-1);
84
- /**
85
- * Whether a claude version's home has a resolvable oauth account email
86
- * (claude-account-token.ts:29 readClaudeAccountEmail's own check), WITHOUT
87
- * going through the secrets bundle at all -- this only reads the plaintext
88
- * `.claude.json` account marker, never a secret value, so it is safe to run
89
- * as bench setup and never prints anything sensitive.
90
- */
91
- function hasResolvableAccount(version) {
92
- const home = path.join(getHistoryDir(), 'versions', 'claude', version, 'home');
93
- for (const p of [path.join(home, '.claude', '.claude.json'), path.join(home, '.claude.json')]) {
94
- try {
95
- const email = JSON.parse(fs.readFileSync(p, 'utf-8')).oauthAccount?.emailAddress;
96
- if (typeof email === 'string' && email.trim().length > 0)
97
- return true;
98
- }
99
- catch { /* try the next candidate path */ }
100
- }
101
- return false;
102
- }
103
- const loggedInClaudeVersion = installedClaudeVersions.find(hasResolvableAccount);
104
- const loggedOutClaudeVersion = installedClaudeVersions.find((v) => !hasResolvableAccount(v));
105
- const metaFile = path.join(getUserAgentsDir(), 'agents.yaml');
106
- /**
107
- * Force the next buildExecEnv call to pay full cold-start cost: clears the
108
- * process-lifetime actor cache (actor.ts:157 `cached`) and bumps agents.yaml's
109
- * mtime so state.ts:1130 readMeta()'s stamp check misses and it re-parses --
110
- * exactly the "another process touched the file" invalidation path the module
111
- * doc at state.ts:1120 describes.
112
- *
113
- * tinybench's bench-level `setup` hook (the only per-task hook vitest's
114
- * `bench(name, fn, options)` actually exposes -- `beforeEach`/`afterEach` are
115
- * tinybench's internal per-Task `FnOptions`, not part of the `Options` shape
116
- * vitest forwards, and using them here is a type error) fires ONCE before a
117
- * task's `run()`, not once per sample. So to measure a genuinely cold call
118
- * rather than one cold sample diluted into a mean with many warm ones, every
119
- * cold bench() below pairs this `setup` with `{ iterations: 1, time: 1,
120
- * warmupIterations: 0, warmupTime: 0 }` -- no warmup, and a near-zero time
121
- * budget so the run loop stops as soon as its 1-sample floor is met.
122
- */
123
- function invalidateCaches(_task, mode) {
124
- if (mode !== 'run')
125
- return;
126
- resetActorCache();
127
- try {
128
- const now = new Date();
129
- fs.utimesSync(metaFile, now, now);
130
- }
131
- catch {
132
- // No ~/.agents/agents.yaml on this box -- readMeta() has nothing cached
133
- // to invalidate either, so the cold/warm distinction collapses; proceed.
134
- }
135
- }
136
- /** Bench options for a single, genuinely-cold sample — see invalidateCaches doc. */
137
- const COLD_SAMPLE_OPTS = { setup: invalidateCaches, iterations: 1, time: 1, warmupIterations: 0, warmupTime: 0 };
138
- describe.skipIf(!loggedInClaudeVersion || !loggedOutClaudeVersion)('buildExecEnv — resolveClaudeSetupToken cost split (exec.ts:424, claude-account-token.ts:51): same code path, signed-in vs not', () => {
139
- bench('pinned version WITH a signed-in account (pays the uncached scrypt decrypt every call)', () => {
140
- buildExecEnv(execOpts({ agent: 'claude', version: loggedInClaudeVersion, sessionId: randomUUID() }));
141
- });
142
- bench('pinned version WITHOUT a signed-in account (readClaudeAccountEmail short-circuits, claude-account-token.ts:56)', () => {
143
- buildExecEnv(execOpts({ agent: 'claude', version: loggedOutClaudeVersion, sessionId: randomUUID() }));
144
- });
145
- });
146
- describe('buildExecEnv — warm cache (steady state: loop.ts/teams/runner calling it repeatedly in one process)', () => {
147
- bench('claude, auto-resolved version (resolveVersion + isVersionInstalled + resolveClaudeSetupToken chain)', () => {
148
- buildExecEnv(execOpts({ agent: 'claude', sessionId: randomUUID() }));
149
- });
150
- bench('codex, explicit pinned version (no claude-account-token path at all)', () => {
151
- buildExecEnv(execOpts({ agent: 'codex', version: installedCodex ?? '0.146.0', sessionId: randomUUID() }));
152
- });
153
- bench('codex, auto-resolved version', () => {
154
- buildExecEnv(execOpts({ agent: 'codex', sessionId: randomUUID() }));
155
- });
156
- });
157
- describe('buildExecEnv — cold cache (single sample: the first call in a fresh `agents run` process)', () => {
158
- bench('claude, auto-resolved version', () => {
159
- buildExecEnv(execOpts({ agent: 'claude', sessionId: randomUUID() }));
160
- }, COLD_SAMPLE_OPTS);
161
- bench.skipIf(!loggedOutClaudeVersion)('claude, pinned version WITHOUT a signed-in account (isolates readMeta+actor cold cost from the scrypt cost above)', () => {
162
- buildExecEnv(execOpts({ agent: 'claude', version: loggedOutClaudeVersion, sessionId: randomUUID() }));
163
- }, COLD_SAMPLE_OPTS);
164
- });
165
- describe('buildExecCommand — argv assembly (runs immediately before spawn in spawnAgent, exec.ts:1745)', () => {
166
- bench('claude headless, explicit pinned version', () => {
167
- buildExecCommand(execOpts({
168
- agent: 'claude', version: loggedOutClaudeVersion ?? installedClaudeVersions.at(-1) ?? '2.1.221', prompt: 'benchmark prompt', sessionId: randomUUID(),
169
- }));
170
- });
171
- bench('claude headless, auto-resolved version + model tier (re-walks resolveVersion a second time, exec.ts:972)', () => {
172
- buildExecCommand(execOpts({
173
- agent: 'claude', prompt: 'benchmark prompt', model: 'sonnet', sessionId: randomUUID(),
174
- }));
175
- });
176
- });
177
- describe.skipIf(!loggedOutClaudeVersion)('execAgent — real subprocess spawn (real claude binary; --version passthrough keeps it network-free)', () => {
178
- bench('claude headless spawn, pinned version without a signed-in account (isolates spawn overhead from the scrypt cost above)', async () => {
179
- await execAgent(execOpts({
180
- agent: 'claude',
181
- version: loggedOutClaudeVersion,
182
- prompt: 'benchmark prompt',
183
- passthroughArgs: ['--version'],
184
- }));
185
- }, { time: 3000, iterations: 15 });
186
- });