@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.
- package/CHANGELOG.md +85 -0
- package/README.md +14 -12
- package/dist/bootstrap.js +3 -6
- package/dist/cli/command-registry.d.ts +0 -7
- package/dist/cli/command-registry.js +1 -21
- package/dist/commands/accounts.d.ts +1 -9
- package/dist/commands/accounts.js +12 -90
- package/dist/commands/apply.d.ts +7 -10
- package/dist/commands/apply.js +15 -35
- package/dist/commands/artifacts.js +4 -5
- package/dist/commands/audit.d.ts +6 -6
- package/dist/commands/audit.js +12 -12
- package/dist/commands/beta.d.ts +7 -1
- package/dist/commands/beta.js +16 -9
- package/dist/commands/commands.js +1 -1
- package/dist/commands/doctor.js +1 -4
- package/dist/commands/events.d.ts +2 -2
- package/dist/commands/events.js +6 -5
- package/dist/commands/exec.d.ts +1 -1
- package/dist/commands/exec.js +8 -4
- package/dist/commands/factory.d.ts +1 -1
- package/dist/commands/fleet-capture.d.ts +1 -1
- package/dist/commands/fleet-capture.js +4 -5
- package/dist/commands/harness.js +1 -49
- package/dist/commands/hooks.js +1 -1
- package/dist/commands/insights.d.ts +1 -1
- package/dist/commands/insights.js +85 -156
- package/dist/commands/menubar.js +58 -1
- package/dist/commands/output.js +2 -2
- package/dist/commands/prune.js +5 -4
- package/dist/commands/routines.test-fixture.d.ts +86 -0
- package/dist/commands/routines.test-fixture.js +345 -0
- package/dist/commands/sessions.test-fixture.d.ts +24 -0
- package/dist/commands/sessions.test-fixture.js +224 -0
- package/dist/commands/setup.js +5 -1
- package/dist/commands/share.d.ts +9 -10
- package/dist/commands/share.js +68 -50
- package/dist/commands/skills.js +1 -1
- package/dist/commands/ssh.js +15 -62
- package/dist/commands/subagents.js +1 -1
- package/dist/commands/view.js +5 -2
- package/dist/lib/analytics/mix-commands.d.ts +6 -28
- package/dist/lib/analytics/mix-commands.js +14 -40
- package/dist/lib/audit/log.d.ts +2 -2
- package/dist/lib/audit/log.js +2 -2
- package/dist/lib/beta.js +1 -1
- package/dist/lib/browser/domain-skills.d.ts +26 -6
- package/dist/lib/browser/domain-skills.js +81 -43
- package/dist/lib/daemon/daemon.js +11 -0
- package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
- package/dist/lib/daemon/daemon.test-fixture.js +71 -0
- package/dist/lib/daemon-ticks.d.ts +38 -1
- package/dist/lib/daemon-ticks.js +52 -5
- package/dist/lib/device-config.js +1 -2
- package/dist/lib/devices/discovery-policy.d.ts +3 -3
- package/dist/lib/devices/discovery-policy.js +3 -3
- package/dist/lib/devices/fleet.d.ts +4 -8
- package/dist/lib/devices/fleet.js +3 -15
- package/dist/lib/devices/health-report.js +3 -3
- package/dist/lib/devices/pool.d.ts +0 -6
- package/dist/lib/devices/pool.js +0 -6
- package/dist/lib/devices/registry.d.ts +0 -15
- package/dist/lib/devices/registry.js +0 -9
- package/dist/lib/devices/stats-cache.d.ts +12 -0
- package/dist/lib/devices/stats-cache.js +24 -5
- package/dist/lib/fleet/capture.d.ts +1 -1
- package/dist/lib/fleet/manifest.js +1 -1
- package/dist/lib/fleet/remote-login.d.ts +2 -2
- package/dist/lib/fleet/remote-login.js +3 -4
- package/dist/lib/hosts/dispatch.d.ts +2 -0
- package/dist/lib/hosts/dispatch.js +8 -1
- package/dist/lib/hosts/passthrough.js +0 -1
- package/dist/lib/hosts/providers/devices.js +1 -13
- package/dist/lib/hosts/ready.d.ts +12 -3
- package/dist/lib/hosts/ready.js +27 -12
- package/dist/lib/hosts/registry.d.ts +4 -5
- package/dist/lib/hosts/registry.js +3 -7
- package/dist/lib/installations/versions.js +21 -2
- package/dist/lib/menubar/install-menubar.d.ts +109 -0
- package/dist/lib/menubar/install-menubar.js +199 -4
- package/dist/lib/remote-agents-json.js +1 -7
- package/dist/lib/routines-placement.d.ts +1 -1
- package/dist/lib/routines-placement.js +1 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
- package/dist/lib/session/active.d.ts +4 -16
- package/dist/lib/session/active.js +2 -6
- package/dist/lib/session/db.d.ts +13 -98
- package/dist/lib/session/db.js +4 -11
- package/dist/lib/session/discover.d.ts +5 -81
- package/dist/lib/session/discover.js +5 -5
- package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
- package/dist/lib/session/remote/remote-bundle.js +61 -0
- package/dist/lib/session/remote/remote-list.d.ts +148 -0
- package/dist/lib/session/remote/remote-list.js +607 -0
- package/dist/lib/session/remote/remote.d.ts +132 -0
- package/dist/lib/session/remote/remote.js +314 -0
- package/dist/lib/session/remote/watch.d.ts +101 -0
- package/dist/lib/session/remote/watch.js +241 -0
- package/dist/lib/session/remote-bundle.d.ts +1 -12
- package/dist/lib/session/remote-bundle.js +3 -61
- package/dist/lib/session/remote-list.d.ts +1 -148
- package/dist/lib/session/remote-list.js +3 -607
- package/dist/lib/session/remote.d.ts +1 -132
- package/dist/lib/session/remote.js +3 -314
- package/dist/lib/session/watch.d.ts +1 -101
- package/dist/lib/session/watch.js +3 -242
- package/dist/lib/share/config.d.ts +12 -2
- package/dist/lib/share/config.js +47 -11
- package/dist/lib/share/delete.js +1 -1
- package/dist/lib/share/publish.js +1 -1
- package/dist/lib/share/worker-template.js +2 -2
- package/dist/lib/smart-launch.d.ts +4 -4
- package/dist/lib/smart-launch.js +8 -18
- package/dist/lib/staleness/index.d.ts +3 -1
- package/dist/lib/staleness/index.js +21 -1
- package/dist/lib/staleness/types.d.ts +11 -0
- package/dist/lib/staleness/writers/commands.js +13 -4
- package/dist/lib/staleness/writers/hooks.js +3 -1
- package/dist/lib/staleness/writers/mcp.d.ts +0 -7
- package/dist/lib/staleness/writers/mcp.js +16 -1
- package/dist/lib/staleness/writers/rules.js +1 -1
- package/dist/lib/staleness/writers/skills.js +3 -1
- package/dist/lib/staleness/writers/subagents.d.ts +0 -9
- package/dist/lib/staleness/writers/subagents.js +18 -1
- package/dist/lib/staleness/writers/types.d.ts +10 -0
- package/dist/lib/startup/command-registry.d.ts +8 -1
- package/dist/lib/startup/command-registry.js +21 -6
- package/dist/lib/startup/root-command.d.ts +18 -1
- package/dist/lib/startup/root-command.js +18 -1
- package/dist/lib/state.js +7 -0
- package/dist/lib/teams/placement-probe.js +27 -12
- package/dist/lib/teams/scheduler.d.ts +3 -1
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/view-types.d.ts +3 -0
- package/package.json +2 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/auth.d.ts +0 -9
- package/dist/commands/auth.js +0 -108
- package/dist/commands/org.d.ts +0 -6
- package/dist/commands/org.js +0 -175
- package/dist/commands/serve.d.ts +0 -10
- package/dist/commands/serve.js +0 -68
- package/dist/commands/trends.d.ts +0 -10
- package/dist/commands/trends.js +0 -12
- package/dist/lib/entitlement.d.ts +0 -31
- package/dist/lib/entitlement.js +0 -137
- package/dist/lib/prix-account.d.ts +0 -158
- package/dist/lib/prix-account.js +0 -214
- package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
- package/dist/lib/serve/control.d.ts +0 -95
- package/dist/lib/serve/control.js +0 -260
- package/dist/lib/serve/data.d.ts +0 -81
- package/dist/lib/serve/data.js +0 -91
- package/dist/lib/serve/page.d.ts +0 -7
- package/dist/lib/serve/page.js +0 -140
- package/dist/lib/serve/server.d.ts +0 -80
- package/dist/lib/serve/server.js +0 -145
- package/dist/lib/serve/stream.d.ts +0 -43
- package/dist/lib/serve/stream.js +0 -116
- package/dist/lib/serve/token.d.ts +0 -35
- package/dist/lib/serve/token.js +0 -85
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* the coaching prose by piping the AGGREGATE (never raw transcripts) through a headless
|
|
32
32
|
* `claude -p`.
|
|
33
33
|
*
|
|
34
|
-
* `agents trends` is
|
|
34
|
+
* Former top-level `agents trends` is `agents insights mix` / `agents insights trends`.
|
|
35
35
|
*/
|
|
36
36
|
import * as fs from 'fs';
|
|
37
37
|
import chalk from 'chalk';
|
|
@@ -49,24 +49,7 @@ import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/
|
|
|
49
49
|
import { registerMixCommands } from '../lib/analytics/mix-commands.js';
|
|
50
50
|
import { registerCostCommand } from './cost.js';
|
|
51
51
|
import { registerOutputCommand } from './output.js';
|
|
52
|
-
import { getTier } from '../lib/entitlement.js';
|
|
53
52
|
const execFileAsync = promisify(execFile);
|
|
54
|
-
/**
|
|
55
|
-
* Plan-tier gate for the behavioural report (RUSH-2424). Free keeps top-line
|
|
56
|
-
* counts and the harness mix (and `insights mix` / `agents perf`, which never
|
|
57
|
-
* enter this file's gating since they're separate command trees); the
|
|
58
|
-
* friction/correction-signal sections, grouping `--by account`, and
|
|
59
|
-
* `--narrative` are paid. `insights mix`/`cost`/`output` are unaffected — this
|
|
60
|
-
* gate applies only to the default behavioural report.
|
|
61
|
-
*/
|
|
62
|
-
const PAID_PLAN_NOTICE = 'Friction and account-split analysis are on the paid plan.';
|
|
63
|
-
function resolveInsightsGate(tier, dim) {
|
|
64
|
-
return {
|
|
65
|
-
tier,
|
|
66
|
-
groupGated: !tier.isPaid && dim === 'account',
|
|
67
|
-
frictionGated: !tier.isPaid,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
53
|
function collectAgent(value, previous) {
|
|
71
54
|
return [...previous, value];
|
|
72
55
|
}
|
|
@@ -225,7 +208,7 @@ function renderHours(hours, out) {
|
|
|
225
208
|
out.push(` ${chalk.cyan(spark)}`);
|
|
226
209
|
out.push(` ${chalk.gray('0h'.padEnd(6))}${chalk.gray('6h'.padEnd(6))}${chalk.gray('12h'.padEnd(6))}${chalk.gray('18h'.padEnd(5))}${chalk.gray('23h')}`);
|
|
227
210
|
}
|
|
228
|
-
function renderReport(groups, dim, meta, actions, harnesses
|
|
211
|
+
function renderReport(groups, dim, meta, actions, harnesses) {
|
|
229
212
|
const out = [];
|
|
230
213
|
const scope = meta.since ? `last ${meta.since}` : 'all time';
|
|
231
214
|
out.push(chalk.bold('Insights') + chalk.gray(` ${scope} · ${meta.analyzed} of ${meta.scanned} sessions`));
|
|
@@ -235,44 +218,28 @@ function renderReport(groups, dim, meta, actions, harnesses, gate) {
|
|
|
235
218
|
console.log(out.join('\n'));
|
|
236
219
|
return;
|
|
237
220
|
}
|
|
238
|
-
let noticePrinted = false;
|
|
239
|
-
const printPlanNotice = () => {
|
|
240
|
-
if (noticePrinted)
|
|
241
|
-
return;
|
|
242
|
-
noticePrinted = true;
|
|
243
|
-
out.push('');
|
|
244
|
-
out.push(chalk.gray(` ${PAID_PLAN_NOTICE}`));
|
|
245
|
-
};
|
|
246
221
|
// Per-group table — the headline, and the thing no sibling command produces.
|
|
247
222
|
// Includes silent-stall counts so harness/account laziness is visible without --json.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
out.push(
|
|
265
|
-
`${
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const dur = g.durationMs > 0 ? formatDuration(g.durationMs) : '—';
|
|
269
|
-
const stalls = gate.frictionGated ? '—' : String(stallOf(g));
|
|
270
|
-
const resumes = gate.frictionGated ? '' : String(resumeOf(g));
|
|
271
|
-
out.push(` ${padToWidth(truncateToWidth(g.label, labelW), labelW)} ` +
|
|
272
|
-
`${chalk.gray(String(g.sessions).padStart(sessW))} ${chalk.gray('sess')} ` +
|
|
273
|
-
`${chalk.green(padToWidth(cost, 9))} ${chalk.gray(padToWidth(dur, 8))} ` +
|
|
274
|
-
`${chalk.cyan(stalls.padStart(stallW))} ${chalk.cyan(resumes)}`);
|
|
275
|
-
}
|
|
223
|
+
out.push('');
|
|
224
|
+
out.push(chalk.bold(`By ${dim}`));
|
|
225
|
+
const labelW = Math.min(Math.max(...groups.map((g) => stringWidth(g.label)), 5), Math.max(16, terminalWidth() - 58));
|
|
226
|
+
const sessW = Math.max(...groups.map((g) => String(g.sessions).length), 3);
|
|
227
|
+
const stallOf = (g) => Object.entries(g.facets.frictionSignals)
|
|
228
|
+
.filter(([k]) => k.startsWith('silent stall:'))
|
|
229
|
+
.reduce((n, [, c]) => n + c, 0);
|
|
230
|
+
const resumeOf = (g) => g.facets.correctionSignals['resume after silent stall'] ?? 0;
|
|
231
|
+
const stallW = Math.max(...groups.map((g) => String(stallOf(g)).length), 5);
|
|
232
|
+
out.push(chalk.gray(` ${padToWidth('', labelW)} ${''.padStart(sessW)} ` +
|
|
233
|
+
`${''.padStart(9)} ${''.padStart(8)} ${'stalls'.padStart(stallW)} resume`));
|
|
234
|
+
for (const g of groups) {
|
|
235
|
+
const cost = g.costUsd > 0 ? formatUsd(g.costUsd) : '—';
|
|
236
|
+
const dur = g.durationMs > 0 ? formatDuration(g.durationMs) : '—';
|
|
237
|
+
const stalls = String(stallOf(g));
|
|
238
|
+
const resumes = String(resumeOf(g));
|
|
239
|
+
out.push(` ${padToWidth(truncateToWidth(g.label, labelW), labelW)} ` +
|
|
240
|
+
`${chalk.gray(String(g.sessions).padStart(sessW))} ${chalk.gray('sess')} ` +
|
|
241
|
+
`${chalk.green(padToWidth(cost, 9))} ${chalk.gray(padToWidth(dur, 8))} ` +
|
|
242
|
+
`${chalk.cyan(stalls.padStart(stallW))} ${chalk.cyan(resumes)}`);
|
|
276
243
|
}
|
|
277
244
|
// Everything below is the whole scope folded together; per-group detail is in --json.
|
|
278
245
|
const all = newFacetAccumulator();
|
|
@@ -281,64 +248,54 @@ function renderReport(groups, dim, meta, actions, harnesses, gate) {
|
|
|
281
248
|
renderCounts('Top tools', topEntries(all.toolCounts, 8), out);
|
|
282
249
|
renderCounts('Languages', topEntries(all.languages, 6), out);
|
|
283
250
|
renderCounts('Models', topEntries(all.models, 6), out);
|
|
284
|
-
// Friction — the section that earns the command.
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
251
|
+
// Friction — the section that earns the command.
|
|
252
|
+
renderCounts('Silent stalls by model', topEntries(all.silentStallsByModel ?? {}, 8), out);
|
|
253
|
+
const gaps = all.responseGaps;
|
|
254
|
+
const silentStalls = Object.entries(all.frictionSignals)
|
|
255
|
+
.filter(([k]) => k.startsWith('silent stall:'))
|
|
256
|
+
.reduce((n, [, c]) => n + c, 0);
|
|
257
|
+
const resumeNudges = all.correctionSignals['resume after silent stall'] ?? 0;
|
|
258
|
+
out.push('');
|
|
259
|
+
out.push(chalk.bold('Friction'));
|
|
260
|
+
out.push(` ${padToWidth('interruptions', 18)} ${chalk.cyan(String(all.interruptions))}` +
|
|
261
|
+
chalk.gray(' turns you cut short'));
|
|
262
|
+
out.push(` ${padToWidth('tool errors', 18)} ${chalk.cyan(String(all.errorCount))}`);
|
|
263
|
+
if (gaps.length > 0) {
|
|
264
|
+
// Same timestamps as silent stalls; this line is the distribution. Silent
|
|
265
|
+
// stalls (below) are the agent-attributed long gaps after the model stopped.
|
|
266
|
+
out.push(` ${padToWidth('gap until next msg', 18)} ` +
|
|
267
|
+
chalk.cyan(`p50 ${Math.round(percentile(gaps, 50))}s`) + chalk.gray(` · p90 ${Math.round(percentile(gaps, 90))}s`) +
|
|
268
|
+
chalk.gray(' after assistant last spoke'));
|
|
288
269
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
if (gaps.length > 0) {
|
|
302
|
-
// Same timestamps as silent stalls; this line is the distribution. Silent
|
|
303
|
-
// stalls (below) are the agent-attributed long gaps after the model stopped.
|
|
304
|
-
out.push(` ${padToWidth('gap until next msg', 18)} ` +
|
|
305
|
-
chalk.cyan(`p50 ${Math.round(percentile(gaps, 50))}s`) + chalk.gray(` · p90 ${Math.round(percentile(gaps, 90))}s`) +
|
|
306
|
-
chalk.gray(' after assistant last spoke'));
|
|
307
|
-
}
|
|
308
|
-
if (silentStalls > 0) {
|
|
309
|
-
out.push(` ${padToWidth('silent stalls', 18)} ${chalk.cyan(String(silentStalls))}` +
|
|
310
|
-
chalk.gray(' agent idle ≥5m until you resumed (also in By ' + dim + ' table)'));
|
|
311
|
-
}
|
|
312
|
-
if (resumeNudges > 0) {
|
|
313
|
-
out.push(` ${padToWidth('resume nudges', 18)} ${chalk.cyan(String(resumeNudges))}` +
|
|
314
|
-
chalk.gray(' "continue"/"keep going" after a silent stall'));
|
|
315
|
-
}
|
|
316
|
-
const errs = topEntries(all.errorCategories, 6);
|
|
317
|
-
if (errs.length > 0) {
|
|
318
|
-
for (const e of errs)
|
|
319
|
-
out.push(` ${chalk.gray('·')} ${padToWidth(e.name, 16)} ${chalk.gray(String(e.count))}`);
|
|
320
|
-
}
|
|
321
|
-
renderCounts('Friction / thrash', topEntries(all.frictionSignals, 10), out);
|
|
322
|
-
renderCounts('Dissatisfaction / corrections', topEntries(all.correctionSignals, 10), out);
|
|
270
|
+
if (silentStalls > 0) {
|
|
271
|
+
out.push(` ${padToWidth('silent stalls', 18)} ${chalk.cyan(String(silentStalls))}` +
|
|
272
|
+
chalk.gray(' agent idle ≥5m until you resumed (also in By ' + dim + ' table)'));
|
|
273
|
+
}
|
|
274
|
+
if (resumeNudges > 0) {
|
|
275
|
+
out.push(` ${padToWidth('resume nudges', 18)} ${chalk.cyan(String(resumeNudges))}` +
|
|
276
|
+
chalk.gray(' "continue"/"keep going" after a silent stall'));
|
|
277
|
+
}
|
|
278
|
+
const errs = topEntries(all.errorCategories, 6);
|
|
279
|
+
if (errs.length > 0) {
|
|
280
|
+
for (const e of errs)
|
|
281
|
+
out.push(` ${chalk.gray('·')} ${padToWidth(e.name, 16)} ${chalk.gray(String(e.count))}`);
|
|
323
282
|
}
|
|
283
|
+
renderCounts('Friction / thrash', topEntries(all.frictionSignals, 10), out);
|
|
284
|
+
renderCounts('Dissatisfaction / corrections', topEntries(all.correctionSignals, 10), out);
|
|
324
285
|
renderCounts('Automatable repeats', topEntries(all.automationSignals, 10), out);
|
|
325
286
|
renderCounts('Harness split', harnesses, out);
|
|
326
287
|
// Actions are built from frictionSignals/correctionSignals/automationSignals
|
|
327
|
-
// together (buildInsightActions)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if (
|
|
331
|
-
out.push('');
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
for (const action of actions.slice(0, 12)) {
|
|
339
|
-
out.push(` ${padToWidth(action.priority, 7)} ${padToWidth(action.category, 11)} ` +
|
|
340
|
-
`${String(action.evidenceCount).padStart(8)} ${padToWidth(action.sampleSessionIds.join(', '), 25)} ${action.action}`);
|
|
341
|
-
}
|
|
288
|
+
// together (buildInsightActions).
|
|
289
|
+
out.push('');
|
|
290
|
+
out.push(chalk.bold('Actions'));
|
|
291
|
+
if (actions.length === 0) {
|
|
292
|
+
out.push(chalk.gray(' No repeated action pattern met the evidence threshold in this window.'));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
out.push(chalk.gray(' pri category evidence sample sessions action'));
|
|
296
|
+
for (const action of actions.slice(0, 12)) {
|
|
297
|
+
out.push(` ${padToWidth(action.priority, 7)} ${padToWidth(action.category, 11)} ` +
|
|
298
|
+
`${String(action.evidenceCount).padStart(8)} ${padToWidth(action.sampleSessionIds.join(', '), 25)} ${action.action}`);
|
|
342
299
|
}
|
|
343
300
|
}
|
|
344
301
|
// Output
|
|
@@ -394,9 +351,7 @@ function renderReport(groups, dim, meta, actions, harnesses, gate) {
|
|
|
394
351
|
out.push('');
|
|
395
352
|
out.push(chalk.yellow(` ${meta.unreadable} transcripts could not be read; their behaviour is missing from these totals.`));
|
|
396
353
|
}
|
|
397
|
-
|
|
398
|
-
// "silent stall" outright — must not render on the free plan.
|
|
399
|
-
if (!gate.frictionGated && all.gapsOverCeiling > 0) {
|
|
354
|
+
if (all.gapsOverCeiling > 0) {
|
|
400
355
|
out.push(chalk.gray(` ${all.gapsOverCeiling} gaps over an hour excluded from p50/p90 (still counted as silent stall: 1h+ when the assistant last spoke).`));
|
|
401
356
|
}
|
|
402
357
|
out.push('');
|
|
@@ -451,23 +406,8 @@ async function renderNarrative(payload) {
|
|
|
451
406
|
process.exitCode = 1;
|
|
452
407
|
}
|
|
453
408
|
}
|
|
454
|
-
/** Facet keys that belong to the paid friction/correction sections — stripped from `--json` on free (RUSH-2424). */
|
|
455
|
-
const PAID_FACET_KEYS = new Set([
|
|
456
|
-
'frictionSignals', 'correctionSignals', 'silentStallsByModel',
|
|
457
|
-
'interruptions', 'errorCount', 'errorCategories', 'responseGaps', 'gapsOverCeiling',
|
|
458
|
-
]);
|
|
459
|
-
function freeFacetSubset(facets) {
|
|
460
|
-
const out = {};
|
|
461
|
-
for (const [key, value] of Object.entries(facets)) {
|
|
462
|
-
if (!PAID_FACET_KEYS.has(key))
|
|
463
|
-
out[key] = value;
|
|
464
|
-
}
|
|
465
|
-
return out;
|
|
466
|
-
}
|
|
467
409
|
async function insightsAction(options) {
|
|
468
410
|
const dim = resolveGroup(options.by);
|
|
469
|
-
const tier = await getTier();
|
|
470
|
-
const gate = resolveInsightsGate(tier, dim);
|
|
471
411
|
const minMessages = Number.parseInt(options.minMessages ?? '2', 10);
|
|
472
412
|
if (!Number.isFinite(minMessages) || minMessages < 0) {
|
|
473
413
|
console.error(chalk.red('error: --min-messages must be a non-negative integer'));
|
|
@@ -533,37 +473,31 @@ async function insightsAction(options) {
|
|
|
533
473
|
unreadable,
|
|
534
474
|
minMessages,
|
|
535
475
|
by: dim,
|
|
536
|
-
plan: { tierName: tier.tierName, isPaid: tier.isPaid },
|
|
537
|
-
...(gate.groupGated || gate.frictionGated ? { notice: PAID_PLAN_NOTICE } : {}),
|
|
538
476
|
overlap,
|
|
539
477
|
// Built from frictionSignals/correctionSignals/automationSignals together
|
|
540
478
|
// (buildInsightActions) — same paid friction/correction data as above.
|
|
541
|
-
actions
|
|
479
|
+
actions,
|
|
542
480
|
harnesses,
|
|
543
|
-
groups:
|
|
481
|
+
groups: groups.map((g) => ({
|
|
544
482
|
key: g.key,
|
|
545
483
|
label: g.label,
|
|
546
484
|
sessions: g.sessions,
|
|
547
485
|
costUsd: g.costUsd,
|
|
548
486
|
durationMs: g.durationMs,
|
|
549
487
|
outputTokens: g.outputTokens,
|
|
550
|
-
...
|
|
488
|
+
...{
|
|
551
489
|
...g.facets,
|
|
552
490
|
responseGapP50: Math.round(percentile(g.facets.responseGaps, 50)),
|
|
553
491
|
responseGapP90: Math.round(percentile(g.facets.responseGaps, 90)),
|
|
554
492
|
responseGapBuckets: bucketGaps(g.facets.responseGaps),
|
|
555
493
|
// The raw sample is large and uninteresting once bucketed.
|
|
556
494
|
responseGaps: undefined,
|
|
557
|
-
}
|
|
495
|
+
},
|
|
558
496
|
})),
|
|
559
497
|
};
|
|
560
498
|
console.log(JSON.stringify(payload, null, 2));
|
|
561
|
-
if (options.narrative)
|
|
562
|
-
|
|
563
|
-
console.error(chalk.gray(` ${PAID_PLAN_NOTICE}`));
|
|
564
|
-
else
|
|
565
|
-
await renderNarrative(payload);
|
|
566
|
-
}
|
|
499
|
+
if (options.narrative)
|
|
500
|
+
await renderNarrative(payload);
|
|
567
501
|
return;
|
|
568
502
|
}
|
|
569
503
|
renderReport(groups, dim, {
|
|
@@ -574,24 +508,18 @@ async function insightsAction(options) {
|
|
|
574
508
|
unreadable,
|
|
575
509
|
minMessages,
|
|
576
510
|
overlap,
|
|
577
|
-
}, actions, harnesses
|
|
511
|
+
}, actions, harnesses);
|
|
578
512
|
if (options.narrative) {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
interruptions: g.facets.interruptions,
|
|
590
|
-
linesTouchedAfter: g.facets.linesTouchedAfter, linesTouchedBefore: g.facets.linesTouchedBefore,
|
|
591
|
-
gitCommits: g.facets.gitCommits,
|
|
592
|
-
replyP50s: Math.round(percentile(g.facets.responseGaps, 50)),
|
|
593
|
-
})));
|
|
594
|
-
}
|
|
513
|
+
await renderNarrative(groups.map((g) => ({
|
|
514
|
+
account: g.label, sessions: g.sessions, costUsd: g.costUsd,
|
|
515
|
+
topTools: topEntries(g.facets.toolCounts, 8),
|
|
516
|
+
languages: topEntries(g.facets.languages, 6),
|
|
517
|
+
errorCategories: topEntries(g.facets.errorCategories, 6),
|
|
518
|
+
interruptions: g.facets.interruptions,
|
|
519
|
+
linesTouchedAfter: g.facets.linesTouchedAfter, linesTouchedBefore: g.facets.linesTouchedBefore,
|
|
520
|
+
gitCommits: g.facets.gitCommits,
|
|
521
|
+
replyP50s: Math.round(percentile(g.facets.responseGaps, 50)),
|
|
522
|
+
})));
|
|
595
523
|
}
|
|
596
524
|
}
|
|
597
525
|
function configureInsightsCommand(cmd) {
|
|
@@ -651,7 +579,8 @@ function configureInsightsCommand(cmd) {
|
|
|
651
579
|
\`agents insights mix\` — cheap counters from sessions.db + usage.db
|
|
652
580
|
Latency is \`agents perf\` (not mix). Quota is \`agents usage\`. Spend is
|
|
653
581
|
\`agents insights cost\`; shipped output is \`agents insights output\`. Skill/slash popularity
|
|
654
|
-
is \`agents sessions stats\`. \`agents trends\` is
|
|
582
|
+
is \`agents sessions stats\`. Former top-level \`agents trends\` is \`agents insights mix\`
|
|
583
|
+
(also \`agents insights trends\`).
|
|
655
584
|
|
|
656
585
|
The behavioural report parses in-scope transcripts once and caches facets; later runs
|
|
657
586
|
re-read only files that changed. \`--refresh\` forces a full re-read.
|
package/dist/commands/menubar.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { setHelpSections } from '../lib/help.js';
|
|
11
|
-
import { enableMenubarService, disableMenubarService, getMenubarStatus, runMenubarSetup, } from '../lib/menubar/install-menubar.js';
|
|
11
|
+
import { enableMenubarService, disableMenubarService, getMenubarStatus, runMenubarSetup, buildMenubarDoctorReport, } from '../lib/menubar/install-menubar.js';
|
|
12
12
|
function notMac() {
|
|
13
13
|
if (process.platform !== 'darwin') {
|
|
14
14
|
console.log(chalk.yellow('AGI Menu is macOS only.'));
|
|
@@ -77,6 +77,32 @@ function printSetupResult(r) {
|
|
|
77
77
|
console.log(chalk.red('AGI Menu not fully configured.') + chalk.gray(' See the failed step above.'));
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
function printDoctorReport(r) {
|
|
81
|
+
console.log(chalk.bold('AGI Menu doctor\n'));
|
|
82
|
+
console.log(` install path ${r.installPath ? chalk.gray(r.installPath) : chalk.red('not installed')}`);
|
|
83
|
+
console.log(` installed version ${r.installedVersion ? chalk.gray(r.installedVersion) : chalk.gray('unknown')}`);
|
|
84
|
+
console.log(` CLI version ${chalk.gray(r.currentVersion)}${r.versionMatches ? '' : chalk.yellow(' (mismatch — `agents menubar setup` updates it)')}`);
|
|
85
|
+
const identity = r.signingIdentity === 'developer-id'
|
|
86
|
+
? chalk.green('Developer ID (update-stable)')
|
|
87
|
+
: r.signingIdentity === 'ad-hoc'
|
|
88
|
+
? chalk.red('ad-hoc (unstable — Accessibility grant breaks on every update)')
|
|
89
|
+
: chalk.gray('unknown (nothing installed)');
|
|
90
|
+
console.log(` signing identity ${identity}`);
|
|
91
|
+
console.log(` running ${r.running ? chalk.green('yes') : chalk.gray('no')}`);
|
|
92
|
+
if (r.staleRunningProcess.length > 0) {
|
|
93
|
+
for (const p of r.staleRunningProcess) {
|
|
94
|
+
const mark = p.stale
|
|
95
|
+
? chalk.red('started BEFORE the on-disk bundle — running the OLD binary')
|
|
96
|
+
: chalk.green('matches the on-disk bundle');
|
|
97
|
+
console.log(` pid ${String(p.pid).padEnd(10)} ${mark}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (r.accessibilityHintNeeded) {
|
|
101
|
+
console.log(chalk.yellow('\n Accessibility grant may not be trusted for the current binary.'));
|
|
102
|
+
console.log(chalk.gray(' Run `agents menubar setup` to reinstall + restart the helper, then grant access:'));
|
|
103
|
+
console.log(chalk.gray(' open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"'));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
80
106
|
export function registerMenubarCommands(program) {
|
|
81
107
|
const menubar = program
|
|
82
108
|
.command('menubar')
|
|
@@ -184,6 +210,37 @@ export function registerMenubarCommands(program) {
|
|
|
184
210
|
}
|
|
185
211
|
printStatus(s);
|
|
186
212
|
});
|
|
213
|
+
const doctor = menubar
|
|
214
|
+
.command('doctor')
|
|
215
|
+
.description('Diagnose AGI Menu: install path, version skew, signing identity, stale process')
|
|
216
|
+
.option('--json', 'Emit machine-readable JSON')
|
|
217
|
+
.action((options) => {
|
|
218
|
+
const r = buildMenubarDoctorReport();
|
|
219
|
+
if (options.json) {
|
|
220
|
+
process.stdout.write(JSON.stringify(r) + '\n');
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (r.platform !== 'darwin') {
|
|
224
|
+
console.log(chalk.yellow('AGI Menu is macOS only.'));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
printDoctorReport(r);
|
|
228
|
+
});
|
|
229
|
+
setHelpSections(doctor, {
|
|
230
|
+
examples: `
|
|
231
|
+
# Why did Accessibility ask again after an update?
|
|
232
|
+
agents menubar doctor
|
|
233
|
+
`,
|
|
234
|
+
notes: `
|
|
235
|
+
Read-only — never installs, restarts, or resets anything. Reports the
|
|
236
|
+
installed bundle's signing identity (ad-hoc signing breaks the
|
|
237
|
+
Accessibility grant on every update; Developer ID is stable across
|
|
238
|
+
updates) and whether a live helper pid started before the on-disk
|
|
239
|
+
bundle's last write — the sign that a running process is still the
|
|
240
|
+
binary an update just replaced. \`agents menubar setup\` is the fix for
|
|
241
|
+
anything this flags.
|
|
242
|
+
`,
|
|
243
|
+
});
|
|
187
244
|
// Bare `agents menubar` -> status.
|
|
188
245
|
menubar.action(() => {
|
|
189
246
|
const s = getMenubarStatus();
|
package/dist/commands/output.js
CHANGED
|
@@ -10,7 +10,7 @@ import { formatUsd, PRICING_VERSION } from '../lib/pricing/index.js';
|
|
|
10
10
|
import { formatDuration } from '../lib/session/render.js';
|
|
11
11
|
import { terminalWidth, truncateToWidth, padToWidth } from '../lib/session/width.js';
|
|
12
12
|
import { collectGitOutput } from '../lib/output/git-output.js';
|
|
13
|
-
import { loadDevices
|
|
13
|
+
import { loadDevices } from '../lib/devices/registry.js';
|
|
14
14
|
import { machineId } from '../lib/session/sync/config.js';
|
|
15
15
|
import { stripClixml } from '../lib/hosts/remote-cmd.js';
|
|
16
16
|
const execFileAsync = promisify(execFile);
|
|
@@ -189,7 +189,7 @@ async function outputAction(options) {
|
|
|
189
189
|
const self = machineId();
|
|
190
190
|
const registry = await loadDevices();
|
|
191
191
|
const remotes = Object.values(registry)
|
|
192
|
-
.filter(d => d.tailscale?.online && d.name !== self
|
|
192
|
+
.filter(d => d.tailscale?.online && d.name !== self)
|
|
193
193
|
.map(d => d.name);
|
|
194
194
|
if (!options.json)
|
|
195
195
|
console.error(chalk.gray(`Folding in ${remotes.length} online device${remotes.length !== 1 ? 's' : ''}…`));
|
package/dist/commands/prune.js
CHANGED
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
* agents-cli.
|
|
13
13
|
* - Runs: routine execution logs, keeping only the last N per job.
|
|
14
14
|
*
|
|
15
|
-
* Sync (additive: copy missing/changed files into version homes) is
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* Sync (additive: copy missing/changed files into version homes) is the job
|
|
16
|
+
* of `agents sync` / `agents refresh` — the shim's launch hook
|
|
17
|
+
* (`agents sync --launch`) is project-scoped only and deliberately skips
|
|
18
|
+
* version-home reconciliation. Pruning, however, is destructive, so it stays
|
|
19
|
+
* explicit.
|
|
19
20
|
*
|
|
20
21
|
* Default scope: each agent's currently-pinned default version for orphan
|
|
21
22
|
* cleanup, plus the standard cross-agent version-dedup pass. Pass `--all`
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe } from 'vitest';
|
|
2
|
+
import { spawnSync, spawn } from 'child_process';
|
|
3
|
+
/**
|
|
4
|
+
* Shared fixture for the routines.*.test.ts suite slices (RUSH-2819).
|
|
5
|
+
*
|
|
6
|
+
* routines.test.ts was one 2,249-line file measured at ~194s of test time —
|
|
7
|
+
* one of the slowest files in CI, serializing an entire fork while every
|
|
8
|
+
* other selected file finished. The suite is split into topical slices so
|
|
9
|
+
* vitest's per-file fork parallelism can spread the subprocess-heavy tests
|
|
10
|
+
* across workers; the helpers each slice shares live here.
|
|
11
|
+
*
|
|
12
|
+
* Every test spawns the real CLI (`node --import tsx src/index.ts routines ...`)
|
|
13
|
+
* against an isolated mkdtemp HOME — no live ~/.agents state, no mocks, no
|
|
14
|
+
* imported writeJob/readJob. Modeled on `routines-webhook.test.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const REPO_ROOT: string;
|
|
17
|
+
export declare const TSX_IMPORT: string;
|
|
18
|
+
export declare const CLI_ENTRYPOINT: string;
|
|
19
|
+
export declare const describeRoutines: typeof describe.skip;
|
|
20
|
+
/** Provision an isolated HOME with agents.yaml, .system/.git, and optional routines + device registry. */
|
|
21
|
+
export declare function makeHome(opts?: {
|
|
22
|
+
jobs?: Record<string, unknown>[];
|
|
23
|
+
projectJobs?: Record<string, unknown>[];
|
|
24
|
+
registry?: Record<string, unknown>;
|
|
25
|
+
deviceRoutines?: Record<string, string[]>;
|
|
26
|
+
/** Full mkdtemp prefix override — the daemon harness scopes its leak sweep by it. */
|
|
27
|
+
tmpPrefix?: string;
|
|
28
|
+
}): string;
|
|
29
|
+
/** Run `agents routines <args>` against an isolated HOME. */
|
|
30
|
+
export declare function run(home: string, args: string[], extraEnv?: Record<string, string>, cwd?: string): ReturnType<typeof spawnSync>;
|
|
31
|
+
export declare function readRoutineYaml(home: string, name: string): Record<string, unknown> | null;
|
|
32
|
+
export declare function writeRunMeta(home: string, jobName: string, runId: string, meta: Record<string, unknown>): void;
|
|
33
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Per-file isolated-daemon harness (RUSH-2367, RUSH-2819). Each slice that
|
|
36
|
+
* spawns a real `__daemon-run` process against an isolated HOME calls this
|
|
37
|
+
* once at module scope and registers its own leak detector — the tracked pid
|
|
38
|
+
* set MUST be private to the file that spawned it, since vitest's per-file
|
|
39
|
+
* fork isolation means the leak sweep only ever needs to answer for what THIS
|
|
40
|
+
* file started.
|
|
41
|
+
*
|
|
42
|
+
* `fileSlug` MUST be unique per test file: it names the mkdtemp prefix
|
|
43
|
+
* `makeDaemonHome` uses, and the CI leak sweep matches ONLY that prefix.
|
|
44
|
+
* The suite slices run in parallel forks, so a sweep over the shared
|
|
45
|
+
* `agents-routines-test-` prefix would catch a sibling file's still-running
|
|
46
|
+
* daemon and kill it mid-test — exactly what failed the first CI run of the
|
|
47
|
+
* RUSH-2819 split (run 32552954164: add's sweep killed a live daemon whose
|
|
48
|
+
* HOME belonged to a concurrently running slice).
|
|
49
|
+
*/
|
|
50
|
+
export declare function createDaemonHarness(fileSlug: string): {
|
|
51
|
+
startIsolatedDaemon: (home: string) => {
|
|
52
|
+
child: ReturnType<typeof spawn>;
|
|
53
|
+
pidPromise: Promise<number | null>;
|
|
54
|
+
};
|
|
55
|
+
stopIsolatedDaemon: (child: ReturnType<typeof spawn>) => Promise<void>;
|
|
56
|
+
registerLeakDetector: () => void;
|
|
57
|
+
makeDaemonHome: (opts?: Omit<Parameters<typeof makeHome>[0], 'tmpPrefix'>) => string;
|
|
58
|
+
};
|
|
59
|
+
export declare const baseJob: {
|
|
60
|
+
name: string;
|
|
61
|
+
schedule: string;
|
|
62
|
+
agent: string;
|
|
63
|
+
prompt: string;
|
|
64
|
+
cwd: string;
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
};
|
|
67
|
+
export declare const registry: {
|
|
68
|
+
'yosemite-s0': {
|
|
69
|
+
name: string;
|
|
70
|
+
platform: string;
|
|
71
|
+
};
|
|
72
|
+
'mac-mini': {
|
|
73
|
+
name: string;
|
|
74
|
+
platform: string;
|
|
75
|
+
};
|
|
76
|
+
zion: {
|
|
77
|
+
name: string;
|
|
78
|
+
platform: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
export declare function readDeviceRoutines(home: string, device: string): string[];
|
|
82
|
+
export declare function writeDeviceRoutines(home: string, device: string, routines: string[]): void;
|
|
83
|
+
/** Write a minimal project YAML so listProjectDefs() sees it. */
|
|
84
|
+
export declare function writeProject(home: string, name: string): void;
|
|
85
|
+
/** Env that points the projects dir at the isolated home. */
|
|
86
|
+
export declare function projectsEnv(home: string): Record<string, string>;
|