@phnx-labs/agents-cli 1.20.93 → 1.21.1
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 +290 -0
- package/README.md +4 -1
- package/dist/bin/agents +0 -0
- package/dist/commands/beta.js +3 -5
- package/dist/commands/doctor.js +23 -0
- package/dist/commands/exec.js +13 -0
- package/dist/commands/feed.js +37 -23
- package/dist/commands/mailboxes.js +39 -1
- package/dist/commands/message.js +12 -1
- package/dist/commands/monitors.js +8 -6
- package/dist/commands/packages.js +14 -5
- package/dist/commands/projects.d.ts +28 -0
- package/dist/commands/projects.js +173 -47
- package/dist/commands/routines.js +27 -1
- package/dist/commands/rules.js +6 -0
- package/dist/commands/sessions.d.ts +17 -0
- package/dist/commands/sessions.js +16 -8
- package/dist/commands/setup-browser.js +5 -1
- package/dist/commands/setup-fleet.js +5 -0
- package/dist/commands/setup-preferences.d.ts +53 -0
- package/dist/commands/setup-preferences.js +142 -0
- package/dist/commands/setup.js +8 -9
- package/dist/commands/ssh.js +220 -10
- package/dist/commands/sync.d.ts +2 -2
- package/dist/commands/sync.js +4 -9
- package/dist/commands/watchdog.js +26 -3
- package/dist/index.js +22 -6
- package/dist/lib/activity.d.ts +13 -0
- package/dist/lib/activity.js +22 -2
- package/dist/lib/auto-dispatch.d.ts +6 -1
- package/dist/lib/auto-dispatch.js +7 -2
- package/dist/lib/beta.d.ts +1 -1
- package/dist/lib/beta.js +1 -1
- package/dist/lib/channels/providers/openclaw-telegram.js +1 -1
- package/dist/lib/channels/resolve.d.ts +18 -1
- package/dist/lib/channels/resolve.js +15 -5
- package/dist/lib/daemon.d.ts +13 -0
- package/dist/lib/daemon.js +80 -60
- package/dist/lib/device-config.d.ts +82 -0
- package/dist/lib/device-config.js +296 -0
- package/dist/lib/event-provenance.d.ts +19 -0
- package/dist/lib/event-provenance.js +48 -0
- package/dist/lib/events.d.ts +2 -2
- package/dist/lib/events.js +4 -53
- package/dist/lib/feed-broadcast.d.ts +41 -5
- package/dist/lib/feed-broadcast.js +110 -12
- package/dist/lib/feed-post.d.ts +16 -2
- package/dist/lib/feed-post.js +35 -7
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +3 -1
- package/dist/lib/git.d.ts +13 -2
- package/dist/lib/git.js +38 -6
- package/dist/lib/hooks.d.ts +16 -1
- package/dist/lib/hooks.js +61 -1
- package/dist/lib/linear-project-counts.d.ts +72 -5
- package/dist/lib/linear-project-counts.js +90 -6
- package/dist/lib/linear-projects.d.ts +12 -0
- package/dist/lib/linear-projects.js +30 -0
- package/dist/lib/mailbox-gc.js +30 -7
- package/dist/lib/mailbox.d.ts +14 -1
- package/dist/lib/mailbox.js +35 -3
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/menubar/install-menubar.d.ts +5 -21
- package/dist/lib/menubar/install-menubar.js +35 -44
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/models.js +21 -11
- package/dist/lib/monitors/config.d.ts +1 -1
- package/dist/lib/monitors/dispatch.d.ts +6 -2
- package/dist/lib/monitors/dispatch.js +10 -15
- package/dist/lib/notify.d.ts +42 -6
- package/dist/lib/notify.js +41 -32
- package/dist/lib/overdue.d.ts +1 -1
- package/dist/lib/overdue.js +54 -19
- package/dist/lib/project-import.d.ts +96 -0
- package/dist/lib/project-import.js +171 -0
- package/dist/lib/remote-agents-json.d.ts +14 -1
- package/dist/lib/remote-agents-json.js +21 -3
- package/dist/lib/routines.d.ts +53 -0
- package/dist/lib/routines.js +105 -4
- package/dist/lib/rules/run-sync.d.ts +18 -0
- package/dist/lib/rules/run-sync.js +92 -0
- package/dist/lib/scheduler.js +12 -2
- 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/index.d.ts +4 -5
- package/dist/lib/secrets/index.js +4 -5
- package/dist/lib/session/active.d.ts +16 -2
- package/dist/lib/session/active.js +8 -4
- package/dist/lib/session/bundle.js +5 -1
- package/dist/lib/session/remote-list.d.ts +1 -15
- package/dist/lib/session/remote-list.js +22 -111
- package/dist/lib/session/sync/config.d.ts +13 -5
- package/dist/lib/session/sync/config.js +21 -13
- package/dist/lib/startup/command-registry.d.ts +0 -2
- package/dist/lib/startup/command-registry.js +1 -5
- package/dist/lib/state.d.ts +7 -2
- package/dist/lib/state.js +85 -10
- package/dist/lib/sync-umbrella.d.ts +11 -23
- package/dist/lib/sync-umbrella.js +14 -45
- package/dist/lib/teams/agents.d.ts +35 -3
- package/dist/lib/teams/agents.js +52 -8
- package/dist/lib/teams/scheduler.d.ts +29 -2
- package/dist/lib/teams/scheduler.js +60 -16
- package/dist/lib/types.d.ts +23 -1
- package/package.json +1 -2
- package/dist/commands/drive.d.ts +0 -10
- package/dist/commands/drive.js +0 -183
- package/dist/commands/hq.d.ts +0 -2
- package/dist/commands/hq.js +0 -58
- package/dist/commands/sessions-sync.d.ts +0 -17
- package/dist/commands/sessions-sync.js +0 -135
- package/dist/commands/sync-provision.d.ts +0 -23
- package/dist/commands/sync-provision.js +0 -107
- package/dist/lib/drive-sync.d.ts +0 -45
- package/dist/lib/drive-sync.js +0 -238
- package/dist/lib/hq/floor.d.ts +0 -87
- package/dist/lib/hq/floor.js +0 -243
- package/dist/lib/session/sync/crdt.d.ts +0 -44
- package/dist/lib/session/sync/crdt.js +0 -119
- package/dist/lib/session/sync/manifest.d.ts +0 -62
- package/dist/lib/session/sync/manifest.js +0 -100
- package/dist/lib/session/sync/provision.d.ts +0 -49
- package/dist/lib/session/sync/provision.js +0 -91
- package/dist/lib/session/sync/r2.d.ts +0 -32
- package/dist/lib/session/sync/r2.js +0 -121
- package/dist/lib/session/sync/sync.d.ts +0 -106
- package/dist/lib/session/sync/sync.js +0 -374
package/dist/commands/sync.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `agents sync` — synchronize central resources into an installed agent version.
|
|
3
3
|
*
|
|
4
4
|
* Forms:
|
|
5
|
-
* agents sync # umbrella: fetch config repos -> reconcile all (secrets
|
|
6
|
-
* agents sync --repos|--secrets
|
|
5
|
+
* agents sync # umbrella: fetch config repos -> reconcile all (secrets opt-in)
|
|
6
|
+
* agents sync --repos|--secrets # umbrella: fetch only those, then reconcile
|
|
7
7
|
* agents sync --cloud # umbrella: fetch all, skip reconcile
|
|
8
8
|
* agents sync --local # umbrella: reconcile all, no fetch
|
|
9
9
|
* agents sync system # one repo: git pull --rebase (pull-only mirror)
|
package/dist/commands/sync.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `agents sync` — synchronize central resources into an installed agent version.
|
|
3
3
|
*
|
|
4
4
|
* Forms:
|
|
5
|
-
* agents sync # umbrella: fetch config repos -> reconcile all (secrets
|
|
6
|
-
* agents sync --repos|--secrets
|
|
5
|
+
* agents sync # umbrella: fetch config repos -> reconcile all (secrets opt-in)
|
|
6
|
+
* agents sync --repos|--secrets # umbrella: fetch only those, then reconcile
|
|
7
7
|
* agents sync --cloud # umbrella: fetch all, skip reconcile
|
|
8
8
|
* agents sync --local # umbrella: reconcile all, no fetch
|
|
9
9
|
* agents sync system # one repo: git pull --rebase (pull-only mirror)
|
|
@@ -47,7 +47,7 @@ import { getSystemAgentsDir, getUserAgentsDir, getEnabledExtraRepos } from '../l
|
|
|
47
47
|
export function registerSyncCommand(program) {
|
|
48
48
|
addHostOption(program.command('sync [agentSpec] [repo]'))
|
|
49
49
|
.summary('Make this machine current, or sync resources into one agent')
|
|
50
|
-
.description('With an [agentSpec], syncs resources (commands, skills, hooks, rules, MCPs, plugins, etc.) into that installed agent version — previews changes and lets you pick. e.g. "claude", "claude@2.1.142", a selector: @latest / @oldest / @pinned (= @default), or @all for every installed version.\n\nAppend a [repo] (or pass --repo) to scope the sync to a single DotAgent repo — system / user / project / <alias>. e.g. "agents sync claude@all system" reconciles only the system repo\'s resources into every installed Claude.\n\nGive a DotAgent repo name ALONE — "agents sync system" / "agents sync user" / "agents sync <alias>" — to git-sync that one repo: refuse if the tree is dirty, else git pull --rebase against origin. The user repo and extra aliases also push local commits up; the system repo is a pull-only mirror.\n\nWith NO agent, runs the umbrella verb: fetch the config repos then reconcile them into every installed agent. Secrets
|
|
50
|
+
.description('With an [agentSpec], syncs resources (commands, skills, hooks, rules, MCPs, plugins, etc.) into that installed agent version — previews changes and lets you pick. e.g. "claude", "claude@2.1.142", a selector: @latest / @oldest / @pinned (= @default), or @all for every installed version.\n\nAppend a [repo] (or pass --repo) to scope the sync to a single DotAgent repo — system / user / project / <alias>. e.g. "agents sync claude@all system" reconciles only the system repo\'s resources into every installed Claude.\n\nGive a DotAgent repo name ALONE — "agents sync system" / "agents sync user" / "agents sync <alias>" — to git-sync that one repo: refuse if the tree is dirty, else git pull --rebase against origin. The user repo and extra aliases also push local commits up; the system repo is a pull-only mirror.\n\nWith NO agent, runs the umbrella verb: fetch the config repos then reconcile them into every installed agent. Secrets are opt-in — add --secrets to pull secret bundles. Session transcripts are queryable live via "agents sessions --host <machine>", or moved with "agents sessions export/import". Also: --cloud (fetch only), --local (reconcile only).')
|
|
51
51
|
.option('--agent <agent>', 'Agent identifier (legacy form; prefer the positional spec)')
|
|
52
52
|
.option('--agent-version <version>', 'Version to sync into (legacy form; prefer "agent@version")')
|
|
53
53
|
.option('--repo <name>', 'Scope the sync to a single DotAgent repo: system / user / project / <alias> (also accepted as a positional)')
|
|
@@ -60,7 +60,6 @@ export function registerSyncCommand(program) {
|
|
|
60
60
|
// Umbrella verb (no agent given): make this machine current.
|
|
61
61
|
.option('--repos', 'Umbrella: git-pull ~/.agents + enabled ~/.agents-* extras', false)
|
|
62
62
|
.option('--secrets', 'Umbrella: pull encrypted secret bundles from the remote', false)
|
|
63
|
-
.option('--sessions', 'Umbrella: sync session transcripts across machines', false)
|
|
64
63
|
.option('--cloud', 'Umbrella: fetch all remote state but skip the local reconcile', false)
|
|
65
64
|
.option('--local', "Umbrella: reconcile resources into installed agents only (no fetch)", false)
|
|
66
65
|
.action(async (agentSpec, repo, opts) => {
|
|
@@ -206,7 +205,7 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
206
205
|
// Interactive bare `agents sync` (a TTY, no --yes, no scope flag) drops into
|
|
207
206
|
// the two-checklist picker: which repos to sync from, which agents to sync
|
|
208
207
|
// into. Any explicit flag or --yes keeps the non-interactive umbrella below.
|
|
209
|
-
const anyExplicitFlag = !!(opts.repos || opts.secrets || opts.
|
|
208
|
+
const anyExplicitFlag = !!(opts.repos || opts.secrets || opts.cloud || opts.local);
|
|
210
209
|
if (!quiet && !opts.yes && !anyExplicitFlag && isInteractiveTerminal()) {
|
|
211
210
|
await runInteractiveReconcile(opts, outLog, errLog);
|
|
212
211
|
return;
|
|
@@ -214,7 +213,6 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
214
213
|
const flags = {
|
|
215
214
|
repos: opts.repos,
|
|
216
215
|
secrets: opts.secrets,
|
|
217
|
-
sessions: opts.sessions,
|
|
218
216
|
cloud: opts.cloud,
|
|
219
217
|
local: opts.local,
|
|
220
218
|
};
|
|
@@ -238,9 +236,6 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
238
236
|
if (result.secrets) {
|
|
239
237
|
parts.push(result.secrets.skipped ? 'secrets skipped' : `secrets ${result.secrets.pulled} pulled`);
|
|
240
238
|
}
|
|
241
|
-
if (result.sessions) {
|
|
242
|
-
parts.push(result.sessions.ran ? `sessions ${result.sessions.merged} merged` : 'sessions off');
|
|
243
|
-
}
|
|
244
239
|
if (result.reconciled)
|
|
245
240
|
parts.push('reconciled');
|
|
246
241
|
outLog(chalk.green(`✓ sync: ${parts.join(' · ') || 'nothing to do'}`));
|
|
@@ -24,6 +24,9 @@ import { setHelpSections } from '../lib/help.js';
|
|
|
24
24
|
import { parseDuration } from '../lib/hooks/cache.js';
|
|
25
25
|
import { getRuntimeStateDir } from '../lib/state.js';
|
|
26
26
|
import { setJobEnabled } from '../lib/routines.js';
|
|
27
|
+
import { getActiveSessions } from '../lib/session/active.js';
|
|
28
|
+
import { mailboxIdForActiveSession } from '../lib/mailbox-target.js';
|
|
29
|
+
import { gcMailbox } from '../lib/mailbox-gc.js';
|
|
27
30
|
import { ensureWatchdogRoutine, isWatchdogRoutineEnabled, watchdogRoutineExists, WATCHDOG_ROUTINE_NAME, WATCHDOG_ROUTINE_SCHEDULE, } from '../lib/watchdog/routine.js';
|
|
28
31
|
import { runWatchdogTick, writePolicySentinel, DEFAULT_THRESHOLDS, } from '../lib/watchdog/runner.js';
|
|
29
32
|
/** Default state dir the runner and these subcommands share. */
|
|
@@ -58,6 +61,21 @@ function humanMs(ms) {
|
|
|
58
61
|
return `${Math.round(ms / 60_000)}m`;
|
|
59
62
|
return `${Math.round(ms / 1000)}s`;
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Run the mailbox liveness sweep against the same session set the tick just used.
|
|
66
|
+
* Idempotent and cheap: archiving a message twice is a no-op, and GC only scans
|
|
67
|
+
* directories. Failures are swallowed so a mailbox-root problem cannot break the
|
|
68
|
+
* watchdog tick.
|
|
69
|
+
*/
|
|
70
|
+
async function runMailboxGc(sessions) {
|
|
71
|
+
const activeBoxIds = new Set(sessions.map(mailboxIdForActiveSession).filter((id) => !!id));
|
|
72
|
+
try {
|
|
73
|
+
gcMailbox(activeBoxIds);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// GC is best-effort housekeeping; the next tick will retry.
|
|
77
|
+
}
|
|
78
|
+
}
|
|
61
79
|
function colorForOutcome(o) {
|
|
62
80
|
if (o.injected)
|
|
63
81
|
return chalk.green;
|
|
@@ -118,7 +136,7 @@ export function registerWatchdogCommand(program) {
|
|
|
118
136
|
// path is the daemon routine, which runs `agents watchdog --nudge` — so the
|
|
119
137
|
// routine's enabled state IS the on/off switch, not a flag read here.
|
|
120
138
|
const computeWillInject = () => opts.nudge === true;
|
|
121
|
-
const tickOnce = async (willInject) => runWatchdogTick({
|
|
139
|
+
const tickOnce = async (willInject, sessions) => runWatchdogTick({
|
|
122
140
|
nudge: willInject,
|
|
123
141
|
nudgeText: opts.text,
|
|
124
142
|
smart: opts.smart === true,
|
|
@@ -126,10 +144,13 @@ export function registerWatchdogCommand(program) {
|
|
|
126
144
|
thresholds,
|
|
127
145
|
allowGhosttyFocus: opts.allowGhosttyFocus === true,
|
|
128
146
|
stateDir: stateDir(),
|
|
147
|
+
sessions,
|
|
129
148
|
});
|
|
130
149
|
if (!opts.watch) {
|
|
131
150
|
const willInject = computeWillInject();
|
|
132
|
-
const
|
|
151
|
+
const sessions = await getActiveSessions();
|
|
152
|
+
const result = await tickOnce(willInject, sessions);
|
|
153
|
+
await runMailboxGc(sessions);
|
|
133
154
|
if (opts.json)
|
|
134
155
|
console.log(JSON.stringify(result, null, 2));
|
|
135
156
|
else
|
|
@@ -146,7 +167,9 @@ export function registerWatchdogCommand(program) {
|
|
|
146
167
|
while (true) {
|
|
147
168
|
// Re-evaluated each tick: picks up enable/disable flips mid-run.
|
|
148
169
|
const willInject = computeWillInject();
|
|
149
|
-
const
|
|
170
|
+
const sessions = await getActiveSessions();
|
|
171
|
+
const result = await tickOnce(willInject, sessions);
|
|
172
|
+
await runMailboxGc(sessions);
|
|
150
173
|
if (opts.json)
|
|
151
174
|
console.log(JSON.stringify(result));
|
|
152
175
|
else
|
package/dist/index.js
CHANGED
|
@@ -94,11 +94,12 @@ 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, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules,
|
|
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, loadProfiles, loadHarness, loadSecrets, loadLogin, loadWallet, loadHelper, loadMenubar, loadBeta, loadSync, loadLock, loadRefreshRules, loadFactory, loadUsage, loadCost, loadPerf, loadOutput, loadBudget, loadAlias, loadMine, loadPty, loadTmux, loadWatchdog, loadBrowser, loadComputer, loadHosts, loadLogs, loadEvents, loadAudit, loadWebhook, loadFunnel, loadSsh, loadPull, loadPush, loadRepo, loadSetup, loadUninstall, loadShare, loadSend, loadFeed, loadActivity, 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';
|
|
101
101
|
import { emit, emitFriction, redactArgs } from './lib/events.js';
|
|
102
|
+
import { die } from './lib/format.js';
|
|
102
103
|
// Transparent shim delegate: the generated Windows `.cmd` shims invoke
|
|
103
104
|
// `agents __shim <agent>[@version] <raw args>`. Intercept here, before commander
|
|
104
105
|
// parses anything, so the agent's own flags (`--help`, `--version`, etc.) pass
|
|
@@ -281,7 +282,6 @@ Run and dispatch:
|
|
|
281
282
|
run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
|
|
282
283
|
defaults Configure run defaults by agent/version selector
|
|
283
284
|
teams Coordinate multiple agents on shared work
|
|
284
|
-
hq JSON bridge for the interactive Agents HQ floor
|
|
285
285
|
routines Run agents on a cron schedule (scheduler auto-starts)
|
|
286
286
|
webhook Receive signed GitHub/Linear webhooks for trigger routines
|
|
287
287
|
funnel Expose a webhook receiver through Tailscale Funnel
|
|
@@ -301,14 +301,13 @@ Diagnostics:
|
|
|
301
301
|
perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
|
|
302
302
|
|
|
303
303
|
Config sync:
|
|
304
|
-
drive Sync session history across machines via rsync
|
|
305
304
|
pull Clone or pull the system repo at ~/.agents/.system/
|
|
306
305
|
repo init --path <dir> Scaffold your own editable repo from a template
|
|
307
306
|
repo add <path|gh:user/repo> Merge an extra repo after the system repo
|
|
308
307
|
lock [--frozen] Write/verify agents.lock (SHA-256 of resolved resources); --frozen fails on drift
|
|
309
308
|
|
|
310
309
|
Beta features:
|
|
311
|
-
beta Enable preview features (factory
|
|
310
|
+
beta Enable preview features (factory and more)
|
|
312
311
|
|
|
313
312
|
Automation tips:
|
|
314
313
|
Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
|
|
@@ -721,6 +720,21 @@ function registerResourcesTombstoneCommand(p) {
|
|
|
721
720
|
await program.parseAsync(['node', 'agents', ...args]);
|
|
722
721
|
});
|
|
723
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Removed `hq` command — the JSON bridge for the interactive Agents HQ floor
|
|
725
|
+
* (`agents hq floor --json`). No UI ever consumed it (apps/factory has zero
|
|
726
|
+
* references) and it had no external users, so it is gone with no replacement.
|
|
727
|
+
* Kept as a hidden tombstone so a stale invocation gets a clear message and a
|
|
728
|
+
* non-zero exit instead of commander's raw "unknown command".
|
|
729
|
+
*/
|
|
730
|
+
function registerHqTombstoneCommand(p) {
|
|
731
|
+
p.command('hq', { hidden: true })
|
|
732
|
+
.allowUnknownOption()
|
|
733
|
+
.allowExcessArguments()
|
|
734
|
+
.action(() => {
|
|
735
|
+
die('"agents hq" was removed (internal Agents HQ floor bridge, no longer used).');
|
|
736
|
+
});
|
|
737
|
+
}
|
|
724
738
|
/**
|
|
725
739
|
* Hidden `agents _internal <sub>` namespace for machine-to-machine calls that
|
|
726
740
|
* are not user-facing. The first subcommand is `friction`, used by shell guard
|
|
@@ -840,6 +854,9 @@ async function registerEagerForRequest(name) {
|
|
|
840
854
|
registerResourcesTombstoneCommand(program);
|
|
841
855
|
await reg(loadView);
|
|
842
856
|
return true;
|
|
857
|
+
case 'hq':
|
|
858
|
+
registerHqTombstoneCommand(program);
|
|
859
|
+
return true;
|
|
843
860
|
case '_internal':
|
|
844
861
|
registerInternalCommand(program);
|
|
845
862
|
return true;
|
|
@@ -912,7 +929,6 @@ async function registerAllEagerCommands() {
|
|
|
912
929
|
await reg(loadSync);
|
|
913
930
|
await reg(loadLock);
|
|
914
931
|
await reg(loadRefreshRules);
|
|
915
|
-
await reg(loadDrive);
|
|
916
932
|
await reg(loadFactory);
|
|
917
933
|
await reg(loadUsage);
|
|
918
934
|
await reg(loadCost);
|
|
@@ -932,7 +948,7 @@ async function registerAllEagerCommands() {
|
|
|
932
948
|
await reg(loadAudit);
|
|
933
949
|
await reg(loadWebhook);
|
|
934
950
|
await reg(loadFunnel);
|
|
935
|
-
|
|
951
|
+
registerHqTombstoneCommand(program);
|
|
936
952
|
await reg(loadFeed);
|
|
937
953
|
await reg(loadActivity);
|
|
938
954
|
await reg(loadMailboxes);
|
package/dist/lib/activity.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ActorKind } from './actor.js';
|
|
1
2
|
import type { EventRecord } from './events.js';
|
|
2
3
|
/** Recognizable milestone events, ordered first in any activity lane. */
|
|
3
4
|
export type MilestoneEvent = 'plan.created' | 'pr.opened' | 'pr.merged' | 'worktree.created' | 'worktree.removed' | 'commit.created' | 'pushed' | 'subagent.spawned' | 'artifact.created' | 'task.completed' | 'checklist.created'
|
|
@@ -80,16 +81,28 @@ export interface ActivityEvent {
|
|
|
80
81
|
project?: string;
|
|
81
82
|
/** Agent that produced the event (claude, codex, ...). */
|
|
82
83
|
agent?: string;
|
|
84
|
+
/** Resolved actor id shared with the operational event stream. */
|
|
85
|
+
actor?: string;
|
|
86
|
+
/** Resolved actor kind shared with the operational event stream. */
|
|
87
|
+
kind?: ActorKind | 'unknown';
|
|
83
88
|
/** Tool that triggered the event (Bash, Task, ExitPlanMode, feed.post, ...). */
|
|
84
89
|
tool?: string;
|
|
85
90
|
/** One-line human summary (plan title, PR command, sub-agent role, status text). */
|
|
86
91
|
detail?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Short subject for deliberate status posts (`feed post --title`). Phone
|
|
94
|
+
* broadcasts put this on the first line; `detail` is the body. Optional on
|
|
95
|
+
* older events that only carried `detail`.
|
|
96
|
+
*/
|
|
97
|
+
title?: string;
|
|
87
98
|
/** Extracted URL when the event has one (e.g. the opened PR). */
|
|
88
99
|
url?: string;
|
|
89
100
|
/** Auto-stamped process identity for deliberate posts (from pid registry / env). */
|
|
90
101
|
pid?: number;
|
|
91
102
|
/** Spawn-time join key (`AGENT_LAUNCH_ID`) when known. */
|
|
92
103
|
launchId?: string;
|
|
104
|
+
/** Session that spawned this session (`AGENTS_PARENT_SESSION_ID`) when known. */
|
|
105
|
+
parentSessionId?: string;
|
|
93
106
|
/** Factory terminal id when the launch inherited one. */
|
|
94
107
|
terminalId?: string;
|
|
95
108
|
/** `$TMUX_PANE` at launch when recorded. */
|
package/dist/lib/activity.js
CHANGED
|
@@ -25,6 +25,7 @@ import { relTime, truncate } from './format.js';
|
|
|
25
25
|
import { getActivityDir, getUserAgentsDir } from './state.js';
|
|
26
26
|
import { normalizeHost } from './machine-id.js';
|
|
27
27
|
import { projectKeyFromCwd } from './project-key.js';
|
|
28
|
+
import { stampProvenance } from './event-provenance.js';
|
|
28
29
|
/** The set of milestone events, for tier classification and ordering. */
|
|
29
30
|
export const MILESTONE_EVENTS = [
|
|
30
31
|
'plan.created',
|
|
@@ -109,6 +110,7 @@ export function appendActivityEvent(event, root) {
|
|
|
109
110
|
const dir = root ?? getActivityDir();
|
|
110
111
|
fs.mkdirSync(dir, { recursive: true });
|
|
111
112
|
const record = {
|
|
113
|
+
...stampProvenance(),
|
|
112
114
|
v: event.v ?? 1,
|
|
113
115
|
tier: event.tier ?? tierForEvent(event.event),
|
|
114
116
|
...event,
|
|
@@ -135,11 +137,15 @@ function parseLine(line) {
|
|
|
135
137
|
cwd: parsed.cwd,
|
|
136
138
|
project: parsed.project,
|
|
137
139
|
agent: parsed.agent,
|
|
140
|
+
actor: parsed.actor,
|
|
141
|
+
kind: parsed.kind,
|
|
138
142
|
tool: parsed.tool,
|
|
139
143
|
detail: parsed.detail,
|
|
144
|
+
title: typeof parsed.title === 'string' ? parsed.title : undefined,
|
|
140
145
|
url: parsed.url,
|
|
141
146
|
pid: typeof parsed.pid === 'number' ? parsed.pid : undefined,
|
|
142
147
|
launchId: parsed.launchId,
|
|
148
|
+
parentSessionId: parsed.parentSessionId,
|
|
143
149
|
terminalId: parsed.terminalId,
|
|
144
150
|
tmuxPane: parsed.tmuxPane,
|
|
145
151
|
category: typeof parsed.category === 'string' ? parsed.category : undefined,
|
|
@@ -268,10 +274,12 @@ export function activityEventToRecord(ev) {
|
|
|
268
274
|
level: 'info',
|
|
269
275
|
caller: ev.tool === 'feed.post' ? 'agent' : 'hook',
|
|
270
276
|
session: ev.sessionId,
|
|
271
|
-
osUser:
|
|
277
|
+
osUser: 'unknown',
|
|
272
278
|
transport: 'local',
|
|
273
279
|
// payload
|
|
274
280
|
agent: ev.agent,
|
|
281
|
+
actor: ev.actor ?? 'unknown',
|
|
282
|
+
kind: ev.kind ?? 'unknown',
|
|
275
283
|
sessionId: ev.sessionId,
|
|
276
284
|
cwd: ev.cwd,
|
|
277
285
|
module: 'activity',
|
|
@@ -281,6 +289,7 @@ export function activityEventToRecord(ev) {
|
|
|
281
289
|
tier: ev.tier,
|
|
282
290
|
...(ev.project ? { project: ev.project } : {}),
|
|
283
291
|
...(ev.launchId ? { launchId: ev.launchId } : {}),
|
|
292
|
+
...(ev.parentSessionId ? { parentSessionId: ev.parentSessionId } : {}),
|
|
284
293
|
...(ev.terminalId ? { terminalId: ev.terminalId } : {}),
|
|
285
294
|
...(ev.tmuxPane ? { tmuxPane: ev.tmuxPane } : {}),
|
|
286
295
|
...(ev.attachments?.length ? { attachments: ev.attachments } : {}),
|
|
@@ -390,6 +399,8 @@ export function formatProgressUpdate(ev, opts = {}) {
|
|
|
390
399
|
].filter((c) => Boolean(c));
|
|
391
400
|
if (chips.length > 0)
|
|
392
401
|
lines.push(` ${chalk.gray(chips.join(' · '))}`);
|
|
402
|
+
if (ev.title)
|
|
403
|
+
lines.push(` ${chalk.white.bold(ev.title)}`);
|
|
393
404
|
if (ev.detail)
|
|
394
405
|
lines.push(` ${chalk.white(`"${ev.detail}"`)}`);
|
|
395
406
|
if (ev.attachments?.length) {
|
|
@@ -1521,8 +1532,17 @@ def main():
|
|
|
1521
1532
|
cwd = payload.get("cwd") or os.environ.get("AGENTS_CWD")
|
|
1522
1533
|
if cwd:
|
|
1523
1534
|
record["cwd"] = cwd
|
|
1524
|
-
agent = os.environ.get("AGENTS_AGENT_NAME") or "
|
|
1535
|
+
agent = os.environ.get("AGENTS_AGENT_NAME") or "unknown"
|
|
1525
1536
|
record["agent"] = agent
|
|
1537
|
+
record["actor"] = os.environ.get("AGENTS_ACTOR") or "unknown"
|
|
1538
|
+
actor_kind = os.environ.get("AGENTS_ACTOR_KIND")
|
|
1539
|
+
record["kind"] = actor_kind if actor_kind in ("human", "agent") else "unknown"
|
|
1540
|
+
launch_id = os.environ.get("AGENT_LAUNCH_ID")
|
|
1541
|
+
if launch_id:
|
|
1542
|
+
record["launchId"] = launch_id
|
|
1543
|
+
parent_session_id = os.environ.get("AGENTS_PARENT_SESSION_ID")
|
|
1544
|
+
if parent_session_id:
|
|
1545
|
+
record["parentSessionId"] = parent_session_id
|
|
1526
1546
|
|
|
1527
1547
|
try:
|
|
1528
1548
|
os.makedirs(activity_dir, exist_ok=True)
|
|
@@ -43,7 +43,12 @@ export interface PlannedDispatch {
|
|
|
43
43
|
title: string;
|
|
44
44
|
delegateName: string;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Path to the shared factory project registry (written by the factory UI).
|
|
48
|
+
* `AGENTS_FACTORY_PROJECTS_PATH` overrides it, mirroring `AGENTS_PROJECTS_DIR`
|
|
49
|
+
* (`state.ts`) — that seam is what lets `projects import --from-factory` be
|
|
50
|
+
* tested end-to-end against a real fixture file instead of a mock.
|
|
51
|
+
*/
|
|
47
52
|
export declare function factoryProjectsPath(): string;
|
|
48
53
|
/** Read the project registry rows this module cares about. */
|
|
49
54
|
export declare function readAutoDispatchProjects(): AutoDispatchProject[];
|
|
@@ -16,9 +16,14 @@
|
|
|
16
16
|
import * as fs from 'fs';
|
|
17
17
|
import * as path from 'path';
|
|
18
18
|
import { homedir } from 'os';
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Path to the shared factory project registry (written by the factory UI).
|
|
21
|
+
* `AGENTS_FACTORY_PROJECTS_PATH` overrides it, mirroring `AGENTS_PROJECTS_DIR`
|
|
22
|
+
* (`state.ts`) — that seam is what lets `projects import --from-factory` be
|
|
23
|
+
* tested end-to-end against a real fixture file instead of a mock.
|
|
24
|
+
*/
|
|
20
25
|
export function factoryProjectsPath() {
|
|
21
|
-
return path.join(homedir(), '.agents', 'factory', 'projects.json');
|
|
26
|
+
return process.env.AGENTS_FACTORY_PROJECTS_PATH ?? path.join(homedir(), '.agents', 'factory', 'projects.json');
|
|
22
27
|
}
|
|
23
28
|
/** Read the project registry rows this module cares about. */
|
|
24
29
|
export function readAutoDispatchProjects() {
|
package/dist/lib/beta.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* checks.
|
|
9
9
|
*/
|
|
10
10
|
import type { BetaFeatureName } from './types.js';
|
|
11
|
-
export declare const ALL_BETA_FEATURES: readonly ["
|
|
11
|
+
export declare const ALL_BETA_FEATURES: readonly ["factory", "projects"];
|
|
12
12
|
export declare function getEnabledBetaFeatures(): BetaFeatureName[];
|
|
13
13
|
export declare function isBetaEnabled(feature: BetaFeatureName): boolean;
|
|
14
14
|
export declare function getBetaConfigLocation(): {
|
package/dist/lib/beta.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import { getAgentsDir, getOptionalUserAgentsDir, readMeta, writeMeta } from './state.js';
|
|
12
12
|
import { readManifest, writeManifest } from './manifest.js';
|
|
13
|
-
export const ALL_BETA_FEATURES = ['
|
|
13
|
+
export const ALL_BETA_FEATURES = ['factory', 'projects'];
|
|
14
14
|
function isBetaFeatureName(value) {
|
|
15
15
|
return typeof value === 'string' && ALL_BETA_FEATURES.includes(value);
|
|
16
16
|
}
|
|
@@ -24,7 +24,7 @@ export const openclawTelegramProvider = {
|
|
|
24
24
|
return { ok: false, channel: name, id: opts.target, error: 'openclaw CLI not found on PATH' };
|
|
25
25
|
}
|
|
26
26
|
try {
|
|
27
|
-
await execFileAsync('openclaw', buildOpenClawNotifyArgs(text, {
|
|
27
|
+
await execFileAsync('openclaw', buildOpenClawNotifyArgs(text, { target: opts.target }));
|
|
28
28
|
return { ok: true, channel: name, id: opts.target };
|
|
29
29
|
}
|
|
30
30
|
catch (err) {
|
|
@@ -4,8 +4,25 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Default is name-identity (`--channel slack` -> `slack` provider) — NOT a
|
|
6
6
|
* fallback to a different transport. Only telegram is dual-homed (rush vs
|
|
7
|
-
* openclaw-telegram); config picks.
|
|
7
|
+
* openclaw-telegram); config picks.
|
|
8
|
+
*
|
|
9
|
+
* Two entry points, deliberately: `lookupTransport` *returns* the failure, for
|
|
10
|
+
* long-lived callers (the monitor daemon, the feed-dispatch loop) that must
|
|
11
|
+
* survive a bad channel name; `resolveTransport` `die()`s on it, for the
|
|
12
|
+
* interactive `agents send` / `agents notify` command path where exiting with a
|
|
13
|
+
* loud message is the right answer. Never give a daemon the dying one.
|
|
8
14
|
*/
|
|
9
15
|
import type { Meta } from '../types.js';
|
|
10
16
|
import { type ChannelProvider } from './registry.js';
|
|
17
|
+
export interface TransportLookup {
|
|
18
|
+
/** Provider name after applying the `notify.transports` mapping. */
|
|
19
|
+
providerName: string;
|
|
20
|
+
/** Registered provider, or undefined when `providerName` resolves to nothing. */
|
|
21
|
+
provider?: ChannelProvider;
|
|
22
|
+
/** Why resolution failed — set exactly when `provider` is undefined. */
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Resolve a channel to its provider, returning the failure instead of exiting. */
|
|
26
|
+
export declare function lookupTransport(channel: string, meta: Meta): TransportLookup;
|
|
27
|
+
/** Interactive-command resolution: an unregistered provider dies loud. */
|
|
11
28
|
export declare function resolveTransport(channel: string, meta: Meta): ChannelProvider;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import { die } from '../format.js';
|
|
2
2
|
import { resolveChannelProvider, listChannelProviders } from './registry.js';
|
|
3
|
-
|
|
3
|
+
/** Resolve a channel to its provider, returning the failure instead of exiting. */
|
|
4
|
+
export function lookupTransport(channel, meta) {
|
|
4
5
|
const providerName = meta.notify?.transports?.[channel] ?? channel;
|
|
5
6
|
const provider = resolveChannelProvider(providerName);
|
|
6
|
-
if (
|
|
7
|
-
|
|
7
|
+
if (provider)
|
|
8
|
+
return { providerName, provider };
|
|
9
|
+
return {
|
|
10
|
+
providerName,
|
|
11
|
+
error: `No channel provider '${providerName}'` +
|
|
8
12
|
(providerName === channel ? '' : ` (mapped from channel '${channel}' via notify.transports)`) +
|
|
9
|
-
`. Registered: ${listChannelProviders().join(', ')}
|
|
10
|
-
}
|
|
13
|
+
`. Registered: ${listChannelProviders().join(', ')}.`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/** Interactive-command resolution: an unregistered provider dies loud. */
|
|
17
|
+
export function resolveTransport(channel, meta) {
|
|
18
|
+
const { provider, error } = lookupTransport(channel, meta);
|
|
19
|
+
if (!provider)
|
|
20
|
+
die(error);
|
|
11
21
|
return provider;
|
|
12
22
|
}
|
package/dist/lib/daemon.d.ts
CHANGED
|
@@ -16,6 +16,19 @@ import { getAgentsBinPath } from './cli-entry.js';
|
|
|
16
16
|
* broker is hosting, and never clobber a reachable (healthy) broker.
|
|
17
17
|
*/
|
|
18
18
|
export declare function shouldTakeOverBroker(isHosting: boolean, brokerReachable: boolean): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* What a gate re-evaluation must do with the routines scheduler. The daemon
|
|
21
|
+
* re-evaluates `scheduler.enabled` on every SIGHUP reload so flipping the key
|
|
22
|
+
* takes effect without a daemon restart (and `routines add`'s reload signal on
|
|
23
|
+
* a re-enabled box boots the scheduler — the reload is truthful, not a no-op).
|
|
24
|
+
*
|
|
25
|
+
* running + enabled → reload (the normal SIGHUP path)
|
|
26
|
+
* running + !enabled → stop (gate flipped off since boot)
|
|
27
|
+
* !running + enabled → boot (gate flipped on since boot)
|
|
28
|
+
* !running + !enabled → none (stay dark)
|
|
29
|
+
*/
|
|
30
|
+
export type SchedulerGateTransition = 'reload' | 'stop' | 'boot' | 'none';
|
|
31
|
+
export declare function schedulerGateTransition(running: boolean, enabled: boolean): SchedulerGateTransition;
|
|
19
32
|
/** Read the stored daemon PID from disk. Returns null if not present or invalid. */
|
|
20
33
|
export declare function readDaemonPid(): number | null;
|
|
21
34
|
/** Write the daemon PID to the pid file. */
|