@phnx-labs/agents-cli 1.22.22 → 1.22.23

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 (91) hide show
  1. package/CHANGELOG.md +209 -0
  2. package/README.md +8 -2
  3. package/dist/commands/doctor.js +15 -7
  4. package/dist/commands/exec.js +20 -6
  5. package/dist/commands/focus.d.ts +76 -4
  6. package/dist/commands/focus.js +219 -40
  7. package/dist/commands/fork.d.ts +20 -2
  8. package/dist/commands/fork.js +91 -64
  9. package/dist/commands/go.d.ts +25 -0
  10. package/dist/commands/go.js +63 -2
  11. package/dist/commands/harness-wizard.d.ts +206 -0
  12. package/dist/commands/harness-wizard.js +403 -0
  13. package/dist/commands/harness.d.ts +12 -0
  14. package/dist/commands/harness.js +97 -101
  15. package/dist/commands/resume.js +10 -3
  16. package/dist/commands/secrets.js +25 -30
  17. package/dist/commands/sessions-resume.d.ts +21 -3
  18. package/dist/commands/sessions-resume.js +50 -11
  19. package/dist/commands/sessions.d.ts +81 -5
  20. package/dist/commands/sessions.js +325 -66
  21. package/dist/commands/watchdog.js +13 -2
  22. package/dist/lib/agents.d.ts +1 -1
  23. package/dist/lib/agents.js +132 -0
  24. package/dist/lib/codex-policy.d.ts +17 -0
  25. package/dist/lib/codex-policy.js +48 -0
  26. package/dist/lib/crabbox/lease.d.ts +25 -0
  27. package/dist/lib/crabbox/lease.js +62 -0
  28. package/dist/lib/daemon.js +70 -0
  29. package/dist/lib/exec.d.ts +4 -0
  30. package/dist/lib/exec.js +88 -54
  31. package/dist/lib/feed-broadcast.d.ts +1 -20
  32. package/dist/lib/feed-broadcast.js +31 -1
  33. package/dist/lib/hooks.js +12 -2
  34. package/dist/lib/mcp.js +44 -0
  35. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
  36. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  37. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
  38. package/dist/lib/models.d.ts +0 -5
  39. package/dist/lib/models.js +48 -0
  40. package/dist/lib/plugin-marketplace.js +9 -0
  41. package/dist/lib/pricing/prices.json +119 -92
  42. package/dist/lib/pricing/table.js +13 -0
  43. package/dist/lib/remote-agents-json.d.ts +29 -1
  44. package/dist/lib/remote-agents-json.js +47 -10
  45. package/dist/lib/resources/mcp.js +2 -0
  46. package/dist/lib/resources/permissions.js +3 -0
  47. package/dist/lib/resources/types.d.ts +2 -1
  48. package/dist/lib/runner.js +28 -19
  49. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  50. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  51. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  52. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  53. package/dist/lib/secrets/index.d.ts +15 -1
  54. package/dist/lib/secrets/index.js +118 -49
  55. package/dist/lib/secrets/reaper.d.ts +87 -0
  56. package/dist/lib/secrets/reaper.js +184 -0
  57. package/dist/lib/secrets/remote.d.ts +29 -0
  58. package/dist/lib/secrets/remote.js +37 -1
  59. package/dist/lib/session/active.d.ts +36 -1
  60. package/dist/lib/session/active.js +60 -19
  61. package/dist/lib/session/actor-sidecar.d.ts +14 -0
  62. package/dist/lib/session/actor-sidecar.js +67 -4
  63. package/dist/lib/session/db.d.ts +1 -1
  64. package/dist/lib/session/db.js +32 -1
  65. package/dist/lib/session/discover.js +168 -0
  66. package/dist/lib/session/parse.d.ts +10 -0
  67. package/dist/lib/session/parse.js +98 -0
  68. package/dist/lib/session/remote-list.d.ts +10 -1
  69. package/dist/lib/session/remote-list.js +2 -8
  70. package/dist/lib/session/remote.d.ts +57 -3
  71. package/dist/lib/session/remote.js +90 -26
  72. package/dist/lib/session/resume-command.d.ts +6 -0
  73. package/dist/lib/session/resume-command.js +8 -0
  74. package/dist/lib/session/session-cache.d.ts +173 -0
  75. package/dist/lib/session/session-cache.js +399 -0
  76. package/dist/lib/session/types.d.ts +1 -1
  77. package/dist/lib/session/types.js +1 -1
  78. package/dist/lib/session/width.d.ts +1 -1
  79. package/dist/lib/session/width.js +12 -2
  80. package/dist/lib/shims.d.ts +2 -2
  81. package/dist/lib/shims.js +40 -5
  82. package/dist/lib/staleness/writers/hooks.js +1 -1
  83. package/dist/lib/types.d.ts +1 -1
  84. package/dist/lib/usage.d.ts +13 -0
  85. package/dist/lib/usage.js +215 -0
  86. package/dist/lib/versions.js +13 -2
  87. package/package.json +1 -1
  88. package/dist/bin/agents +0 -0
  89. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  90. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  91. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
