@phnx-labs/agents-cli 1.22.23 → 1.22.25

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.
Files changed (143) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/README.md +19 -8
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/attach.d.ts +2 -0
  5. package/dist/commands/attach.js +32 -7
  6. package/dist/commands/cloud.js +9 -5
  7. package/dist/commands/defaults.js +2 -0
  8. package/dist/commands/doctor.d.ts +24 -0
  9. package/dist/commands/doctor.js +105 -9
  10. package/dist/commands/exec.js +99 -44
  11. package/dist/commands/feed.d.ts +23 -0
  12. package/dist/commands/feed.js +65 -2
  13. package/dist/commands/focus.d.ts +34 -3
  14. package/dist/commands/focus.js +301 -57
  15. package/dist/commands/go.d.ts +14 -1
  16. package/dist/commands/go.js +49 -5
  17. package/dist/commands/insights.js +6 -2
  18. package/dist/commands/inspect.js +39 -5
  19. package/dist/commands/menubar.js +14 -1
  20. package/dist/commands/models.js +1 -0
  21. package/dist/commands/modes.d.ts +12 -0
  22. package/dist/commands/modes.js +147 -0
  23. package/dist/commands/routines.d.ts +3 -0
  24. package/dist/commands/routines.js +70 -47
  25. package/dist/commands/run-cloud.js +1 -1
  26. package/dist/commands/secrets-sync.js +11 -13
  27. package/dist/commands/secrets.d.ts +2 -0
  28. package/dist/commands/secrets.js +54 -17
  29. package/dist/commands/sessions-browser.d.ts +36 -1
  30. package/dist/commands/sessions-browser.js +165 -19
  31. package/dist/commands/sessions-resume.d.ts +2 -0
  32. package/dist/commands/sessions-resume.js +28 -12
  33. package/dist/commands/sessions.d.ts +23 -1
  34. package/dist/commands/sessions.js +58 -61
  35. package/dist/commands/setup-secrets.js +1 -1
  36. package/dist/commands/setup-watchdog.js +5 -10
  37. package/dist/commands/setup.js +1 -1
  38. package/dist/commands/sync.js +246 -42
  39. package/dist/commands/teams.js +3 -2
  40. package/dist/commands/view.js +2 -0
  41. package/dist/commands/watchdog.d.ts +3 -4
  42. package/dist/commands/watchdog.js +26 -66
  43. package/dist/index.js +38 -2
  44. package/dist/lib/agent-modes.d.ts +49 -0
  45. package/dist/lib/agent-modes.js +70 -0
  46. package/dist/lib/agents.js +126 -21
  47. package/dist/lib/cloud/cursor.d.ts +79 -0
  48. package/dist/lib/cloud/cursor.js +228 -0
  49. package/dist/lib/cloud/registry.js +2 -0
  50. package/dist/lib/cloud/types.d.ts +7 -2
  51. package/dist/lib/cloud/types.js +14 -0
  52. package/dist/lib/crabbox/cli.d.ts +2 -2
  53. package/dist/lib/crabbox/config.d.ts +7 -8
  54. package/dist/lib/crabbox/config.js +14 -14
  55. package/dist/lib/crabbox/lease.d.ts +11 -4
  56. package/dist/lib/crabbox/lease.js +40 -8
  57. package/dist/lib/crabbox/setup-copy.d.ts +5 -0
  58. package/dist/lib/crabbox/setup-copy.js +17 -1
  59. package/dist/lib/daemon.js +27 -1
  60. package/dist/lib/device-config.js +7 -0
  61. package/dist/lib/devices/doctor-findings.d.ts +7 -1
  62. package/dist/lib/devices/doctor-findings.js +40 -1
  63. package/dist/lib/doctor-diff.d.ts +3 -0
  64. package/dist/lib/doctor-diff.js +15 -13
  65. package/dist/lib/event-stream.d.ts +3 -1
  66. package/dist/lib/event-stream.js +14 -1
  67. package/dist/lib/events.d.ts +9 -0
  68. package/dist/lib/events.js +58 -0
  69. package/dist/lib/exec.d.ts +15 -3
  70. package/dist/lib/exec.js +112 -19
  71. package/dist/lib/feed-outcome.d.ts +3 -0
  72. package/dist/lib/feed-outcome.js +18 -10
  73. package/dist/lib/feed.d.ts +4 -0
  74. package/dist/lib/hooks/cache.js +36 -3
  75. package/dist/lib/hooks.d.ts +19 -7
  76. package/dist/lib/hooks.js +88 -38
  77. package/dist/lib/hosts/passthrough.d.ts +21 -0
  78. package/dist/lib/hosts/passthrough.js +39 -12
  79. package/dist/lib/hosts/session-index.d.ts +4 -0
  80. package/dist/lib/hosts/session-index.js +7 -0
  81. package/dist/lib/manifest.d.ts +12 -2
  82. package/dist/lib/manifest.js +60 -5
  83. package/dist/lib/mcp.js +5 -1
  84. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  85. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +5 -1
  86. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  87. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  88. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +15 -2
  89. package/dist/lib/menubar/install-menubar.d.ts +52 -2
  90. package/dist/lib/menubar/install-menubar.js +128 -6
  91. package/dist/lib/menubar/snapshot.d.ts +15 -0
  92. package/dist/lib/menubar/snapshot.js +40 -0
  93. package/dist/lib/plugins.js +13 -1
  94. package/dist/lib/refresh.d.ts +5 -0
  95. package/dist/lib/refresh.js +37 -33
  96. package/dist/lib/resource-inventory.d.ts +79 -0
  97. package/dist/lib/resource-inventory.js +122 -0
  98. package/dist/lib/resources/mcp.js +3 -0
  99. package/dist/lib/resources.js +8 -5
  100. package/dist/lib/routine-process-cleanup.d.ts +9 -0
  101. package/dist/lib/routine-process-cleanup.js +73 -0
  102. package/dist/lib/run-defaults.d.ts +2 -0
  103. package/dist/lib/run-defaults.js +23 -2
  104. package/dist/lib/runner.js +27 -22
  105. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  106. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +2 -0
  107. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  108. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  109. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +13 -1
  110. package/dist/lib/secrets/bundles.js +158 -85
  111. package/dist/lib/secrets/filestore.d.ts +7 -3
  112. package/dist/lib/secrets/filestore.js +25 -8
  113. package/dist/lib/secrets/index.js +34 -8
  114. package/dist/lib/secrets/rc-hygiene.d.ts +14 -0
  115. package/dist/lib/secrets/rc-hygiene.js +14 -1
  116. package/dist/lib/secrets/reaper.d.ts +10 -0
  117. package/dist/lib/secrets/reaper.js +50 -15
  118. package/dist/lib/secrets/sync-passphrase.d.ts +27 -0
  119. package/dist/lib/secrets/sync-passphrase.js +78 -0
  120. package/dist/lib/session/active.d.ts +4 -0
  121. package/dist/lib/session/db.js +8 -2
  122. package/dist/lib/session/recovery.d.ts +37 -0
  123. package/dist/lib/session/recovery.js +95 -0
  124. package/dist/lib/session/remote-list.d.ts +2 -0
  125. package/dist/lib/session/remote-list.js +1 -0
  126. package/dist/lib/session/session-cache.d.ts +4 -4
  127. package/dist/lib/session/session-cache.js +4 -4
  128. package/dist/lib/shims.d.ts +2 -2
  129. package/dist/lib/shims.js +45 -6
  130. package/dist/lib/signin-badge.js +2 -0
  131. package/dist/lib/startup/command-registry.d.ts +16 -0
  132. package/dist/lib/startup/command-registry.js +44 -0
  133. package/dist/lib/sync-umbrella.d.ts +5 -0
  134. package/dist/lib/sync-umbrella.js +5 -4
  135. package/dist/lib/teams/agents.js +1 -1
  136. package/dist/lib/teams/parsers.d.ts +1 -1
  137. package/dist/lib/tmux/session.d.ts +4 -2
  138. package/dist/lib/tmux/session.js +5 -5
  139. package/dist/lib/types.d.ts +3 -1
  140. package/dist/lib/versions.js +16 -1
  141. package/dist/lib/watchdog/service.d.ts +17 -0
  142. package/dist/lib/watchdog/service.js +39 -0
  143. package/package.json +1 -1
