@phnx-labs/agents-cli 1.22.15 → 1.22.17
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 +52 -0
- package/README.md +13 -2
- package/dist/bin/agents +0 -0
- package/dist/commands/cloud.d.ts +0 -1
- package/dist/commands/cloud.js +19 -185
- package/dist/commands/doctor.js +16 -14
- package/dist/commands/exec.js +153 -16
- package/dist/commands/feed.d.ts +1 -0
- package/dist/commands/feed.js +36 -32
- package/dist/commands/humans.d.ts +10 -0
- package/dist/commands/humans.js +91 -0
- package/dist/commands/resume.d.ts +17 -0
- package/dist/commands/resume.js +80 -0
- package/dist/commands/run-cloud.d.ts +26 -0
- package/dist/commands/run-cloud.js +162 -0
- package/dist/commands/sessions.d.ts +13 -4
- package/dist/commands/sessions.js +37 -19
- package/dist/commands/versions.js +4 -2
- package/dist/commands/view.js +1 -54
- package/dist/index.js +3 -2
- package/dist/lib/agents.js +4 -2
- package/dist/lib/channels/send.d.ts +5 -2
- package/dist/lib/channels/send.js +16 -13
- package/dist/lib/cloud/dispatch.d.ts +27 -0
- package/dist/lib/cloud/dispatch.js +214 -0
- package/dist/lib/devices/doctor-findings.d.ts +14 -0
- package/dist/lib/devices/doctor-findings.js +92 -51
- package/dist/lib/exec.d.ts +6 -2
- package/dist/lib/exec.js +22 -4
- package/dist/lib/feed-post.d.ts +2 -2
- package/dist/lib/feed-post.js +15 -10
- package/dist/lib/hooks.d.ts +4 -2
- package/dist/lib/hooks.js +173 -23
- package/dist/lib/hosts/remote-cmd.js +8 -0
- package/dist/lib/humans.d.ts +26 -0
- package/dist/lib/humans.js +73 -0
- package/dist/lib/memory.js +2 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/migrate.js +112 -4
- package/dist/lib/notify.d.ts +4 -4
- package/dist/lib/notify.js +4 -3
- package/dist/lib/permissions.js +12 -10
- package/dist/lib/placement.d.ts +8 -4
- package/dist/lib/placement.js +14 -8
- package/dist/lib/projects.d.ts +3 -3
- package/dist/lib/projects.js +19 -17
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/bundles.d.ts +11 -0
- package/dist/lib/secrets/bundles.js +122 -5
- package/dist/lib/session/actor-sidecar.d.ts +5 -2
- package/dist/lib/session/actor-sidecar.js +4 -2
- package/dist/lib/session/db.d.ts +2 -1
- package/dist/lib/session/db.js +19 -3
- package/dist/lib/session/types.d.ts +4 -0
- package/dist/lib/settings-manifest.js +7 -1
- package/dist/lib/staleness/writers/sources.d.ts +6 -1
- package/dist/lib/staleness/writers/sources.js +93 -7
- package/dist/lib/startup/command-registry.d.ts +2 -0
- package/dist/lib/startup/command-registry.js +5 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +6 -6
- package/dist/lib/types.d.ts +52 -0
- package/dist/lib/versions.js +48 -14
- package/package.json +1 -1
package/dist/commands/exec.js
CHANGED
|
@@ -532,7 +532,7 @@ export function registerRunCommand(program) {
|
|
|
532
532
|
.option('--headless', 'Force headless mode. Auto-enabled when a prompt is provided; pass explicitly to stay headless with no prompt (reads the prompt from stdin).', false)
|
|
533
533
|
.option('--no-auth-check', 'Skip the pre-launch "looks logged out" warning on an interactive run (advisory; never blocks anyway). Also silenced by AGENTS_NO_AUTH_CHECK=1.')
|
|
534
534
|
.option('-i, --interactive', 'Force interactive mode even when a prompt is provided. Mutually exclusive with --headless.')
|
|
535
|
-
.option('--resume [id]', 'Resume a previous conversation.
|
|
535
|
+
.option('--resume [id]', 'Resume a previous conversation. Full IDs resolve locally first, then fleet-wide. Claude, Codex, Grok, Kimi, Droid, and Cursor use version-gated native resume; other agents replay via /continue. Pair with a prompt to continue headlessly.')
|
|
536
536
|
.option('--session-id <id>', 'Force a NEW conversation to use this exact session UUID (Claude only). This CREATES a session — to resume an existing one, use --resume.')
|
|
537
537
|
.option('--name <slug>', 'Name the run — seeds the session label so it shows up as `<name>` in `agents sessions` and resolves by it (and `agents hosts logs <name>` for --host runs) instead of an opaque id. An agent-generated title later refines the label; your name shows until then. Optional.')
|
|
538
538
|
.option('--notify', 'Post a desktop notification when a headless run finishes. Fired by this process on exit, so it survives whatever launched the run (the menu bar dispatching it, a terminal you closed).')
|
|
@@ -553,7 +553,7 @@ export function registerRunCommand(program) {
|
|
|
553
553
|
.option('--budget <tokens>', 'Loop token hard-cap: stop once cumulative tokens reach this (stoppedBy: budget), enforced outside the agent. Loop only.')
|
|
554
554
|
.option('--until <signal>', 'Loop stop condition. `signal` reads <runDir>/loop-signal.json {continue,reason} each iteration; absent or continue:false stops (fail-closed). Loop only.')
|
|
555
555
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
556
|
-
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend]. Expands to --host/--lease. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
556
|
+
.option('--where <spec>', 'Where this run\'s body executes (one placement door): local | device:<name> | auto | lease[:backend] | cloud[:provider]. Expands to --host/--lease/--cloud. Do not combine with those flags. See docs/00-concepts.md#placement.')
|
|
557
557
|
.option('--host <name>', 'Offload this run onto another machine over SSH — a device name, registered host, or user@host. Pass "auto" to pick from 14d usage affinity (most-used online device has highest probability). Same as --where device:<name>. See `agents devices`.')
|
|
558
558
|
.option('--device <name>', 'Alias of --host. Pass "auto" for affinity-based device pick (same as --where auto).')
|
|
559
559
|
.option('--remote-cwd <dir>', "Explicit host working directory for --host runs, used VERBATIM (overrides --cwd; usually --cwd suffices — it re-roots a local-home path onto the remote home). Pass a single-quoted '$HOME/…' or a valid remote absolute path; a local ~ expands here and won't exist there (/Users/you vs /home/you).")
|
|
@@ -567,7 +567,12 @@ export function registerRunCommand(program) {
|
|
|
567
567
|
.option('--reuse', 'With --lease, reuse the most-recently-used warm box if one exists (else provision fresh). The scriptable form of the interactive reuse picker.')
|
|
568
568
|
.option('--bare', 'With --lease, skip copying your local ~/.agents setup (skills/hooks/commands/MCP) onto the box.')
|
|
569
569
|
.option('--tailscale', 'Lease the box onto your tailnet (reachable only over Tailscale) rather than a public IP.')
|
|
570
|
-
.option('--no-tailscale', 'Force a public-IP lease even when a reuse context would default to Tailscale.')
|
|
570
|
+
.option('--no-tailscale', 'Force a public-IP lease even when a reuse context would default to Tailscale.')
|
|
571
|
+
.option('--cloud', 'Vendor cloud placement: dispatch to the agent\'s native cloud (claude→rush, codex→codex, droid→factory, antigravity→antigravity) and stream the result. Same dispatch as `agents cloud run --agent <agent>`; tracked by `agents cloud list/status/logs`. Same as --where cloud. Mutually exclusive with --host/--lease and local-run flags.')
|
|
572
|
+
.option('--provider <id>', 'With --cloud: override the agent\'s native cloud provider (rush | codex | factory | antigravity | host).')
|
|
573
|
+
.option('--repo <owner/repo>', 'With --cloud: GitHub repository. Repeatable for multi-repo dispatch (Rush Cloud only).', (val, prev) => [...prev, val], [])
|
|
574
|
+
.option('--branch <name>', 'With --cloud: target git branch.')
|
|
575
|
+
.option('--cloud-env <id>', 'With --cloud: Codex Cloud environment ID (run\'s --env is the KEY=VAL passthrough, so the cloud env id gets its own flag).');
|
|
571
576
|
// `--on` and `--computer` are hidden aliases of `--host` — same behavior.
|
|
572
577
|
runCmd.addOption(new Option('--on <name>', 'Alias of --host.').hideHelp());
|
|
573
578
|
runCmd.addOption(new Option('--computer <name>', 'Alias of --host.').hideHelp());
|
|
@@ -608,6 +613,13 @@ export function registerRunCommand(program) {
|
|
|
608
613
|
agents run claude "…" --where auto # = --device auto
|
|
609
614
|
agents run claude "fix CI" --where lease --mode edit
|
|
610
615
|
|
|
616
|
+
# Vendor cloud placement — the agent's own cloud runs the task and
|
|
617
|
+
# agents cloud list/status/logs tracks it. Fire-and-forget: --no-follow
|
|
618
|
+
agents run claude "fix the flaky e2e" --cloud --repo acme/example
|
|
619
|
+
agents run codex "add parser tests" --cloud --cloud-env env_a1b2c3
|
|
620
|
+
agents run droid "QA the onboarding flow" --cloud --no-follow
|
|
621
|
+
agents run claude "…" --where cloud # same as --cloud
|
|
622
|
+
|
|
611
623
|
# Open the session in a terminal tab — detected from where your sessions
|
|
612
624
|
# already run (Ghostty / iTerm / Terminal.app); force one with a value
|
|
613
625
|
agents run claude --terminal
|
|
@@ -668,7 +680,16 @@ export function registerRunCommand(program) {
|
|
|
668
680
|
|
|
669
681
|
Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
670
682
|
|
|
671
|
-
|
|
683
|
+
Cloud placement: --cloud sends the run to the agent's native vendor cloud
|
|
684
|
+
(claude→rush, codex→codex, droid→factory, antigravity→antigravity) — the
|
|
685
|
+
same dispatch as agents cloud run --agent <agent>, tracked by agents
|
|
686
|
+
cloud list/status/logs/cancel/message. --provider overrides the routing;
|
|
687
|
+
--repo/--branch/--cloud-env refine the task. Agents without a native
|
|
688
|
+
cloud (kimi, grok, cursor, opencode, …) fail loud unless --provider is
|
|
689
|
+
given. --cloud is mutually exclusive with --host/--lease and with
|
|
690
|
+
local-run flags (--loop, --resume, --secrets, --terminal, …).
|
|
691
|
+
|
|
692
|
+
Resume: --resume <id> resolves full IDs locally first, then fleet-wide, and restores the source version/device/mode. Claude, Codex, Grok, Kimi, Droid, and Cursor use version-gated native resume; others replay via /continue. agents resume <id> infers the harness too.
|
|
672
693
|
|
|
673
694
|
Passthrough: everything after -- is forwarded verbatim to the underlying agent CLI.
|
|
674
695
|
agents run kimi -- --plan --some-native-flag value
|
|
@@ -694,6 +715,17 @@ export function registerRunCommand(program) {
|
|
|
694
715
|
// a native flag, not a prompt. Run interactively.
|
|
695
716
|
prompt = undefined;
|
|
696
717
|
}
|
|
718
|
+
// --cloud: vendor cloud placement. Validate BEFORE the terminal handoff
|
|
719
|
+
// and every local dispatch path — flags like --terminal/--loop/--resume
|
|
720
|
+
// belong to the local runner and must error, never ride along silently.
|
|
721
|
+
if (options.cloud || (typeof options.where === 'string' && /^cloud(:|$)/i.test(options.where.trim()))) {
|
|
722
|
+
const { runCloudConflicts } = await import('./run-cloud.js');
|
|
723
|
+
const conflicts = runCloudConflicts(options);
|
|
724
|
+
if (conflicts.length > 0) {
|
|
725
|
+
console.error(chalk.red(`--cloud is a vendor cloud placement; these only apply to local/machine runs: ${conflicts.join(', ')}. Drop them, or drop --cloud.`));
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
697
729
|
// --terminal: this process can't host the TUI (a menu-bar click, a script),
|
|
698
730
|
// so hand the run to a real terminal and exit. Resolved from the user's own
|
|
699
731
|
// live sessions, so it opens where they already work. Done before every
|
|
@@ -703,9 +735,9 @@ export function registerRunCommand(program) {
|
|
|
703
735
|
await handleTerminalHandoff(agentSpec, options, prompt);
|
|
704
736
|
return;
|
|
705
737
|
}
|
|
706
|
-
// Placement: --where expands into --host / --lease before any
|
|
707
|
-
// One door for "where does the body run?" — old flags remain
|
|
708
|
-
// See lib/placement.ts and docs/00-concepts.md#placement.
|
|
738
|
+
// Placement: --where expands into --host / --lease / --cloud before any
|
|
739
|
+
// dispatch. One door for "where does the body run?" — old flags remain
|
|
740
|
+
// aliases. See lib/placement.ts and docs/00-concepts.md#placement.
|
|
709
741
|
{
|
|
710
742
|
const { placementFromRunFlags, expandPlacementToRunFlags, PlacementError } = await import('../lib/placement.js');
|
|
711
743
|
try {
|
|
@@ -720,6 +752,10 @@ export function registerRunCommand(program) {
|
|
|
720
752
|
options.lease = expanded.lease;
|
|
721
753
|
if (expanded.box !== undefined)
|
|
722
754
|
options.box = expanded.box;
|
|
755
|
+
if (expanded.cloud !== undefined)
|
|
756
|
+
options.cloud = expanded.cloud;
|
|
757
|
+
if (expanded.provider !== undefined)
|
|
758
|
+
options.provider = expanded.provider;
|
|
723
759
|
// Clear the where flag so remote re-entry (host dispatch) does not
|
|
724
760
|
// re-expand and conflict with the concrete host we just set.
|
|
725
761
|
options.where = undefined;
|
|
@@ -733,6 +769,22 @@ export function registerRunCommand(program) {
|
|
|
733
769
|
throw err;
|
|
734
770
|
}
|
|
735
771
|
}
|
|
772
|
+
// Cloud refinement flags without the placement are a typo, not a no-op.
|
|
773
|
+
if (!options.cloud) {
|
|
774
|
+
const { cloudFlagsWithoutCloud } = await import('./run-cloud.js');
|
|
775
|
+
const stray = cloudFlagsWithoutCloud(options);
|
|
776
|
+
if (stray.length > 0) {
|
|
777
|
+
console.error(chalk.red(`${stray.join(', ')} ${stray.length > 1 ? 'require' : 'requires'} --cloud (vendor cloud placement).`));
|
|
778
|
+
process.exit(1);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// --cloud: dispatch through the cloud provider registry and return — the
|
|
782
|
+
// run never touches the local/host/lease paths below.
|
|
783
|
+
if (options.cloud) {
|
|
784
|
+
const { handleRunCloud } = await import('./run-cloud.js');
|
|
785
|
+
await handleRunCloud(agentSpec, prompt, options, command);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
736
788
|
// --notify: post a desktop notification when this run finishes. Armed on
|
|
737
789
|
// process exit so it covers EVERY dispatch path below (local, --host,
|
|
738
790
|
// --lease, the error path) instead of one branch. Only for headless runs
|
|
@@ -781,7 +833,89 @@ export function registerRunCommand(program) {
|
|
|
781
833
|
`Pin a concrete harness instead: agents run <harness>@<version>.`));
|
|
782
834
|
process.exit(1);
|
|
783
835
|
}
|
|
784
|
-
|
|
836
|
+
let autoHarnessRequested = normalizedAgentSpec === RUN_AUTO_KEYWORD;
|
|
837
|
+
let resolvedResumeSource;
|
|
838
|
+
// Concrete resume ids resolve BEFORE placement. Full UUIDs take the local
|
|
839
|
+
// SQLite fast path; only a local miss fans out to the fleet. This lets a
|
|
840
|
+
// command entered on zion discover that the owning version-home is on a
|
|
841
|
+
// worker, while a command entered on that worker never pays for SSH.
|
|
842
|
+
if (typeof options.resume === 'string' && options.resume.trim()) {
|
|
843
|
+
const selector = options.resume.trim();
|
|
844
|
+
const injectedSource = (() => {
|
|
845
|
+
try {
|
|
846
|
+
const parsed = JSON.parse(process.env.AGENTS_RESUME_SOURCE_JSON ?? 'null');
|
|
847
|
+
return parsed?.id === selector ? parsed : undefined;
|
|
848
|
+
}
|
|
849
|
+
catch {
|
|
850
|
+
return undefined;
|
|
851
|
+
}
|
|
852
|
+
})();
|
|
853
|
+
delete process.env.AGENTS_RESUME_SOURCE_JSON;
|
|
854
|
+
const outcome = injectedSource
|
|
855
|
+
? { kind: 'resolved', session: injectedSource }
|
|
856
|
+
: await (await import('./sessions.js')).resolveSessionMetadataValue(selector);
|
|
857
|
+
if (outcome.kind === 'partial') {
|
|
858
|
+
console.error(chalk.red(`Could not resolve session while these devices were unavailable: ${outcome.failedPeers.join(', ')}`));
|
|
859
|
+
process.exit(2);
|
|
860
|
+
}
|
|
861
|
+
if (outcome.kind === 'not-found') {
|
|
862
|
+
console.error(chalk.red(`No session matching "${selector}".`));
|
|
863
|
+
process.exit(1);
|
|
864
|
+
}
|
|
865
|
+
if (outcome.kind === 'ambiguous') {
|
|
866
|
+
console.error(chalk.red(`"${selector}" matches ${outcome.candidates.length} sessions. Pass the full session id.`));
|
|
867
|
+
process.exit(1);
|
|
868
|
+
}
|
|
869
|
+
resolvedResumeSource = outcome.session;
|
|
870
|
+
const [requestedAgent, requestedVersion] = normalizedAgentSpec.split('@');
|
|
871
|
+
if (!autoHarnessRequested && requestedAgent !== resolvedResumeSource.agent) {
|
|
872
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} belongs to ${resolvedResumeSource.agent}, not ${requestedAgent}. ` +
|
|
873
|
+
`Use: agents resume ${resolvedResumeSource.id}`));
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
876
|
+
if (!autoHarnessRequested && requestedVersion && requestedVersion !== resolvedResumeSource.version) {
|
|
877
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} started with ${resolvedResumeSource.agent}@${resolvedResumeSource.version ?? 'unknown'}, ` +
|
|
878
|
+
`not @${requestedVersion}.`));
|
|
879
|
+
process.exit(1);
|
|
880
|
+
}
|
|
881
|
+
// Omitted --mode inherits the effective source mode. Commander keeps
|
|
882
|
+
// the normal new-run default as a real value, so consult its provenance
|
|
883
|
+
// rather than mistaking the default for an explicit override.
|
|
884
|
+
if (command.getOptionValueSource('mode') === 'default') {
|
|
885
|
+
if (resolvedResumeSource.mode)
|
|
886
|
+
options.mode = resolvedResumeSource.mode;
|
|
887
|
+
else if (!options.quiet)
|
|
888
|
+
process.stderr.write(chalk.yellow(`[agents] session ${resolvedResumeSource.shortId} predates stored launch modes; using --mode ${options.mode}\n`));
|
|
889
|
+
}
|
|
890
|
+
const { machineId } = await import('../lib/machine-id.js');
|
|
891
|
+
const sourceMachine = resolvedResumeSource.machine;
|
|
892
|
+
const explicitPlacement = hostTargetGiven(options).length > 0;
|
|
893
|
+
if (sourceMachine && sourceMachine !== machineId() && !explicitPlacement) {
|
|
894
|
+
options.host = sourceMachine;
|
|
895
|
+
}
|
|
896
|
+
else if (!autoHarnessRequested && sourceMachine && sourceMachine !== machineId() && explicitPlacement && !hostTargetGiven(options).includes(sourceMachine)) {
|
|
897
|
+
console.error(chalk.red(`Strict resume must run on ${sourceMachine}, where session ${resolvedResumeSource.shortId} is owned. ` +
|
|
898
|
+
`Use agents run auto --resume ${resolvedResumeSource.id} to hand off elsewhere.`));
|
|
899
|
+
process.exit(1);
|
|
900
|
+
}
|
|
901
|
+
// On the owning machine, `auto` first asks the existing account router
|
|
902
|
+
// whether the exact source version is healthy. If it is, native resume
|
|
903
|
+
// wins. If not, keep `auto` so the normal harness/account router selects
|
|
904
|
+
// a healthy local target and the later resume block performs /continue.
|
|
905
|
+
if (autoHarnessRequested && (!sourceMachine || sourceMachine === machineId())) {
|
|
906
|
+
const sourceAgent = resolvedResumeSource.agent;
|
|
907
|
+
if (sourceAgent in AGENTS && resolvedResumeSource.version) {
|
|
908
|
+
const { resolveRunVersion } = await import('../lib/rotate.js');
|
|
909
|
+
const health = await resolveRunVersion(sourceAgent, 'available', resolvedResumeSource.cwd ?? process.cwd());
|
|
910
|
+
if (!health.exhausted && health.version === resolvedResumeSource.version) {
|
|
911
|
+
normalizedAgentSpec = `${sourceAgent}@${resolvedResumeSource.version}`;
|
|
912
|
+
autoHarnessRequested = false;
|
|
913
|
+
if (!options.quiet)
|
|
914
|
+
process.stderr.write(chalk.gray(`[agents] auto resume → native ${normalizedAgentSpec} on ${sourceMachine ?? machineId()}\n`));
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
785
919
|
if (autoHarnessRequested) {
|
|
786
920
|
// `auto` is reserved. If a future harness registers that id, the
|
|
787
921
|
// keyword collides — fail loud rather than silently shadow the harness.
|
|
@@ -797,7 +931,7 @@ export function registerRunCommand(program) {
|
|
|
797
931
|
// Host layer: with no explicit --host/--device, default to the
|
|
798
932
|
// affinity pick. Skipped on a host-dispatched run — its dispatcher
|
|
799
933
|
// already resolved this layer (see runAutoDefaultsToAffinity).
|
|
800
|
-
if (runAutoDefaultsToAffinity(options))
|
|
934
|
+
if (!resolvedResumeSource && runAutoDefaultsToAffinity(options))
|
|
801
935
|
options.device = 'auto';
|
|
802
936
|
}
|
|
803
937
|
// --device auto / --host auto (and deprecated --smart): affinity-pick host.
|
|
@@ -2013,7 +2147,8 @@ export function registerRunCommand(program) {
|
|
|
2013
2147
|
// AgentId is wider than SessionAgentId (amp/kiro/goose/copilot keep no transcripts);
|
|
2014
2148
|
// those simply yield no matches and fall through to the not-found error.
|
|
2015
2149
|
const sessionAgent = agent;
|
|
2016
|
-
|
|
2150
|
+
if (!resolvedResumeSource)
|
|
2151
|
+
await discoverSessions({ agent: sessionAgent, version });
|
|
2017
2152
|
// Resume is interactive unless a follow-on prompt makes it headless.
|
|
2018
2153
|
const wantsInteractive = resolveInteractive({ interactive: options.interactive, headless: options.headless, prompt });
|
|
2019
2154
|
const idArg = typeof options.resume === 'string' ? options.resume.trim() : '';
|
|
@@ -2024,9 +2159,9 @@ export function registerRunCommand(program) {
|
|
|
2024
2159
|
catch {
|
|
2025
2160
|
scopeCwd = cwd;
|
|
2026
2161
|
}
|
|
2027
|
-
let session;
|
|
2162
|
+
let session = resolvedResumeSource;
|
|
2028
2163
|
if (idArg) {
|
|
2029
|
-
let matches = findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2164
|
+
let matches = session ? [session] : findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2030
2165
|
if (matches.length === 0) {
|
|
2031
2166
|
const wide = findSessionsById(idArg, { agent: sessionAgent, version });
|
|
2032
2167
|
if (wide.length > 0) {
|
|
@@ -2076,10 +2211,12 @@ export function registerRunCommand(program) {
|
|
|
2076
2211
|
session = picked.session;
|
|
2077
2212
|
forceInteractive = true; // bare resume always lands in the agent's TUI
|
|
2078
2213
|
}
|
|
2079
|
-
//
|
|
2080
|
-
//
|
|
2081
|
-
version
|
|
2082
|
-
|
|
2214
|
+
// Native resume is valid only for the source harness + exact isolated
|
|
2215
|
+
// version. `auto` may have selected another healthy harness/account; in
|
|
2216
|
+
// that case keep the target version and hand off through /continue.
|
|
2217
|
+
const canResumeNatively = session.agent === agent && nativeResume(agent, session.version);
|
|
2218
|
+
if (canResumeNatively) {
|
|
2219
|
+
version = session.version;
|
|
2083
2220
|
resumeNative = true;
|
|
2084
2221
|
resumeSessionId = session.id;
|
|
2085
2222
|
// Native `--resume` (claude/codex) resolves the transcript relative to the
|
package/dist/commands/feed.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { type OpenBlock } from '../lib/feed.js';
|
|
|
20
20
|
import { type OutcomeGroup, type SessionOutcomeHint } from '../lib/feed-outcome.js';
|
|
21
21
|
import { filterBlocksForFeed } from '../lib/ask-classifier.js';
|
|
22
22
|
import { type FeedSessionSignal } from '../lib/feed-ranking.js';
|
|
23
|
+
export declare const FEED_POST_HELP = "\nExamples:\n # Title (subject) + body. Phone broadcasts put title first, body after a\n # blank line, then a \"Sent from agent/session on host\" footer.\n agents feed post --title \"CHANGELOG pushed\" \"Watching CI and mac-mini E2E\"\n agents feed post --title \"Cover ready\" \"render at ./out/cover.png\" --attach ./out/cover.png\n agents feed post --title \"Ready for review\" \"PR opened, waiting on prix-cloud\" --json\n\n # Worth interrupting someone over - reaches sinks gated on minLevel: important:\n agents feed post --title \"npm token expired\" \"Cannot publish the release\" --level important\n\n # Stuck: opens a needs-you block and always broadcasts at important:\n agents feed post --title \"Force-push denied\" \"git-guard blocked PR #1749\" --blocked\n agents feed post --title \"Publish or wait?\" \"npm publish now or after review\" --blocked --option publish --option wait\n agents feed post --title \"Delete preview env?\" \"stale preview still running\" --blocked --default \"leave it\"\n\n # Exhaust self-serve FIRST. A block is for what you genuinely cannot do:\n # a credential only the user holds, a decision only they can make, an\n # approval only they can give. Not \"should I do the obvious next step?\".\n\n # Outside a run, pass the session explicitly:\n agents feed post --title \"Manual note\" \"context for the next agent\" --session 00998b0e-2d15-4d2f-a58b-974a886c9b47\n\nIdentity (session, agent, host, runtime, pid, launchId) is stamped automatically\nand rides the phone footer of feed.broadcast {message}. Domain facts (tickets,\nPRs) are not CLI flags - the ticket is joined from the session index at post\ntime. No em-dashes in title/body - they are scrubbed on the way out.\n\nConfigure where a post is mirrored under feed.broadcast in agents.yaml - see\ndocs/06-observability.md. A milestone is always recorded, but it does not text\nthe owner when the sink has minLevel: important. Add --level important for a\nphone-worthy successful update. Use --blocked only when work cannot continue.\nThe owner destination comes from humans.yaml; do not duplicate it in agents.yaml.\n";
|
|
23
24
|
export declare const FEED_NO_FANOUT_ENV = "AGENTS_FEED_LOCAL";
|
|
24
25
|
/** Right-hand masthead summary: `N blocks · M agents`. */
|
|
25
26
|
export declare function formatFeedMastheadRight(blocks: OpenBlock[]): string;
|
package/dist/commands/feed.js
CHANGED
|
@@ -21,6 +21,40 @@ import { GLYPH, masthead } from '../lib/comms-render.js';
|
|
|
21
21
|
import { discoverSessions } from '../lib/session/discover.js';
|
|
22
22
|
import { resolveProvider } from '../lib/cloud/registry.js';
|
|
23
23
|
import { buildSessionSignals, rankFeedBlocks, synthesizeControlCards, } from '../lib/feed-ranking.js';
|
|
24
|
+
export const FEED_POST_HELP = `
|
|
25
|
+
Examples:
|
|
26
|
+
# Title (subject) + body. Phone broadcasts put title first, body after a
|
|
27
|
+
# blank line, then a "Sent from agent/session on host" footer.
|
|
28
|
+
agents feed post --title "CHANGELOG pushed" "Watching CI and mac-mini E2E"
|
|
29
|
+
agents feed post --title "Cover ready" "render at ./out/cover.png" --attach ./out/cover.png
|
|
30
|
+
agents feed post --title "Ready for review" "PR opened, waiting on prix-cloud" --json
|
|
31
|
+
|
|
32
|
+
# Worth interrupting someone over - reaches sinks gated on minLevel: important:
|
|
33
|
+
agents feed post --title "npm token expired" "Cannot publish the release" --level important
|
|
34
|
+
|
|
35
|
+
# Stuck: opens a needs-you block and always broadcasts at important:
|
|
36
|
+
agents feed post --title "Force-push denied" "git-guard blocked PR #1749" --blocked
|
|
37
|
+
agents feed post --title "Publish or wait?" "npm publish now or after review" --blocked --option publish --option wait
|
|
38
|
+
agents feed post --title "Delete preview env?" "stale preview still running" --blocked --default "leave it"
|
|
39
|
+
|
|
40
|
+
# Exhaust self-serve FIRST. A block is for what you genuinely cannot do:
|
|
41
|
+
# a credential only the user holds, a decision only they can make, an
|
|
42
|
+
# approval only they can give. Not "should I do the obvious next step?".
|
|
43
|
+
|
|
44
|
+
# Outside a run, pass the session explicitly:
|
|
45
|
+
agents feed post --title "Manual note" "context for the next agent" --session 00998b0e-2d15-4d2f-a58b-974a886c9b47
|
|
46
|
+
|
|
47
|
+
Identity (session, agent, host, runtime, pid, launchId) is stamped automatically
|
|
48
|
+
and rides the phone footer of feed.broadcast {message}. Domain facts (tickets,
|
|
49
|
+
PRs) are not CLI flags - the ticket is joined from the session index at post
|
|
50
|
+
time. No em-dashes in title/body - they are scrubbed on the way out.
|
|
51
|
+
|
|
52
|
+
Configure where a post is mirrored under feed.broadcast in agents.yaml - see
|
|
53
|
+
docs/06-observability.md. A milestone is always recorded, but it does not text
|
|
54
|
+
the owner when the sink has minLevel: important. Add --level important for a
|
|
55
|
+
phone-worthy successful update. Use --blocked only when work cannot continue.
|
|
56
|
+
The owner destination comes from humans.yaml; do not duplicate it in agents.yaml.
|
|
57
|
+
`;
|
|
24
58
|
export const FEED_NO_FANOUT_ENV = 'AGENTS_FEED_LOCAL';
|
|
25
59
|
/** Right-hand masthead summary: `N blocks · M agents`. */
|
|
26
60
|
export function formatFeedMastheadRight(blocks) {
|
|
@@ -239,44 +273,14 @@ export function registerFeedCommand(program) {
|
|
|
239
273
|
.description('Post a status update to the fleet activity stream (for agents)')
|
|
240
274
|
.argument('<text...>', 'Body: what just happened (after --title)')
|
|
241
275
|
.requiredOption('--title <title>', 'Short subject, ~4-5 words (phone first line)')
|
|
242
|
-
.option('--session <id>', 'Session id escape hatch (default: auto from env / pid registry)')
|
|
276
|
+
.option('--session <id>', 'Session id escape hatch (default: auto from env / launch activity / pid registry)')
|
|
243
277
|
.option('--attach <path-or-url...>', 'Attach an artifact (local file or URL); repeatable')
|
|
244
278
|
.option('--level <level>', 'How loudly to broadcast: milestone (default) or important. Configured sinks with minLevel: important only fire on the latter.', 'milestone')
|
|
245
279
|
.option('--blocked', 'You are STUCK and need the user. Opens an answerable block and always broadcasts at important - do not also pass --level.')
|
|
246
280
|
.option('--option <label...>', 'With --blocked: an answer the user can pick; repeatable')
|
|
247
281
|
.option('--default <answer>', 'With --blocked: a safe default policy may apply if nobody answers in time')
|
|
248
282
|
.option('--json', 'Emit the written event as JSON')
|
|
249
|
-
.addHelpText('after',
|
|
250
|
-
Examples:
|
|
251
|
-
# Title (subject) + body. Phone broadcasts put title first, body after a
|
|
252
|
-
# blank line, then a "Sent from agent/session on host" footer.
|
|
253
|
-
agents feed post --title "CHANGELOG pushed" "Watching CI and mac-mini E2E"
|
|
254
|
-
agents feed post --title "Cover ready" "render at ./out/cover.png" --attach ./out/cover.png
|
|
255
|
-
agents feed post --title "Ready for review" "PR opened, waiting on prix-cloud" --json
|
|
256
|
-
|
|
257
|
-
# Worth interrupting someone over - reaches sinks gated on minLevel: important:
|
|
258
|
-
agents feed post --title "npm token expired" "Cannot publish the release" --level important
|
|
259
|
-
|
|
260
|
-
# Stuck: opens a needs-you block and always broadcasts at important:
|
|
261
|
-
agents feed post --title "Force-push denied" "git-guard blocked PR #1749" --blocked
|
|
262
|
-
agents feed post --title "Publish or wait?" "npm publish now or after review" --blocked --option publish --option wait
|
|
263
|
-
agents feed post --title "Delete preview env?" "stale preview still running" --blocked --default "leave it"
|
|
264
|
-
|
|
265
|
-
# Exhaust self-serve FIRST. A block is for what you genuinely cannot do:
|
|
266
|
-
# a credential only the user holds, a decision only they can make, an
|
|
267
|
-
# approval only they can give. Not "should I do the obvious next step?".
|
|
268
|
-
|
|
269
|
-
# Outside a run, pass the session explicitly:
|
|
270
|
-
agents feed post --title "Manual note" "context for the next agent" --session 00998b0e-2d15-4d2f-a58b-974a886c9b47
|
|
271
|
-
|
|
272
|
-
Identity (session, agent, host, runtime, pid, launchId) is stamped automatically
|
|
273
|
-
and rides the phone footer of feed.broadcast {message}. Domain facts (tickets,
|
|
274
|
-
PRs) are not CLI flags - the ticket is joined from the session index at post
|
|
275
|
-
time. No em-dashes in title/body - they are scrubbed on the way out.
|
|
276
|
-
|
|
277
|
-
Configure where a post is mirrored under feed.broadcast in agents.yaml - see
|
|
278
|
-
docs/06-observability.md.
|
|
279
|
-
`)
|
|
283
|
+
.addHelpText('after', FEED_POST_HELP)
|
|
280
284
|
.action(async (textParts, opts, cmd) => {
|
|
281
285
|
// Parent `feed` also declares `--json` (for the list view). Commander
|
|
282
286
|
// binds the flag on the parent, so a `feed post … --json` lands on
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents humans` — owner identity and notification channel management.
|
|
3
|
+
*
|
|
4
|
+
* Reads from ~/.agents/humans.yaml (created by migration from owner.md and
|
|
5
|
+
* agents.yaml notify.owner). Provides inspection commands for the current
|
|
6
|
+
* owner config.
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from 'commander';
|
|
9
|
+
/** Register the `agents humans` command tree. */
|
|
10
|
+
export declare function registerHumansCommands(program: Command): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents humans` — owner identity and notification channel management.
|
|
3
|
+
*
|
|
4
|
+
* Reads from ~/.agents/humans.yaml (created by migration from owner.md and
|
|
5
|
+
* agents.yaml notify.owner). Provides inspection commands for the current
|
|
6
|
+
* owner config.
|
|
7
|
+
*/
|
|
8
|
+
import { setHelpSections } from '../lib/help.js';
|
|
9
|
+
import { readHumans, getOwnerFromHumans } from '../lib/humans.js';
|
|
10
|
+
/** Register the `agents humans` command tree. */
|
|
11
|
+
export function registerHumansCommands(program) {
|
|
12
|
+
const humansCmd = program
|
|
13
|
+
.command('humans')
|
|
14
|
+
.description('Inspect owner identity and notification channel config (humans.yaml)');
|
|
15
|
+
setHelpSections(humansCmd, {
|
|
16
|
+
examples: `
|
|
17
|
+
# Show owner identity and channel config as JSON
|
|
18
|
+
agents humans show owner --json
|
|
19
|
+
|
|
20
|
+
# Show owner identity in human-readable form
|
|
21
|
+
agents humans show owner
|
|
22
|
+
`,
|
|
23
|
+
notes: `
|
|
24
|
+
humans.yaml is the canonical owner identity file at ~/.agents/humans.yaml.
|
|
25
|
+
It is populated by migrating notify.owner from agents.yaml and frontmatter
|
|
26
|
+
from owner.md. Use \`agents humans show owner\` to inspect the current config.
|
|
27
|
+
`,
|
|
28
|
+
});
|
|
29
|
+
const showCmd = humansCmd
|
|
30
|
+
.command('show')
|
|
31
|
+
.description('Show config from humans.yaml');
|
|
32
|
+
setHelpSections(showCmd, {
|
|
33
|
+
examples: `
|
|
34
|
+
agents humans show owner
|
|
35
|
+
agents humans show owner --json
|
|
36
|
+
`,
|
|
37
|
+
});
|
|
38
|
+
showCmd
|
|
39
|
+
.command('owner')
|
|
40
|
+
.description('Show the configured owner identity and notification channels')
|
|
41
|
+
.option('--json', 'Output as JSON')
|
|
42
|
+
.action((opts) => {
|
|
43
|
+
const humans = readHumans();
|
|
44
|
+
if (!humans) {
|
|
45
|
+
if (opts.json) {
|
|
46
|
+
process.stdout.write(JSON.stringify(null) + '\n');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.stderr.write('humans.yaml not found — run `agents` once to trigger migration\n');
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const owner = getOwnerFromHumans();
|
|
55
|
+
if (opts.json) {
|
|
56
|
+
process.stdout.write(JSON.stringify(owner ?? null, null, 2) + '\n');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (!owner) {
|
|
60
|
+
process.stdout.write('No owner configured in humans.yaml\n');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (owner.name)
|
|
64
|
+
process.stdout.write(`Name: ${owner.name}\n`);
|
|
65
|
+
if (owner.timezone)
|
|
66
|
+
process.stdout.write(`Timezone: ${owner.timezone}\n`);
|
|
67
|
+
if (owner.quiet_hours)
|
|
68
|
+
process.stdout.write(`Quiet: ${owner.quiet_hours}\n`);
|
|
69
|
+
if (owner.default_severity)
|
|
70
|
+
process.stdout.write(`Severity: ${owner.default_severity}\n`);
|
|
71
|
+
if (owner.notify) {
|
|
72
|
+
process.stdout.write(`Notify: channel=${owner.notify.channel} to=${owner.notify.to}\n`);
|
|
73
|
+
}
|
|
74
|
+
if (owner.channels?.length) {
|
|
75
|
+
process.stdout.write(`Channels:\n`);
|
|
76
|
+
for (const ch of owner.channels) {
|
|
77
|
+
const extra = [ch.transport, ch.intrusive ? 'intrusive' : null].filter(Boolean).join(', ');
|
|
78
|
+
process.stdout.write(` - ${ch.id}${extra ? ` (${extra})` : ''}\n`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (owner.policy) {
|
|
82
|
+
process.stdout.write(`Policy:\n`);
|
|
83
|
+
for (const [sev, channels] of Object.entries(owner.policy)) {
|
|
84
|
+
if (Array.isArray(channels) && channels.length) {
|
|
85
|
+
process.stdout.write(` ${sev}: ${channels.join(', ')}\n`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
interface ResumeOptions {
|
|
3
|
+
mode?: string;
|
|
4
|
+
interactive?: boolean;
|
|
5
|
+
headless?: boolean;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
quiet?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Translate `agents resume <id>` to the canonical `agents run` surface after
|
|
10
|
+
* resolving the source identity. The run command remains the only executor. */
|
|
11
|
+
export declare function buildResumeRunArgs(session: {
|
|
12
|
+
id: string;
|
|
13
|
+
agent: string;
|
|
14
|
+
version?: string;
|
|
15
|
+
}, prompt: string | undefined, options: ResumeOptions): string[];
|
|
16
|
+
export declare function registerResumeCommand(program: Command): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Strict session resume convenience command. */
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { setHelpSections } from '../lib/help.js';
|
|
5
|
+
import { resolveSessionMetadataValue } from './sessions.js';
|
|
6
|
+
/** Translate `agents resume <id>` to the canonical `agents run` surface after
|
|
7
|
+
* resolving the source identity. The run command remains the only executor. */
|
|
8
|
+
export function buildResumeRunArgs(session, prompt, options) {
|
|
9
|
+
const spec = session.version ? `${session.agent}@${session.version}` : session.agent;
|
|
10
|
+
const args = ['run', spec, ...(prompt === undefined ? [] : [prompt]), '--resume', session.id];
|
|
11
|
+
if (options.mode)
|
|
12
|
+
args.push('--mode', options.mode);
|
|
13
|
+
if (options.interactive)
|
|
14
|
+
args.push('--interactive');
|
|
15
|
+
if (options.headless)
|
|
16
|
+
args.push('--headless');
|
|
17
|
+
if (options.cwd)
|
|
18
|
+
args.push('--cwd', options.cwd);
|
|
19
|
+
if (options.quiet)
|
|
20
|
+
args.push('--quiet');
|
|
21
|
+
return args;
|
|
22
|
+
}
|
|
23
|
+
export function registerResumeCommand(program) {
|
|
24
|
+
const cmd = program
|
|
25
|
+
.command('resume <session-id> [prompt]')
|
|
26
|
+
.description('Resume a session with its original harness, version, device, account, cwd, and mode')
|
|
27
|
+
.option('-m, --mode <mode>', 'Override the recorded launch mode')
|
|
28
|
+
.option('-i, --interactive', 'Resume interactively even when a prompt is provided')
|
|
29
|
+
.option('--headless', 'Resume headlessly (a prompt is required)')
|
|
30
|
+
.option('--cwd <path>', 'Override the recorded working directory')
|
|
31
|
+
.option('-q, --quiet', 'Suppress routing banners')
|
|
32
|
+
.action(async (sessionId, prompt, options) => {
|
|
33
|
+
const outcome = await resolveSessionMetadataValue(sessionId.trim());
|
|
34
|
+
if (outcome.kind === 'partial') {
|
|
35
|
+
console.error(chalk.red(`Could not resolve session while these devices were unavailable: ${outcome.failedPeers.join(', ')}`));
|
|
36
|
+
process.exitCode = 2;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (outcome.kind === 'not-found') {
|
|
40
|
+
console.error(chalk.red(`No session matching "${sessionId}".`));
|
|
41
|
+
process.exitCode = 1;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (outcome.kind === 'ambiguous') {
|
|
45
|
+
console.error(chalk.red(`"${sessionId}" matches ${outcome.candidates.length} sessions. Pass the full session id.`));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const args = buildResumeRunArgs(outcome.session, prompt, options);
|
|
50
|
+
const child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
51
|
+
stdio: 'inherit',
|
|
52
|
+
env: {
|
|
53
|
+
...process.env,
|
|
54
|
+
// Avoid repeating the fleet lookup in the delegated local `run`
|
|
55
|
+
// process. The value is metadata-only and is not forwarded over SSH;
|
|
56
|
+
// the owner performs its own local SQLite lookup.
|
|
57
|
+
AGENTS_RESUME_SOURCE_JSON: JSON.stringify(outcome.session),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
const exitCode = await new Promise((resolve) => {
|
|
61
|
+
child.once('error', () => resolve(127));
|
|
62
|
+
child.once('exit', (code, signal) => resolve(code ?? (signal ? 1 : 0)));
|
|
63
|
+
});
|
|
64
|
+
process.exitCode = exitCode;
|
|
65
|
+
});
|
|
66
|
+
setHelpSections(cmd, {
|
|
67
|
+
examples: `
|
|
68
|
+
# Resume by full id
|
|
69
|
+
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897
|
|
70
|
+
|
|
71
|
+
# Resume and continue headlessly
|
|
72
|
+
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 "finish the tests"
|
|
73
|
+
|
|
74
|
+
# Deliberately change permissions
|
|
75
|
+
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 --mode edit`,
|
|
76
|
+
notes: `
|
|
77
|
+
Full IDs resolve from the local session database first; an SSH fleet lookup runs only after a local miss.
|
|
78
|
+
Use agents run auto --resume <id> when the original account is unavailable and another harness may continue.`,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { CloudProvider } from '../lib/cloud/types.js';
|
|
3
|
+
/** Error type for run --cloud validation failures (exit 1, red message). */
|
|
4
|
+
export declare class RunCloudError extends Error {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
/** Flags the user passed that cannot ride a cloud placement. */
|
|
8
|
+
export declare function runCloudConflicts(options: Record<string, unknown>): string[];
|
|
9
|
+
/** Cloud-only flags passed without the --cloud placement. */
|
|
10
|
+
export declare function cloudFlagsWithoutCloud(options: Record<string, unknown>): string[];
|
|
11
|
+
/** Agents that route to a native cloud provider (the registry's truth). */
|
|
12
|
+
export declare function cloudCapableAgentIds(): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the cloud provider for a run: explicit --provider wins, else the
|
|
15
|
+
* agent's native cloud. An agent with no native cloud fails loud with the
|
|
16
|
+
* capable list — never a silent ride onto the configured default.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRunCloudProvider(agentId: string, explicitProvider?: string): CloudProvider;
|
|
19
|
+
/** Validate the agent half of `agents run <agent> --cloud` and return the registry id. */
|
|
20
|
+
export declare function resolveRunCloudAgent(agentSpec: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Handle `agents run <agent> [prompt] --cloud`. Validation lives in the
|
|
23
|
+
* exported helpers above (unit-tested); this wires them to the shared
|
|
24
|
+
* dispatch core and dies on any validation failure.
|
|
25
|
+
*/
|
|
26
|
+
export declare function handleRunCloud(agentSpec: string, prompt: string | undefined, options: Record<string, unknown>, command: Command): Promise<void>;
|