@@ -21,7 +21,10 @@ import { getActiveSessions, findSessionFileForKind } from '../lib/session/active
21
21
  import { gatherRemoteActive } from '../lib/session/remote-active.js';
22
22
  import { discoverSessions } from '../lib/session/discover.js';
23
23
  import { deriveShortId } from '../lib/session/short-id.js';
24
- import { dedupeByMachineSession, mergeLocalFirst, pickSessionInteractive } from './sessions.js';
24
+ import { dedupeByMachineSession, mergeLocalFirst, pickSessionInteractive, matchesLiveStatus, filterSessionsByQuery, formatPickerLabel, pickerColumnsFor, } from './sessions.js';
25
+ import { buildPreview } from './sessions-picker.js';
26
+ import { multiItemPicker } from '../lib/picker.js';
27
+ import { isPromptCancelled } from './utils.js';
25
28
  import { focusAction } from './focus.js';
26
29
  import { machineId } from '../lib/session/sync/config.js';
27
30
  import { attachTmux, runTmux } from '../lib/tmux/binary.js';
@@ -40,11 +43,33 @@ export function registerGoCommand(program) {
40
43
  await focusAction(id, { local: opts.local, attachOnly: true });
41
44
  });
42
45
  }
46
+ /**
47
+ * Scope a live-session pool by device and live status. Pure so the `focus`
48
+ * device/status filters are unit-testable without touching the sweep. `hosts`
49
+ * keeps only sessions whose `machine` is in the set (local rows carry `self`,
50
+ * remote rows carry their peer tag); `statuses` reuses `--active`'s exact
51
+ * `matchesLiveStatus` derivation rather than a parallel status table.
52
+ */
53
+ export function filterLivePool(sessions, opts = {}) {
54
+ let out = sessions;
55
+ if (opts.hosts?.length) {
56
+ const set = new Set(opts.hosts);
57
+ out = out.filter((s) => !!s.machine && set.has(s.machine));
58
+ }
59
+ if (opts.statuses?.length) {
60
+ out = out.filter((s) => opts.statuses.some((status) => matchesLiveStatus(s, status)));
61
+ }
62
+ return out;
63
+ }
43
64
  /**
44
65
  * Live jump targets (local + remote), keyed by session id. Cloud is excluded by
45
66
  * default (it has no local pid to attach), but `detach` opts in with
46
67
  * `includeCloud` so it can resolve a cloud id and refuse it with a clear message
47
68
  * instead of a bare "no live session".
69
+ *
70
+ * `hosts` scopes the sweep to named devices — the fan-out only dials them, and the
71
+ * pool is then filtered to `s.machine ∈ hosts` so a stray local row can't leak in.
72
+ * `statuses` narrows to the live-state words `--active` uses (orphan/crashed/…).
48
73
  */
