@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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents run <agent> [prompt] --cloud` — the vendor-cloud placement for a run.
|
|
3
|
+
*
|
|
4
|
+
* One of three run placements: local (default), machine (--host/--device,
|
|
5
|
+
* --lease), cloud (--cloud). Routing goes through the cloud provider registry
|
|
6
|
+
* exactly like `agents cloud run --agent <agent>`: the agent's native
|
|
7
|
+
* `cloudProvider` wins, `--provider` overrides. The dispatch itself is the
|
|
8
|
+
* shared core in lib/cloud/dispatch.ts — this module only validates the run
|
|
9
|
+
* surface and translates run flags into a DispatchOptions.
|
|
10
|
+
*/
|
|
11
|
+
import chalk from 'chalk';
|
|
12
|
+
import { AGENTS, resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
|
|
13
|
+
import { RUN_AUTO_KEYWORD } from '../lib/types.js';
|
|
14
|
+
import { resolveProvider, nativeProviderForAgent } from '../lib/cloud/registry.js';
|
|
15
|
+
import { resolveCloudPrompt, executeCloudDispatch } from '../lib/cloud/dispatch.js';
|
|
16
|
+
/** Error type for run --cloud validation failures (exit 1, red message). */
|
|
17
|
+
export class RunCloudError extends Error {
|
|
18
|
+
constructor(message) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'RunCloudError';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Run flags that are meaningless on a cloud placement. Each entry maps the
|
|
25
|
+
* option field to the flag the user typed. Cloud tasks run in the provider's
|
|
26
|
+
* workspace on the provider's accounts, so local-run knobs (account strategy,
|
|
27
|
+
* secrets injection, loop guards, resume, terminal handoff, cwd) have nothing
|
|
28
|
+
* to act on — passing one is an error, never a silent ignore.
|
|
29
|
+
*/
|
|
30
|
+
const RUN_CLOUD_CONFLICTS = [
|
|
31
|
+
{ field: 'terminal', flag: '--terminal', set: (v) => v !== undefined && v !== false },
|
|
32
|
+
{ field: 'interactive', flag: '--interactive', set: (v) => v === true },
|
|
33
|
+
{ field: 'acp', flag: '--acp', set: (v) => v === true },
|
|
34
|
+
{ field: 'loop', flag: '--loop', set: (v) => v === true },
|
|
35
|
+
{ field: 'resumeCheckpoint', flag: '--resume-checkpoint', set: (v) => v !== undefined },
|
|
36
|
+
{ field: 'maxIterations', flag: '--max-iterations', set: (v) => v !== undefined },
|
|
37
|
+
{ field: 'budget', flag: '--budget', set: (v) => v !== undefined },
|
|
38
|
+
{ field: 'until', flag: '--until', set: (v) => v !== undefined },
|
|
39
|
+
{ field: 'interval', flag: '--interval', set: (v) => v !== undefined },
|
|
40
|
+
{ field: 'resume', flag: '--resume', set: (v) => v !== undefined && v !== false },
|
|
41
|
+
{ field: 'sessionId', flag: '--session-id', set: (v) => v !== undefined },
|
|
42
|
+
{ field: 'secrets', flag: '--secrets', set: (v) => Array.isArray(v) && v.length > 0 },
|
|
43
|
+
{ field: 'secretsKeys', flag: '--secrets-keys', set: (v) => v !== undefined },
|
|
44
|
+
{ field: 'allowExpired', flag: '--allow-expired', set: (v) => v === true },
|
|
45
|
+
{ field: 'autoSecrets', flag: '--no-auto-secrets', set: (v) => v === false },
|
|
46
|
+
{ field: 'copyCreds', flag: '--copy-creds', set: (v) => v === true },
|
|
47
|
+
{ field: 'fallback', flag: '--fallback', set: (v) => v !== undefined },
|
|
48
|
+
{ field: 'strategy', flag: '--strategy', set: (v) => v !== undefined },
|
|
49
|
+
{ field: 'balanced', flag: '--balanced', set: (v) => v === true },
|
|
50
|
+
{ field: 'cwd', flag: '--cwd', set: (v) => v !== undefined },
|
|
51
|
+
{ field: 'project', flag: '--project', set: (v) => v !== undefined },
|
|
52
|
+
{ field: 'addDir', flag: '--add-dir', set: (v) => Array.isArray(v) && v.length > 0 },
|
|
53
|
+
{ field: 'remoteCwd', flag: '--remote-cwd', set: (v) => v !== undefined },
|
|
54
|
+
{ field: 'env', flag: '--env', set: (v) => Array.isArray(v) && v.length > 0 },
|
|
55
|
+
{ field: 'notify', flag: '--notify', set: (v) => v === true },
|
|
56
|
+
// effort defaults to 'auto' on run; only an explicit non-default value is a
|
|
57
|
+
// conflict (no provider consumes an effort option — it would be dropped).
|
|
58
|
+
{ field: 'effort', flag: '--effort', set: (v) => v !== undefined && v !== 'auto' },
|
|
59
|
+
// --name seeds the LOCAL session label; a cloud task has no local session.
|
|
60
|
+
{ field: 'name', flag: '--name', set: (v) => v !== undefined },
|
|
61
|
+
];
|
|
62
|
+
/** Flags that refine a cloud dispatch; meaningless without the placement. */
|
|
63
|
+
const CLOUD_ONLY_FLAGS = [
|
|
64
|
+
{ field: 'provider', flag: '--provider', set: (v) => v !== undefined },
|
|
65
|
+
{ field: 'repo', flag: '--repo', set: (v) => Array.isArray(v) && v.length > 0 },
|
|
66
|
+
{ field: 'branch', flag: '--branch', set: (v) => v !== undefined },
|
|
67
|
+
{ field: 'cloudEnv', flag: '--cloud-env', set: (v) => v !== undefined },
|
|
68
|
+
];
|
|
69
|
+
/** Flags the user passed that cannot ride a cloud placement. */
|
|
70
|
+
export function runCloudConflicts(options) {
|
|
71
|
+
return RUN_CLOUD_CONFLICTS.filter((c) => c.set(options[c.field])).map((c) => c.flag);
|
|
72
|
+
}
|
|
73
|
+
/** Cloud-only flags passed without the --cloud placement. */
|
|
74
|
+
export function cloudFlagsWithoutCloud(options) {
|
|
75
|
+
return CLOUD_ONLY_FLAGS.filter((c) => c.set(options[c.field])).map((c) => c.flag);
|
|
76
|
+
}
|
|
77
|
+
/** Agents that route to a native cloud provider (the registry's truth). */
|
|
78
|
+
export function cloudCapableAgentIds() {
|
|
79
|
+
return Object.values(AGENTS)
|
|
80
|
+
.filter((a) => a.cloudProvider)
|
|
81
|
+
.map((a) => a.id)
|
|
82
|
+
.sort();
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the cloud provider for a run: explicit --provider wins, else the
|
|
86
|
+
* agent's native cloud. An agent with no native cloud fails loud with the
|
|
87
|
+
* capable list — never a silent ride onto the configured default.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveRunCloudProvider(agentId, explicitProvider) {
|
|
90
|
+
if (explicitProvider)
|
|
91
|
+
return resolveProvider(explicitProvider);
|
|
92
|
+
if (!nativeProviderForAgent(agentId)) {
|
|
93
|
+
throw new RunCloudError(`${agentId} has no native cloud. Cloud-capable agents: ${cloudCapableAgentIds().join(', ')}. ` +
|
|
94
|
+
`Override with --provider <id> (rush | codex | factory | antigravity | host).`);
|
|
95
|
+
}
|
|
96
|
+
return resolveProvider(undefined, agentId);
|
|
97
|
+
}
|
|
98
|
+
/** Validate the agent half of `agents run <agent> --cloud` and return the registry id. */
|
|
99
|
+
export function resolveRunCloudAgent(agentSpec) {
|
|
100
|
+
if (agentSpec === RUN_AUTO_KEYWORD || agentSpec.startsWith(`${RUN_AUTO_KEYWORD}@`)) {
|
|
101
|
+
throw new RunCloudError(`agents run auto --cloud: auto harness-pick is a local-run feature. ` +
|
|
102
|
+
`Name a cloud-capable agent: ${cloudCapableAgentIds().join(', ')}.`);
|
|
103
|
+
}
|
|
104
|
+
if (agentSpec.includes('@')) {
|
|
105
|
+
throw new RunCloudError(`Version pins (<agent>@<version>) do not apply to --cloud — the provider runs its own agent version. ` +
|
|
106
|
+
`Drop the pin: agents run ${agentSpec.split('@')[0]} "<task>" --cloud.`);
|
|
107
|
+
}
|
|
108
|
+
const agentId = resolveAgentName(agentSpec);
|
|
109
|
+
if (!agentId) {
|
|
110
|
+
throw new RunCloudError(`Unknown agent: ${agentSpec}. Cloud-capable agents: ${cloudCapableAgentIds().join(', ')}.`);
|
|
111
|
+
}
|
|
112
|
+
if (isAgentHardDeprecated(agentId)) {
|
|
113
|
+
throw new RunCloudError(hardDeprecationError(agentId));
|
|
114
|
+
}
|
|
115
|
+
return agentId;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Handle `agents run <agent> [prompt] --cloud`. Validation lives in the
|
|
119
|
+
* exported helpers above (unit-tested); this wires them to the shared
|
|
120
|
+
* dispatch core and dies on any validation failure.
|
|
121
|
+
*/
|
|
122
|
+
export async function handleRunCloud(agentSpec, prompt, options, command) {
|
|
123
|
+
const json = options.json === true;
|
|
124
|
+
try {
|
|
125
|
+
const agentId = resolveRunCloudAgent(agentSpec);
|
|
126
|
+
const provider = resolveRunCloudProvider(agentId, options.provider);
|
|
127
|
+
const resolvedPrompt = resolveCloudPrompt(prompt, {
|
|
128
|
+
json,
|
|
129
|
+
hint: `agents run ${agentId} "<task>" --cloud${agentId === 'claude' ? ' --repo <owner/repo>' : ''}`,
|
|
130
|
+
});
|
|
131
|
+
const repoValues = Array.isArray(options.repo) ? options.repo : [];
|
|
132
|
+
const dispatchOptions = {
|
|
133
|
+
prompt: resolvedPrompt,
|
|
134
|
+
agent: agentId,
|
|
135
|
+
repo: repoValues[0],
|
|
136
|
+
repos: repoValues.length > 0 ? repoValues : undefined,
|
|
137
|
+
branch: options.branch,
|
|
138
|
+
timeout: options.timeout,
|
|
139
|
+
model: options.model,
|
|
140
|
+
providerOptions: {},
|
|
141
|
+
};
|
|
142
|
+
if (options.cloudEnv)
|
|
143
|
+
dispatchOptions.providerOptions.env = options.cloudEnv;
|
|
144
|
+
// --mode defaults to 'plan' on run; forward it only when the user set it.
|
|
145
|
+
if (command.getOptionValueSource('mode') === 'cli') {
|
|
146
|
+
dispatchOptions.providerOptions.mode = options.mode;
|
|
147
|
+
}
|
|
148
|
+
await executeCloudDispatch({
|
|
149
|
+
provider,
|
|
150
|
+
dispatchOptions,
|
|
151
|
+
follow: options.follow !== false,
|
|
152
|
+
json,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (err instanceof RunCloudError) {
|
|
157
|
+
console.error(chalk.red(err.message));
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -281,8 +281,8 @@ export declare function mergeLocalFirst(sessions: SessionMeta[], localMachine: s
|
|
|
281
281
|
*/
|
|
282
282
|
export declare function serializeSessionsJson(sessions: SessionMeta[]): string;
|
|
283
283
|
/** The intentionally small metadata contract emitted by `sessions --resolve`.
|
|
284
|
-
*
|
|
285
|
-
*
|
|
284
|
+
* It includes only launch identity needed to route/resume. Transcript paths,
|
|
285
|
+
* extracted plans, costs, and content stay on the machine that owns them. */
|
|
286
286
|
export declare function serializeResolvedSessionsJson(sessions: SessionMeta[]): string;
|
|
287
287
|
/**
|
|
288
288
|
* Whether the local machine's sessions belong in an `--active` view. Local is
|
|
@@ -636,12 +636,21 @@ export declare function fleetCandidatesByQuery(rows: SessionMeta[], query: strin
|
|
|
636
636
|
export declare function metadataResolveForwardedArgs(selector: string, scope: Pick<SessionFilterOptions, 'agent' | 'project'>): string[];
|
|
637
637
|
/** Build the local-only peer argv for a distributed tool search. */
|
|
638
638
|
export declare function toolSearchForwardedArgs(argv: string[], hosts: string[]): string[];
|
|
639
|
-
/** Resolution
|
|
640
|
-
*
|
|
639
|
+
/** Resolution fails closed for prefixes/keywords when a peer did not answer.
|
|
640
|
+
* A full UUID is globally unique, so one exact hit is sufficient even when an
|
|
641
|
+
* unrelated registered device is offline. */
|
|
641
642
|
export declare function metadataResolveOutcome(localMatches: SessionMeta[], remote: {
|
|
642
643
|
sessions: SessionMeta[];
|
|
643
644
|
unreachable: string[];
|
|
644
645
|
}, selector: string): MetadataResolveOutcome;
|
|
646
|
+
/** Reusable local-first resolver for `agents run --resume` and `agents resume`.
|
|
647
|
+
* Full UUIDs hit the local SQLite index without any SSH fan-out. */
|
|
648
|
+
export declare function resolveSessionMetadataValue(selector: string, scope?: {
|
|
649
|
+
agent?: string;
|
|
650
|
+
project?: string;
|
|
651
|
+
local?: boolean;
|
|
652
|
+
hosts?: string[];
|
|
653
|
+
}, deps?: Pick<FleetResolveDeps, 'gatherRemoteList'>): Promise<MetadataResolveOutcome>;
|
|
645
654
|
/**
|
|
646
655
|
* Locate a full session id or short id prefix across the online fleet and render it from the machine
|
|
647
656
|
* that holds it. The local disk already missed; this fans `sessions <id> --json
|
|
@@ -814,8 +814,8 @@ export function serializeSessionsJson(sessions) {
|
|
|
814
814
|
return JSON.stringify(serializable, null, 2) + '\n';
|
|
815
815
|
}
|
|
816
816
|
/** The intentionally small metadata contract emitted by `sessions --resolve`.
|
|
817
|
-
*
|
|
818
|
-
*
|
|
817
|
+
* It includes only launch identity needed to route/resume. Transcript paths,
|
|
818
|
+
* extracted plans, costs, and content stay on the machine that owns them. */
|
|
819
819
|
export function serializeResolvedSessionsJson(sessions) {
|
|
820
820
|
const safe = sessions.map((session) => ({
|
|
821
821
|
id: session.id,
|
|
@@ -826,6 +826,7 @@ export function serializeResolvedSessionsJson(sessions) {
|
|
|
826
826
|
lastActivity: session.lastActivity,
|
|
827
827
|
project: session.project,
|
|
828
828
|
version: session.version,
|
|
829
|
+
mode: session.mode,
|
|
829
830
|
label: session.label,
|
|
830
831
|
topic: session.topic,
|
|
831
832
|
machine: session.machine,
|
|
@@ -3447,6 +3448,7 @@ function modeFlag(mode) {
|
|
|
3447
3448
|
return '--json';
|
|
3448
3449
|
return undefined; // summary — the peer's default render
|
|
3449
3450
|
}
|
|
3451
|
+
const FULL_SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3450
3452
|
/** Resolve a fleet sweep through the same canonical full-id / prefix resolver as
|
|
3451
3453
|
* local lookups, then group copies by logical session id. Synced mirrors of one
|
|
3452
3454
|
* session therefore stay one candidate even when several machines report them;
|
|
@@ -3501,19 +3503,46 @@ export function toolSearchForwardedArgs(argv, hosts) {
|
|
|
3501
3503
|
args.push('--local');
|
|
3502
3504
|
return args;
|
|
3503
3505
|
}
|
|
3504
|
-
/** Resolution
|
|
3505
|
-
*
|
|
3506
|
+
/** Resolution fails closed for prefixes/keywords when a peer did not answer.
|
|
3507
|
+
* A full UUID is globally unique, so one exact hit is sufficient even when an
|
|
3508
|
+
* unrelated registered device is offline. */
|
|
3506
3509
|
export function metadataResolveOutcome(localMatches, remote, selector) {
|
|
3507
|
-
if (remote.unreachable.length > 0) {
|
|
3508
|
-
return { kind: 'partial', failedPeers: remote.unreachable };
|
|
3509
|
-
}
|
|
3510
3510
|
const candidates = fleetCandidatesByQuery([...localMatches, ...remote.sessions], selector);
|
|
3511
|
+
if (FULL_SESSION_ID_RE.test(selector) && candidates.length === 1 && candidates[0].id.toLowerCase() === selector.toLowerCase()) {
|
|
3512
|
+
return { kind: 'resolved', session: candidates[0].hits[0].session };
|
|
3513
|
+
}
|
|
3514
|
+
if (remote.unreachable.length > 0)
|
|
3515
|
+
return { kind: 'partial', failedPeers: remote.unreachable };
|
|
3511
3516
|
if (candidates.length === 0)
|
|
3512
3517
|
return { kind: 'not-found' };
|
|
3513
3518
|
if (candidates.length > 1)
|
|
3514
3519
|
return { kind: 'ambiguous', candidates };
|
|
3515
3520
|
return { kind: 'resolved', session: candidates[0].hits[0].session };
|
|
3516
3521
|
}
|
|
3522
|
+
/** Reusable local-first resolver for `agents run --resume` and `agents resume`.
|
|
3523
|
+
* Full UUIDs hit the local SQLite index without any SSH fan-out. */
|
|
3524
|
+
export async function resolveSessionMetadataValue(selector, scope = {}, deps = { gatherRemoteList }) {
|
|
3525
|
+
const localMachine = machineId();
|
|
3526
|
+
const includeLocal = !scope.hosts?.length || shouldIncludeLocal(scope.hosts, localMachine);
|
|
3527
|
+
const indexed = includeLocal ? applyScopeFilters(querySessions(), scope) : [];
|
|
3528
|
+
const localMatches = resolveIndexedMetadataRows(indexed, selector)
|
|
3529
|
+
.map(session => ({ ...session, machine: session.machine || localMachine }));
|
|
3530
|
+
if (FULL_SESSION_ID_RE.test(selector)) {
|
|
3531
|
+
const localOutcome = metadataResolveOutcome(localMatches, { sessions: [], unreachable: [] }, selector);
|
|
3532
|
+
if (localOutcome.kind === 'resolved')
|
|
3533
|
+
return localOutcome;
|
|
3534
|
+
}
|
|
3535
|
+
if (scope.local === true)
|
|
3536
|
+
return metadataResolveOutcome(localMatches, { sessions: [], unreachable: [] }, selector);
|
|
3537
|
+
try {
|
|
3538
|
+
const forwarded = metadataResolveForwardedArgs(selector, scope);
|
|
3539
|
+
const remote = await deps.gatherRemoteList(forwarded, scope.hosts);
|
|
3540
|
+
return metadataResolveOutcome(localMatches, remote, selector);
|
|
3541
|
+
}
|
|
3542
|
+
catch (error) {
|
|
3543
|
+
return metadataResolveOutcome(localMatches, { sessions: [], unreachable: [error?.message ?? 'fleet fan-out'] }, selector);
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3517
3546
|
/** Resolve one selector to indexed metadata across the fleet without reading or
|
|
3518
3547
|
* rendering transcript events. A peer answering the parent sweep returns every
|
|
3519
3548
|
* local candidate; the parent performs the one logical-session uniqueness gate. */
|
|
@@ -3529,18 +3558,7 @@ async function resolveSessionMetadata(selector, scope, deps = { gatherRemoteList
|
|
|
3529
3558
|
process.stdout.write(serializeResolvedSessionsJson(localMatches));
|
|
3530
3559
|
return;
|
|
3531
3560
|
}
|
|
3532
|
-
|
|
3533
|
-
if (scope.local !== true) {
|
|
3534
|
-
try {
|
|
3535
|
-
const forwarded = metadataResolveForwardedArgs(selector, scope);
|
|
3536
|
-
const result = await deps.gatherRemoteList(forwarded, scope.hosts);
|
|
3537
|
-
remoteResult = { sessions: result.sessions, unreachable: result.unreachable };
|
|
3538
|
-
}
|
|
3539
|
-
catch (error) {
|
|
3540
|
-
remoteResult = { sessions: [], unreachable: [error?.message ?? 'fleet fan-out'] };
|
|
3541
|
-
}
|
|
3542
|
-
}
|
|
3543
|
-
const outcome = metadataResolveOutcome(localMatches, remoteResult, selector);
|
|
3561
|
+
const outcome = await resolveSessionMetadataValue(selector, scope, deps);
|
|
3544
3562
|
if (outcome.kind === 'partial') {
|
|
3545
3563
|
console.error(chalk.red(`Partial session resolution: ${outcome.failedPeers.join(', ')} did not answer.`));
|
|
3546
3564
|
console.error(chalk.gray('No unique/no-match decision was made. Upgrade or reconnect every peer, then retry.'));
|
|
@@ -438,8 +438,10 @@ export function registerVersionsCommands(program) {
|
|
|
438
438
|
console.log(chalk.gray(` Created shim: ${getShimsDir()}/${agentConfig.cliCommand}`));
|
|
439
439
|
}
|
|
440
440
|
// Seed the fresh version home with user settings from the current
|
|
441
|
-
// default version (settings.json, keybindings, codex config
|
|
442
|
-
//
|
|
441
|
+
// default version (settings.json, keybindings, codex config).
|
|
442
|
+
// Credentials are deliberately excluded so each version keeps its own
|
|
443
|
+
// login (see SETTINGS_MANIFEST). Gap-filling only — never overwrites
|
|
444
|
+
// what the new home has.
|
|
443
445
|
const carrySource = getGlobalDefault(agent);
|
|
444
446
|
if (carrySource && carrySource !== installedVersion) {
|
|
445
447
|
const carried = carryForwardSettings(agent, getVersionHomePath(agent, carrySource), getVersionHomePath(agent, installedVersion));
|
package/dist/commands/view.js
CHANGED
|
@@ -10,8 +10,7 @@ import { machineId } from '../lib/machine-id.js';
|
|
|
10
10
|
import { authCacheKey, formatCheckedAge, readAuthHealthCache } from '../lib/auth-health.js';
|
|
11
11
|
import { agentReportsUsage, deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
12
12
|
import { readManifest } from '../lib/manifest.js';
|
|
13
|
-
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir,
|
|
14
|
-
import { formatKeptProjectResources } from '../lib/project-resources.js';
|
|
13
|
+
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, isVersionIsolated, getIsolatedDefault, } from '../lib/versions.js';
|
|
15
14
|
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
|
|
16
15
|
import { getAgentResources } from '../lib/resources.js';
|
|
17
16
|
import { renderMergedResources } from '../lib/merged-resources.js';
|
|
@@ -29,7 +28,6 @@ import { resolveConfiguredModel } from '../lib/models.js';
|
|
|
29
28
|
import { listProfiles, profileExists, profileSummary, readProfile } from '../lib/profiles.js';
|
|
30
29
|
import { getByokUsageForHarness, hasByokProvider, renderByokBar } from '../lib/byok-usage.js';
|
|
31
30
|
import { renderHarnessDetail } from './harness.js';
|
|
32
|
-
import { loadManifest, isStale } from '../lib/staleness/index.js';
|
|
33
31
|
import { confirm } from '@inquirer/prompts';
|
|
34
32
|
import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
35
33
|
import { terminalWidth, truncateToWidth, stringWidth, padToWidth } from '../lib/session/width.js';
|
|
@@ -781,57 +779,6 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
781
779
|
if (!filterAgentId) {
|
|
782
780
|
renderHostClisSection(process.cwd());
|
|
783
781
|
}
|
|
784
|
-
// Check for new resources when viewing a specific agent
|
|
785
|
-
if (filterAgentId && versionManaged.length > 0) {
|
|
786
|
-
const defaultVersion = getGlobalDefault(filterAgentId);
|
|
787
|
-
if (defaultVersion) {
|
|
788
|
-
const manifest = loadManifest(filterAgentId, defaultVersion);
|
|
789
|
-
const cwd = process.cwd();
|
|
790
|
-
if (manifest && !isStale(manifest, filterAgentId, defaultVersion, cwd)) {
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
const available = getAvailableResources();
|
|
794
|
-
const synced = getActuallySyncedResources(filterAgentId, defaultVersion);
|
|
795
|
-
const projectOnly = getProjectOnlyResources();
|
|
796
|
-
const newResources = getNewResources(available, synced, projectOnly);
|
|
797
|
-
if (hasNewResources(newResources, filterAgentId, defaultVersion)) {
|
|
798
|
-
try {
|
|
799
|
-
const selection = await promptNewResourceSelection(filterAgentId, newResources, defaultVersion);
|
|
800
|
-
if (selection && Object.keys(selection).length > 0) {
|
|
801
|
-
const result = syncResourcesToVersion(filterAgentId, defaultVersion, selection);
|
|
802
|
-
const synced = [];
|
|
803
|
-
if (result.commands)
|
|
804
|
-
synced.push('commands');
|
|
805
|
-
if (result.skills)
|
|
806
|
-
synced.push('skills');
|
|
807
|
-
if (result.hooks)
|
|
808
|
-
synced.push('hooks');
|
|
809
|
-
if (result.memory.length > 0)
|
|
810
|
-
synced.push('memory');
|
|
811
|
-
if (result.permissions)
|
|
812
|
-
synced.push('permissions');
|
|
813
|
-
if (result.mcp.length > 0)
|
|
814
|
-
synced.push('mcp');
|
|
815
|
-
if (result.plugins.length > 0)
|
|
816
|
-
synced.push('plugins');
|
|
817
|
-
if (result.workflows.length > 0)
|
|
818
|
-
synced.push('workflows');
|
|
819
|
-
if (synced.length > 0) {
|
|
820
|
-
console.log(chalk.green(`\nSynced to ${agentLabel(filterAgentId)}@${defaultVersion}: ${synced.join(', ')}`));
|
|
821
|
-
}
|
|
822
|
-
const kept = formatKeptProjectResources(result.projectSkipped);
|
|
823
|
-
if (kept)
|
|
824
|
-
console.log(chalk.gray(kept));
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
catch (err) {
|
|
828
|
-
if (isPromptCancelled(err))
|
|
829
|
-
return;
|
|
830
|
-
throw err;
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
782
|
}
|
|
836
783
|
/**
|
|
837
784
|
* Show detailed resources for a specific agent version.
|
package/dist/index.js
CHANGED
|
@@ -94,7 +94,7 @@ if (IS_DEV_BUILD) {
|
|
|
94
94
|
// module on each invocation (which loaded the whole ~50-module tree before the
|
|
95
95
|
// first byte of output), the registry maps a command name to a thunk that
|
|
96
96
|
// imports only what that command needs. See src/lib/startup/command-registry.ts.
|
|
97
|
-
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
97
|
+
import { COMMAND_LOADERS, LAZY_COMMAND_NAMES, loadView, loadInspect, loadFeedback, loadCommands, loadHooks, loadSkills, loadRules, loadMemory, loadPermissions, loadMcp, loadCli, loadSubagents, loadPlugins, loadWorkflows, loadWorktree, loadVersions, loadImport, loadExport, loadPackages, loadRoutines, loadMonitors, loadProjects, loadRun, loadFork, loadDefaults, loadSet, loadModels, loadPrune, loadTrash, loadRestore, loadDoctor, loadApply, loadStatus, loadSnapshot, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadTrends, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadHumans, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadMailboxes, } from './lib/startup/command-registry.js';
|
|
98
98
|
import { applyGlobalHelpConventions } from './lib/help.js';
|
|
99
99
|
import { renderWhatsNew } from './lib/whats-new.js';
|
|
100
100
|
import { getCliLaunch } from './lib/cli-entry.js';
|
|
@@ -980,6 +980,7 @@ async function registerAllEagerCommands() {
|
|
|
980
980
|
await reg(loadAudit);
|
|
981
981
|
await reg(loadWebhook);
|
|
982
982
|
await reg(loadFunnel);
|
|
983
|
+
await reg(loadHumans);
|
|
983
984
|
registerHqTombstoneCommand(program);
|
|
984
985
|
await reg(loadFeed);
|
|
985
986
|
await reg(loadMailboxes);
|
|
@@ -1177,7 +1178,7 @@ if (process.env.AGENTS_SKIP_MIGRATION !== '1') {
|
|
|
1177
1178
|
// Bumping the suffix re-runs migrations for every user; binary releases that
|
|
1178
1179
|
// don't change the schema must NOT re-run (they would destroy user content
|
|
1179
1180
|
// when migration steps overlap with user-authored paths). See issue #20.
|
|
1180
|
-
const sentinelValue = '
|
|
1181
|
+
const sentinelValue = 'v15';
|
|
1181
1182
|
let needRun = true;
|
|
1182
1183
|
try {
|
|
1183
1184
|
if (fs.existsSync(sentinel) && fs.readFileSync(sentinel, 'utf-8').trim() === sentinelValue) {
|
package/dist/lib/agents.js
CHANGED
|
@@ -212,8 +212,10 @@ export const AGENTS = {
|
|
|
212
212
|
format: 'markdown',
|
|
213
213
|
variableSyntax: '$ARGUMENTS',
|
|
214
214
|
supportsHooks: true,
|
|
215
|
-
// Claude Code
|
|
216
|
-
//
|
|
215
|
+
// Claude Code grew a native `claude --cloud "<prompt>"` (Anthropic-managed
|
|
216
|
+
// infra, claude.ai/code; requires claude.ai subscription auth). Routing
|
|
217
|
+
// still goes to Rush Cloud deliberately — it keeps cloud tasks in one
|
|
218
|
+
// tracked fleet (agents cloud list/status/logs) regardless of harness.
|
|
217
219
|
cloudProvider: 'rush',
|
|
218
220
|
capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true, interactiveRepl: true },
|
|
219
221
|
},
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* One primitive: resolve a destination (channel + target), compose text + urls +
|
|
5
5
|
* attachments, hand off to a channel provider. `notify` is the same path with
|
|
6
|
-
* destination defaulted
|
|
6
|
+
* destination defaulted from `humans.yaml` — owner is an address
|
|
7
7
|
* alias (`--to owner`), not a separate stack.
|
|
8
8
|
*
|
|
9
9
|
* Agent control (`agents message`, `sessions inject`) stays outside this module.
|
|
@@ -59,7 +59,10 @@ export type ResolveSendResult = {
|
|
|
59
59
|
export declare function isOwnerAlias(to: string | undefined): boolean;
|
|
60
60
|
/** Compose body + optional URL lines (skip urls already present in the body). */
|
|
61
61
|
export declare function composeSendText(text: string, urls?: string[]): string;
|
|
62
|
-
/**
|
|
62
|
+
/**
|
|
63
|
+
* Read the owner destination. humans.yaml is canonical; notify.owner remains a
|
|
64
|
+
* migration fallback for installations that have not run the schema migration.
|
|
65
|
+
*/
|
|
63
66
|
export declare function readOwnerDest(meta: Meta): {
|
|
64
67
|
channel: string;
|
|
65
68
|
to: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getOwnerNotifyFromHumans } from '../humans.js';
|
|
1
2
|
import { registerBuiltinProviders } from './providers/index.js';
|
|
2
3
|
import { resolveTransport } from './resolve.js';
|
|
3
4
|
const OWNER_ALIAS = 'owner';
|
|
@@ -16,14 +17,17 @@ export function composeSendText(text, urls) {
|
|
|
16
17
|
return body;
|
|
17
18
|
return body ? `${body}\n${extra.join('\n')}` : extra.join('\n');
|
|
18
19
|
}
|
|
19
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* Read the owner destination. humans.yaml is canonical; notify.owner remains a
|
|
22
|
+
* migration fallback for installations that have not run the schema migration.
|
|
23
|
+
*/
|
|
20
24
|
export function readOwnerDest(meta) {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return { channel, to };
|
|
25
|
+
const canonical = getOwnerNotifyFromHumans();
|
|
26
|
+
if (canonical)
|
|
27
|
+
return canonical;
|
|
28
|
+
const channel = meta.notify?.owner?.channel?.trim();
|
|
29
|
+
const to = meta.notify?.owner?.to?.trim();
|
|
30
|
+
return channel && to ? { channel, to } : null;
|
|
27
31
|
}
|
|
28
32
|
/**
|
|
29
33
|
* Resolve CLI/config into a send envelope. Pure except for reading `meta` —
|
|
@@ -48,15 +52,14 @@ export function resolveSendEnvelope(input, meta) {
|
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
// Owner defaults fill only missing fields (and expand the bare "owner" alias).
|
|
51
|
-
// Explicit --channel/--to always win
|
|
52
|
-
// when both flags are already set (same merge-then-require shape as main).
|
|
53
|
-
const ownerCfg = meta.notify?.owner;
|
|
54
|
-
const ownerChannel = ownerCfg?.channel?.trim() || '';
|
|
55
|
-
const ownerTo = ownerCfg?.to?.trim() || '';
|
|
55
|
+
// humans.yaml is the canonical source. Explicit --channel/--to always win.
|
|
56
56
|
let channel = (input.channel ?? '').trim();
|
|
57
57
|
let to = (input.to ?? '').trim();
|
|
58
58
|
const usedOwnerAlias = isOwnerAlias(to);
|
|
59
59
|
if (input.ownerMode || usedOwnerAlias) {
|
|
60
|
+
const owner = getOwnerNotifyFromHumans() ?? meta.notify?.owner;
|
|
61
|
+
const ownerChannel = owner?.channel ?? '';
|
|
62
|
+
const ownerTo = owner?.to ?? '';
|
|
60
63
|
if (!channel)
|
|
61
64
|
channel = ownerChannel;
|
|
62
65
|
if (!to || usedOwnerAlias)
|
|
@@ -64,7 +67,7 @@ export function resolveSendEnvelope(input, meta) {
|
|
|
64
67
|
}
|
|
65
68
|
if (!channel || !to) {
|
|
66
69
|
const hint = input.ownerMode || usedOwnerAlias
|
|
67
|
-
? 'Set
|
|
70
|
+
? 'Set owner.channels and owner.policy.normal in humans.yaml, or pass --channel and --to explicitly.'
|
|
68
71
|
: 'Need --channel and --to (or --to owner with notify.owner configured). ' +
|
|
69
72
|
'Example: agents send --channel desktop --to local --text "hi"';
|
|
70
73
|
return { ok: false, error: hint };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { CloudProvider, DispatchOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the prompt for a cloud dispatch: the raw value, or the contents of
|
|
4
|
+
* the file it points at (with a dim note on TTY). Dies when empty.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveCloudPrompt(raw: string | undefined, opts: {
|
|
7
|
+
json: boolean;
|
|
8
|
+
hint: string;
|
|
9
|
+
}): string;
|
|
10
|
+
export interface ExecuteCloudDispatchParams {
|
|
11
|
+
provider: CloudProvider;
|
|
12
|
+
dispatchOptions: DispatchOptions;
|
|
13
|
+
/** Image file paths for vision dispatch (checked against provider capability). */
|
|
14
|
+
imagePaths?: string[];
|
|
15
|
+
/** Raw skill refs (`id` or `id@version`) for ride-along skills. */
|
|
16
|
+
skillIds?: string[];
|
|
17
|
+
/** Stream the task output after dispatch; false = fire-and-forget. */
|
|
18
|
+
follow: boolean;
|
|
19
|
+
json: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Dispatch a cloud task and (unless follow=false) stream it to completion.
|
|
23
|
+
* Owns: share-env injection, capability checks, the dispatch spinner +
|
|
24
|
+
* missing-target picker, local persistence, event emission, and the budget
|
|
25
|
+
* kill-switch. Dies on any dispatch failure — callers never see a partial.
|
|
26
|
+
*/
|
|
27
|
+
export declare function executeCloudDispatch(params: ExecuteCloudDispatchParams): Promise<void>;
|