@phnx-labs/agents-cli 1.22.15 → 1.22.16
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 +40 -0
- package/README.md +3 -1
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +96 -11
- 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/sessions.d.ts +13 -4
- package/dist/commands/sessions.js +37 -19
- package/dist/index.js +3 -2
- package/dist/lib/channels/send.d.ts +4 -1
- package/dist/lib/channels/send.js +15 -7
- 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/hooks.d.ts +4 -2
- package/dist/lib/hooks.js +173 -23
- package/dist/lib/humans.d.ts +25 -0
- package/dist/lib/humans.js +65 -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.js +6 -2
- package/dist/lib/permissions.js +12 -10
- 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/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 +50 -0
- package/dist/lib/versions.js +48 -14
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.16
|
|
4
|
+
|
|
5
|
+
- **Resume exact sessions locally or across the fleet with `agents resume <id>` and `agents run <agent|auto> --resume <id>`.** Full IDs use the local SQLite index before any SSH fan-out; remote owners route to the recorded device and version home. Session metadata now records launch mode alongside harness, version, account, cwd, and machine so strict resume reconstructs the original run. Claude, Codex, Grok, Kimi, Droid, and Cursor use their verified version-specific native resume syntax; `run auto --resume` can select another healthy harness/account and continue through `/continue` when native resume is unavailable. Source: `apps/cli/src/commands/{exec,resume,sessions}.ts`, `apps/cli/src/lib/{exec,session/db}.ts`, `packages/session-tracker/src/hook.sh`.
|
|
6
|
+
|
|
7
|
+
- **Hooks: one-level event dirs (`hooks/<event-name>/<script>`) are first-class.**
|
|
8
|
+
System hooks organize by harness event (`session-start/`, `pre-tool-use/`, …).
|
|
9
|
+
Install names stay the file basename. Dirs with top-level scripts expand into
|
|
10
|
+
individual hooks; fixture-only dirs remain directory bundles. Manifest `script:`
|
|
11
|
+
may be a relative path under `hooks/`. Source: `apps/cli/src/lib/hooks.ts`,
|
|
12
|
+
`apps/cli/src/lib/staleness/writers/sources.ts`, `apps/cli/src/lib/versions.ts`,
|
|
13
|
+
`apps/cli/src/lib/__tests__/hooks-nested-groups.test.ts`.
|
|
14
|
+
|
|
15
|
+
- **`agents humans show owner [--json]`** — new command to display the owner config from `~/.agents/humans.yaml`. The file is written automatically on first run when `notify.owner` exists in `agents.yaml`. Source: `apps/cli/src/lib/humans.ts`, `apps/cli/src/commands/humans.ts`.
|
|
16
|
+
|
|
17
|
+
- **`humans.yaml` — typed, versioned owner config.** `~/.agents/humans.yaml` (`version: 1`) now stores owner identity (name, timezone, quiet hours, severity), notification channels, and escalation policy. `notify.owner` in `agents.yaml` is migrated into it on first run and the `notify.owner` key is removed from `agents.yaml`; unrelated keys are preserved. `agents send --to owner` / `agents notify` prefer `humans.yaml` with a fallback to `agents.yaml` during the migration window. Source: `apps/cli/src/lib/humans.ts`, `apps/cli/src/commands/humans.ts`, `apps/cli/src/lib/migrate.ts`.
|
|
18
|
+
|
|
19
|
+
- **`agents memory` ignores `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and `MEMORY.md`.** These rule/index files lived in `~/.agents/memory/` but were incorrectly surfaced as memory facts. `isFactFile()` now excludes them by name (case-insensitive). Source: `apps/cli/src/lib/memory.ts`.
|
|
20
|
+
|
|
21
|
+
- **Permissions write path fixed — `groups/` subdirectory.** `installPermissionSet`, `removePermissionSet`, and `savePermissionSet` now all write to the `groups/` subdirectory (matching `discoverPermissionGroups()` which already reads from `groups/`). Source: `apps/cli/src/lib/permissions.ts`.
|
|
22
|
+
|
|
23
|
+
- **Stop eagerly creating webhooks directories.** `ensureAgentsDir()` no longer creates `~/.agents/webhooks/` or `~/.agents/.system/webhooks/` on startup — both dirs are created on first actual use. Source: `apps/cli/src/lib/state.ts`.
|
|
24
|
+
|
|
25
|
+
- **Terminals canonically under `.cache/`.** The stale migration comment that blocked `terminals/` from moving to `~/.agents/.cache/terminals/` is replaced by the actual move. Factory already writes to `.cache/terminals/` (`foreman.registry.ts:9`), so no app-level change is needed. Source: `apps/cli/src/lib/migrate.ts`.
|
|
26
|
+
|
|
27
|
+
- **Menu bar warns when a device is under high load (local or remote).** The
|
|
28
|
+
agents-cli menu bar now shows a `⚠ <device> — high load N%` row in NEEDS YOU when
|
|
29
|
+
a machine's load or memory crosses the `headroom()` "loaded" threshold (≥75%), and
|
|
30
|
+
a red `✕` when critical. The local machine is probed natively via `getloadavg`
|
|
31
|
+
(zero subprocess); fleet peers come from the daemon-warmed `.fleet-stats.json`
|
|
32
|
+
cache with a freshness guard — never the slow `agents doctor` path. Action-required
|
|
33
|
+
rows are now emphasized so items that need you stand out. Source:
|
|
34
|
+
`apps/cli/menubar/Sources/MenubarHelper/LocalState.swift`,
|
|
35
|
+
`apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`.
|
|
36
|
+
|
|
37
|
+
- **Projects canonicalization contract.** `agents projects import --from-factory` / `--min-confidence` / `--all` are gone; import is `--from-linear` only, and `~/.agents/factory/projects.json` is never read or migrated. `agents projects list --json` returns definitions only (zero session scan / SSH); `--with-agents` is an explicit opt-in for local active counts. New `agents projects save --json` reads one complete `ProjectDef` from stdin, validates, writes atomically under `~/.agents/projects/`, and prints the saved def. `agents projects rm <name> --json` returns machine-readable success/error. Factory's `managedProjects.ts` shells only through `agents projects list|save|rm` — never reads or writes project YAML/JSON directly, never seeds or migrates legacy Factory state; errors stay explicit for inline UI display. Source: `apps/cli/src/commands/projects.ts`, `apps/cli/src/lib/projects.ts`, `apps/factory/src/core/managedProjects.ts`, `apps/cli/docs/11-projects.md`.
|
|
38
|
+
|
|
39
|
+
- **`ProjectDef` YAML gains `dispatch` block and `linear.name`.** `~/.agents/projects/<name>.yaml` now accepts a `dispatch:` block (`enabled`, `maxAgents`, `provider`, `host`) that opts a project into auto-dispatch and is read directly by `agents __auto-dispatch` — previously these fields lived only in Factory's own registry. `linear.name` stores the Linear project display name alongside the existing `projectId` and `url`. Both fields are optional; existing YAMLs are unchanged. Source: `apps/cli/src/lib/projects.ts`, `apps/cli/src/lib/auto-dispatch.ts`.
|
|
40
|
+
|
|
41
|
+
- **`agents secrets` no longer pops a generic "Agents CLI needs to authenticate" Touch ID sheet on every agent launch.** `listBundles` — which runs on essentially every secrets touch (session-title generation, `agents devices list`, every `agents run`, every remote launch that resolves secrets on the host) — could not ask the keychain for just the bundle *metadata* items: with hashed service names (#316) those names are opaque, so it fell back to a **broad `agents-cli.` keychain scan** that also matched the ACL'd secret *value* items. On machines where a bundle carries a biometric ACL (e.g. a `hold`-tier bundle holding an SSN or a password), macOS evaluated that value ACL during the attributes-only scan and raised a **generic, context-less** Touch ID prompt — on every launch, so a busy fleet felt like a machine-wide bombardment. Neither `kSecUseAuthenticationUIFail` nor `LAContext.interactionNotAllowed` can list the no-ACL items while skipping the ACL'd ones (both return nothing), so the fix is to stop doing the broad scan: a per-machine **no-ACL metadata-name index** (opaque hashes only, in the regenerable helpers dir — it leaks nothing #316 didn't) is read as a silent file instead. The write paths keep it current; an absent/stale index self-heals by rebuilding from the one-time scan, and a missing entry only makes `secrets list` cosmetically incomplete — it never affects a resolve-by-name. Your sensitive bundles keep their biometric gate on real value reads; only the bundle *listing* goes silent. Source: `apps/cli/src/lib/secrets/bundles.ts`.
|
|
42
|
+
|
|
3
43
|
## 1.22.15
|
|
4
44
|
|
|
5
45
|
- **Separate routine definitions from device activation (#2023).** Enable a routine by listing its name in `~/.agents/devices/<hostname>/agents.yaml`; built-in Watchdog setup and `watchdog on|off` now update that host-owned manifest without rewriting the routine definition. Source: `apps/cli/src/lib/routine-activation.ts`, `apps/cli/src/commands/setup-watchdog.ts`.
|
package/README.md
CHANGED
|
@@ -341,9 +341,11 @@ agents sessions resume # multi-select; packs two sessions pe
|
|
|
341
341
|
agents sessions resume "auth middleware" # pre-filter the pool, then choose
|
|
342
342
|
agents sessions resume --tmux # into persistent tmux — survives editor restarts
|
|
343
343
|
agents sessions resume --host zion --tmux # resume on another machine over SSH
|
|
344
|
+
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # original harness/version/device/mode
|
|
345
|
+
agents run auto --resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # adapt if its account is unavailable
|
|
344
346
|
```
|
|
345
347
|
|
|
346
|
-
`agents sessions resume` reopens sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
|
|
348
|
+
`agents sessions resume` reopens several sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. `agents resume <id>` resumes one session without requiring you to name its harness: exact IDs take a local SQLite fast path, then resolve fleet-wide and restore the source harness, version, device, cwd, and recorded launch mode. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
|
|
347
349
|
|
|
348
350
|
### Send an agent to the background — and bring it back
|
|
349
351
|
|
package/dist/bin/agents
CHANGED
|
Binary file
|
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).')
|
|
@@ -668,7 +668,7 @@ export function registerRunCommand(program) {
|
|
|
668
668
|
|
|
669
669
|
Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
670
670
|
|
|
671
|
-
Resume: --resume <id>
|
|
671
|
+
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
672
|
|
|
673
673
|
Passthrough: everything after -- is forwarded verbatim to the underlying agent CLI.
|
|
674
674
|
agents run kimi -- --plan --some-native-flag value
|
|
@@ -781,7 +781,89 @@ export function registerRunCommand(program) {
|
|
|
781
781
|
`Pin a concrete harness instead: agents run <harness>@<version>.`));
|
|
782
782
|
process.exit(1);
|
|
783
783
|
}
|
|
784
|
-
|
|
784
|
+
let autoHarnessRequested = normalizedAgentSpec === RUN_AUTO_KEYWORD;
|
|
785
|
+
let resolvedResumeSource;
|
|
786
|
+
// Concrete resume ids resolve BEFORE placement. Full UUIDs take the local
|
|
787
|
+
// SQLite fast path; only a local miss fans out to the fleet. This lets a
|
|
788
|
+
// command entered on zion discover that the owning version-home is on a
|
|
789
|
+
// worker, while a command entered on that worker never pays for SSH.
|
|
790
|
+
if (typeof options.resume === 'string' && options.resume.trim()) {
|
|
791
|
+
const selector = options.resume.trim();
|
|
792
|
+
const injectedSource = (() => {
|
|
793
|
+
try {
|
|
794
|
+
const parsed = JSON.parse(process.env.AGENTS_RESUME_SOURCE_JSON ?? 'null');
|
|
795
|
+
return parsed?.id === selector ? parsed : undefined;
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
})();
|
|
801
|
+
delete process.env.AGENTS_RESUME_SOURCE_JSON;
|
|
802
|
+
const outcome = injectedSource
|
|
803
|
+
? { kind: 'resolved', session: injectedSource }
|
|
804
|
+
: await (await import('./sessions.js')).resolveSessionMetadataValue(selector);
|
|
805
|
+
if (outcome.kind === 'partial') {
|
|
806
|
+
console.error(chalk.red(`Could not resolve session while these devices were unavailable: ${outcome.failedPeers.join(', ')}`));
|
|
807
|
+
process.exit(2);
|
|
808
|
+
}
|
|
809
|
+
if (outcome.kind === 'not-found') {
|
|
810
|
+
console.error(chalk.red(`No session matching "${selector}".`));
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
if (outcome.kind === 'ambiguous') {
|
|
814
|
+
console.error(chalk.red(`"${selector}" matches ${outcome.candidates.length} sessions. Pass the full session id.`));
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
resolvedResumeSource = outcome.session;
|
|
818
|
+
const [requestedAgent, requestedVersion] = normalizedAgentSpec.split('@');
|
|
819
|
+
if (!autoHarnessRequested && requestedAgent !== resolvedResumeSource.agent) {
|
|
820
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} belongs to ${resolvedResumeSource.agent}, not ${requestedAgent}. ` +
|
|
821
|
+
`Use: agents resume ${resolvedResumeSource.id}`));
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
if (!autoHarnessRequested && requestedVersion && requestedVersion !== resolvedResumeSource.version) {
|
|
825
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} started with ${resolvedResumeSource.agent}@${resolvedResumeSource.version ?? 'unknown'}, ` +
|
|
826
|
+
`not @${requestedVersion}.`));
|
|
827
|
+
process.exit(1);
|
|
828
|
+
}
|
|
829
|
+
// Omitted --mode inherits the effective source mode. Commander keeps
|
|
830
|
+
// the normal new-run default as a real value, so consult its provenance
|
|
831
|
+
// rather than mistaking the default for an explicit override.
|
|
832
|
+
if (command.getOptionValueSource('mode') === 'default') {
|
|
833
|
+
if (resolvedResumeSource.mode)
|
|
834
|
+
options.mode = resolvedResumeSource.mode;
|
|
835
|
+
else if (!options.quiet)
|
|
836
|
+
process.stderr.write(chalk.yellow(`[agents] session ${resolvedResumeSource.shortId} predates stored launch modes; using --mode ${options.mode}\n`));
|
|
837
|
+
}
|
|
838
|
+
const { machineId } = await import('../lib/machine-id.js');
|
|
839
|
+
const sourceMachine = resolvedResumeSource.machine;
|
|
840
|
+
const explicitPlacement = hostTargetGiven(options).length > 0;
|
|
841
|
+
if (sourceMachine && sourceMachine !== machineId() && !explicitPlacement) {
|
|
842
|
+
options.host = sourceMachine;
|
|
843
|
+
}
|
|
844
|
+
else if (!autoHarnessRequested && sourceMachine && sourceMachine !== machineId() && explicitPlacement && !hostTargetGiven(options).includes(sourceMachine)) {
|
|
845
|
+
console.error(chalk.red(`Strict resume must run on ${sourceMachine}, where session ${resolvedResumeSource.shortId} is owned. ` +
|
|
846
|
+
`Use agents run auto --resume ${resolvedResumeSource.id} to hand off elsewhere.`));
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
// On the owning machine, `auto` first asks the existing account router
|
|
850
|
+
// whether the exact source version is healthy. If it is, native resume
|
|
851
|
+
// wins. If not, keep `auto` so the normal harness/account router selects
|
|
852
|
+
// a healthy local target and the later resume block performs /continue.
|
|
853
|
+
if (autoHarnessRequested && (!sourceMachine || sourceMachine === machineId())) {
|
|
854
|
+
const sourceAgent = resolvedResumeSource.agent;
|
|
855
|
+
if (sourceAgent in AGENTS && resolvedResumeSource.version) {
|
|
856
|
+
const { resolveRunVersion } = await import('../lib/rotate.js');
|
|
857
|
+
const health = await resolveRunVersion(sourceAgent, 'available', resolvedResumeSource.cwd ?? process.cwd());
|
|
858
|
+
if (!health.exhausted && health.version === resolvedResumeSource.version) {
|
|
859
|
+
normalizedAgentSpec = `${sourceAgent}@${resolvedResumeSource.version}`;
|
|
860
|
+
autoHarnessRequested = false;
|
|
861
|
+
if (!options.quiet)
|
|
862
|
+
process.stderr.write(chalk.gray(`[agents] auto resume → native ${normalizedAgentSpec} on ${sourceMachine ?? machineId()}\n`));
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
785
867
|
if (autoHarnessRequested) {
|
|
786
868
|
// `auto` is reserved. If a future harness registers that id, the
|
|
787
869
|
// keyword collides — fail loud rather than silently shadow the harness.
|
|
@@ -797,7 +879,7 @@ export function registerRunCommand(program) {
|
|
|
797
879
|
// Host layer: with no explicit --host/--device, default to the
|
|
798
880
|
// affinity pick. Skipped on a host-dispatched run — its dispatcher
|
|
799
881
|
// already resolved this layer (see runAutoDefaultsToAffinity).
|
|
800
|
-
if (runAutoDefaultsToAffinity(options))
|
|
882
|
+
if (!resolvedResumeSource && runAutoDefaultsToAffinity(options))
|
|
801
883
|
options.device = 'auto';
|
|
802
884
|
}
|
|
803
885
|
// --device auto / --host auto (and deprecated --smart): affinity-pick host.
|
|
@@ -2013,7 +2095,8 @@ export function registerRunCommand(program) {
|
|
|
2013
2095
|
// AgentId is wider than SessionAgentId (amp/kiro/goose/copilot keep no transcripts);
|
|
2014
2096
|
// those simply yield no matches and fall through to the not-found error.
|
|
2015
2097
|
const sessionAgent = agent;
|
|
2016
|
-
|
|
2098
|
+
if (!resolvedResumeSource)
|
|
2099
|
+
await discoverSessions({ agent: sessionAgent, version });
|
|
2017
2100
|
// Resume is interactive unless a follow-on prompt makes it headless.
|
|
2018
2101
|
const wantsInteractive = resolveInteractive({ interactive: options.interactive, headless: options.headless, prompt });
|
|
2019
2102
|
const idArg = typeof options.resume === 'string' ? options.resume.trim() : '';
|
|
@@ -2024,9 +2107,9 @@ export function registerRunCommand(program) {
|
|
|
2024
2107
|
catch {
|
|
2025
2108
|
scopeCwd = cwd;
|
|
2026
2109
|
}
|
|
2027
|
-
let session;
|
|
2110
|
+
let session = resolvedResumeSource;
|
|
2028
2111
|
if (idArg) {
|
|
2029
|
-
let matches = findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2112
|
+
let matches = session ? [session] : findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2030
2113
|
if (matches.length === 0) {
|
|
2031
2114
|
const wide = findSessionsById(idArg, { agent: sessionAgent, version });
|
|
2032
2115
|
if (wide.length > 0) {
|
|
@@ -2076,10 +2159,12 @@ export function registerRunCommand(program) {
|
|
|
2076
2159
|
session = picked.session;
|
|
2077
2160
|
forceInteractive = true; // bare resume always lands in the agent's TUI
|
|
2078
2161
|
}
|
|
2079
|
-
//
|
|
2080
|
-
//
|
|
2081
|
-
version
|
|
2082
|
-
|
|
2162
|
+
// Native resume is valid only for the source harness + exact isolated
|
|
2163
|
+
// version. `auto` may have selected another healthy harness/account; in
|
|
2164
|
+
// that case keep the target version and hand off through /continue.
|
|
2165
|
+
const canResumeNatively = session.agent === agent && nativeResume(agent, session.version);
|
|
2166
|
+
if (canResumeNatively) {
|
|
2167
|
+
version = session.version;
|
|
2083
2168
|
resumeNative = true;
|
|
2084
2169
|
resumeSessionId = session.id;
|
|
2085
2170
|
// Native `--resume` (claude/codex) resolves the transcript relative to the
|
|
@@ -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
|
+
}
|
|
@@ -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.'));
|
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) {
|
|
@@ -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 the primary source, agents.yaml
|
|
64
|
+
* notify.owner is the fallback. Returns null when neither is set.
|
|
65
|
+
*/
|
|
63
66
|
export declare function readOwnerDest(meta: Meta): {
|
|
64
67
|
channel: string;
|
|
65
68
|
to: string;
|