@phnx-labs/agents-cli 1.20.0 → 1.20.4
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 +81 -0
- package/README.md +4 -4
- package/dist/commands/cli.js +3 -3
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +24 -7
- package/dist/commands/exec.js +36 -16
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +86 -7
- package/dist/commands/import.js +90 -37
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +117 -4
- package/dist/commands/pull.js +4 -4
- package/dist/commands/routines.js +6 -6
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +74 -39
- package/dist/commands/skills.js +22 -5
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +48 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.js +4 -4
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +169 -8
- package/dist/commands/workflows.js +29 -6
- package/dist/index.js +4 -0
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +41 -17
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/chrome.js +4 -0
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/profiles.d.ts +3 -3
- package/dist/lib/browser/profiles.js +3 -3
- package/dist/lib/browser/service.js +19 -0
- package/dist/lib/browser/types.d.ts +4 -4
- package/dist/lib/cli-resources.d.ts +36 -8
- package/dist/lib/cli-resources.js +268 -46
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +39 -11
- package/dist/lib/exec.js +90 -31
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +68 -15
- package/dist/lib/import.d.ts +21 -0
- package/dist/lib/import.js +55 -2
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +40 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +51 -1
- package/dist/lib/plugin-marketplace.d.ts +10 -0
- package/dist/lib/plugin-marketplace.js +47 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +187 -8
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/pty-server.js +27 -3
- package/dist/lib/routines-format.d.ts +17 -5
- package/dist/lib/routines-format.js +37 -16
- package/dist/lib/routines.d.ts +1 -1
- package/dist/lib/routines.js +2 -2
- package/dist/lib/runner.js +64 -10
- 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/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +18 -22
- package/dist/lib/secrets/bundles.js +75 -99
- package/dist/lib/secrets/index.d.ts +51 -27
- package/dist/lib/secrets/index.js +147 -156
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/shims.d.ts +4 -1
- package/dist/lib/shims.js +5 -35
- package/dist/lib/state.d.ts +14 -1
- package/dist/lib/state.js +49 -5
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +47 -21
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/types.d.ts +57 -1
- package/dist/lib/types.js +2 -0
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +35 -1
- package/dist/lib/versions.js +288 -64
- package/package.json +13 -12
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
package/dist/lib/teams/agents.js
CHANGED
|
@@ -19,6 +19,7 @@ import { debug } from './debug.js';
|
|
|
19
19
|
import { setGeminiAutoUpdateDisabled, updateGeminiSettings } from '../gemini-settings.js';
|
|
20
20
|
import { getAgentsDir as getSystemAgentsDir } from '../state.js';
|
|
21
21
|
import { AGENTS } from '../agents.js';
|
|
22
|
+
import { sanitizeProcessEnv } from '../secrets/bundles.js';
|
|
22
23
|
let lastMemoryWarnAt = 0;
|
|
23
24
|
// On macOS, os.freemem() returns only the truly-free pool and ignores the
|
|
24
25
|
// large inactive+purgeable cache the kernel will reclaim under pressure, so
|
|
@@ -183,12 +184,17 @@ When you're done, provide a brief summary of:
|
|
|
183
184
|
const CLAUDE_PLAN_MODE_PREFIX = `You are running in HEADLESS PLAN MODE. This mode works like normal plan mode with one exception: you cannot write to ~/.claude/plans/ directory. Instead of writing a plan file, output your complete plan/response as your final message.
|
|
184
185
|
|
|
185
186
|
`;
|
|
186
|
-
|
|
187
|
+
// Canonical modes plus the historical `full` alias (rewritten to `skip` by
|
|
188
|
+
// normalizeModeValue). Keep `full` listed so user-typed CLI flags and stored
|
|
189
|
+
// metadata that pre-date the rename continue to parse.
|
|
190
|
+
export const VALID_MODES = ['plan', 'edit', 'auto', 'skip', 'full'];
|
|
187
191
|
function normalizeModeValue(modeValue) {
|
|
188
192
|
if (!modeValue)
|
|
189
193
|
return null;
|
|
190
194
|
const normalized = modeValue.trim().toLowerCase();
|
|
191
|
-
if (
|
|
195
|
+
if (normalized === 'full')
|
|
196
|
+
return 'skip';
|
|
197
|
+
if (['plan', 'edit', 'auto', 'skip'].includes(normalized)) {
|
|
192
198
|
return normalized;
|
|
193
199
|
}
|
|
194
200
|
return null;
|
|
@@ -201,7 +207,7 @@ function defaultModeFromEnv() {
|
|
|
201
207
|
return parsed;
|
|
202
208
|
}
|
|
203
209
|
if (rawValue) {
|
|
204
|
-
console.warn(`Invalid ${envVar}='${rawValue}'. Use
|
|
210
|
+
console.warn(`Invalid ${envVar}='${rawValue}'. Use plan, edit, auto, or skip. Falling back to plan mode.`);
|
|
205
211
|
}
|
|
206
212
|
}
|
|
207
213
|
return 'plan';
|
|
@@ -253,12 +259,12 @@ function extractTimestamp(raw) {
|
|
|
253
259
|
export function resolveMode(requestedMode, defaultMode = 'plan') {
|
|
254
260
|
const normalizedDefault = normalizeModeValue(defaultMode);
|
|
255
261
|
if (!normalizedDefault) {
|
|
256
|
-
throw new Error(`Invalid default mode '${defaultMode}'. Use
|
|
262
|
+
throw new Error(`Invalid default mode '${defaultMode}'. Use plan, edit, auto, or skip.`);
|
|
257
263
|
}
|
|
258
264
|
if (requestedMode !== null && requestedMode !== undefined) {
|
|
259
265
|
const normalizedMode = normalizeModeValue(requestedMode);
|
|
260
266
|
if (!normalizedMode) {
|
|
261
|
-
throw new Error(`Invalid mode '${requestedMode}'. Valid modes:
|
|
267
|
+
throw new Error(`Invalid mode '${requestedMode}'. Valid modes: plan (read-only), edit (can write), auto (smart classifier), skip (bypass all permissions). 'full' is accepted as alias for skip.`);
|
|
262
268
|
}
|
|
263
269
|
return normalizedMode;
|
|
264
270
|
}
|
|
@@ -363,6 +369,11 @@ export class AgentProcess {
|
|
|
363
369
|
// Pinned model for this teammate. When null, the agent's CLI picks its
|
|
364
370
|
// own default (no --model forwarded).
|
|
365
371
|
model = null;
|
|
372
|
+
// Profile target name when the teammate was added via `agents teams add
|
|
373
|
+
// <team> <profile>`. The launcher targets the profile name so env/keychain
|
|
374
|
+
// injection happens; agentType stays the underlying harness so event
|
|
375
|
+
// parsers and CLI availability checks keep working.
|
|
376
|
+
profileName = null;
|
|
366
377
|
// Extra env vars passed through to the child process (from --env KEY=VALUE).
|
|
367
378
|
envOverrides = null;
|
|
368
379
|
// Factory task-type label. Drives planner fan-out. Null for plain teammates — no behavioral change.
|
|
@@ -378,13 +389,14 @@ export class AgentProcess {
|
|
|
378
389
|
eventsCache = [];
|
|
379
390
|
lastReadPos = 0;
|
|
380
391
|
baseDir = null;
|
|
381
|
-
constructor(agentId, taskName, agentType, prompt, cwd = null, mode = 'plan', pid = null, status = AgentStatus.RUNNING, startedAt = new Date(), completedAt = null, baseDir = null, parentSessionId = null, workspaceDir = null, cloudSessionId = null, cloudProvider = null, prUrl = null, version = null, remoteSessionId = null, name = null, after = [], effort = null, model = null, envOverrides = null, taskType = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null) {
|
|
392
|
+
constructor(agentId, taskName, agentType, prompt, cwd = null, mode = 'plan', pid = null, status = AgentStatus.RUNNING, startedAt = new Date(), completedAt = null, baseDir = null, parentSessionId = null, workspaceDir = null, cloudSessionId = null, cloudProvider = null, prUrl = null, version = null, remoteSessionId = null, name = null, after = [], effort = null, model = null, envOverrides = null, taskType = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null) {
|
|
382
393
|
this.agentId = agentId;
|
|
383
394
|
this.remoteSessionId = remoteSessionId;
|
|
384
395
|
this.name = name;
|
|
385
396
|
this.after = after;
|
|
386
397
|
this.effort = effort;
|
|
387
398
|
this.model = model;
|
|
399
|
+
this.profileName = profileName;
|
|
388
400
|
this.envOverrides = envOverrides;
|
|
389
401
|
this.taskType = taskType;
|
|
390
402
|
this.cloudRepo = cloudRepo;
|
|
@@ -409,7 +421,9 @@ export class AgentProcess {
|
|
|
409
421
|
this.version = version;
|
|
410
422
|
}
|
|
411
423
|
get isEditMode() {
|
|
412
|
-
|
|
424
|
+
// Any mode that can mutate the workspace counts as "edit mode" for the
|
|
425
|
+
// purposes of guarding read-only flows (plan-mode teammates).
|
|
426
|
+
return this.mode === 'edit' || this.mode === 'auto' || this.mode === 'skip';
|
|
413
427
|
}
|
|
414
428
|
async getAgentDir() {
|
|
415
429
|
const base = this.baseDir || await getAgentsDir();
|
|
@@ -466,6 +480,7 @@ export class AgentProcess {
|
|
|
466
480
|
after: this.after,
|
|
467
481
|
effort: this.effort,
|
|
468
482
|
model: this.model,
|
|
483
|
+
profile_name: this.profileName,
|
|
469
484
|
env_overrides: this.envOverrides,
|
|
470
485
|
task_type: this.taskType,
|
|
471
486
|
cloud_repo: this.cloudRepo,
|
|
@@ -596,6 +611,7 @@ export class AgentProcess {
|
|
|
596
611
|
after: this.after,
|
|
597
612
|
effort: this.effort,
|
|
598
613
|
model: this.model,
|
|
614
|
+
profile_name: this.profileName,
|
|
599
615
|
env_overrides: this.envOverrides,
|
|
600
616
|
task_type: this.taskType,
|
|
601
617
|
cloud_repo: this.cloudRepo,
|
|
@@ -619,12 +635,16 @@ export class AgentProcess {
|
|
|
619
635
|
try {
|
|
620
636
|
const metaContent = await fs.readFile(metaPath, 'utf-8');
|
|
621
637
|
const meta = JSON.parse(metaContent);
|
|
622
|
-
// Legacy teammates may have mode='ralph' or '
|
|
623
|
-
// were narrowed. Coerce to the closest current mode so they
|
|
638
|
+
// Legacy teammates may have mode='ralph', 'cloud', or 'full' from before
|
|
639
|
+
// modes were narrowed/renamed. Coerce to the closest current mode so they
|
|
640
|
+
// still load.
|
|
624
641
|
const modeMap = {
|
|
642
|
+
plan: 'plan',
|
|
625
643
|
edit: 'edit',
|
|
626
|
-
|
|
627
|
-
|
|
644
|
+
auto: 'auto',
|
|
645
|
+
skip: 'skip',
|
|
646
|
+
full: 'skip', // historical alias — `full` is the old name for `skip`
|
|
647
|
+
ralph: 'skip', // ralph used the same "no-permission" flags as full
|
|
628
648
|
cloud: 'edit', // cloud teammates had edit-level write access
|
|
629
649
|
};
|
|
630
650
|
const resolvedMode = modeMap[meta.mode] || 'plan';
|
|
@@ -637,7 +657,7 @@ export class AgentProcess {
|
|
|
637
657
|
: AgentStatus.RUNNING;
|
|
638
658
|
const agent = new AgentProcess(meta.agent_id, meta.task_name || 'default', meta.agent_type, meta.prompt, meta.cwd || null, resolvedMode, meta.pid || null, resolvedStatus, new Date(meta.started_at), meta.completed_at ? new Date(meta.completed_at) : null, baseDir, meta.parent_session_id || null, meta.workspace_dir || null, meta.cloud_session_id || null, meta.cloud_provider || null, meta.pr_url || null, meta.version || null, meta.remote_session_id || null, meta.name || null, Array.isArray(meta.after) ? meta.after : [], meta.effort || null, meta.model || null, meta.env_overrides || null, meta.task_type && VALID_TASK_TYPES.includes(meta.task_type)
|
|
639
659
|
? meta.task_type
|
|
640
|
-
: null, meta.cloud_repo || null, meta.cloud_branch || null, meta.worktree_name || null, meta.worktree_path || null);
|
|
660
|
+
: null, meta.cloud_repo || null, meta.cloud_branch || null, meta.worktree_name || null, meta.worktree_path || null, meta.profile_name || null);
|
|
641
661
|
agent.startTime = typeof meta.start_time === 'string' ? meta.start_time : null;
|
|
642
662
|
return agent;
|
|
643
663
|
}
|
|
@@ -759,7 +779,7 @@ export class AgentManager {
|
|
|
759
779
|
this.cleanupAgeDays = cleanupAgeDays;
|
|
760
780
|
const resolvedDefaultMode = defaultMode ? normalizeModeValue(defaultMode) : defaultModeFromEnv();
|
|
761
781
|
if (!resolvedDefaultMode) {
|
|
762
|
-
throw new Error(`Invalid default_mode '${defaultMode}'. Use
|
|
782
|
+
throw new Error(`Invalid default_mode '${defaultMode}'. Use plan, edit, auto, or skip.`);
|
|
763
783
|
}
|
|
764
784
|
this.defaultMode = resolvedDefaultMode;
|
|
765
785
|
this.initPromise = this.doInitialize();
|
|
@@ -875,7 +895,7 @@ export class AgentManager {
|
|
|
875
895
|
}
|
|
876
896
|
debug(`Loaded ${loadedCount} agents from disk`);
|
|
877
897
|
}
|
|
878
|
-
async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null) {
|
|
898
|
+
async spawn(taskName, agentType, prompt, cwd = null, mode = null, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null) {
|
|
879
899
|
await this.initialize();
|
|
880
900
|
const resolvedMode = resolveMode(mode, this.defaultMode);
|
|
881
901
|
// Enforce: teammate names are unique within a team.
|
|
@@ -922,6 +942,9 @@ export class AgentManager {
|
|
|
922
942
|
// dispatched via the cloud provider and passed us the provider + session.
|
|
923
943
|
const isCloudBacked = Boolean(cloudProvider);
|
|
924
944
|
if (!isCloudBacked) {
|
|
945
|
+
// Profile-backed teammates still spawn through `agents run`, which
|
|
946
|
+
// resolves the profile to its host harness — so the CLI we need to be
|
|
947
|
+
// present is the underlying agentType, not the profile name.
|
|
925
948
|
const [available, pathOrError] = checkCliAvailable(agentType);
|
|
926
949
|
if (!available) {
|
|
927
950
|
throw new Error(pathOrError || 'CLI tool not available');
|
|
@@ -934,7 +957,7 @@ export class AgentManager {
|
|
|
934
957
|
const initialStatus = isStaged || !isCloudBacked
|
|
935
958
|
? AgentStatus.PENDING
|
|
936
959
|
: AgentStatus.RUNNING;
|
|
937
|
-
const agent = new AgentProcess(agentId, taskName, agentType, prompt, resolvedCwd, resolvedMode, null, initialStatus, new Date(), null, this.agentsDir, parentSessionId, workspaceDir, cloudSessionId, cloudProvider, null, version, null, name, cleanAfter, effort, model, envOverrides && Object.keys(envOverrides).length > 0 ? envOverrides : null, taskType, cloudRepo, cloudBranch, worktreeName, worktreePath);
|
|
960
|
+
const agent = new AgentProcess(agentId, taskName, agentType, prompt, resolvedCwd, resolvedMode, null, initialStatus, new Date(), null, this.agentsDir, parentSessionId, workspaceDir, cloudSessionId, cloudProvider, null, version, null, name, cleanAfter, effort, model, envOverrides && Object.keys(envOverrides).length > 0 ? envOverrides : null, taskType, cloudRepo, cloudBranch, worktreeName, worktreePath, profileName);
|
|
938
961
|
const agentDir = await agent.getAgentDir();
|
|
939
962
|
try {
|
|
940
963
|
await fs.mkdir(agentDir, { recursive: true });
|
|
@@ -971,7 +994,7 @@ export class AgentManager {
|
|
|
971
994
|
// forwarded). Effort is a separate knob wired into buildReasoningFlags
|
|
972
995
|
// inside buildCommand.
|
|
973
996
|
const resolvedModel = agent.model ?? null;
|
|
974
|
-
const cmd = this.buildCommand(agent.agentType, agent.prompt, agent.mode, resolvedModel, agent.cwd, agent.agentId, effort, agent.version);
|
|
997
|
+
const cmd = this.buildCommand(agent.agentType, agent.prompt, agent.mode, resolvedModel, agent.cwd, agent.agentId, effort, agent.version, agent.profileName);
|
|
975
998
|
debug(`Launching ${agent.agentType} agent ${agent.agentId} [${agent.mode}]: ${cmd.slice(0, 3).join(' ')}...`);
|
|
976
999
|
try {
|
|
977
1000
|
const stdoutPath = await agent.getStdoutPath();
|
|
@@ -982,8 +1005,8 @@ export class AgentManager {
|
|
|
982
1005
|
cwd: agent.cwd || undefined,
|
|
983
1006
|
detached: true,
|
|
984
1007
|
env: agent.envOverrides
|
|
985
|
-
? { ...process.env, ...agent.envOverrides }
|
|
986
|
-
: process.env,
|
|
1008
|
+
? { ...sanitizeProcessEnv(process.env), ...agent.envOverrides }
|
|
1009
|
+
: sanitizeProcessEnv(process.env),
|
|
987
1010
|
});
|
|
988
1011
|
childProcess.unref();
|
|
989
1012
|
stdoutFile.close().catch(() => { });
|
|
@@ -1055,15 +1078,18 @@ export class AgentManager {
|
|
|
1055
1078
|
* exec path (src/lib/exec.ts). The team runner just supplies prompt + mode
|
|
1056
1079
|
* and reads stream-json events off stdout.
|
|
1057
1080
|
*/
|
|
1058
|
-
buildCommand(agentType, prompt, mode, model, cwd = null, sessionId = null, effort = 'medium', version = null) {
|
|
1081
|
+
buildCommand(agentType, prompt, mode, model, cwd = null, sessionId = null, effort = 'medium', version = null, profileName = null) {
|
|
1059
1082
|
// Compose the prompt: a plan-mode prefix for Claude (clarifying headless
|
|
1060
1083
|
// plan-mode restrictions) and a universal summary suffix. These are
|
|
1061
1084
|
// team-specific prompt scaffolding — `agents run` does not apply them.
|
|
1062
1085
|
let fullPrompt = prompt + PROMPT_SUFFIX;
|
|
1063
|
-
if (agentType === 'claude' && mode
|
|
1086
|
+
if (agentType === 'claude' && mode === 'plan') {
|
|
1064
1087
|
fullPrompt = CLAUDE_PLAN_MODE_PREFIX + fullPrompt;
|
|
1065
1088
|
}
|
|
1066
|
-
|
|
1089
|
+
// Profile target takes precedence — `agents run <profile>` resolves the
|
|
1090
|
+
// host harness, version pin, and env injection in one place. Plain
|
|
1091
|
+
// version pins only apply when no profile is selected.
|
|
1092
|
+
const target = profileName ?? (version ? `${agentType}@${version}` : agentType);
|
|
1067
1093
|
const agentsCli = process.argv[1];
|
|
1068
1094
|
const cmd = [
|
|
1069
1095
|
process.execPath,
|
package/dist/lib/teams/api.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface SpawnResult {
|
|
|
13
13
|
status: string;
|
|
14
14
|
started_at: string;
|
|
15
15
|
version?: string | null;
|
|
16
|
+
profile_name?: string | null;
|
|
16
17
|
remote_session_id?: string | null;
|
|
17
18
|
name?: string | null;
|
|
18
19
|
after?: string[];
|
|
@@ -88,7 +89,7 @@ export interface TasksResult {
|
|
|
88
89
|
tasks: TaskInfo[];
|
|
89
90
|
}
|
|
90
91
|
/** Spawn a new teammate in a task and return its initial metadata. */
|
|
91
|
-
export declare function handleSpawn(manager: AgentManager, taskName: string, agentType: AgentType, prompt: string, cwd: string | null, mode: string | null, effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto' | null, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null): Promise<SpawnResult>;
|
|
92
|
+
export declare function handleSpawn(manager: AgentManager, taskName: string, agentType: AgentType, prompt: string, cwd: string | null, mode: string | null, effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto' | null, parentSessionId?: string | null, workspaceDir?: string | null, version?: string | null, name?: string | null, after?: string[], model?: string | null, envOverrides?: Record<string, string> | null, taskType?: TaskType | null, cloudProvider?: string | null, cloudSessionId?: string | null, cloudRepo?: string | null, cloudBranch?: string | null, worktreeName?: string | null, worktreePath?: string | null, profileName?: string | null): Promise<SpawnResult>;
|
|
92
93
|
/** Retrieve the current status of all teammates in a task, with optional timestamp-based delta filtering. */
|
|
93
94
|
export declare function handleStatus(manager: AgentManager, taskName: string | null | undefined, filter?: string, since?: string, // Optional ISO timestamp - return only events after this time
|
|
94
95
|
parentSessionId?: string | null): Promise<TaskStatusResult>;
|
package/dist/lib/teams/api.js
CHANGED
|
@@ -56,12 +56,12 @@ function recentToolCalls(events, max = 10) {
|
|
|
56
56
|
}));
|
|
57
57
|
}
|
|
58
58
|
/** Spawn a new teammate in a task and return its initial metadata. */
|
|
59
|
-
export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mode, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null) {
|
|
59
|
+
export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mode, effort = 'medium', parentSessionId = null, workspaceDir = null, version = null, name = null, after = [], model = null, envOverrides = null, taskType = null, cloudProvider = null, cloudSessionId = null, cloudRepo = null, cloudBranch = null, worktreeName = null, worktreePath = null, profileName = null) {
|
|
60
60
|
const defaultMode = manager.getDefaultMode();
|
|
61
61
|
const resolvedMode = resolveMode(mode, defaultMode);
|
|
62
62
|
const resolvedEffort = effort ?? 'medium';
|
|
63
|
-
debug(`[spawn] Spawning ${agentType} agent for task "${taskName}" [${resolvedMode}] effort=${resolvedEffort}...`);
|
|
64
|
-
const agent = await manager.spawn(taskName, agentType, prompt, cwd, resolvedMode, resolvedEffort, parentSessionId, workspaceDir, version, name, after, model, envOverrides, taskType, cloudProvider, cloudSessionId, cloudRepo, cloudBranch, worktreeName, worktreePath);
|
|
63
|
+
debug(`[spawn] Spawning ${agentType} agent for task "${taskName}" [${resolvedMode}] effort=${resolvedEffort}${profileName ? ` profile=${profileName}` : ''}...`);
|
|
64
|
+
const agent = await manager.spawn(taskName, agentType, prompt, cwd, resolvedMode, resolvedEffort, parentSessionId, workspaceDir, version, name, after, model, envOverrides, taskType, cloudProvider, cloudSessionId, cloudRepo, cloudBranch, worktreeName, worktreePath, profileName);
|
|
65
65
|
debug(`[spawn] Spawned ${agentType} agent ${agent.agentId} for task "${taskName}"`);
|
|
66
66
|
return {
|
|
67
67
|
task_name: taskName,
|
|
@@ -70,6 +70,7 @@ export async function handleSpawn(manager, taskName, agentType, prompt, cwd, mod
|
|
|
70
70
|
status: agent.status,
|
|
71
71
|
started_at: agent.startedAt.toISOString(),
|
|
72
72
|
version: agent.version,
|
|
73
|
+
profile_name: agent.profileName,
|
|
73
74
|
remote_session_id: agent.remoteSessionId,
|
|
74
75
|
name: agent.name,
|
|
75
76
|
after: agent.after,
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -40,6 +40,12 @@ export interface AgentConfig {
|
|
|
40
40
|
skills: Capability;
|
|
41
41
|
commands: Capability;
|
|
42
42
|
plugins: Capability;
|
|
43
|
+
/**
|
|
44
|
+
* Permission modes this agent natively supports. Modes outside this set
|
|
45
|
+
* are gated by buildExecCommand: `auto` silently degrades to `edit`,
|
|
46
|
+
* `skip` errors with a clear message naming the supported modes.
|
|
47
|
+
*/
|
|
48
|
+
modes: Mode[];
|
|
43
49
|
/**
|
|
44
50
|
* Whether the agent natively resolves `@path/to/file` imports inside its
|
|
45
51
|
* rules file at session start. If false, agents-cli must pre-compile the
|
|
@@ -60,6 +66,19 @@ export type Capability = boolean | {
|
|
|
60
66
|
};
|
|
61
67
|
/** Names of every gateable capability on AgentConfig. */
|
|
62
68
|
export type CapabilityName = 'hooks' | 'mcp' | 'allowlist' | 'skills' | 'commands' | 'plugins';
|
|
69
|
+
/**
|
|
70
|
+
* Permission modes controlling agent autonomy.
|
|
71
|
+
* plan read-only investigation; no writes, no shell side-effects
|
|
72
|
+
* edit may edit files; prompts for shell/risky operations
|
|
73
|
+
* auto smart classifier auto-approves safe operations, prompts for risky ones
|
|
74
|
+
* skip bypasses every permission prompt (dangerously-skip-permissions)
|
|
75
|
+
*
|
|
76
|
+
* `full` is accepted as a permanent silent alias for `skip` via normalizeMode().
|
|
77
|
+
* Per-agent support is declared on AgentConfig.capabilities.modes.
|
|
78
|
+
*/
|
|
79
|
+
export type Mode = 'plan' | 'edit' | 'auto' | 'skip';
|
|
80
|
+
/** Every canonical mode in declaration order. Useful for iteration / validation. */
|
|
81
|
+
export declare const ALL_MODES: readonly Mode[];
|
|
63
82
|
/** Reason a capability check failed. */
|
|
64
83
|
export type CapabilityFailReason = 'unsupported' | 'too_old' | 'too_new';
|
|
65
84
|
/** Result of `supports(agent, cap, version?)`. */
|
|
@@ -100,6 +119,36 @@ export interface HookMatches {
|
|
|
100
119
|
cwd_includes?: string | string[];
|
|
101
120
|
project_has?: string;
|
|
102
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Cache scoping. Determines which cache file a hook invocation reads/writes:
|
|
124
|
+
* - `global` one file per hook, shared across cwds/sessions. Right for
|
|
125
|
+
* SessionStart hooks pulling org-wide context (Linear sprint).
|
|
126
|
+
* - `per-cwd` keyed on the working directory the hook fires from.
|
|
127
|
+
* - `per-session` keyed on the agent's session_id (read from stdin JSON).
|
|
128
|
+
* - `per-project` keyed on the nearest git repo root above cwd.
|
|
129
|
+
*/
|
|
130
|
+
export type HookCacheKey = 'global' | 'per-cwd' | 'per-session' | 'per-project';
|
|
131
|
+
/** Prefetch strategy when the cache is stale. */
|
|
132
|
+
export type HookCachePrefetch = 'none' | 'background';
|
|
133
|
+
/**
|
|
134
|
+
* Full hook cache config. Authors usually use the shorthand string form
|
|
135
|
+
* (`HookCache`) below. Shorthand examples in hooks.yaml:
|
|
136
|
+
*
|
|
137
|
+
* cache: 5m # → { ttl: 300, key: 'global', prefetch: 'none' }
|
|
138
|
+
* cache: 5m-bg # → { ttl: 300, key: 'global', prefetch: 'background' }
|
|
139
|
+
* cache: # full form
|
|
140
|
+
* ttl: 1h
|
|
141
|
+
* key: per-cwd
|
|
142
|
+
* prefetch: background
|
|
143
|
+
*/
|
|
144
|
+
export interface HookCacheConfig {
|
|
145
|
+
/** TTL in seconds or duration string ("30s", "5m", "1h"). */
|
|
146
|
+
ttl: number | string;
|
|
147
|
+
key?: HookCacheKey;
|
|
148
|
+
prefetch?: HookCachePrefetch;
|
|
149
|
+
}
|
|
150
|
+
/** Cache shorthand: duration string, optionally suffixed `-bg` for background prefetch. */
|
|
151
|
+
export type HookCache = string | HookCacheConfig;
|
|
103
152
|
/** Hook entry as declared in a package manifest (agents.yaml). */
|
|
104
153
|
export interface ManifestHook {
|
|
105
154
|
script: string;
|
|
@@ -114,6 +163,13 @@ export interface ManifestHook {
|
|
|
114
163
|
override?: boolean;
|
|
115
164
|
/** Optional pre-filter predicates evaluated before invoking the script. */
|
|
116
165
|
matches?: HookMatches;
|
|
166
|
+
/**
|
|
167
|
+
* Opt-in caching. When set, the registrar generates a per-hook shim
|
|
168
|
+
* under the hook shims dir that handles cache lookup, stale-while-revalidate,
|
|
169
|
+
* and per-invocation timing/logging, then registers that shim with the agent
|
|
170
|
+
* instead of the raw script. The underlying script is unchanged.
|
|
171
|
+
*/
|
|
172
|
+
cache?: HookCache;
|
|
117
173
|
}
|
|
118
174
|
/** Lightweight hook descriptor used in resource listings. */
|
|
119
175
|
export interface HookResourceEntry {
|
|
@@ -469,7 +525,7 @@ export interface BrowserProfileConfig {
|
|
|
469
525
|
};
|
|
470
526
|
/** Directory holding source-side JSONL logs (e.g. ~/.rush/logs). */
|
|
471
527
|
logDir?: string;
|
|
472
|
-
/** Optional SSH host where logDir lives, e.g. "user@
|
|
528
|
+
/** Optional SSH host where logDir lives, e.g. "user@remote-host". */
|
|
473
529
|
logHost?: string;
|
|
474
530
|
}
|
|
475
531
|
/** Options controlling which agents and resources are synced during `agents pull` / `agents use`. */
|
package/dist/lib/types.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* configuration schemas, resource tracking, registry types, and permission
|
|
6
6
|
* formats for each supported agent.
|
|
7
7
|
*/
|
|
8
|
+
/** Every canonical mode in declaration order. Useful for iteration / validation. */
|
|
9
|
+
export const ALL_MODES = ['plan', 'edit', 'auto', 'skip'];
|
|
8
10
|
/** Canonical system repo cloned into ~/.agents-system/. */
|
|
9
11
|
export const DEFAULT_SYSTEM_REPO = 'gh:phnx-labs/.agents-system';
|
|
10
12
|
/** Strip the `gh:` prefix and `.git` suffix to get a GitHub `owner/repo` slug. */
|
package/dist/lib/usage.d.ts
CHANGED
|
@@ -71,12 +71,37 @@ export declare function getUsageInfoByIdentity(inputs: UsageIdentityInput[]): Pr
|
|
|
71
71
|
usageByKey: Map<string, UsageInfo>;
|
|
72
72
|
}>;
|
|
73
73
|
/**
|
|
74
|
-
* Fetch usage for a single identity
|
|
75
|
-
*
|
|
74
|
+
* Fetch usage for a single identity using stale-while-revalidate.
|
|
75
|
+
*
|
|
76
|
+
* - Cache fresh (< 2 min): return cached snapshot, NO network.
|
|
77
|
+
* - Cache stale but < 24h: return cached snapshot instantly, fire background refresh.
|
|
78
|
+
* - Cache too stale or absent: block on live fetch, fall back to cache on error.
|
|
79
|
+
*
|
|
80
|
+
* This keeps `agents run` startup off the network on the hot path. The first
|
|
81
|
+
* invocation after a cold install or 24h gap still blocks once to seed the
|
|
82
|
+
* cache; every run after that returns instantly while the cache silently
|
|
83
|
+
* refreshes in the background.
|
|
76
84
|
*/
|
|
77
85
|
export declare function getUsageInfoForIdentity(input: UsageIdentityInput): Promise<UsageInfo>;
|
|
78
86
|
/** Format a one-line usage summary with compact bars for inline display. */
|
|
79
87
|
export declare function formatUsageSummary(plan: string | null, snapshot: UsageSnapshot | null, planWidth?: number): string;
|
|
88
|
+
/**
|
|
89
|
+
* Compact colored badge for the account's overall usage status. Renders only
|
|
90
|
+
* when the account is throttled — `available` and `null` return ''.
|
|
91
|
+
*
|
|
92
|
+
* - `out_of_credits` → red "out of credits" (terminal account, all buckets dry)
|
|
93
|
+
* - `rate_limited` → yellow "rate-limited" (transient throttling)
|
|
94
|
+
*
|
|
95
|
+
* The badge sits between the usage bars and `lastActive` in `agents view`, so
|
|
96
|
+
* a glance at the row tells the user whether the version can do useful work.
|
|
97
|
+
* The same signal is exposed as `usageStatus` in `agents view --json` for
|
|
98
|
+
* programmatic consumers (e.g. the swarmify panel's "resume in healthy agent").
|
|
99
|
+
*
|
|
100
|
+
* The switch is exhaustive on purpose — adding a new `AccountInfo.usageStatus`
|
|
101
|
+
* value without updating the cases here is a build error at `_exhaustive`,
|
|
102
|
+
* which is exactly the bug class this PR is fixing.
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatUsageStatusBadge(usageStatus: 'available' | 'rate_limited' | 'out_of_credits' | null | undefined): string;
|
|
80
105
|
/** Format a multi-line usage section for detailed agent views. */
|
|
81
106
|
export declare function formatUsageSection(usage: UsageInfo): string[];
|
|
82
107
|
/** Load Claude OAuth credentials from the system keychain/keyring. */
|
package/dist/lib/usage.js
CHANGED
|
@@ -96,42 +96,95 @@ export async function getUsageInfoByIdentity(inputs) {
|
|
|
96
96
|
usageByKey: new Map(usageResults.map(({ key, usage }) => [key, usage])),
|
|
97
97
|
};
|
|
98
98
|
}
|
|
99
|
-
const USAGE_CACHE_FRESH_MS = 2 * 60 * 1000; // 2 minutes
|
|
99
|
+
const USAGE_CACHE_FRESH_MS = 2 * 60 * 1000; // 2 minutes — fresh window: don't refresh.
|
|
100
|
+
const USAGE_CACHE_SWR_MS = 24 * 60 * 60 * 1000; // 24 hours — beyond this, block on live fetch.
|
|
101
|
+
/** In-process dedup: don't fire concurrent background refreshes for the same identity. */
|
|
102
|
+
const inFlightRefreshes = new Map();
|
|
100
103
|
/**
|
|
101
|
-
* Fetch usage for a single identity
|
|
102
|
-
*
|
|
104
|
+
* Fetch usage for a single identity using stale-while-revalidate.
|
|
105
|
+
*
|
|
106
|
+
* - Cache fresh (< 2 min): return cached snapshot, NO network.
|
|
107
|
+
* - Cache stale but < 24h: return cached snapshot instantly, fire background refresh.
|
|
108
|
+
* - Cache too stale or absent: block on live fetch, fall back to cache on error.
|
|
109
|
+
*
|
|
110
|
+
* This keeps `agents run` startup off the network on the hot path. The first
|
|
111
|
+
* invocation after a cold install or 24h gap still blocks once to seed the
|
|
112
|
+
* cache; every run after that returns instantly while the cache silently
|
|
113
|
+
* refreshes in the background.
|
|
103
114
|
*/
|
|
104
115
|
export async function getUsageInfoForIdentity(input) {
|
|
105
116
|
const usageKey = getUsageLookupKey(input.info);
|
|
106
|
-
//
|
|
107
|
-
if (input.agentId
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
117
|
+
// Non-Claude or no identity: legacy path, blocking fetch.
|
|
118
|
+
if (input.agentId !== 'claude' || !usageKey) {
|
|
119
|
+
return getUsageInfo(input.agentId, {
|
|
120
|
+
home: input.home,
|
|
121
|
+
cliVersion: input.cliVersion,
|
|
122
|
+
organizationId: input.info.organizationId,
|
|
123
|
+
});
|
|
115
124
|
}
|
|
116
|
-
|
|
125
|
+
const cached = readClaudeUsageCache(usageKey);
|
|
126
|
+
const ageMs = cached?.capturedAt ? Date.now() - cached.capturedAt.getTime() : Infinity;
|
|
127
|
+
// Fresh: cache is recent enough, skip network entirely.
|
|
128
|
+
if (cached && ageMs < USAGE_CACHE_FRESH_MS) {
|
|
129
|
+
return { snapshot: cached, error: null };
|
|
130
|
+
}
|
|
131
|
+
// Stale-while-revalidate: cache exists and isn't ancient, return it now and
|
|
132
|
+
// refresh in the background so the next invocation has fresh data.
|
|
133
|
+
if (cached && ageMs < USAGE_CACHE_SWR_MS) {
|
|
134
|
+
triggerBackgroundUsageRefresh(input, usageKey);
|
|
135
|
+
return { snapshot: cached, error: null };
|
|
136
|
+
}
|
|
137
|
+
// Cold cache or > 24h old: block on live fetch.
|
|
117
138
|
const usage = await getUsageInfo(input.agentId, {
|
|
118
139
|
home: input.home,
|
|
119
140
|
cliVersion: input.cliVersion,
|
|
120
141
|
organizationId: input.info.organizationId,
|
|
121
142
|
});
|
|
122
|
-
if (input.agentId !== 'claude' || !usageKey) {
|
|
123
|
-
return usage;
|
|
124
|
-
}
|
|
125
143
|
if (usage.snapshot?.source === 'live') {
|
|
126
144
|
writeClaudeUsageCache(usageKey, usage.snapshot);
|
|
127
145
|
return usage;
|
|
128
146
|
}
|
|
129
|
-
|
|
147
|
+
// Live fetch failed — last-resort fallback to whatever cache we had.
|
|
130
148
|
if (cached) {
|
|
131
149
|
return { snapshot: cached, error: usage.error };
|
|
132
150
|
}
|
|
133
151
|
return usage;
|
|
134
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* Kick off a background refresh of the usage cache. Errors are swallowed —
|
|
155
|
+
* a failed background refresh leaves the existing cache in place for the
|
|
156
|
+
* next invocation. The work is deferred to a future event-loop tick via
|
|
157
|
+
* `setImmediate` because some of the call chain (loadClaudeOauth →
|
|
158
|
+
* getKeychainToken → execFileSync) does synchronous I/O even though the
|
|
159
|
+
* functions are declared `async`. Without the defer, that sync I/O blocks
|
|
160
|
+
* the SWR caller and defeats the whole point of returning the cache instantly.
|
|
161
|
+
*/
|
|
162
|
+
function triggerBackgroundUsageRefresh(input, usageKey) {
|
|
163
|
+
if (inFlightRefreshes.has(usageKey))
|
|
164
|
+
return;
|
|
165
|
+
const promise = new Promise((resolve) => {
|
|
166
|
+
setImmediate(async () => {
|
|
167
|
+
try {
|
|
168
|
+
const usage = await getUsageInfo(input.agentId, {
|
|
169
|
+
home: input.home,
|
|
170
|
+
cliVersion: input.cliVersion,
|
|
171
|
+
organizationId: input.info.organizationId,
|
|
172
|
+
});
|
|
173
|
+
if (usage.snapshot?.source === 'live') {
|
|
174
|
+
writeClaudeUsageCache(usageKey, usage.snapshot);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* background refresh failed — leave existing cache in place */
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
inFlightRefreshes.delete(usageKey);
|
|
182
|
+
resolve();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
inFlightRefreshes.set(usageKey, promise);
|
|
187
|
+
}
|
|
135
188
|
/** Format a one-line usage summary with compact bars for inline display. */
|
|
136
189
|
export function formatUsageSummary(plan, snapshot, planWidth = 3) {
|
|
137
190
|
const parts = [];
|
|
@@ -148,6 +201,36 @@ export function formatUsageSummary(plan, snapshot, planWidth = 3) {
|
|
|
148
201
|
}
|
|
149
202
|
return parts.join(' ');
|
|
150
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Compact colored badge for the account's overall usage status. Renders only
|
|
206
|
+
* when the account is throttled — `available` and `null` return ''.
|
|
207
|
+
*
|
|
208
|
+
* - `out_of_credits` → red "out of credits" (terminal account, all buckets dry)
|
|
209
|
+
* - `rate_limited` → yellow "rate-limited" (transient throttling)
|
|
210
|
+
*
|
|
211
|
+
* The badge sits between the usage bars and `lastActive` in `agents view`, so
|
|
212
|
+
* a glance at the row tells the user whether the version can do useful work.
|
|
213
|
+
* The same signal is exposed as `usageStatus` in `agents view --json` for
|
|
214
|
+
* programmatic consumers (e.g. the swarmify panel's "resume in healthy agent").
|
|
215
|
+
*
|
|
216
|
+
* The switch is exhaustive on purpose — adding a new `AccountInfo.usageStatus`
|
|
217
|
+
* value without updating the cases here is a build error at `_exhaustive`,
|
|
218
|
+
* which is exactly the bug class this PR is fixing.
|
|
219
|
+
*/
|
|
220
|
+
export function formatUsageStatusBadge(usageStatus) {
|
|
221
|
+
if (usageStatus === null || usageStatus === undefined)
|
|
222
|
+
return '';
|
|
223
|
+
switch (usageStatus) {
|
|
224
|
+
case 'available': return '';
|
|
225
|
+
case 'out_of_credits': return chalk.red('out of credits');
|
|
226
|
+
case 'rate_limited': return chalk.yellow('rate-limited');
|
|
227
|
+
default: {
|
|
228
|
+
const _exhaustive = usageStatus;
|
|
229
|
+
void _exhaustive;
|
|
230
|
+
return '';
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
151
234
|
/** Format a multi-line usage section for detailed agent views. */
|
|
152
235
|
export function formatUsageSection(usage) {
|
|
153
236
|
if (!usage.snapshot && !usage.error) {
|
package/dist/lib/versions.d.ts
CHANGED
|
@@ -48,12 +48,35 @@ export declare function getAvailableResources(cwd?: string): AvailableResources;
|
|
|
48
48
|
export declare function getActuallySyncedResources(agent: AgentId, version: string, options?: {
|
|
49
49
|
cwd?: string;
|
|
50
50
|
}): AvailableResources;
|
|
51
|
+
/** Resource names that only exist in the project's `.agents/` layer, grouped by kind. */
|
|
52
|
+
export interface ProjectOnlyResources {
|
|
53
|
+
commands: Set<string>;
|
|
54
|
+
skills: Set<string>;
|
|
55
|
+
hooks: Set<string>;
|
|
56
|
+
subagents: Set<string>;
|
|
57
|
+
plugins: Set<string>;
|
|
58
|
+
workflows: Set<string>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Names that exist ONLY in the project's `.agents/` layer (no matching entry in
|
|
62
|
+
* user/system/extra layers). Sync intentionally skips project-layer commands,
|
|
63
|
+
* skills, hooks, subagents, plugins, and workflows for security — see the
|
|
64
|
+
* defense comments above each sync branch in syncResourcesToVersion. Without
|
|
65
|
+
* this filter, those names would forever appear in the "New resources" diff
|
|
66
|
+
* because they live in `available` but never reach `actuallySynced`.
|
|
67
|
+
*/
|
|
68
|
+
export declare function getProjectOnlyResources(cwd?: string): ProjectOnlyResources;
|
|
51
69
|
/**
|
|
52
70
|
* Compare available resources with what's ACTUALLY synced to version home.
|
|
53
71
|
* Returns only NEW resources that haven't been synced yet.
|
|
54
72
|
* Source of truth: the actual files/config, NOT agents.yaml tracking.
|
|
73
|
+
*
|
|
74
|
+
* `projectOnly` (recommended): the result of `getProjectOnlyResources(cwd)`.
|
|
75
|
+
* Names listed there are filtered out for kinds that sync intentionally
|
|
76
|
+
* excludes the project layer — otherwise they would re-appear as "new"
|
|
77
|
+
* on every run and "Yes, sync all new" would silently do nothing for them.
|
|
55
78
|
*/
|
|
56
|
-
export declare function getNewResources(available: AvailableResources, actuallySynced: AvailableResources): AvailableResources;
|
|
79
|
+
export declare function getNewResources(available: AvailableResources, actuallySynced: AvailableResources, projectOnly?: ProjectOnlyResources): AvailableResources;
|
|
57
80
|
/**
|
|
58
81
|
* Check if there are any new resources to sync.
|
|
59
82
|
* When version is provided, uses version-specific capability checks.
|
|
@@ -285,6 +308,17 @@ export interface InstalledAgentTargetResult {
|
|
|
285
308
|
directAgents: AgentId[];
|
|
286
309
|
versionSelections: Map<AgentId, string[]>;
|
|
287
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Thrown when the user references an agent@version that is not installed.
|
|
313
|
+
* Carries the parsed (agentId, version) so callers can react — e.g. prompt
|
|
314
|
+
* to install it on demand — without having to parse the error message.
|
|
315
|
+
*/
|
|
316
|
+
export declare class VersionNotInstalledError extends Error {
|
|
317
|
+
readonly agentId: AgentId;
|
|
318
|
+
readonly version: string;
|
|
319
|
+
readonly installedVersions: readonly string[];
|
|
320
|
+
constructor(agentId: AgentId, version: string, installedVersions: readonly string[]);
|
|
321
|
+
}
|
|
288
322
|
/**
|
|
289
323
|
* Resolve a comma-separated --agents list into concrete version selections.
|
|
290
324
|
* Bare agents target the default version, or the newest installed version when no default exists.
|