@phnx-labs/agents-cli 1.22.106 → 1.22.107
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 +26 -0
- package/README.md +9 -5
- package/dist/commands/exec.d.ts +78 -8
- package/dist/commands/exec.js +272 -51
- package/dist/commands/run-account-picker.d.ts +11 -0
- package/dist/commands/run-account-picker.js +11 -1
- package/dist/lib/hosts/dispatch.d.ts +1 -1
- package/dist/lib/hosts/dispatch.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.107
|
|
4
|
+
|
|
5
|
+
- **A bare interactive run places itself like `--device auto` (PHNX-4083).**
|
|
6
|
+
`agents run <harness>` with no prompt on a real TTY (no `--json`) now
|
|
7
|
+
auto-places onto a fleet worker — the same engine, pool, and banner as
|
|
8
|
+
`--device auto` — instead of always running on the machine you typed it at;
|
|
9
|
+
headless runs (any prompt, `--json`, teams/routines/hooks) are unchanged and
|
|
10
|
+
still run in place. To keep a local interactive run, pass
|
|
11
|
+
`--device <this machine>` or pick this machine (listed first) in the
|
|
12
|
+
`<harness>@` device picker; when placement finds no healthy device (empty
|
|
13
|
+
pool, or the PHNX-4051 stale-usage refusal) the run fails loud with the
|
|
14
|
+
placement error plus `Run here instead: agents run <harness> --device
|
|
15
|
+
<this machine>` — never a silent local fallback. Source:
|
|
16
|
+
`src/commands/exec.ts`, `src/commands/run-account-picker.ts`.
|
|
17
|
+
|
|
18
|
+
- **Run pickers move to `#` (account) and `@` (device), `#@` asks both
|
|
19
|
+
(PHNX-4083).** `agents run claude#` now opens the account picker (the old
|
|
20
|
+
`claude@` menu, same rows and code path); `agents run claude@` opens the new
|
|
21
|
+
fleet device picker — this machine first, offline rows disabled, cached-state
|
|
22
|
+
age in the prompt — and `claude#@` asks the account first, then the device,
|
|
23
|
+
dispatching with the picked account label. A picker combined with an explicit
|
|
24
|
+
pin of the same thing (`claude@2.1.218#`, `claude#work#`, `claude@@`) or a
|
|
25
|
+
conflicting flag (`--account`, `--device`/`--on`/`--computer`/`--host`) fails
|
|
26
|
+
loud; a cancelled menu launches nothing. Source: `src/commands/exec.ts`,
|
|
27
|
+
`src/lib/hosts/dispatch.ts`.
|
|
28
|
+
|
|
3
29
|
## 1.22.106
|
|
4
30
|
|
|
5
31
|
- Fixed release packages omitting the session-tracking hook. Release qualification now uses the complete CLI build, so installed agents can record the account that owns each native session.
|
package/README.md
CHANGED
|
@@ -202,11 +202,15 @@ agents run claude "refactor auth module" --mode edit --fallback codex,antigravit
|
|
|
202
202
|
# Picks the signed-in account you haven't used recently.
|
|
203
203
|
agents run claude "summarize recent commits" --strategy balanced
|
|
204
204
|
|
|
205
|
-
# Or choose one account/version interactively for only this run.
|
|
206
|
-
agents run claude
|
|
207
|
-
agents run codex
|
|
208
|
-
agents run claude
|
|
209
|
-
agents run claude
|
|
205
|
+
# Or choose one account/version interactively for only this run (# = account picker).
|
|
206
|
+
agents run claude#
|
|
207
|
+
agents run codex# "review this branch"
|
|
208
|
+
agents run claude# --device auto # pick the device, then choose there
|
|
209
|
+
agents run claude# --device yosemite-s0 # choose from one device's accounts
|
|
210
|
+
|
|
211
|
+
# @ picks the device instead; #@ asks both (account first, then device).
|
|
212
|
+
agents run claude@ # pick the device for this run
|
|
213
|
+
agents run claude#@ # pick the account, then the device
|
|
210
214
|
```
|
|
211
215
|
|
|
212
216
|
`--strategy balanced` spreads work across available versions of the same agent -- useful when you have multiple accounts and want to avoid burning through one. When a Claude run reports a session limit, agents-cli records the stated reset time, shows `session-limited` in `agents view`, and excludes that account until the reset. When every account is rate-limited, the run exits nonzero naming each excluded account and the earliest window reset (use `--strategy pinned` to force a rate-limited default) -- it never launches into an exhausted account. A logged-out default is never forced: unpinned dispatch picks a signed-in version on the execution device instead of dying on a credential-less default home.
|
package/dist/commands/exec.d.ts
CHANGED
|
@@ -10,13 +10,24 @@ import type { ExecEffort } from '../lib/exec.js';
|
|
|
10
10
|
import { RUN_AUTO_KEYWORD } from '../lib/types.js';
|
|
11
11
|
/** Validate a caller-supplied session id before it reaches tmux, paths, indexes, or remote dispatch. */
|
|
12
12
|
export declare function parseExplicitSessionId(value: string): string;
|
|
13
|
-
export interface
|
|
14
|
-
|
|
13
|
+
export interface RunPickerMarkers {
|
|
14
|
+
accountPicker: boolean;
|
|
15
|
+
devicePicker: boolean;
|
|
15
16
|
normalizedAgentSpec: string;
|
|
16
17
|
valid: boolean;
|
|
18
|
+
reason?: string;
|
|
17
19
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Parse the trailing picker markers on an `agents run` agent spec. A terminal
|
|
22
|
+
* run of `#`/`@` characters requests the account picker (`#`) and/or the
|
|
23
|
+
* device picker (`@`) — each at most once, in either order (`claude#@`,
|
|
24
|
+
* `claude@#`). Stripping them yields the normalized spec (`claude#work@` →
|
|
25
|
+
* `claude#work`, version pins and `#label` account pins intact). A picker
|
|
26
|
+
* cannot combine with an explicit pin of what it picks: `claude@2.1.218#`
|
|
27
|
+
* (the account picker already chooses the version — the old `claude@2.1.218@`
|
|
28
|
+
* rule), `claude#work#`, `claude@@`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseRunPickerMarkers(agentSpec: string): RunPickerMarkers;
|
|
20
31
|
/**
|
|
21
32
|
* The `--device` alias family — the flags that mean "dispatch this run to another
|
|
22
33
|
* machine over SSH". `--device` is canonical; `--on`/`--computer` are hidden
|
|
@@ -31,11 +42,29 @@ export declare function hostTargetGiven(options: {
|
|
|
31
42
|
on?: string;
|
|
32
43
|
computer?: string;
|
|
33
44
|
}): string[];
|
|
34
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Return every option whose selection semantics conflict with an account
|
|
47
|
+
* choice (`agent#`). Device routing is deliberately absent: the marker rides
|
|
48
|
+
* the hop and the peer picks from ITS slots.
|
|
49
|
+
*/
|
|
35
50
|
export declare function runAccountPickerConflicts(options: {
|
|
36
51
|
resume?: string | boolean;
|
|
37
52
|
strategy?: string;
|
|
38
53
|
balanced?: boolean;
|
|
54
|
+
lease?: string | boolean;
|
|
55
|
+
box?: string;
|
|
56
|
+
account?: string;
|
|
57
|
+
host?: string;
|
|
58
|
+
device?: string;
|
|
59
|
+
on?: string;
|
|
60
|
+
computer?: string;
|
|
61
|
+
}): string[];
|
|
62
|
+
/**
|
|
63
|
+
* Return every option that already decides where the run lands, so a device
|
|
64
|
+
* choice (`agent@`) would be silently ignored: any explicit host flag, and the
|
|
65
|
+
* lease/box paths, which own placement outright.
|
|
66
|
+
*/
|
|
67
|
+
export declare function runDevicePickerConflicts(options: {
|
|
39
68
|
lease?: string | boolean;
|
|
40
69
|
box?: string;
|
|
41
70
|
host?: string;
|
|
@@ -47,11 +76,15 @@ export { RUN_AUTO_KEYWORD };
|
|
|
47
76
|
/**
|
|
48
77
|
* Whether `run auto` should default its host layer to the affinity pick (the
|
|
49
78
|
* same machinery as `--device auto`). False when the caller pinned any host
|
|
50
|
-
* flag, and false when this process was itself dispatched by a host run —
|
|
51
|
-
* dispatcher exports AGENTS_RUN_AUTO_HOST_RESOLVED=1 into the remote SHELL
|
|
79
|
+
* flag, and false when this process was itself dispatched by a host run —
|
|
80
|
+
* the dispatcher exports AGENTS_RUN_AUTO_HOST_RESOLVED=1 into the remote SHELL
|
|
52
81
|
* (hosts/dispatch.ts remoteRunShellPrelude) because it already resolved the
|
|
53
82
|
* host layer, and re-picking here would chain-hop the run across the fleet.
|
|
54
|
-
*
|
|
83
|
+
* An INTERACTIVE dispatch of a named harness also reaches the remote as a bare
|
|
84
|
+
* `agents run <harness>` (its argv forwards without the routing flag), so the
|
|
85
|
+
* interactive prelude's AGENTS_REMOTE_INTERACTIVE=1 counts as "already placed"
|
|
86
|
+
* too — without it the remote would re-place the run and ping-pong across the
|
|
87
|
+
* fleet. Pure so the pinning matrix is unit-testable.
|
|
55
88
|
*/
|
|
56
89
|
export declare function runAutoDefaultsToAffinity(options: {
|
|
57
90
|
host?: string;
|
|
@@ -59,6 +92,43 @@ export declare function runAutoDefaultsToAffinity(options: {
|
|
|
59
92
|
on?: string;
|
|
60
93
|
computer?: string;
|
|
61
94
|
}, env?: NodeJS.ProcessEnv): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Whether a bare human-facing `agents run <harness>` — no prompt, so an
|
|
97
|
+
* interactive TUI run — defaults its placement to `--device auto` (PHNX-4083).
|
|
98
|
+
* A marker left off is decided for you: no `#` means balanced rotation, and no
|
|
99
|
+
* `@` (and no other placement flag) now means automatic device placement — the
|
|
100
|
+
* same engine as `--device auto`, whose pool never contains a box marked
|
|
101
|
+
* `personal`. ALL of these must hold:
|
|
102
|
+
*
|
|
103
|
+
* - no prompt (headless runs — teams, routines, hooks, `run <agent> "…"` —
|
|
104
|
+
* keep running in place, unchanged);
|
|
105
|
+
* - a human-facing surface: a real TTY and no `--json`. This is the same
|
|
106
|
+
* two-condition gate `signInLaunchDecision` uses in run-account-picker.ts —
|
|
107
|
+
* reused here through isHumanFacingRun, not re-derived;
|
|
108
|
+
* - no device-picker marker (`agent@` already chose the device; picking this
|
|
109
|
+
* machine there is a plain local run);
|
|
110
|
+
* - no `--resume` / `--lease` / `--box` / `--cloud` (those own placement);
|
|
111
|
+
* - the host layer is unpinned and this process is not itself a dispatched
|
|
112
|
+
* hop — delegated to runAutoDefaultsToAffinity, which encodes both.
|
|
113
|
+
*
|
|
114
|
+
* Pure so the default-placement matrix is unit-testable.
|
|
115
|
+
*/
|
|
116
|
+
export declare function bareInteractiveRunDefaultsToDeviceAuto(options: {
|
|
117
|
+
host?: string;
|
|
118
|
+
device?: string;
|
|
119
|
+
on?: string;
|
|
120
|
+
computer?: string;
|
|
121
|
+
resume?: string | boolean;
|
|
122
|
+
lease?: string | boolean;
|
|
123
|
+
box?: string;
|
|
124
|
+
cloud?: boolean;
|
|
125
|
+
}, run: {
|
|
126
|
+
prompt?: string;
|
|
127
|
+
devicePickerRequested?: boolean;
|
|
128
|
+
}, surface: {
|
|
129
|
+
tty: boolean;
|
|
130
|
+
json?: boolean;
|
|
131
|
+
}, env?: NodeJS.ProcessEnv): boolean;
|
|
62
132
|
/**
|
|
63
133
|
* Whether an interactive host dispatch must mint a correlation launch id and
|
|
64
134
|
* resolve the remote session via the launch-id join (RUSH-2034), rather than
|
package/dist/commands/exec.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isTierToken } from '../lib/model-tiers.js';
|
|
|
11
11
|
import { RUN_AUTO_KEYWORD } from '../lib/types.js';
|
|
12
12
|
import { setHelpSections } from '../lib/help.js';
|
|
13
13
|
import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
|
|
14
|
+
import { isHumanFacingRun } from './run-account-picker.js';
|
|
14
15
|
import { getUserAgentsDir, readMeta } from '../lib/state.js';
|
|
15
16
|
import { parseLoopInterval } from '../lib/loop.js';
|
|
16
17
|
import { AGENTS, resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
@@ -36,15 +37,47 @@ export function parseExplicitSessionId(value) {
|
|
|
36
37
|
}
|
|
37
38
|
return value;
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Parse the trailing picker markers on an `agents run` agent spec. A terminal
|
|
42
|
+
* run of `#`/`@` characters requests the account picker (`#`) and/or the
|
|
43
|
+
* device picker (`@`) — each at most once, in either order (`claude#@`,
|
|
44
|
+
* `claude@#`). Stripping them yields the normalized spec (`claude#work@` →
|
|
45
|
+
* `claude#work`, version pins and `#label` account pins intact). A picker
|
|
46
|
+
* cannot combine with an explicit pin of what it picks: `claude@2.1.218#`
|
|
47
|
+
* (the account picker already chooses the version — the old `claude@2.1.218@`
|
|
48
|
+
* rule), `claude#work#`, `claude@@`.
|
|
49
|
+
*/
|
|
50
|
+
export function parseRunPickerMarkers(agentSpec) {
|
|
51
|
+
let rest = agentSpec;
|
|
52
|
+
let accountPicker = false;
|
|
53
|
+
let devicePicker = false;
|
|
54
|
+
let reason;
|
|
55
|
+
while (rest.endsWith('#') || rest.endsWith('@')) {
|
|
56
|
+
const marker = rest.endsWith('#') ? '#' : '@';
|
|
57
|
+
if (marker === '#') {
|
|
58
|
+
if (accountPicker && reason === undefined)
|
|
59
|
+
reason = `the # picker marker may appear at most once in '${agentSpec}'`;
|
|
60
|
+
accountPicker = true;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
if (devicePicker && reason === undefined)
|
|
64
|
+
reason = `the @ picker marker may appear at most once in '${agentSpec}'`;
|
|
65
|
+
devicePicker = true;
|
|
66
|
+
}
|
|
67
|
+
rest = rest.slice(0, -1);
|
|
68
|
+
}
|
|
69
|
+
if (reason === undefined) {
|
|
70
|
+
if (!rest) {
|
|
71
|
+
reason = `'${agentSpec}' names no agent before the picker markers`;
|
|
72
|
+
}
|
|
73
|
+
else if (accountPicker && (rest.includes('@') || rest.includes('#'))) {
|
|
74
|
+
reason = `an explicit pin in '${rest}' already selects what the # account picker chooses`;
|
|
75
|
+
}
|
|
76
|
+
else if (devicePicker && rest.includes('@')) {
|
|
77
|
+
reason = `an explicit pin in '${rest}' cannot combine with the @ device picker`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { accountPicker, devicePicker, normalizedAgentSpec: rest, valid: reason === undefined, reason };
|
|
48
81
|
}
|
|
49
82
|
/**
|
|
50
83
|
* The `--device` alias family — the flags that mean "dispatch this run to another
|
|
@@ -57,7 +90,11 @@ export function parseRunAccountPickerRequest(agentSpec) {
|
|
|
57
90
|
export function hostTargetGiven(options) {
|
|
58
91
|
return [options.host, options.device, options.on, options.computer].filter((v) => !!v);
|
|
59
92
|
}
|
|
60
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* Return every option whose selection semantics conflict with an account
|
|
95
|
+
* choice (`agent#`). Device routing is deliberately absent: the marker rides
|
|
96
|
+
* the hop and the peer picks from ITS slots.
|
|
97
|
+
*/
|
|
61
98
|
export function runAccountPickerConflicts(options) {
|
|
62
99
|
const conflicts = [];
|
|
63
100
|
if (options.resume !== undefined)
|
|
@@ -66,6 +103,21 @@ export function runAccountPickerConflicts(options) {
|
|
|
66
103
|
conflicts.push('--strategy');
|
|
67
104
|
if (options.balanced)
|
|
68
105
|
conflicts.push('--balanced');
|
|
106
|
+
if (options.lease)
|
|
107
|
+
conflicts.push('--lease');
|
|
108
|
+
if (options.box)
|
|
109
|
+
conflicts.push('--box');
|
|
110
|
+
if (options.account)
|
|
111
|
+
conflicts.push(`--account ${options.account}`);
|
|
112
|
+
return conflicts;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Return every option that already decides where the run lands, so a device
|
|
116
|
+
* choice (`agent@`) would be silently ignored: any explicit host flag, and the
|
|
117
|
+
* lease/box paths, which own placement outright.
|
|
118
|
+
*/
|
|
119
|
+
export function runDevicePickerConflicts(options) {
|
|
120
|
+
const conflicts = hostTargetGiven(options).map((h) => `--device ${h}`);
|
|
69
121
|
if (options.lease)
|
|
70
122
|
conflicts.push('--lease');
|
|
71
123
|
if (options.box)
|
|
@@ -82,16 +134,54 @@ export { RUN_AUTO_KEYWORD };
|
|
|
82
134
|
/**
|
|
83
135
|
* Whether `run auto` should default its host layer to the affinity pick (the
|
|
84
136
|
* same machinery as `--device auto`). False when the caller pinned any host
|
|
85
|
-
* flag, and false when this process was itself dispatched by a host run —
|
|
86
|
-
* dispatcher exports AGENTS_RUN_AUTO_HOST_RESOLVED=1 into the remote SHELL
|
|
137
|
+
* flag, and false when this process was itself dispatched by a host run —
|
|
138
|
+
* the dispatcher exports AGENTS_RUN_AUTO_HOST_RESOLVED=1 into the remote SHELL
|
|
87
139
|
* (hosts/dispatch.ts remoteRunShellPrelude) because it already resolved the
|
|
88
140
|
* host layer, and re-picking here would chain-hop the run across the fleet.
|
|
89
|
-
*
|
|
141
|
+
* An INTERACTIVE dispatch of a named harness also reaches the remote as a bare
|
|
142
|
+
* `agents run <harness>` (its argv forwards without the routing flag), so the
|
|
143
|
+
* interactive prelude's AGENTS_REMOTE_INTERACTIVE=1 counts as "already placed"
|
|
144
|
+
* too — without it the remote would re-place the run and ping-pong across the
|
|
145
|
+
* fleet. Pure so the pinning matrix is unit-testable.
|
|
90
146
|
*/
|
|
91
147
|
export function runAutoDefaultsToAffinity(options, env = process.env) {
|
|
92
148
|
if (hostTargetGiven(options).length > 0)
|
|
93
149
|
return false;
|
|
94
|
-
|
|
150
|
+
if (env.AGENTS_RUN_AUTO_HOST_RESOLVED === '1')
|
|
151
|
+
return false;
|
|
152
|
+
return env.AGENTS_REMOTE_INTERACTIVE !== '1';
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Whether a bare human-facing `agents run <harness>` — no prompt, so an
|
|
156
|
+
* interactive TUI run — defaults its placement to `--device auto` (PHNX-4083).
|
|
157
|
+
* A marker left off is decided for you: no `#` means balanced rotation, and no
|
|
158
|
+
* `@` (and no other placement flag) now means automatic device placement — the
|
|
159
|
+
* same engine as `--device auto`, whose pool never contains a box marked
|
|
160
|
+
* `personal`. ALL of these must hold:
|
|
161
|
+
*
|
|
162
|
+
* - no prompt (headless runs — teams, routines, hooks, `run <agent> "…"` —
|
|
163
|
+
* keep running in place, unchanged);
|
|
164
|
+
* - a human-facing surface: a real TTY and no `--json`. This is the same
|
|
165
|
+
* two-condition gate `signInLaunchDecision` uses in run-account-picker.ts —
|
|
166
|
+
* reused here through isHumanFacingRun, not re-derived;
|
|
167
|
+
* - no device-picker marker (`agent@` already chose the device; picking this
|
|
168
|
+
* machine there is a plain local run);
|
|
169
|
+
* - no `--resume` / `--lease` / `--box` / `--cloud` (those own placement);
|
|
170
|
+
* - the host layer is unpinned and this process is not itself a dispatched
|
|
171
|
+
* hop — delegated to runAutoDefaultsToAffinity, which encodes both.
|
|
172
|
+
*
|
|
173
|
+
* Pure so the default-placement matrix is unit-testable.
|
|
174
|
+
*/
|
|
175
|
+
export function bareInteractiveRunDefaultsToDeviceAuto(options, run, surface, env = process.env) {
|
|
176
|
+
if (run.prompt !== undefined)
|
|
177
|
+
return false;
|
|
178
|
+
if (!isHumanFacingRun({ tty: surface.tty, json: surface.json === true }))
|
|
179
|
+
return false;
|
|
180
|
+
if (run.devicePickerRequested)
|
|
181
|
+
return false;
|
|
182
|
+
if (options.resume !== undefined || options.lease || options.box || options.cloud)
|
|
183
|
+
return false;
|
|
184
|
+
return runAutoDefaultsToAffinity(options, env);
|
|
95
185
|
}
|
|
96
186
|
/**
|
|
97
187
|
* Whether an interactive host dispatch must mint a correlation launch id and
|
|
@@ -426,7 +516,7 @@ async function handleTerminalHandoff(agentSpec, options, prompt) {
|
|
|
426
516
|
// isValidAgent / profileExists / resolveWorkflowRef chain below), so this must
|
|
427
517
|
// accept all three. Gating on the agent table alone rejected every profile —
|
|
428
518
|
// the whole Kimi/DeepSeek/Qwen/GLM path — for `--terminal` runs only.
|
|
429
|
-
const rawTarget =
|
|
519
|
+
const rawTarget = parseRunPickerMarkers(agentSpec).normalizedAgentSpec.split('#')[0].split('@')[0];
|
|
430
520
|
const knownAgent = resolveAgentName(rawTarget);
|
|
431
521
|
const [{ profileExists }, { resolveWorkflowRef }] = await Promise.all([
|
|
432
522
|
import('../lib/profiles.js'),
|
|
@@ -587,12 +677,22 @@ export function registerRunCommand(program) {
|
|
|
587
677
|
# Headless, can edit: have the agent make changes
|
|
588
678
|
agents run claude "fix lint errors in src/" --mode edit
|
|
589
679
|
|
|
590
|
-
# Interactive (TUI)
|
|
680
|
+
# Interactive (TUI): a bare run places itself like --device auto (a fleet
|
|
681
|
+
# worker, TUI forwarded over SSH); pin the device to stay on this machine
|
|
591
682
|
agents run claude
|
|
683
|
+
agents run claude --device zion # stay local (or pick this machine in claude@)
|
|
684
|
+
|
|
685
|
+
# Pick a signed-in account/version for only this run (# = account picker)
|
|
686
|
+
agents run claude#
|
|
592
687
|
|
|
593
|
-
# Pick
|
|
688
|
+
# Pick the device this run lands on (@ = device picker); this machine
|
|
689
|
+
# first, offline rows disabled, fleet state aged in the prompt
|
|
594
690
|
agents run claude@
|
|
595
691
|
|
|
692
|
+
# Ask both, account first, then device — the run dispatches with the
|
|
693
|
+
# picked account to the picked device
|
|
694
|
+
agents run claude#@
|
|
695
|
+
|
|
596
696
|
# Full-auto: affinity-pick the host, then the harness with the most
|
|
597
697
|
# account headroom, then a balanced account on it
|
|
598
698
|
agents run auto "fix the flaky test" --mode edit
|
|
@@ -677,9 +777,21 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
677
777
|
best-account headroom), and the account (the strategy above). Zero
|
|
678
778
|
healthy accounts on any harness exits nonzero with the earliest reset.
|
|
679
779
|
|
|
680
|
-
|
|
681
|
-
installed account for this run
|
|
682
|
-
and available limits; unsafe accounts stay visible but
|
|
780
|
+
Pickers: a trailing # opens the account picker (agents run claude#) to
|
|
781
|
+
choose one installed account for this run — rows show identity, login
|
|
782
|
+
state, plan, and available limits; unsafe accounts stay visible but
|
|
783
|
+
disabled. A trailing @ opens the device picker (agents run claude@);
|
|
784
|
+
#@ asks both, account first, then device. The pickers cannot combine
|
|
785
|
+
with an explicit pin of the same thing (--account, --device/--on/
|
|
786
|
+
--computer/--host) or with --strategy/--balanced/--resume/--lease/--box.
|
|
787
|
+
|
|
788
|
+
Interactive placement: a bare 'agents run <harness>' (no prompt, real TTY)
|
|
789
|
+
places itself like --device auto — a fleet worker runs it, with the TUI
|
|
790
|
+
forwarded over SSH. Headless runs (any prompt, --json, no TTY) are
|
|
791
|
+
unchanged: they run in place. To stay on this machine, pass
|
|
792
|
+
--device <this machine>, or pick this machine (listed first) in the
|
|
793
|
+
'<harness>@' device picker. When placement finds no healthy device the
|
|
794
|
+
run fails loud and names the local spellings.
|
|
683
795
|
|
|
684
796
|
Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
685
797
|
|
|
@@ -834,19 +946,25 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
834
946
|
host: options.host,
|
|
835
947
|
});
|
|
836
948
|
}
|
|
837
|
-
//
|
|
838
|
-
//
|
|
839
|
-
//
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
949
|
+
// Trailing picker markers request an interactive choice: `#` picks the
|
|
950
|
+
// account (and the version it signs in with), `@` picks the device, and
|
|
951
|
+
// `#@`/`@#` ask both. Strip only those terminal markers; concrete
|
|
952
|
+
// agent@version pins and `#label` account pins retain their meaning in
|
|
953
|
+
// every dispatch path below.
|
|
954
|
+
const pickerMarkers = parseRunPickerMarkers(agentSpec);
|
|
955
|
+
const accountPickerRequested = pickerMarkers.accountPicker;
|
|
956
|
+
const devicePickerRequested = pickerMarkers.devicePicker;
|
|
957
|
+
let normalizedAgentSpec = pickerMarkers.normalizedAgentSpec;
|
|
958
|
+
if (!pickerMarkers.valid) {
|
|
959
|
+
console.error(chalk.red(`Invalid run picker target: ${agentSpec}. ` +
|
|
960
|
+
`${pickerMarkers.reason ?? 'unrecognized picker markers'}. ` +
|
|
961
|
+
'Use agents run <agent># to pick an account, <agent>@ to pick a device, <agent>#@ for both.'));
|
|
845
962
|
process.exit(1);
|
|
846
963
|
}
|
|
847
964
|
// Peel `#name` off before --device dispatch so the selector rides the hop
|
|
848
965
|
// unchanged and the peer resolves ITS slot (PHNX-3940 T5). The later local
|
|
849
|
-
// parse is idempotent when the values match.
|
|
966
|
+
// parse is idempotent when the values match. A bare trailing `#` was the
|
|
967
|
+
// picker marker above, so it can never land here as an empty label.
|
|
850
968
|
{
|
|
851
969
|
const labelParts = normalizedAgentSpec.split('#');
|
|
852
970
|
if (labelParts.length > 2 || labelParts[1] === '') {
|
|
@@ -890,14 +1008,22 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
890
1008
|
console.error(chalk.red(hardDeprecationError(runBaseAgentId)));
|
|
891
1009
|
process.exit(1);
|
|
892
1010
|
}
|
|
893
|
-
//
|
|
894
|
-
// so an
|
|
895
|
-
// "cannot be combined with --
|
|
1011
|
+
// Picker conflict checks run BEFORE --resume/--device auto may place the
|
|
1012
|
+
// run implicitly, so an implied placement never surfaces as a fake
|
|
1013
|
+
// "cannot be combined with --device" when the user only typed a marker.
|
|
896
1014
|
if (accountPickerRequested) {
|
|
897
1015
|
const conflicts = runAccountPickerConflicts(options);
|
|
898
1016
|
if (conflicts.length > 0) {
|
|
899
1017
|
console.error(chalk.red(`Account selection with ${agentSpec} cannot be combined with ${conflicts.join(', ')}. ` +
|
|
900
|
-
'Remove the conflicting selector, or
|
|
1018
|
+
'Remove the conflicting selector, or pin the target explicitly (agent@version or agent#label).'));
|
|
1019
|
+
process.exit(1);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (devicePickerRequested) {
|
|
1023
|
+
const conflicts = runDevicePickerConflicts(options);
|
|
1024
|
+
if (conflicts.length > 0) {
|
|
1025
|
+
console.error(chalk.red(`Device selection with ${agentSpec} cannot be combined with ${conflicts.join(', ')}. ` +
|
|
1026
|
+
'Remove one — the picker already chooses where the run lands.'));
|
|
901
1027
|
process.exit(1);
|
|
902
1028
|
}
|
|
903
1029
|
}
|
|
@@ -1013,7 +1139,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1013
1139
|
process.exit(1);
|
|
1014
1140
|
}
|
|
1015
1141
|
if (accountPickerRequested) {
|
|
1016
|
-
console.error(chalk.red(`agents run auto picks the harness and account itself — the trailing
|
|
1142
|
+
console.error(chalk.red(`agents run auto picks the harness and account itself — the trailing-# account picker needs a concrete harness (agents run <harness>#).`));
|
|
1143
|
+
process.exit(1);
|
|
1144
|
+
}
|
|
1145
|
+
if (devicePickerRequested) {
|
|
1146
|
+
console.error(chalk.red(`agents run auto picks the harness and its placement itself — the trailing-@ device picker needs a concrete harness (agents run <harness>@).`));
|
|
1017
1147
|
process.exit(1);
|
|
1018
1148
|
}
|
|
1019
1149
|
// Host layer: with no explicit --device, default to the
|
|
@@ -1022,19 +1152,100 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1022
1152
|
if (!resolvedResumeSource && runAutoDefaultsToAffinity(options))
|
|
1023
1153
|
options.device = 'auto';
|
|
1024
1154
|
}
|
|
1155
|
+
// The picker menus run HERE — after every conflict check, before
|
|
1156
|
+
// placement resolves — so the picked account labels the device rows and
|
|
1157
|
+
// the picked device is concrete for every host branch below.
|
|
1158
|
+
// `#@`/`@#`: account first, against THIS machine's slots; the picked
|
|
1159
|
+
// account becomes options.account and the device menu shows its
|
|
1160
|
+
// ✓/– per-device mark. A remote device pick dispatches with
|
|
1161
|
+
// `<agent>#<label>` so the peer resolves its own slot.
|
|
1162
|
+
// `@`: just the device menu; picking this machine is a plain local run.
|
|
1163
|
+
// Either menu cancelled (Esc/Ctrl-C) launches nothing and exits 0.
|
|
1164
|
+
let upFrontAccountPick = null;
|
|
1165
|
+
if (accountPickerRequested && devicePickerRequested) {
|
|
1166
|
+
const baseName = normalizedAgentSpec.split('#')[0].split('@')[0];
|
|
1167
|
+
const baseAgentId = resolveAgentName(baseName);
|
|
1168
|
+
const { profileExists: baseProfileExists } = await import('../lib/profiles.js');
|
|
1169
|
+
if (!baseAgentId || baseProfileExists(baseName)) {
|
|
1170
|
+
console.error(chalk.red(baseProfileExists(baseName)
|
|
1171
|
+
? `Account selection is not available for custom harness '${baseName}'. Run its concrete host agent with # instead.`
|
|
1172
|
+
: `Account selection is not available for '${baseName}'. Run a concrete agent with # instead.`));
|
|
1173
|
+
process.exit(1);
|
|
1174
|
+
}
|
|
1175
|
+
const { supportsAccountInspection: baseSupportsInspection, agentLabel: baseAgentLabel, ACCOUNT_INSPECTION_AGENT_IDS: inspectionAgentIds } = await import('../lib/agents.js');
|
|
1176
|
+
if (!baseSupportsInspection(baseAgentId)) {
|
|
1177
|
+
console.error(chalk.red(`${baseAgentLabel(baseAgentId)} does not expose local account state, so agents-cli cannot safely select an account.`));
|
|
1178
|
+
console.error(chalk.gray(`Supported account pickers: ${inspectionAgentIds.join(', ')}`));
|
|
1179
|
+
process.exit(1);
|
|
1180
|
+
}
|
|
1181
|
+
const { pickRunAccountCandidate } = await import('./run-account-picker.js');
|
|
1182
|
+
const selected = await pickRunAccountCandidate(baseAgentId);
|
|
1183
|
+
if (!selected)
|
|
1184
|
+
return; // Esc/Ctrl-C: launch nothing.
|
|
1185
|
+
if (selected.nativeAccount)
|
|
1186
|
+
options.account = selected.nativeAccount;
|
|
1187
|
+
upFrontAccountPick = selected;
|
|
1188
|
+
if (!options.quiet) {
|
|
1189
|
+
const identity = selected.accountLabel || 'signed-in account';
|
|
1190
|
+
process.stderr.write(chalk.gray(`[agents] selected ${identity} · ${baseAgentId}@${selected.version} for this run\n`));
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (devicePickerRequested) {
|
|
1194
|
+
// --resume above may have pinned the host implicitly (recovery to the
|
|
1195
|
+
// source peer); a device menu against it would be a silent no-op.
|
|
1196
|
+
const impliedHost = hostTargetGiven(options);
|
|
1197
|
+
if (impliedHost.length > 0) {
|
|
1198
|
+
console.error(chalk.red(`Device selection with ${agentSpec} cannot be combined with the placement --resume already chose (${impliedHost.join(', ')}). ` +
|
|
1199
|
+
'Remove one — both pick where the run lands.'));
|
|
1200
|
+
process.exit(1);
|
|
1201
|
+
}
|
|
1202
|
+
const { pickRunDevice } = await import('./run-device-picker.js');
|
|
1203
|
+
const baseName = normalizedAgentSpec.split('#')[0].split('@')[0];
|
|
1204
|
+
const pickedDevice = await pickRunDevice({
|
|
1205
|
+
agent: (resolveAgentName(baseName) ?? baseName),
|
|
1206
|
+
accountLabel: options.account,
|
|
1207
|
+
});
|
|
1208
|
+
if (pickedDevice === null)
|
|
1209
|
+
return; // Esc/Ctrl-C: launch nothing.
|
|
1210
|
+
const { isSelfHost } = await import('../lib/devices/self-host.js');
|
|
1211
|
+
// Picking this machine is a plain local run — leave options.device unset.
|
|
1212
|
+
if (!isSelfHost(pickedDevice))
|
|
1213
|
+
options.device = pickedDevice;
|
|
1214
|
+
}
|
|
1215
|
+
// Default placement (PHNX-4083): a bare human-facing `agents run
|
|
1216
|
+
// <harness>` (no prompt, real TTY, no placement flags, not a dispatched
|
|
1217
|
+
// hop) places itself like `--device auto` — a marker left off is decided
|
|
1218
|
+
// for you. Headless runs (a prompt, --json, teams/routines/hooks) keep
|
|
1219
|
+
// running in place, unchanged.
|
|
1220
|
+
const defaultPlacement = bareInteractiveRunDefaultsToDeviceAuto(options, { prompt, devicePickerRequested }, { tty: isInteractiveTerminal(), json: options.json });
|
|
1221
|
+
if (defaultPlacement)
|
|
1222
|
+
options.device = 'auto';
|
|
1025
1223
|
// --device auto (and deprecated --smart): live fleet pick.
|
|
1026
1224
|
// Harness is always the agent the user typed — never auto-picked.
|
|
1027
1225
|
// Placement failure propagates; an automatic request never becomes local.
|
|
1028
1226
|
{
|
|
1029
1227
|
const { applyDeviceAutoToOptions } = await import('../lib/smart-launch.js');
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
:
|
|
1037
|
-
|
|
1228
|
+
let result;
|
|
1229
|
+
try {
|
|
1230
|
+
result = await applyDeviceAutoToOptions(options, {
|
|
1231
|
+
accountPickerRequested,
|
|
1232
|
+
// `run auto` selects its harness after placement, so do not filter
|
|
1233
|
+
// candidates against an arbitrary proxy harness at this stage.
|
|
1234
|
+
agent: normalizedAgentSpec.split('#')[0].split('@')[0] === RUN_AUTO_KEYWORD
|
|
1235
|
+
? undefined
|
|
1236
|
+
: (resolveAgentName(normalizedAgentSpec.split('#')[0].split('@')[0]) ?? undefined),
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
catch (err) {
|
|
1240
|
+
// Placement the DEFAULT chose must fail loud AND name the local
|
|
1241
|
+
// escape hatch — never silently fall back to a local launch.
|
|
1242
|
+
if (!defaultPlacement)
|
|
1243
|
+
throw err;
|
|
1244
|
+
console.error(chalk.red(err.message));
|
|
1245
|
+
const { machineId } = await import('../lib/machine-id.js');
|
|
1246
|
+
console.error(chalk.gray(`Run here instead: agents run ${runBaseAgentName} --device ${machineId()}`));
|
|
1247
|
+
process.exit(1);
|
|
1248
|
+
}
|
|
1038
1249
|
if (!options.quiet && result.deprecationSmart) {
|
|
1039
1250
|
process.stderr.write(chalk.yellow('[agents] --smart is deprecated; use --device auto\n'));
|
|
1040
1251
|
}
|
|
@@ -1512,9 +1723,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1512
1723
|
process.exit(1);
|
|
1513
1724
|
}
|
|
1514
1725
|
const interactiveHost = options.interactive === true || (prompt === undefined && options.headless !== true);
|
|
1515
|
-
|
|
1726
|
+
// When the account was already picked up front (`#@`), the dispatch
|
|
1727
|
+
// forwards `#label` and the peer needs no interactive picker.
|
|
1728
|
+
if (accountPickerRequested && !upFrontAccountPick && !interactiveHost) {
|
|
1516
1729
|
console.error(chalk.red(`Account selection with ${agentSpec} requires an interactive host run. ` +
|
|
1517
|
-
`Use agents run ${runAgent}
|
|
1730
|
+
`Use agents run ${runAgent}# --device ${host.name} --interactive.`));
|
|
1518
1731
|
process.exit(1);
|
|
1519
1732
|
}
|
|
1520
1733
|
if (interactiveHost) {
|
|
@@ -1576,7 +1789,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1576
1789
|
const exitCode = await runInteractiveOnHost(host, {
|
|
1577
1790
|
agent: runAgent,
|
|
1578
1791
|
version: resumeId ? undefined : runVersion,
|
|
1579
|
-
accountPicker: accountPickerRequested,
|
|
1792
|
+
accountPicker: accountPickerRequested && !upFrontAccountPick,
|
|
1580
1793
|
strategy: resumeId ? undefined : runStrategy,
|
|
1581
1794
|
account: options.account,
|
|
1582
1795
|
fallback: options.fallback,
|
|
@@ -1908,12 +2121,12 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
1908
2121
|
let workflowHasSubagents = false;
|
|
1909
2122
|
const cwd = options.cwd ?? process.cwd();
|
|
1910
2123
|
if (accountPickerRequested && profileExists(rawAgent)) {
|
|
1911
|
-
console.error(chalk.red(`Account selection is not available for custom harness '${rawAgent}'. Run its concrete host agent with
|
|
2124
|
+
console.error(chalk.red(`Account selection is not available for custom harness '${rawAgent}'. Run its concrete host agent with # instead.`));
|
|
1912
2125
|
process.exit(1);
|
|
1913
2126
|
}
|
|
1914
2127
|
if (accountPickerRequested && !isValidAgent(rawAgent)) {
|
|
1915
2128
|
if (resolveWorkflowRef(rawAgent, cwd)) {
|
|
1916
|
-
console.error(chalk.red(`Account selection is not available for workflow '${rawAgent}'. Run a concrete agent with
|
|
2129
|
+
console.error(chalk.red(`Account selection is not available for workflow '${rawAgent}'. Run a concrete agent with # instead.`));
|
|
1917
2130
|
process.exit(1);
|
|
1918
2131
|
}
|
|
1919
2132
|
}
|
|
@@ -2194,7 +2407,10 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2194
2407
|
process.exit(1);
|
|
2195
2408
|
}
|
|
2196
2409
|
}
|
|
2197
|
-
|
|
2410
|
+
// `#@` already asked the account up front (before the device menu) — a
|
|
2411
|
+
// remote device pick never reaches here, so apply the local pick's
|
|
2412
|
+
// version/home and skip the on-the-spot menu.
|
|
2413
|
+
if (accountPickerRequested && !upFrontAccountPick) {
|
|
2198
2414
|
if (!supportsAccountInspection(agent)) {
|
|
2199
2415
|
console.error(chalk.red(`${agentLabel(agent)} does not expose local account state, so agents-cli cannot safely select an account.`));
|
|
2200
2416
|
console.error(chalk.gray(`Supported account pickers: ${ACCOUNT_INSPECTION_AGENT_IDS.join(', ')}`));
|
|
@@ -2220,6 +2436,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2220
2436
|
process.exit(1);
|
|
2221
2437
|
}
|
|
2222
2438
|
}
|
|
2439
|
+
if (upFrontAccountPick) {
|
|
2440
|
+
version = upFrontAccountPick.version;
|
|
2441
|
+
if (upFrontAccountPick.slotDir)
|
|
2442
|
+
execHome = upFrontAccountPick.slotDir;
|
|
2443
|
+
}
|
|
2223
2444
|
version = resolveVersionAlias(agent, version);
|
|
2224
2445
|
const { resolveSpawnAccount } = await import('../lib/account-registry.js');
|
|
2225
2446
|
let spawnAccount = null;
|
|
@@ -2437,7 +2658,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2437
2658
|
// - NEEDS A SIGN-IN (signed_out / revoked) -> launching IS the fix,
|
|
2438
2659
|
// because the harness's own TUI is the login surface. So on a TTY we
|
|
2439
2660
|
// carry the user into that login instead of erroring. Exiting here
|
|
2440
|
-
// made `agents run <agent>`, `agents run <agent
|
|
2661
|
+
// made `agents run <agent>`, `agents run <agent>#`, and `agents use`
|
|
2441
2662
|
// all dead-end with no reachable way to authenticate.
|
|
2442
2663
|
const recoverable = signInRecoverableCandidates(resolved.exhausted);
|
|
2443
2664
|
const { signInLaunchDecision } = await import('./run-account-picker.js');
|
|
@@ -2450,7 +2671,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2450
2671
|
const { pickSignInLaunchVersion } = await import('./run-account-picker.js');
|
|
2451
2672
|
const signInVersion = await pickSignInLaunchVersion(agent, recoverable, !!options.quiet);
|
|
2452
2673
|
// A cancelled prompt launches nothing — same contract as the
|
|
2453
|
-
// trailing
|
|
2674
|
+
// trailing-# account picker above.
|
|
2454
2675
|
if (!signInVersion)
|
|
2455
2676
|
return;
|
|
2456
2677
|
version = signInVersion;
|
|
@@ -2488,7 +2709,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
|
|
|
2488
2709
|
if (decision === 'picker') {
|
|
2489
2710
|
const selected = await pickRunAccountCandidate(agent);
|
|
2490
2711
|
// A cancelled picker launches nothing — same contract as the
|
|
2491
|
-
// trailing
|
|
2712
|
+
// trailing-# account picker and the sign-in launch above.
|
|
2492
2713
|
if (!selected)
|
|
2493
2714
|
return;
|
|
2494
2715
|
version = selected.version;
|
|
@@ -35,6 +35,17 @@ export declare function buildSwitchAccountChoices(rows: SwitchAccountRow[]): Run
|
|
|
35
35
|
* A cancelled picker writes nothing.
|
|
36
36
|
*/
|
|
37
37
|
export declare function pickSwitchAccount(agent: AgentId, rows: SwitchAccountRow[]): Promise<string | null>;
|
|
38
|
+
/**
|
|
39
|
+
* The two-condition "human-facing" gate behind signInLaunchDecision and
|
|
40
|
+
* noVerifiedUsageDecision: a real TTY and no `--json`. Off a TTY nobody can
|
|
41
|
+
* answer a prompt, and `--json` marks a MACHINE consumer, which must never be
|
|
42
|
+
* handed a picker or dropped into a login TUI. Mirrors the canonical
|
|
43
|
+
* `Surface.interactive = tty && !json` in `commands/utils.ts`.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isHumanFacingRun(input: {
|
|
46
|
+
tty: boolean;
|
|
47
|
+
json: boolean;
|
|
48
|
+
}): boolean;
|
|
38
49
|
/**
|
|
39
50
|
* Whether a zero-healthy run may recover by launching for a login, or must keep
|
|
40
51
|
* failing loud. Three inputs, all of which have to hold:
|
|
@@ -204,6 +204,16 @@ export async function pickSwitchAccount(agent, rows) {
|
|
|
204
204
|
throw err;
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* The two-condition "human-facing" gate behind signInLaunchDecision and
|
|
209
|
+
* noVerifiedUsageDecision: a real TTY and no `--json`. Off a TTY nobody can
|
|
210
|
+
* answer a prompt, and `--json` marks a MACHINE consumer, which must never be
|
|
211
|
+
* handed a picker or dropped into a login TUI. Mirrors the canonical
|
|
212
|
+
* `Surface.interactive = tty && !json` in `commands/utils.ts`.
|
|
213
|
+
*/
|
|
214
|
+
export function isHumanFacingRun(input) {
|
|
215
|
+
return input.tty && !input.json;
|
|
216
|
+
}
|
|
207
217
|
/**
|
|
208
218
|
* Whether a zero-healthy run may recover by launching for a login, or must keep
|
|
209
219
|
* failing loud. Three inputs, all of which have to hold:
|
|
@@ -217,7 +227,7 @@ export async function pickSwitchAccount(agent, rows) {
|
|
|
217
227
|
* gets the parseable fail-loud error instead.
|
|
218
228
|
*/
|
|
219
229
|
export function signInLaunchDecision(input) {
|
|
220
|
-
const humanPresent = input
|
|
230
|
+
const humanPresent = isHumanFacingRun(input);
|
|
221
231
|
return input.recoverable > 0 && humanPresent ? 'launch' : 'fail-loud';
|
|
222
232
|
}
|
|
223
233
|
/**
|
|
@@ -182,7 +182,7 @@ export interface InteractiveDispatchOptions {
|
|
|
182
182
|
agent: string;
|
|
183
183
|
/** Explicit agent version pin (e.g. "2.1.207") to forward as `agent@version`. */
|
|
184
184
|
version?: string;
|
|
185
|
-
/** Preserve the trailing
|
|
185
|
+
/** Preserve the trailing-# account picker so selection happens on the execution host. */
|
|
186
186
|
accountPicker?: boolean;
|
|
187
187
|
/** Explicit run strategy (e.g. "balanced") to forward as `--strategy <strategy>`. */
|
|
188
188
|
strategy?: string;
|
|
@@ -362,7 +362,7 @@ async function launchDetached(host, target, opts) {
|
|
|
362
362
|
/** Compose `agent[@version][#account]` so the peer resolves ITS slot (PHNX-3940 T5). */
|
|
363
363
|
export function runAgentSpecArg(opts) {
|
|
364
364
|
if (opts.accountPicker)
|
|
365
|
-
return `${opts.agent}
|
|
365
|
+
return `${opts.agent}#`;
|
|
366
366
|
let spec = opts.agent;
|
|
367
367
|
if (opts.version)
|
|
368
368
|
spec += `@${opts.version}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phnx-labs/agents-cli",
|
|
3
|
-
"version": "1.22.
|
|
3
|
+
"version": "1.22.107",
|
|
4
4
|
"description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|