@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.
- package/CHANGELOG.md +49 -0
- package/README.md +62 -26
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +54 -10
- package/dist/commands/hosts.js +4 -3
- package/dist/commands/perf.d.ts +10 -0
- package/dist/commands/perf.js +14 -6
- package/dist/commands/projects.js +24 -10
- package/dist/commands/routines.js +31 -4
- package/dist/commands/run-account-picker.d.ts +37 -0
- package/dist/commands/run-account-picker.js +101 -17
- package/dist/commands/secrets.js +86 -2
- package/dist/commands/sessions.d.ts +26 -6
- package/dist/commands/sessions.js +46 -12
- package/dist/commands/teams.js +19 -8
- package/dist/lib/activity.js +45 -99
- package/dist/lib/claude-account-token.js +38 -11
- package/dist/lib/cloud/host.js +1 -0
- package/dist/lib/devices/resolve-target.d.ts +1 -0
- package/dist/lib/devices/resolve-target.js +9 -2
- package/dist/lib/events.d.ts +1 -1
- package/dist/lib/events.js +2 -2
- package/dist/lib/git.d.ts +9 -3
- package/dist/lib/git.js +39 -22
- package/dist/lib/hooks/profile.d.ts +12 -1
- package/dist/lib/hooks/profile.js +6 -1
- package/dist/lib/hooks.js +4 -1
- package/dist/lib/hosts/dispatch.d.ts +10 -0
- package/dist/lib/hosts/dispatch.js +78 -22
- package/dist/lib/hosts/logs.js +11 -1
- package/dist/lib/hosts/passthrough.d.ts +5 -0
- package/dist/lib/hosts/passthrough.js +5 -2
- package/dist/lib/hosts/progress.d.ts +10 -0
- package/dist/lib/hosts/progress.js +37 -5
- package/dist/lib/hosts/ready.d.ts +41 -2
- package/dist/lib/hosts/ready.js +100 -12
- package/dist/lib/hosts/reconcile.d.ts +2 -1
- package/dist/lib/hosts/reconcile.js +13 -5
- package/dist/lib/hosts/reconnect.js +5 -3
- package/dist/lib/hosts/registry.js +1 -0
- package/dist/lib/hosts/tasks.d.ts +1 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/snapshot.js +15 -2
- package/dist/lib/models.d.ts +18 -11
- package/dist/lib/models.js +19 -15
- package/dist/lib/perf/db.js +16 -3
- package/dist/lib/perf/types.d.ts +12 -1
- package/dist/lib/project-probe.d.ts +22 -3
- package/dist/lib/project-probe.js +105 -17
- package/dist/lib/project-status.d.ts +9 -0
- package/dist/lib/project-status.js +15 -0
- package/dist/lib/refresh.js +22 -11
- package/dist/lib/rotate.d.ts +22 -0
- package/dist/lib/rotate.js +26 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/agent.d.ts +19 -3
- package/dist/lib/secrets/agent.js +58 -20
- package/dist/lib/secrets/audit.d.ts +1 -1
- package/dist/lib/secrets/audit.js +2 -0
- package/dist/lib/secrets/bundles.d.ts +3 -3
- package/dist/lib/secrets/bundles.js +15 -5
- package/dist/lib/secrets/filestore.d.ts +2 -0
- package/dist/lib/secrets/filestore.js +4 -0
- package/dist/lib/secrets/session-store.d.ts +7 -0
- package/dist/lib/secrets/session-store.js +21 -0
- package/dist/lib/session/active.d.ts +49 -0
- package/dist/lib/session/active.js +175 -11
- package/dist/lib/session/bash-command.d.ts +16 -0
- package/dist/lib/session/bash-command.js +65 -0
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +32 -15
- package/dist/lib/session/remote-bundle.js +1 -1
- package/dist/lib/staleness/detectors/skills.d.ts +2 -0
- package/dist/lib/staleness/detectors/skills.js +17 -2
- package/dist/lib/staleness/index.d.ts +10 -1
- package/dist/lib/staleness/index.js +28 -3
- package/dist/lib/teams/agents.d.ts +1 -0
- package/dist/lib/teams/agents.js +38 -11
- package/dist/lib/teams/remoteWorktree.d.ts +11 -7
- package/dist/lib/teams/remoteWorktree.js +29 -27
- package/dist/lib/versions.d.ts +1 -0
- package/dist/lib/versions.js +40 -22
- package/package.json +3 -1
- package/dist/lib/exec.bench.d.ts +0 -1
- package/dist/lib/exec.bench.js +0 -186
package/dist/lib/exec.bench.js
DELETED
|
@@ -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
|
-
});
|