@phnx-labs/agents-cli 1.20.92 → 1.21.0
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 +385 -0
- package/README.md +3 -0
- package/dist/bin/agents +0 -0
- package/dist/commands/beta.js +3 -5
- package/dist/commands/doctor.js +23 -0
- package/dist/commands/events.js +91 -1
- package/dist/commands/exec.js +13 -0
- package/dist/commands/mailboxes.js +39 -1
- package/dist/commands/message.js +12 -1
- package/dist/commands/packages.js +14 -5
- package/dist/commands/projects.d.ts +38 -0
- package/dist/commands/projects.js +355 -48
- package/dist/commands/routines.js +27 -1
- package/dist/commands/rules.js +6 -0
- package/dist/commands/secrets.d.ts +17 -0
- package/dist/commands/secrets.js +198 -7
- package/dist/commands/send.d.ts +14 -12
- package/dist/commands/send.js +105 -35
- 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 +13 -12
- package/dist/commands/view.js +4 -0
- package/dist/commands/watchdog.js +26 -3
- package/dist/index.js +38 -6
- package/dist/lib/activity.d.ts +15 -0
- package/dist/lib/activity.js +26 -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/send.d.ts +83 -0
- package/dist/lib/channels/send.js +112 -0
- 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-ingest.d.ts +46 -0
- package/dist/lib/events-ingest.js +182 -0
- package/dist/lib/events.d.ts +17 -5
- package/dist/lib/events.js +59 -56
- package/dist/lib/feed.d.ts +1 -1
- package/dist/lib/feed.js +2 -0
- package/dist/lib/git.d.ts +13 -2
- package/dist/lib/git.js +38 -6
- package/dist/lib/linear-project-counts.d.ts +129 -0
- package/dist/lib/linear-project-counts.js +206 -0
- package/dist/lib/linear-projects.d.ts +62 -0
- package/dist/lib/linear-projects.js +144 -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/menubar/notify-desktop.d.ts +17 -2
- package/dist/lib/menubar/notify-desktop.js +8 -2
- package/dist/lib/migrate.js +1 -1
- package/dist/lib/models.js +21 -11
- 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/project-probe.d.ts +75 -0
- package/dist/lib/project-probe.js +160 -0
- package/dist/lib/project-resources.d.ts +8 -0
- package/dist/lib/project-resources.js +31 -3
- package/dist/lib/project-status.d.ts +32 -1
- package/dist/lib/project-status.js +82 -1
- package/dist/lib/projects.d.ts +6 -0
- package/dist/lib/projects.js +12 -0
- package/dist/lib/remote-agents-json.d.ts +14 -1
- package/dist/lib/remote-agents-json.js +21 -3
- package/dist/lib/routine-notify.d.ts +11 -0
- package/dist/lib/routine-notify.js +22 -0
- 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/run-notify.js +3 -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/audit.d.ts +1 -1
- package/dist/lib/secrets/audit.js +53 -10
- package/dist/lib/secrets/index.d.ts +4 -5
- package/dist/lib/secrets/index.js +4 -5
- package/dist/lib/secrets/list-filter.d.ts +20 -5
- package/dist/lib/secrets/list-filter.js +22 -6
- package/dist/lib/secrets/usage-db.d.ts +106 -0
- package/dist/lib/secrets/usage-db.js +236 -0
- 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-active.d.ts +5 -1
- package/dist/lib/session/remote-active.js +4 -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/sqlite.js +28 -1
- 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 +19 -2
- package/dist/lib/state.js +99 -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 +22 -5
- package/dist/lib/versions.d.ts +6 -0
- package/dist/lib/versions.js +6 -4
- 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)
|
|
@@ -37,6 +37,7 @@ import { capableAgents } from '../lib/capabilities.js';
|
|
|
37
37
|
import { parseHookManifest, registerHooksToSettings } from '../lib/hooks.js';
|
|
38
38
|
import { compileRulesForProject } from '../lib/rules/compile.js';
|
|
39
39
|
import { runLaunchSync } from '../lib/project-launch.js';
|
|
40
|
+
import { formatKeptProjectResources } from '../lib/project-resources.js';
|
|
40
41
|
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
41
42
|
import { runUmbrellaSync } from '../lib/sync-umbrella.js';
|
|
42
43
|
import { addHostOption } from '../lib/hosts/option.js';
|
|
@@ -46,7 +47,7 @@ import { getSystemAgentsDir, getUserAgentsDir, getEnabledExtraRepos } from '../l
|
|
|
46
47
|
export function registerSyncCommand(program) {
|
|
47
48
|
addHostOption(program.command('sync [agentSpec] [repo]'))
|
|
48
49
|
.summary('Make this machine current, or sync resources into one agent')
|
|
49
|
-
.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).')
|
|
50
51
|
.option('--agent <agent>', 'Agent identifier (legacy form; prefer the positional spec)')
|
|
51
52
|
.option('--agent-version <version>', 'Version to sync into (legacy form; prefer "agent@version")')
|
|
52
53
|
.option('--repo <name>', 'Scope the sync to a single DotAgent repo: system / user / project / <alias> (also accepted as a positional)')
|
|
@@ -59,7 +60,6 @@ export function registerSyncCommand(program) {
|
|
|
59
60
|
// Umbrella verb (no agent given): make this machine current.
|
|
60
61
|
.option('--repos', 'Umbrella: git-pull ~/.agents + enabled ~/.agents-* extras', false)
|
|
61
62
|
.option('--secrets', 'Umbrella: pull encrypted secret bundles from the remote', false)
|
|
62
|
-
.option('--sessions', 'Umbrella: sync session transcripts across machines', false)
|
|
63
63
|
.option('--cloud', 'Umbrella: fetch all remote state but skip the local reconcile', false)
|
|
64
64
|
.option('--local', "Umbrella: reconcile resources into installed agents only (no fetch)", false)
|
|
65
65
|
.action(async (agentSpec, repo, opts) => {
|
|
@@ -205,7 +205,7 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
205
205
|
// Interactive bare `agents sync` (a TTY, no --yes, no scope flag) drops into
|
|
206
206
|
// the two-checklist picker: which repos to sync from, which agents to sync
|
|
207
207
|
// into. Any explicit flag or --yes keeps the non-interactive umbrella below.
|
|
208
|
-
const anyExplicitFlag = !!(opts.repos || opts.secrets || opts.
|
|
208
|
+
const anyExplicitFlag = !!(opts.repos || opts.secrets || opts.cloud || opts.local);
|
|
209
209
|
if (!quiet && !opts.yes && !anyExplicitFlag && isInteractiveTerminal()) {
|
|
210
210
|
await runInteractiveReconcile(opts, outLog, errLog);
|
|
211
211
|
return;
|
|
@@ -213,7 +213,6 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
213
213
|
const flags = {
|
|
214
214
|
repos: opts.repos,
|
|
215
215
|
secrets: opts.secrets,
|
|
216
|
-
sessions: opts.sessions,
|
|
217
216
|
cloud: opts.cloud,
|
|
218
217
|
local: opts.local,
|
|
219
218
|
};
|
|
@@ -237,9 +236,6 @@ async function runUmbrella(opts, quiet, outLog, errLog) {
|
|
|
237
236
|
if (result.secrets) {
|
|
238
237
|
parts.push(result.secrets.skipped ? 'secrets skipped' : `secrets ${result.secrets.pulled} pulled`);
|
|
239
238
|
}
|
|
240
|
-
if (result.sessions) {
|
|
241
|
-
parts.push(result.sessions.ran ? `sessions ${result.sessions.merged} merged` : 'sessions off');
|
|
242
|
-
}
|
|
243
239
|
if (result.reconciled)
|
|
244
240
|
parts.push('reconciled');
|
|
245
241
|
outLog(chalk.green(`✓ sync: ${parts.join(' · ') || 'nothing to do'}`));
|
|
@@ -516,8 +512,11 @@ function printSyncDetail(result, agent, version, cwd) {
|
|
|
516
512
|
lines.push({ kind: 'plugins', items: result.plugins });
|
|
517
513
|
if (result.workflows.length > 0)
|
|
518
514
|
lines.push({ kind: 'workflows', items: result.workflows });
|
|
515
|
+
const kept = formatKeptProjectResources(result.projectSkipped);
|
|
519
516
|
if (lines.length === 0) {
|
|
520
517
|
console.log(chalk.gray(`Already in sync — ${agentLabel(agent)}@${version}`));
|
|
518
|
+
if (kept)
|
|
519
|
+
console.log(chalk.gray(kept));
|
|
521
520
|
return;
|
|
522
521
|
}
|
|
523
522
|
console.log(chalk.green(`Synced to ${agentLabel(agent)}@${version}:`));
|
|
@@ -531,6 +530,8 @@ function printSyncDetail(result, agent, version, cwd) {
|
|
|
531
530
|
const count = chalk.cyan(`(${sorted.length})`.padStart(5));
|
|
532
531
|
console.log(` ${chalk.bold(padded)} ${count} ${chalk.gray(preview)}${more}`);
|
|
533
532
|
}
|
|
533
|
+
if (kept)
|
|
534
|
+
console.log(chalk.gray(kept));
|
|
534
535
|
}
|
|
535
536
|
function runLaunchMode(agent, version, cwd, quiet) {
|
|
536
537
|
let result;
|
|
@@ -561,7 +562,7 @@ function runLaunchMode(agent, version, cwd, quiet) {
|
|
|
561
562
|
else {
|
|
562
563
|
console.log(chalk.green(`Launch sync: ${bits.join(', ')}`));
|
|
563
564
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
565
|
+
const kept = formatKeptProjectResources(result.workspaceSkipped);
|
|
566
|
+
if (kept)
|
|
567
|
+
console.log(chalk.gray(kept));
|
|
567
568
|
}
|
package/dist/commands/view.js
CHANGED
|
@@ -11,6 +11,7 @@ import { authCacheKey, formatCheckedAge, readAuthHealthCache } from '../lib/auth
|
|
|
11
11
|
import { agentReportsUsage, deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
12
12
|
import { readManifest } from '../lib/manifest.js';
|
|
13
13
|
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, isVersionIsolated, getIsolatedDefault, } from '../lib/versions.js';
|
|
14
|
+
import { formatKeptProjectResources } from '../lib/project-resources.js';
|
|
14
15
|
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
|
|
15
16
|
import { getAgentResources } from '../lib/resources.js';
|
|
16
17
|
import { renderMergedResources } from '../lib/merged-resources.js';
|
|
@@ -722,6 +723,9 @@ async function showInstalledVersions(filterAgentId, viewOpts) {
|
|
|
722
723
|
if (synced.length > 0) {
|
|
723
724
|
console.log(chalk.green(`\nSynced to ${agentLabel(filterAgentId)}@${defaultVersion}: ${synced.join(', ')}`));
|
|
724
725
|
}
|
|
726
|
+
const kept = formatKeptProjectResources(result.projectSkipped);
|
|
727
|
+
if (kept)
|
|
728
|
+
console.log(chalk.gray(kept));
|
|
725
729
|
}
|
|
726
730
|
}
|
|
727
731
|
catch (err) {
|
|
@@ -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
|
|
@@ -147,11 +148,25 @@ function auditCommandPath(cmd) {
|
|
|
147
148
|
return parts;
|
|
148
149
|
}
|
|
149
150
|
const auditStarts = new WeakMap();
|
|
151
|
+
/**
|
|
152
|
+
* Commands that WRITE the event stream, so recording their own invocation would
|
|
153
|
+
* add records to the log they are writing into. `events emit` is batched — one
|
|
154
|
+
* flush every few seconds per open editor window — so auditing it would bury the
|
|
155
|
+
* real events under two `command.*` records per flush. `_internal friction`
|
|
156
|
+
* exists for the same reason (shell guards fire before any `agents` process
|
|
157
|
+
* exists, so they cannot emit in-process) and had the same self-logging bug.
|
|
158
|
+
*/
|
|
159
|
+
const AUDIT_EXEMPT_COMMANDS = new Set([
|
|
160
|
+
'events emit',
|
|
161
|
+
'_internal friction',
|
|
162
|
+
]);
|
|
150
163
|
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
151
164
|
try {
|
|
152
165
|
const parts = auditCommandPath(actionCommand);
|
|
153
166
|
if (parts.length === 0)
|
|
154
167
|
return;
|
|
168
|
+
if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
|
|
169
|
+
return;
|
|
155
170
|
auditStarts.set(actionCommand, Date.now());
|
|
156
171
|
emit('command.start', {
|
|
157
172
|
module: parts[0],
|
|
@@ -172,6 +187,8 @@ program.hook('postAction', (_thisCommand, actionCommand) => {
|
|
|
172
187
|
const parts = auditCommandPath(actionCommand);
|
|
173
188
|
if (parts.length === 0)
|
|
174
189
|
return;
|
|
190
|
+
if (AUDIT_EXEMPT_COMMANDS.has(parts.join(' ')))
|
|
191
|
+
return;
|
|
175
192
|
const started = auditStarts.get(actionCommand);
|
|
176
193
|
const durationMs = started !== undefined ? Date.now() - started : undefined;
|
|
177
194
|
const command = parts.join(' ');
|
|
@@ -265,7 +282,6 @@ Run and dispatch:
|
|
|
265
282
|
run <agent|profile> [prompt] Run an agent. Omit prompt for interactive mode.
|
|
266
283
|
defaults Configure run defaults by agent/version selector
|
|
267
284
|
teams Coordinate multiple agents on shared work
|
|
268
|
-
hq JSON bridge for the interactive Agents HQ floor
|
|
269
285
|
routines Run agents on a cron schedule (scheduler auto-starts)
|
|
270
286
|
webhook Receive signed GitHub/Linear webhooks for trigger routines
|
|
271
287
|
funnel Expose a webhook receiver through Tailscale Funnel
|
|
@@ -285,14 +301,13 @@ Diagnostics:
|
|
|
285
301
|
perf Latency rollups (hooks, commands, runs) from the disposable perf warehouse
|
|
286
302
|
|
|
287
303
|
Config sync:
|
|
288
|
-
drive Sync session history across machines via rsync
|
|
289
304
|
pull Clone or pull the system repo at ~/.agents/.system/
|
|
290
305
|
repo init --path <dir> Scaffold your own editable repo from a template
|
|
291
306
|
repo add <path|gh:user/repo> Merge an extra repo after the system repo
|
|
292
307
|
lock [--frozen] Write/verify agents.lock (SHA-256 of resolved resources); --frozen fails on drift
|
|
293
308
|
|
|
294
309
|
Beta features:
|
|
295
|
-
beta Enable preview features (factory
|
|
310
|
+
beta Enable preview features (factory and more)
|
|
296
311
|
|
|
297
312
|
Automation tips:
|
|
298
313
|
Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
|
|
@@ -705,6 +720,21 @@ function registerResourcesTombstoneCommand(p) {
|
|
|
705
720
|
await program.parseAsync(['node', 'agents', ...args]);
|
|
706
721
|
});
|
|
707
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
|
+
}
|
|
708
738
|
/**
|
|
709
739
|
* Hidden `agents _internal <sub>` namespace for machine-to-machine calls that
|
|
710
740
|
* are not user-facing. The first subcommand is `friction`, used by shell guard
|
|
@@ -824,6 +854,9 @@ async function registerEagerForRequest(name) {
|
|
|
824
854
|
registerResourcesTombstoneCommand(program);
|
|
825
855
|
await reg(loadView);
|
|
826
856
|
return true;
|
|
857
|
+
case 'hq':
|
|
858
|
+
registerHqTombstoneCommand(program);
|
|
859
|
+
return true;
|
|
827
860
|
case '_internal':
|
|
828
861
|
registerInternalCommand(program);
|
|
829
862
|
return true;
|
|
@@ -896,7 +929,6 @@ async function registerAllEagerCommands() {
|
|
|
896
929
|
await reg(loadSync);
|
|
897
930
|
await reg(loadLock);
|
|
898
931
|
await reg(loadRefreshRules);
|
|
899
|
-
await reg(loadDrive);
|
|
900
932
|
await reg(loadFactory);
|
|
901
933
|
await reg(loadUsage);
|
|
902
934
|
await reg(loadCost);
|
|
@@ -916,7 +948,7 @@ async function registerAllEagerCommands() {
|
|
|
916
948
|
await reg(loadAudit);
|
|
917
949
|
await reg(loadWebhook);
|
|
918
950
|
await reg(loadFunnel);
|
|
919
|
-
|
|
951
|
+
registerHqTombstoneCommand(program);
|
|
920
952
|
await reg(loadFeed);
|
|
921
953
|
await reg(loadActivity);
|
|
922
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'
|
|
@@ -5,6 +6,14 @@ export type MilestoneEvent = 'plan.created' | 'pr.opened' | 'pr.merged' | 'workt
|
|
|
5
6
|
| 'video.rendered' | 'video.converted' | 'image.upscaled' | 'metadata.edited'
|
|
6
7
|
/** Deliberate agent-authored progress post (`agents feed post`). */
|
|
7
8
|
| 'status.posted'
|
|
9
|
+
/**
|
|
10
|
+
* An agent terminal spawned from the Factory VS Code extension. A milestone
|
|
11
|
+
* because it is the BIRTH of a session — it carries the sessionId and the
|
|
12
|
+
* terminalId that every later event on that session joins through, the same
|
|
13
|
+
* way `subagent.spawned` roots a sub-agent. Written out of process by
|
|
14
|
+
* `agents events emit`; the other factory.* kinds are operational-only.
|
|
15
|
+
*/
|
|
16
|
+
| 'factory.launch'
|
|
8
17
|
/**
|
|
9
18
|
* The same post, but the agent is STUCK (`agents feed post --blocked`).
|
|
10
19
|
*
|
|
@@ -72,6 +81,10 @@ export interface ActivityEvent {
|
|
|
72
81
|
project?: string;
|
|
73
82
|
/** Agent that produced the event (claude, codex, ...). */
|
|
74
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';
|
|
75
88
|
/** Tool that triggered the event (Bash, Task, ExitPlanMode, feed.post, ...). */
|
|
76
89
|
tool?: string;
|
|
77
90
|
/** One-line human summary (plan title, PR command, sub-agent role, status text). */
|
|
@@ -82,6 +95,8 @@ export interface ActivityEvent {
|
|
|
82
95
|
pid?: number;
|
|
83
96
|
/** Spawn-time join key (`AGENT_LAUNCH_ID`) when known. */
|
|
84
97
|
launchId?: string;
|
|
98
|
+
/** Session that spawned this session (`AGENTS_PARENT_SESSION_ID`) when known. */
|
|
99
|
+
parentSessionId?: string;
|
|
85
100
|
/** Factory terminal id when the launch inherited one. */
|
|
86
101
|
terminalId?: string;
|
|
87
102
|
/** `$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',
|
|
@@ -43,6 +44,12 @@ export const MILESTONE_EVENTS = [
|
|
|
43
44
|
'image.upscaled',
|
|
44
45
|
'metadata.edited',
|
|
45
46
|
'status.posted',
|
|
47
|
+
// NOTE: intentionally absent from the Python hook's own MILESTONE_EVENTS copy
|
|
48
|
+
// (see ACTIVITY_LOG_HOOK_SCRIPT below) — that set only classifies events the
|
|
49
|
+
// hook itself writes from PreToolUse/PostToolUse, and the hook never writes
|
|
50
|
+
// this one. activity.test.ts pins the difference so the divergence stays
|
|
51
|
+
// deliberate rather than becoming drift.
|
|
52
|
+
'factory.launch',
|
|
46
53
|
'status.blocked',
|
|
47
54
|
];
|
|
48
55
|
const MILESTONE_SET = new Set(MILESTONE_EVENTS);
|
|
@@ -103,6 +110,7 @@ export function appendActivityEvent(event, root) {
|
|
|
103
110
|
const dir = root ?? getActivityDir();
|
|
104
111
|
fs.mkdirSync(dir, { recursive: true });
|
|
105
112
|
const record = {
|
|
113
|
+
...stampProvenance(),
|
|
106
114
|
v: event.v ?? 1,
|
|
107
115
|
tier: event.tier ?? tierForEvent(event.event),
|
|
108
116
|
...event,
|
|
@@ -129,11 +137,14 @@ function parseLine(line) {
|
|
|
129
137
|
cwd: parsed.cwd,
|
|
130
138
|
project: parsed.project,
|
|
131
139
|
agent: parsed.agent,
|
|
140
|
+
actor: parsed.actor,
|
|
141
|
+
kind: parsed.kind,
|
|
132
142
|
tool: parsed.tool,
|
|
133
143
|
detail: parsed.detail,
|
|
134
144
|
url: parsed.url,
|
|
135
145
|
pid: typeof parsed.pid === 'number' ? parsed.pid : undefined,
|
|
136
146
|
launchId: parsed.launchId,
|
|
147
|
+
parentSessionId: parsed.parentSessionId,
|
|
137
148
|
terminalId: parsed.terminalId,
|
|
138
149
|
tmuxPane: parsed.tmuxPane,
|
|
139
150
|
category: typeof parsed.category === 'string' ? parsed.category : undefined,
|
|
@@ -262,10 +273,12 @@ export function activityEventToRecord(ev) {
|
|
|
262
273
|
level: 'info',
|
|
263
274
|
caller: ev.tool === 'feed.post' ? 'agent' : 'hook',
|
|
264
275
|
session: ev.sessionId,
|
|
265
|
-
osUser:
|
|
276
|
+
osUser: 'unknown',
|
|
266
277
|
transport: 'local',
|
|
267
278
|
// payload
|
|
268
279
|
agent: ev.agent,
|
|
280
|
+
actor: ev.actor ?? 'unknown',
|
|
281
|
+
kind: ev.kind ?? 'unknown',
|
|
269
282
|
sessionId: ev.sessionId,
|
|
270
283
|
cwd: ev.cwd,
|
|
271
284
|
module: 'activity',
|
|
@@ -275,6 +288,7 @@ export function activityEventToRecord(ev) {
|
|
|
275
288
|
tier: ev.tier,
|
|
276
289
|
...(ev.project ? { project: ev.project } : {}),
|
|
277
290
|
...(ev.launchId ? { launchId: ev.launchId } : {}),
|
|
291
|
+
...(ev.parentSessionId ? { parentSessionId: ev.parentSessionId } : {}),
|
|
278
292
|
...(ev.terminalId ? { terminalId: ev.terminalId } : {}),
|
|
279
293
|
...(ev.tmuxPane ? { tmuxPane: ev.tmuxPane } : {}),
|
|
280
294
|
...(ev.attachments?.length ? { attachments: ev.attachments } : {}),
|
|
@@ -302,6 +316,7 @@ export const EVENT_STYLE = {
|
|
|
302
316
|
'checklist.created': { glyph: '☐', color: chalk.cyan, label: 'checklist created' },
|
|
303
317
|
'status.posted': { glyph: '▸', color: chalk.white, label: 'status' },
|
|
304
318
|
'file.edited': { glyph: '·', color: chalk.gray, label: 'file edited' },
|
|
319
|
+
'factory.launch': { glyph: '⌁', color: chalk.cyan, label: 'factory launch' },
|
|
305
320
|
'bash.executed': { glyph: '$', color: chalk.gray, label: 'command run' },
|
|
306
321
|
};
|
|
307
322
|
export function styleForEvent(event) {
|
|
@@ -1514,8 +1529,17 @@ def main():
|
|
|
1514
1529
|
cwd = payload.get("cwd") or os.environ.get("AGENTS_CWD")
|
|
1515
1530
|
if cwd:
|
|
1516
1531
|
record["cwd"] = cwd
|
|
1517
|
-
agent = os.environ.get("AGENTS_AGENT_NAME") or "
|
|
1532
|
+
agent = os.environ.get("AGENTS_AGENT_NAME") or "unknown"
|
|
1518
1533
|
record["agent"] = agent
|
|
1534
|
+
record["actor"] = os.environ.get("AGENTS_ACTOR") or "unknown"
|
|
1535
|
+
actor_kind = os.environ.get("AGENTS_ACTOR_KIND")
|
|
1536
|
+
record["kind"] = actor_kind if actor_kind in ("human", "agent") else "unknown"
|
|
1537
|
+
launch_id = os.environ.get("AGENT_LAUNCH_ID")
|
|
1538
|
+
if launch_id:
|
|
1539
|
+
record["launchId"] = launch_id
|
|
1540
|
+
parent_session_id = os.environ.get("AGENTS_PARENT_SESSION_ID")
|
|
1541
|
+
if parent_session_id:
|
|
1542
|
+
record["parentSessionId"] = parent_session_id
|
|
1519
1543
|
|
|
1520
1544
|
try:
|
|
1521
1545
|
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
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delivery envelope for `agents send` / `agents notify`.
|
|
3
|
+
*
|
|
4
|
+
* One primitive: resolve a destination (channel + target), compose text + urls +
|
|
5
|
+
* attachments, hand off to a channel provider. `notify` is the same path with
|
|
6
|
+
* destination defaulted to `notify.owner` in agents.yaml — owner is an address
|
|
7
|
+
* alias (`--to owner`), not a separate stack.
|
|
8
|
+
*
|
|
9
|
+
* Agent control (`agents message`, `sessions inject`) stays outside this module.
|
|
10
|
+
*/
|
|
11
|
+
import type { Meta } from '../types.js';
|
|
12
|
+
import type { SendResult } from './registry.js';
|
|
13
|
+
/** Normalized delivery request after CLI/config resolution. */
|
|
14
|
+
export interface SendEnvelope {
|
|
15
|
+
text: string;
|
|
16
|
+
channel: string;
|
|
17
|
+
to: string;
|
|
18
|
+
thread?: string;
|
|
19
|
+
attachments?: string[];
|
|
20
|
+
from?: string;
|
|
21
|
+
dryRun?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ResolveSendInput {
|
|
24
|
+
/**
|
|
25
|
+
* Body text. Prefer `--text`; positional `[text]` is accepted for compat and
|
|
26
|
+
* folded in when `--text` is omitted.
|
|
27
|
+
*/
|
|
28
|
+
text?: string;
|
|
29
|
+
/** Positional `[text]` from commander (legacy). */
|
|
30
|
+
positionalText?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Recipient. Channel-specific id, or the alias `owner` which expands to
|
|
33
|
+
* `notify.owner.{channel,to}`.
|
|
34
|
+
*/
|
|
35
|
+
to?: string;
|
|
36
|
+
/** Provider/channel name. Required unless `to` is `owner` (or ownerMode). */
|
|
37
|
+
channel?: string;
|
|
38
|
+
thread?: string;
|
|
39
|
+
/** Local file paths. */
|
|
40
|
+
attachments?: string[];
|
|
41
|
+
/** Links / remote media refs — appended to the body so every provider sees them. */
|
|
42
|
+
urls?: string[];
|
|
43
|
+
from?: string;
|
|
44
|
+
dryRun?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* When true (`agents notify`), missing channel/to default to `notify.owner`.
|
|
47
|
+
* Explicit flags still win.
|
|
48
|
+
*/
|
|
49
|
+
ownerMode?: boolean;
|
|
50
|
+
}
|
|
51
|
+
export type ResolveSendResult = {
|
|
52
|
+
ok: true;
|
|
53
|
+
envelope: SendEnvelope;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
error: string;
|
|
57
|
+
};
|
|
58
|
+
/** True when the destination token means “the configured owner”. */
|
|
59
|
+
export declare function isOwnerAlias(to: string | undefined): boolean;
|
|
60
|
+
/** Compose body + optional URL lines (skip urls already present in the body). */
|
|
61
|
+
export declare function composeSendText(text: string, urls?: string[]): string;
|
|
62
|
+
/** Read notify.owner; null when either field is missing. */
|
|
63
|
+
export declare function readOwnerDest(meta: Meta): {
|
|
64
|
+
channel: string;
|
|
65
|
+
to: string;
|
|
66
|
+
} | null;
|
|
67
|
+
/**
|
|
68
|
+
* Resolve CLI/config into a send envelope. Pure except for reading `meta` —
|
|
69
|
+
* no I/O, no provider registration — so unit tests do not need a real PATH.
|
|
70
|
+
*/
|
|
71
|
+
export declare function resolveSendEnvelope(input: ResolveSendInput, meta: Meta): ResolveSendResult;
|
|
72
|
+
/**
|
|
73
|
+
* Register providers, resolve transport, deliver. Used by the CLI and by any
|
|
74
|
+
* internal caller that already has a resolved envelope.
|
|
75
|
+
*/
|
|
76
|
+
export declare function deliverEnvelope(envelope: SendEnvelope, meta: Meta): Promise<SendResult>;
|
|
77
|
+
/** Resolve + deliver in one step (CLI happy path). */
|
|
78
|
+
export declare function sendMessage(input: ResolveSendInput, meta: Meta): Promise<{
|
|
79
|
+
result: SendResult;
|
|
80
|
+
envelope: SendEnvelope;
|
|
81
|
+
} | {
|
|
82
|
+
error: string;
|
|
83
|
+
}>;
|