49
74
  export async function gatherLiveTargets(local, opts = {}) {
50
75
  const self = machineId();
@@ -55,11 +80,12 @@ export async function gatherLiveTargets(local, opts = {}) {
55
80
  let active = localActive;
56
81
  if (!local) {
57
82
  try {
58
- const remote = await gatherRemoteActive();
83
+ const remote = await gatherRemoteActive(opts.hosts);
59
84
  active = dedupeByMachineSession([...localActive, ...remote.sessions]);
60
85
  }
61
86
  catch { /* remote sweep is best-effort */ }
62
87
  }
88
+ active = filterLivePool(active, { hosts: opts.hosts, statuses: opts.statuses });
63
89
  const activeById = new Map();
64
90
  for (const s of active) {
65
91
  if (!s.sessionId)
@@ -80,6 +106,41 @@ export async function pickLiveTarget(activeById, self, message, enterHint) {
80
106
  return null;
81
107
  return activeById.get(picked.session.id) ?? null;
82
108
  }
109
+ /**
110
+ * Multi-select over the live sessions' rich SessionMeta (same rows as
111
+ * `pickLiveTarget`, but a checkbox picker) — the plural sibling that lets `focus`
112
+ * open several sessions at once. Mirrors `sessions resume`'s `multiItemPicker`
113
+ * wiring; returns the chosen live sessions in pick order, or `[]` on cancel.
114
+ */
115
+ export async function pickLiveTargets(activeById, self, message) {
116
+ const pool = await buildLivePool(activeById, self);
117
+ if (pool.length === 0)
118
+ return [];
119
+ // gutter: 6 = the multi-select cursor + checkbox ('> [x] ') multiItemPicker prepends.
120
+ const cols = { ...pickerColumnsFor(pool), gutter: 6 };
121
+ let chosen;
122
+ try {
123
+ chosen = await multiItemPicker({
124
+ message,
125
+ items: pool,
126
+ filter: (q) => (q.trim() ? filterSessionsByQuery(pool, q) : pool),
127
+ labelFor: (s, q) => formatPickerLabel(s, q, cols),
128
+ keyFor: (s) => s.id,
129
+ buildPreview,
130
+ pageSize: 15,
131
+ emptyMessage: 'No live sessions match.',
132
+ enterHint: 'focus',
133
+ });
134
+ }
135
+ catch (err) {
136
+ if (isPromptCancelled(err))
137
+ return [];
138
+ throw err;
139
+ }
140
+ if (!chosen)
141
+ return [];
142
+ return chosen.map((m) => activeById.get(m.id)).filter((s) => !!s);
143
+ }
83
144
  /**
84
145
  * Map each live session to its rich SessionMeta (worktree/PR/changes/tools/tests
85
146
  * via the shared picker), reusing `discoverSessions`. Remote or unindexed live
@@ -0,0 +1,206 @@
1
+ /**
2
+ * Shared interactive step engine for `agents harness` create + edit.
3
+ *
4
+ * A single engine drives BOTH the create wizard (`agents harness add`/`fork`,
5
+ * previously `runHarnessWizard`) and the new edit wizard (`agents harness edit`,
6
+ * which was flag-only before). A "step" is a self-contained unit — decide whether
7
+ * it runs for the current draft, then prompt/validate/apply — so the two modes
8
+ * differ only in their step list, not in the runner.
9
+ *
10
+ * Design goals (RUSH-2219, parent RUSH-2218):
11
+ * - One runner, two modes. `create` builds a new harness from a source; `edit`
12
+ * loads an existing profile and re-asks each field pre-filled with its value.
13
+ * - Every step is skippable. A flag that already supplied the value pre-fills
14
+ * the draft and its step is not re-asked — so non-interactive scripting via
15
+ * flags is unchanged and the wizard only asks for what is missing.
16
+ * - Pure and injectable. The engine talks to the user through {@link WizardIO},
17
+ * an injected seam. Tests drive a scripted fake IO and assert which steps ran,
18
+ * with no TTY. {@link defaultWizardIO} is the production driver over
19
+ * `@inquirer/prompts`.
20
+ * - Typed extension points, not stubs. The three sibling subtasks plug in via
21
+ * {@link WizardHooks} without editing the engine: RUSH-2220 (model catalog +
22
+ * secrets surface) via `pickModel`, RUSH-2221 (connection test) via
23
+ * `connectionTest`, RUSH-2222 (edit matrix) via `editable`. Each hook is a
24
+ * real no-op extension point — absent, the scaffold falls back to today's
25
+ * behavior (free-text model, no test, resolver-sourced editability). None of
26
+ * them fakes a result. RUSH-2223 (cross-host portability, `fork --to-host`)
27
+ * extends the create source step; the seam is `HarnessDraft.toHost`.
28
+ */
29
+ import type { AgentId } from '../lib/types.js';
30
+ import { type Profile } from '../lib/profiles.js';
31
+ /** Whether the wizard is creating a new harness or editing an existing one. */
32
+ export type WizardMode = 'create' | 'edit';
33
+ /** A single `select` choice. `disabled` greys the row (used by the edit matrix). */
34
+ export interface WizardChoice<T> {
35
+ name: string;
36
+ value: T;
37
+ disabled?: boolean | string;
38
+ }
39
+ /**
40
+ * The prompt seam the engine drives. Injected so the engine is testable with no
41
+ * TTY: the production implementation ({@link defaultWizardIO}) wraps
42
+ * `@inquirer/prompts`; a test passes a scripted fake that records calls and
43
+ * returns canned answers.
44
+ */
45
+ export interface WizardIO {
46
+ select<T>(opts: {
47
+ message: string;
48
+ choices: WizardChoice<T>[];
49
+ default?: T;
50
+ }): Promise<T>;
51
+ input(opts: {
52
+ message: string;
53
+ default?: string;
54
+ validate?: (v: string) => true | string;
55
+ }): Promise<string>;
56
+ password(opts: {
57
+ message: string;
58
+ }): Promise<string>;
59
+ confirm(opts: {
60
+ message: string;
61
+ default?: boolean;
62
+ }): Promise<boolean>;
63
+ /** Emit an informational line — a disabled field's reason, or a preset note. */
64
+ note(message: string): void;
65
+ }
66
+ /**
67
+ * The mutable draft threaded through every step. It is a superset of both modes'
68
+ * fields; a step reads what it needs and records its answer here. `create` maps
69
+ * the finished draft into `{ source, name, opts }` for `runForkFlow`; `edit` maps
70
+ * it into the `EditOptions` shape the flag-driven edit path already persists — so
71
+ * a wizard-built and a hand-written harness are byte-identical after save.
72
+ */
73
+ export interface HarnessDraft {
74
+ readonly mode: WizardMode;
75
+ /** create: the fork source (a native agent id or an existing harness name). */
76
+ source?: string;
77
+ /** The resolved host CLI — drives the editability seams. edit: the profile host. */
78
+ host?: AgentId;
79
+ name?: string;
80
+ model?: string;
81
+ baseUrl?: string;
82
+ authProvider?: string;
83
+ /** `<bundle>` or `<bundle>:<key>` — a value copied out of an agents secrets bundle. */
84
+ fromSecrets?: string;
85
+ version?: string;
86
+ description?: string;
87
+ fallbackModel?: string;
88
+ /** edit: the profile being edited, providing current values. Read-only. */
89
+ readonly original?: Profile;
90
+ /**
91
+ * RUSH-2223 seam (cross-host portability): the target host to re-key a cloned
92
+ * harness onto (`fork --to-host <host>`). The scaffold never sets it; the
93
+ * portability subtask reads it in an extended source step. Left here so the
94
+ * draft shape is stable when that lands.
95
+ */
96
+ toHost?: AgentId;
97
+ /** The user chose "build custom" over a preset (create only). */
98
+ custom?: boolean;
99
+ /** The preset a create run selected, if any. */
100
+ preset?: string;
101
+ /** Pre-computed default for the name prompt (source or preset name). */
102
+ defaultName?: string;
103
+ /** The provider step has run (distinguishes "no auth chosen" from "not asked"). */
104
+ providerAsked?: boolean;
105
+ }
106
+ /**
107
+ * What the engine does with a step for a given draft:
108
+ * - `'run'` prompt the user, validate, and apply into the draft.
109
+ * - `'skip'` value already supplied (by a flag) or step N/A — silent.
110
+ * - `{ disabled: … }` the step does not apply to this host; surface the reason
111
+ * (the field reads as greyed/disabled) but do not prompt.
112
+ * This is the seam the edit matrix (RUSH-2222) drives.
113
+ */
114
+ export type StepDecision = 'run' | 'skip' | {
115
+ disabled: string;
116
+ };
117
+ /** One wizard step: decide, then (only when decided `'run'`) prompt + apply. */
118
+ export interface WizardStep {
119
+ readonly id: string;
120
+ decide(draft: HarnessDraft): StepDecision;
121
+ run(io: WizardIO, draft: HarnessDraft, hooks: WizardHooks): Promise<void>;
122
+ }
123
+ /** Per-host editability — which params this host's API format lets you change. */
124
+ export interface HarnessEditable {
125
+ model: boolean;
126
+ baseUrl: boolean;
127
+ auth: boolean;
128
+ version: boolean;
129
+ fallback: boolean;
130
+ }
131
+ /**
132
+ * Resolver-sourced editability, the scaffold default behind the RUSH-2222 seam.
133
+ * Every field is read from the same maps the run-time resolver uses
134
+ * (`baseUrlEnvKeyForHost` / `authEnvKeyForHost` / `isSelfUpdatingAgent`), never a
135
+ * table hardcoded alongside them — so the wizard's enable/disable can never drift
136
+ * from what a run actually honors (repo rule: the capability table stays truthful,
137
+ * in lockstep with the code). RUSH-2222 may replace this via {@link WizardHooks.editable}
138
+ * to add per-param reasons; it must stay sourced from the resolver.
139
+ */
140
+ export declare function defaultEditable(host: AgentId): HarnessEditable;
141
+ /** Outcome of a connection test (RUSH-2221 fills the real classifier). */
142
+ export interface ConnectionTestResult {
143
+ ok: boolean;
144
+ /** Machine-readable class when it failed (auth / endpoint / model / unknown). */
145
+ reason?: 'auth' | 'endpoint' | 'model' | 'unknown';
146
+ message?: string;
147
+ }
148
+ /**
149
+ * Extension points the sibling subtasks fill without touching the engine. Each is
150
+ * a real no-op-by-default seam: absent, the scaffold uses today's behavior; none
151
+ * fabricates a result.
152
+ */
153
+ export interface WizardHooks {
154
+ /**
155
+ * RUSH-2220 — model catalog pick. Given the resolved host (+ version and the
156
+ * current value in edit), return a chosen model id, or `null` to fall through
157
+ * to the free-text prompt. Absent → always free-text (today's behavior).
158
+ */
159
+ pickModel?: (io: WizardIO, host: AgentId | undefined, version: string | undefined, current: string | undefined) => Promise<string | null>;
160
+ /**
161
+ * RUSH-2221 — connection test after configure, before save. Absent → no test
162
+ * (the wizard saves without one, exactly as today).
163
+ */
164
+ connectionTest?: (draft: HarnessDraft) => Promise<ConnectionTestResult>;
165
+ /**
166
+ * RUSH-2222 — per-host editability matrix. Absent → {@link defaultEditable}.
167
+ */
168
+ editable?: (host: AgentId) => HarnessEditable;
169
+ }
170
+ /** Resolve the host CLI a fork `source` runs under (the host `buildFork` will use). */
171
+ export declare function hostForSource(source: string | undefined): AgentId | undefined;
172
+ /**
173
+ * The engine. Walk the steps in order; for each, ask `decide` what to do, then
174
+ * prompt only when it says `'run'`. A `{ disabled }` decision surfaces the reason
175
+ * and moves on; `'skip'` is silent. Returns the finished draft.
176
+ */
177
+ export declare function runWizardSteps(steps: WizardStep[], draft: HarnessDraft, io: WizardIO, hooks?: WizardHooks): Promise<HarnessDraft>;
178
+ /**
179
+ * The create step list — the shape of `agents harness add`/`fork`. Faithful to
180
+ * the previous `runHarnessWizard` sequence (source → preset|custom → model →
181
+ * provider → base URL → name → key source), re-expressed as engine steps so the
182
+ * sibling subtasks can gate/replace individual steps. Produces the same
183
+ * `{ source, name, opts }` draft the fork flow already persists.
184
+ */
185
+ export declare function createSteps(): WizardStep[];
186
+ /**
187
+ * The edit step list — `agents harness edit <name>` with no flags on a TTY. Each
188
+ * step is pre-filled with the profile's current value and gated by the host's
189
+ * editability matrix (RUSH-2222 seam): an unsupported param reads as disabled with
190
+ * a reason instead of being silently accepted. Records into the same draft, which
191
+ * `runEditWizard` maps to the `EditOptions` shape the flag path already persists.
192
+ */
193
+ export declare function editSteps(original: Profile): WizardStep[];
194
+ /**
195
+ * Run the connection-test hook against a finished draft, if one is wired
196
+ * (RUSH-2221). Returns `null` when no hook is present (no test performed) so the
197
+ * caller can distinguish "not tested" from "tested and passed". Separated from the
198
+ * step list because the test needs the assembled profile, which the caller builds.
199
+ */
200
+ export declare function runConnectionTest(draft: HarnessDraft, hooks: WizardHooks): Promise<ConnectionTestResult | null>;
201
+ /**
202
+ * Production {@link WizardIO} over `@inquirer/prompts`, lazy-imported so the
203
+ * dependency loads only when a wizard actually runs. `note` prints to stderr so it
204
+ * never contaminates a `--json`/piped stdout.
205
+ */
206
+ export declare function defaultWizardIO(): Promise<WizardIO>;