@@ -29,6 +29,7 @@ export const loadFork = async () => (await import('../../commands/fork.js')).reg
29
29
  export const loadDefaults = async () => (await import('../../commands/defaults.js')).registerDefaultsCommands;
30
30
  export const loadSet = async () => (await import('../../commands/set.js')).registerSetCommand;
31
31
  export const loadModels = async () => (await import('../../commands/models.js')).registerModelsCommand;
32
+ export const loadModes = async () => (await import('../../commands/modes.js')).registerModesCommand;
32
33
  export const loadPrune = async () => (await import('../../commands/prune.js')).registerPruneCommand;
33
34
  export const loadTrash = async () => (await import('../../commands/trash.js')).registerTrashCommands;
34
35
  export const loadRestore = async () => (await import('../../commands/trash.js')).registerRestoreCommand;
@@ -145,6 +146,11 @@ export const COMMAND_LOADERS = {
145
146
  registry: [loadPackages],
146
147
  search: [loadPackages],
147
148
  install: [loadPackages],
149
+ // packages.ts also registers `publish` at top level (commands/packages.ts:435).
150
+ // It was missing here, so `agents publish` only worked via the unknown-command
151
+ // fallback that registers the whole tree — and the --host router could not see
152
+ // it as a real command at all (RUSH-2022).
153
+ publish: [loadPackages],
148
154
  routines: [loadRoutines],
149
155
  monitors: [loadMonitors],
150
156
  projects: [loadProjects],
@@ -154,6 +160,7 @@ export const COMMAND_LOADERS = {
154
160
  defaults: [loadDefaults],
155
161
  set: [loadSet],
156
162
  models: [loadModels],
163
+ modes: [loadModes],
157
164
  trash: [loadTrash],
158
165
  restore: [loadRestore],
159
166
  doctor: [loadDoctor],
@@ -229,3 +236,40 @@ export const COMMAND_LOADERS = {
229
236
  funnel: [loadFunnel],
230
237
  humans: [loadHumans],
231
238
  };
239
+ /**
240
+ * Top-level names that {@link COMMAND_LOADERS} does not carry because they are
241
+ * registered inline in src/index.ts — closures over entry-point state (the
242
+ * deprecated aliases and tombstones) plus the internal/upgrade commands. They are
243
+ * real commands, so anything that asks "does this command exist?" must count them.
244
+ */
245
+ const INLINE_COMMAND_NAMES = [
246
+ 'perms', // deprecated alias -> permissions
247
+ 'exec', // deprecated alias -> run
248
+ 'jobs', // deprecated alias -> routines
249
+ 'cron', // deprecated alias -> routines
250
+ 'check', // tombstone -> doctor --check
251
+ 'resources', // tombstone -> view --merged
252
+ 'hq', // tombstone
253
+ '_internal',
254
+ 'upgrade',
255
+ ];
256
+ /**
257
+ * Every top-level command name the CLI answers to — the loader table plus the
258
+ * inline aliases/tombstones above. This is the "does this command exist?"
259
+ * predicate for code that runs BEFORE commander parses, most importantly the
260
+ * `--host`/`--device` router (lib/hosts/passthrough.ts): without it a typo'd
261
+ * command carrying `--host` reported a flag-support error instead of
262
+ * `unknown command` (RUSH-2022).
263
+ *
264
+ * Commander sub-aliases (`sessions ls`, `teams rm`, …) are deliberately absent —
265
+ * this set is top-level only. `command-registry.test.ts` pins it against the real
266
+ * registered command tree so a new command can never drift out of it.
267
+ */
268
+ export const KNOWN_TOP_LEVEL_COMMANDS = new Set([
269
+ ...Object.keys(COMMAND_LOADERS),
270
+ ...INLINE_COMMAND_NAMES,
271
+ ]);
272
+ /** Whether `name` is a top-level command this CLI registers. See {@link KNOWN_TOP_LEVEL_COMMANDS}. */
273
+ export function isKnownTopLevelCommand(name) {
274
+ return KNOWN_TOP_LEVEL_COMMANDS.has(name);
275
+ }
@@ -68,6 +68,11 @@ export interface RunUmbrellaArgs {
68
68
  yes: boolean;
69
69
  /** Secrets passphrase, if available (env var or prompt). Undefined => skip secrets. */
70
70
  passphrase?: string;
71
+ /**
72
+ * Suppress human progress from the reconcile stage (`refresh`). Required so
73
+ * `agents sync --json` / fleet fan-out leave stdout as a single JSON object.
74
+ */
75
+ quiet?: boolean;
71
76
  }
72
77
  /**
73
78
  * Execute the planned stages in order: repos -> secrets -> reconcile.
@@ -18,6 +18,7 @@
18
18
  import { pullRepo } from './git.js';
19
19
  import { getUserAgentsDir, getEnabledExtraRepos } from './state.js';
20
20
  import { listRemoteBundles, pullBundle } from './secrets/sync.js';
21
+ import { SYNC_PASSPHRASE_ENV } from './secrets/sync-passphrase.js';
21
22
  /**
22
23
  * Decide which stages run. Pure — no I/O. Semantics:
23
24
  * bare (no flags) fetch repos, then reconcile
@@ -52,7 +53,7 @@ export function planUmbrellaStages(f) {
52
53
  * reconcile — `agents sync` should make as much current as it can in one pass.
53
54
  */
54
55
  export async function runUmbrellaSync(args) {
55
- const { flags, log, yes, passphrase } = args;
56
+ const { flags, log, yes, passphrase, quiet = false } = args;
56
57
  const plan = planUmbrellaStages(flags);
57
58
  const result = { plan, reconciled: false };
58
59
  if (plan.fetchRepos) {
@@ -79,10 +80,10 @@ export async function runUmbrellaSync(args) {
79
80
  result.secrets = {
80
81
  pulled: 0,
81
82
  skipped: true,
82
- reason: 'no passphrase — set AGENTS_SECRETS_PASSPHRASE or run `agents login` (#366)',
83
+ reason: `no passphrase — set ${SYNC_PASSPHRASE_ENV} or run \`agents login\` (#366)`,
83
84
  errors: [],
84
85
  };
85
- log('secrets: skipped (no passphrase available)');
86
+ log(`secrets: skipped (no passphrase — set ${SYNC_PASSPHRASE_ENV})`);
86
87
  }
87
88
  else {
88
89
  let pulled = 0;
@@ -108,7 +109,7 @@ export async function runUmbrellaSync(args) {
108
109
  }
109
110
  if (plan.reconcile) {
110
111
  const { refresh } = await import('./refresh.js');
111
- await refresh({ skipPrompts: yes });
112
+ await refresh({ skipPrompts: yes, quiet });
112
113
  result.reconciled = true;
113
114
  // Keep already-registered devices' reachability current, and surface newly
114
115
  // appeared tailnet nodes as "pending" for the menu-bar Register/Ignore gate
@@ -197,7 +197,7 @@ export function buildTeammateSpawnEnv(envOverrides) {
197
197
  */
198
198
  export { captureProcessStartTime };
199
199
  /** Agent types the team runner supports. */
200
- const TEAM_AGENT_TYPES = ['codex', 'cursor', 'claude', 'opencode', 'grok', 'antigravity', 'kimi', 'droid'];
200
+ const TEAM_AGENT_TYPES = ['codex', 'cursor', 'claude', 'opencode', 'grok', 'antigravity', 'kimi', 'droid', 'warp'];
201
201
  // Suffix appended to all prompts to ensure agents provide a summary
202
202
  const PROMPT_SUFFIX = `
203
203
 
@@ -1,5 +1,5 @@
1
1
  /** Supported agent CLI types for team spawning. */
2
- export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode' | 'grok' | 'antigravity' | 'kimi' | 'droid';
2
+ export type AgentType = 'codex' | 'gemini' | 'cursor' | 'claude' | 'opencode' | 'grok' | 'antigravity' | 'kimi' | 'droid' | 'warp';
3
3
  /** Normalize a raw JSON event from any agent type into an array of unified event objects. */
4
4
  export declare function normalizeEvents(agentType: AgentType, raw: any): any[];
5
5
  /** Normalize a raw JSON event, returning only the first unified event (convenience wrapper). */
@@ -114,6 +114,8 @@ export interface TmuxClient {
114
114
  export declare function listClients(socket?: string): Promise<TmuxClient[]>;
115
115
  /** A dead pane's exit status, read from tmux while the pane lingers under remain-on-exit. */
116
116
  export interface PaneExit {
117
+ /** False when the pane/socket no longer exists or tmux could not query it. */
118
+ found: boolean;
117
119
  /** True once the process that ran in the pane has exited (pane is dead). */
118
120
  dead: boolean;
119
121
  /** Exit status of the dead pane's process, when tmux reports it. */
@@ -122,8 +124,8 @@ export interface PaneExit {
122
124
  /**
123
125
  * Read whether a pane's process has exited and, if so, its exit status. Used by
124
126
  * the spawn-wrap path to recover the wrapped agent's exit code after the attach
125
- * client returns. Returns `{ dead: false }` when tmux can't answer (session gone,
126
- * pane missing) so the caller treats an unreadable pane as "still alive / detach".
127
+ * client returns. `found: false` distinguishes a missing pane from a living one;
128
+ * focus must recover the former instead of attaching a retained/stale target.
127
129
  */
128
130
  export declare function paneExitStatus(pane: string, socket?: string): Promise<PaneExit>;
129
131
  /**
@@ -248,8 +248,8 @@ export async function listClients(socket) {
248
248
  /**
249
249
  * Read whether a pane's process has exited and, if so, its exit status. Used by
250
250
  * the spawn-wrap path to recover the wrapped agent's exit code after the attach
251
- * client returns. Returns `{ dead: false }` when tmux can't answer (session gone,
252
- * pane missing) so the caller treats an unreadable pane as "still alive / detach".
251
+ * client returns. `found: false` distinguishes a missing pane from a living one;
252
+ * focus must recover the former instead of attaching a retained/stale target.
253
253
  */
254
254
  export async function paneExitStatus(pane, socket) {
255
255
  let res;
@@ -261,13 +261,13 @@ export async function paneExitStatus(pane, socket) {
261
261
  });
262
262
  }
263
263
  catch {
264
- return { dead: false };
264
+ return { found: false, dead: false };
265
265
  }
266
266
  if (res.code !== 0)
267
- return { dead: false };
267
+ return { found: false, dead: false };
268
268
  const [deadRaw, statusRaw] = res.stdout.trim().split(/\s+/);
269
269
  const status = statusRaw !== undefined && statusRaw !== '' ? parseInt(statusRaw, 10) : undefined;
270
- return { dead: deadRaw === '1', status: Number.isFinite(status) ? status : undefined };
270
+ return { found: true, dead: deadRaw === '1', status: Number.isFinite(status) ? status : undefined };
271
271
  }
272
272
  /**
273
273
  * Bind a per-session hook. Used by the spawn-wrap path to install a `pane-died`
@@ -8,9 +8,10 @@
8
8
  import type { CloudProviderId } from './cloud/types.js';
9
9
  import type { FeedBroadcastConfig } from './feed-broadcast.js';
10
10
  /** Unique identifier for a current or legacy AI coding agent. */
11
- export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'pi' | 'muse';
11
+ export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'copilot' | 'amp' | 'kiro' | 'goose' | 'antigravity' | 'grok' | 'kimi' | 'droid' | 'hermes' | 'pi' | 'muse' | 'warp';
12
12
  /** How `agents run <agent>` chooses an installed version when none is pinned. */
13
13
  export type RunStrategy = 'pinned' | 'available' | 'balanced';
14
+ export type RunEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto';
14
15
  /**
15
16
  * Reserved `<agent>` keyword for `agents run auto` — full-auto dispatch:
16
17
  * host (14d launch affinity) → harness (best-account headroom, weighted) →
@@ -36,6 +37,7 @@ export interface AgentRunConfig {
36
37
  export interface RunDefaults {
37
38
  mode?: Mode;
38
39
  model?: string;
40
+ effort?: RunEffort;
39
41
  }
40
42
  /** `run:` section in agents.yaml. Agent keys keep strategy; `defaults` stores selector rules. */
41
43
  export type RunConfig = Partial<Record<AgentId, AgentRunConfig>> & {
@@ -928,6 +928,21 @@ export function getBinaryPath(agent, version) {
928
928
  ? path.join(getHomeDir(), 'bin', 'muse.exe')
929
929
  : path.join(getHomeDir(), '.local', 'bin', 'muse');
930
930
  }
931
+ if (agent === 'warp') {
932
+ // Warp Agent CLI installs a single global, self-updating `oz` binary — brew
933
+ // cask on macOS, the `oz-stable` apt|yum|pacman package on Linux — so, unlike
934
+ // droid/muse (which land at ~/.local/bin), its install location is
935
+ // platform/package specific. Resolve the real binary on PATH (findInPath
936
+ // skips our own shims dir) so isVersionInstalled / agents view agree with
937
+ // what executes. When oz is not installed, fall back to a deterministic path
938
+ // that won't exist, so isVersionInstalled reports uninstalled honestly.
939
+ const onPath = findInPath('oz');
940
+ if (onPath)
941
+ return onPath;
942
+ return IS_WINDOWS
943
+ ? path.join(getHomeDir(), 'bin', 'oz.exe')
944
+ : '/opt/warpdotdev/oz-stable/oz';
945
+ }
931
946
  const versionDir = getVersionDir(agent, version);
932
947
  return path.join(versionDir, 'node_modules', '.bin', agentConfig.cliCommand);
933
948
  }
@@ -1539,7 +1554,7 @@ export async function installVersion(agent, version, onProgress, opts) {
1539
1554
  // exec itself forever. So we skip the resolver-backed agents here AND, for
1540
1555
  // everyone else, filter the shims dir out of the `which` candidates so the
1541
1556
  // same race can't bite a non-special-cased installScript agent.
1542
- if (agent !== 'grok' && agent !== 'droid' && agent !== 'muse') {
1557
+ if (agent !== 'grok' && agent !== 'droid' && agent !== 'muse' && agent !== 'warp') {
1543
1558
  // findInPath is a pure-Node PATH scan that already skips our own shims
1544
1559
  // dir — so it returns the genuine install, never our dispatcher shim
1545
1560
  // (which sits ahead of ~/.local/bin on PATH and would otherwise be
@@ -0,0 +1,17 @@
1
+ import { type ActiveSession } from '../session/active.js';
2
+ import { type WatchdogThresholds, type WatchdogTickResult } from './runner.js';
3
+ export interface WatchdogPassOptions {
4
+ nudge: boolean;
5
+ nudgeText?: string;
6
+ smart?: boolean;
7
+ smartAgent?: string;
8
+ thresholds?: WatchdogThresholds;
9
+ allowGhosttyFocus?: boolean;
10
+ sessions?: ActiveSession[];
11
+ stateDir?: string;
12
+ mailboxGc?: boolean;
13
+ }
14
+ export declare function loadWatchdogSessions(): Promise<ActiveSession[]>;
15
+ export declare function runWatchdogMailboxGc(sessions: ActiveSession[]): void;
16
+ /** Execute one watchdog pass from the daemon or the explicit CLI command. */
17
+ export declare function runWatchdogPass(opts: WatchdogPassOptions): Promise<WatchdogTickResult>;
@@ -0,0 +1,39 @@
1
+ import * as path from 'path';
2
+ import { gcMailbox } from '../mailbox-gc.js';
3
+ import { mailboxIdForActiveSession } from '../mailbox-target.js';
4
+ import { getActiveSessions } from '../session/active.js';
5
+ import { loadLocalActiveSessions } from '../session/session-cache.js';
6
+ import { getRuntimeStateDir } from '../state.js';
7
+ import { runWatchdogTick } from './runner.js';
8
+ export async function loadWatchdogSessions() {
9
+ const loaded = await loadLocalActiveSessions({
10
+ gather: () => getActiveSessions({ localOnly: true }),
11
+ });
12
+ return loaded.sessions;
13
+ }
14
+ export function runWatchdogMailboxGc(sessions) {
15
+ const activeBoxIds = new Set(sessions.map(mailboxIdForActiveSession).filter((id) => Boolean(id)));
16
+ try {
17
+ gcMailbox(activeBoxIds);
18
+ }
19
+ catch {
20
+ // Housekeeping is retried by the next daemon tick.
21
+ }
22
+ }
23
+ /** Execute one watchdog pass from the daemon or the explicit CLI command. */
24
+ export async function runWatchdogPass(opts) {
25
+ const sessions = opts.sessions ?? await loadWatchdogSessions();
26
+ const result = await runWatchdogTick({
27
+ nudge: opts.nudge,
28
+ nudgeText: opts.nudgeText,
29
+ smart: opts.smart,
30
+ smartAgent: opts.smartAgent,
31
+ thresholds: opts.thresholds,
32
+ allowGhosttyFocus: opts.allowGhosttyFocus,
33
+ stateDir: opts.stateDir ?? path.join(getRuntimeStateDir(), 'watchdog'),
34
+ sessions,
35
+ });
36
+ if (opts.mailboxGc !== false)
37
+ runWatchdogMailboxGc(sessions);
38
+ return result;
39
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.23",
3
+ "version": "1.22.25",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",