@phnx-labs/agents-cli 1.20.56 → 1.20.58

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 (47) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +34 -3
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/defaults.js +24 -0
  5. package/dist/commands/exec.js +28 -4
  6. package/dist/commands/secrets.d.ts +3 -2
  7. package/dist/commands/secrets.js +35 -25
  8. package/dist/commands/teams.d.ts +20 -1
  9. package/dist/commands/teams.js +105 -2
  10. package/dist/commands/versions.js +11 -3
  11. package/dist/commands/view.js +19 -4
  12. package/dist/lib/agents.d.ts +21 -0
  13. package/dist/lib/agents.js +28 -4
  14. package/dist/lib/daemon.d.ts +5 -5
  15. package/dist/lib/daemon.js +88 -17
  16. package/dist/lib/git.d.ts +9 -0
  17. package/dist/lib/git.js +12 -0
  18. package/dist/lib/hosts/dispatch.d.ts +21 -0
  19. package/dist/lib/hosts/dispatch.js +88 -5
  20. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  21. package/dist/lib/permissions.d.ts +19 -1
  22. package/dist/lib/permissions.js +137 -0
  23. package/dist/lib/project-root.d.ts +65 -0
  24. package/dist/lib/project-root.js +133 -0
  25. package/dist/lib/resources/permissions.js +2 -0
  26. package/dist/lib/resources/types.d.ts +1 -1
  27. package/dist/lib/secrets/agent.d.ts +48 -18
  28. package/dist/lib/secrets/agent.js +288 -165
  29. package/dist/lib/secrets/remote.js +1 -0
  30. package/dist/lib/session/active.d.ts +3 -0
  31. package/dist/lib/session/active.js +1 -0
  32. package/dist/lib/session/parse.js +38 -15
  33. package/dist/lib/session/state.d.ts +4 -1
  34. package/dist/lib/session/state.js +18 -1
  35. package/dist/lib/session/types.d.ts +8 -0
  36. package/dist/lib/staleness/detectors/permissions.js +42 -0
  37. package/dist/lib/staleness/detectors/subagents.js +30 -0
  38. package/dist/lib/staleness/writers/subagents.js +13 -1
  39. package/dist/lib/subagents.d.ts +22 -0
  40. package/dist/lib/subagents.js +146 -0
  41. package/dist/lib/teams/agents.d.ts +30 -0
  42. package/dist/lib/teams/agents.js +271 -42
  43. package/dist/lib/types.d.ts +13 -0
  44. package/dist/lib/versions.d.ts +39 -0
  45. package/dist/lib/versions.js +199 -12
  46. package/package.json +1 -1
  47. package/scripts/postinstall.js +26 -11
@@ -440,6 +440,37 @@ export function convertToClaudeFormat(set) {
440
440
  }
441
441
  return { permissions };
442
442
  }
443
+ /**
444
+ * Map a canonical rule to Cursor CLI syntax.
445
+ * Cursor uses Shell(...) instead of Bash(...); other tools keep TitleCase names.
446
+ * https://cursor.com/docs/cli/reference/permissions
447
+ */
448
+ function canonicalToCursorRule(perm) {
449
+ if (perm === 'Bash' || perm.startsWith('Bash(')) {
450
+ return perm.replace(/^Bash/, 'Shell');
451
+ }
452
+ // Canonical WebSearch maps to WebFetch family for network allow.
453
+ if (perm.startsWith('WebSearch(')) {
454
+ return perm.replace(/^WebSearch/, 'WebFetch');
455
+ }
456
+ // Cursor has no Edit prefix — file writes use Write(...).
457
+ if (perm === 'Edit' || perm.startsWith('Edit(')) {
458
+ return perm.replace(/^Edit/, 'Write');
459
+ }
460
+ return perm;
461
+ }
462
+ /**
463
+ * Convert canonical permission set to Cursor CLI format
464
+ * (`~/.cursor/cli-config.json` permissions.allow/deny).
465
+ */
466
+ export function convertToCursorFormat(set) {
467
+ return {
468
+ permissions: {
469
+ allow: set.allow.map(canonicalToCursorRule),
470
+ deny: (set.deny ?? []).map(canonicalToCursorRule),
471
+ },
472
+ };
473
+ }
443
474
  /**
444
475
  * Parse canonical permission pattern to extract tool and pattern.
445
476
  * "Bash(git *)" -> { tool: "bash", pattern: "git *" }
@@ -592,6 +623,57 @@ function canonicalToGrokRule(perm, action) {
592
623
  }
593
624
  return { action, tool, pattern };
594
625
  }
626
+ /**
627
+ * Convert canonical permissions to Kiro CLI v3 capability rules.
628
+ * Kiro stores these rules in ~/.kiro/settings/permissions.yaml.
629
+ */
630
+ export function convertToKiroFormat(set) {
631
+ const rules = [];
632
+ for (const perm of set.allow) {
633
+ const rule = canonicalToKiroRule(perm, 'allow');
634
+ if (rule)
635
+ rules.push(rule);
636
+ }
637
+ for (const perm of set.deny ?? []) {
638
+ const rule = canonicalToKiroRule(perm, 'deny');
639
+ if (rule)
640
+ rules.push(rule);
641
+ }
642
+ return { rules };
643
+ }
644
+ function canonicalToKiroRule(perm, effect) {
645
+ if (BLANKET_BASH_FORMS.has(perm)) {
646
+ return { capability: 'shell', effect };
647
+ }
648
+ const parsed = parseCanonicalPreserveCase(perm);
649
+ const capability = KIRO_CAPABILITY_BY_TOOL[parsed.tool.toLowerCase()];
650
+ if (!capability)
651
+ return null;
652
+ if (parsed.pattern === null || parsed.pattern === '*' || parsed.pattern === '**') {
653
+ return { capability, effect };
654
+ }
655
+ const lowerTool = parsed.tool.toLowerCase();
656
+ const pattern = lowerTool === 'bash'
657
+ ? normalizeBashPattern(parsed.pattern)
658
+ : (lowerTool === 'webfetch' || lowerTool === 'websearch') && parsed.pattern.startsWith('domain:')
659
+ ? parsed.pattern.slice('domain:'.length)
660
+ : parsed.pattern;
661
+ return { capability, effect, match: [pattern] };
662
+ }
663
+ const KIRO_CAPABILITY_BY_TOOL = {
664
+ bash: 'shell',
665
+ read: 'fs_read',
666
+ grep: 'fs_read',
667
+ glob: 'fs_read',
668
+ write: 'fs_write',
669
+ edit: 'fs_write',
670
+ notebookedit: 'fs_write',
671
+ webfetch: 'web_fetch',
672
+ websearch: 'web_search',
673
+ mcp: 'mcp',
674
+ subagent: 'subagent',
675
+ skill: 'skill',
676
+ };
595
677
  /**
596
678
  * Parse a canonical permission string preserving the tool's original casing.
597
679
  * `parseCanonicalPattern` lowercases the tool name, which is fine for Grok
@@ -1258,6 +1340,61 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
1258
1340
  fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
1259
1341
  return { success: true };
1260
1342
  }
1343
+ if (agentId === 'cursor') {
1344
+ // Cursor CLI permissions live in ~/.cursor/cli-config.json
1345
+ const configPath = path.join(configDir, 'cli-config.json');
1346
+ let config = {};
1347
+ if (fs.existsSync(configPath)) {
1348
+ try {
1349
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1350
+ }
1351
+ catch { /* start fresh */ }
1352
+ }
1353
+ const converted = convertToCursorFormat(set);
1354
+ if (merge && config.permissions && typeof config.permissions === 'object') {
1355
+ const existing = config.permissions;
1356
+ const allow = new Set([...(existing.allow || []), ...converted.permissions.allow]);
1357
+ const deny = new Set([...(existing.deny || []), ...converted.permissions.deny]);
1358
+ config.permissions = { allow: [...allow], deny: [...deny] };
1359
+ }
1360
+ else {
1361
+ config.permissions = converted.permissions;
1362
+ }
1363
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
1364
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
1365
+ return { success: true };
1366
+ }
1367
+ if (agentId === 'kiro') {
1368
+ const permissionsPath = path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
1369
+ let config = {};
1370
+ if (fs.existsSync(permissionsPath)) {
1371
+ const parsed = yaml.parse(fs.readFileSync(permissionsPath, 'utf-8'));
1372
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
1373
+ config = parsed;
1374
+ }
1375
+ }
1376
+ const newRules = convertToKiroFormat(set).rules;
1377
+ if (merge) {
1378
+ const existingRules = Array.isArray(config.rules) ? config.rules : [];
1379
+ const seen = new Set();
1380
+ config.rules = [...existingRules, ...newRules].filter((rule) => {
1381
+ if (!rule || typeof rule !== 'object' || Array.isArray(rule))
1382
+ return false;
1383
+ const record = rule;
1384
+ const key = `${String(record.effect)}|${String(record.capability)}|${JSON.stringify(record.match ?? [])}|${JSON.stringify(record.exclude ?? [])}`;
1385
+ if (seen.has(key))
1386
+ return false;
1387
+ seen.add(key);
1388
+ return true;
1389
+ });
1390
+ }
1391
+ else {
1392
+ config.rules = newRules;
1393
+ }
1394
+ fs.mkdirSync(path.dirname(permissionsPath), { recursive: true });
1395
+ fs.writeFileSync(permissionsPath, yaml.stringify(config), 'utf-8');
1396
+ return { success: true };
1397
+ }
1261
1398
  return { success: false, error: `Agent '${agentId}' does not support permissions` };
1262
1399
  }
1263
1400
  catch (err) {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Projects-root resolution for the `agents run --project <slug>` shorthand.
3
+ *
4
+ * Projects follow a predictable layout — `<root>/<repo>` (e.g.
5
+ * `~/src/github.com/<user>/<repo>`), with git worktrees under
6
+ * `<repo>/.agents/worktrees/<slug>`. The root is auto-inferred from the repo you
7
+ * launch inside (the directory ABOVE the git root) and cached in `agents.yaml`
8
+ * so later runs resolve a bare slug from anywhere. It is stored home-relative
9
+ * (`~/…`) when it sits under `$HOME`, so the SAME value resolves on a remote
10
+ * host whose home differs (`/home/<user>` vs `/Users/<user>`): a `--host` run
11
+ * keeps the `~` and lets the remote login shell expand it (see `remoteCdPrefix`
12
+ * in `hosts/dispatch.ts`), while a local run expands `~` against the local home.
13
+ */
14
+ /** Rewrite an absolute path under the local home to a `~/`-relative string; pass others through. */
15
+ export declare function toHomeRelative(abs: string): string;
16
+ /** Expand a leading `~`/`$HOME` against the LOCAL home. Other paths pass through unchanged. */
17
+ export declare function expandLocalHome(p: string): string;
18
+ /**
19
+ * Make a `--cwd`/`--project` value portable to a remote host: an absolute path
20
+ * under the LOCAL home (which the local shell already expanded from `~`) becomes
21
+ * `~/…` so the *remote* shell re-roots it at its own home. Paths already anchored
22
+ * at `~`/`$HOME` pass through; other absolute or relative paths are left as-is
23
+ * (used verbatim on the host). Explicit `--remote-cwd` is NOT run through this —
24
+ * it is a literal remote path by contract.
25
+ */
26
+ export declare function toRemotePortable(p: string): string;
27
+ /** The configured projects root (home-relative or absolute), or undefined when unset. */
28
+ export declare function getProjectRoot(): string | undefined;
29
+ /** Set (override) the cached projects root. Stored home-relative when under `$HOME`. */
30
+ export declare function setProjectRoot(rootPath: string): string;
31
+ /**
32
+ * Infer the projects root from `cwd`: the directory ABOVE the git repo root
33
+ * (cwd inside `~/src/github.com/user/repo` → `~/src/github.com/user`). Returns a
34
+ * home-relative string when under `$HOME`; undefined when `cwd` is not in a repo.
35
+ */
36
+ export declare function inferProjectRoot(cwd: string): Promise<string | undefined>;
37
+ /**
38
+ * Resolve the projects root, auto-inferring and caching on first use. Throws an
39
+ * actionable error when it is neither configured nor inferrable from `cwd`.
40
+ */
41
+ export declare function ensureProjectRoot(cwd: string): Promise<string>;
42
+ export interface ProjectRef {
43
+ slug: string;
44
+ worktree?: string;
45
+ }
46
+ /** Parse a `--project` value of the form `<slug>[@<worktree>]`. */
47
+ export declare function parseProjectRef(ref: string): ProjectRef;
48
+ /**
49
+ * Join a root + `--project` ref into a working directory. Pure (no I/O) so the
50
+ * slug/worktree layout is unit-testable. `forRemote` keeps the path
51
+ * home-relative (`~/…`) for the remote shell to expand; otherwise it is expanded
52
+ * against the local home into an absolute path.
53
+ */
54
+ export declare function buildProjectPath(root: string, ref: string, forRemote: boolean): string;
55
+ /**
56
+ * Resolve a `--project` ref to a working directory, inferring/caching the root.
57
+ *
58
+ * `forRemote: true` returns a home-relative path (`~/…`) so the REMOTE login
59
+ * shell expands `~`/`$HOME` to its own home. `forRemote: false` returns an
60
+ * absolute local path and verifies it exists (so a mistyped slug fails loudly).
61
+ */
62
+ export declare function resolveProjectRef(ref: string, opts: {
63
+ forRemote: boolean;
64
+ cwd?: string;
65
+ }): Promise<string>;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Projects-root resolution for the `agents run --project <slug>` shorthand.
3
+ *
4
+ * Projects follow a predictable layout — `<root>/<repo>` (e.g.
5
+ * `~/src/github.com/<user>/<repo>`), with git worktrees under
6
+ * `<repo>/.agents/worktrees/<slug>`. The root is auto-inferred from the repo you
7
+ * launch inside (the directory ABOVE the git root) and cached in `agents.yaml`
8
+ * so later runs resolve a bare slug from anywhere. It is stored home-relative
9
+ * (`~/…`) when it sits under `$HOME`, so the SAME value resolves on a remote
10
+ * host whose home differs (`/home/<user>` vs `/Users/<user>`): a `--host` run
11
+ * keeps the `~` and lets the remote login shell expand it (see `remoteCdPrefix`
12
+ * in `hosts/dispatch.ts`), while a local run expands `~` against the local home.
13
+ */
14
+ import * as os from 'os';
15
+ import * as path from 'path';
16
+ import * as fs from 'fs';
17
+ import { readMeta, updateMeta } from './state.js';
18
+ import { getMainRepoRoot } from './git.js';
19
+ const HOME = process.env.HOME ?? os.homedir();
20
+ /** Rewrite an absolute path under the local home to a `~/`-relative string; pass others through. */
21
+ export function toHomeRelative(abs) {
22
+ const rel = path.relative(HOME, abs);
23
+ if (rel === '')
24
+ return '~';
25
+ if (!rel.startsWith('..') && !path.isAbsolute(rel))
26
+ return `~/${rel}`;
27
+ return abs;
28
+ }
29
+ /** Expand a leading `~`/`$HOME` against the LOCAL home. Other paths pass through unchanged. */
30
+ export function expandLocalHome(p) {
31
+ if (p === '~' || p === '$HOME')
32
+ return HOME;
33
+ if (p.startsWith('~/'))
34
+ return path.join(HOME, p.slice(2));
35
+ if (p.startsWith('$HOME/'))
36
+ return path.join(HOME, p.slice(6));
37
+ return p;
38
+ }
39
+ /**
40
+ * Make a `--cwd`/`--project` value portable to a remote host: an absolute path
41
+ * under the LOCAL home (which the local shell already expanded from `~`) becomes
42
+ * `~/…` so the *remote* shell re-roots it at its own home. Paths already anchored
43
+ * at `~`/`$HOME` pass through; other absolute or relative paths are left as-is
44
+ * (used verbatim on the host). Explicit `--remote-cwd` is NOT run through this —
45
+ * it is a literal remote path by contract.
46
+ */
47
+ export function toRemotePortable(p) {
48
+ if (p.startsWith('~') || p.startsWith('$HOME'))
49
+ return p;
50
+ if (path.isAbsolute(p))
51
+ return toHomeRelative(p);
52
+ return p;
53
+ }
54
+ /** The configured projects root (home-relative or absolute), or undefined when unset. */
55
+ export function getProjectRoot() {
56
+ return readMeta().projectRoot;
57
+ }
58
+ /** Set (override) the cached projects root. Stored home-relative when under `$HOME`. */
59
+ export function setProjectRoot(rootPath) {
60
+ const stored = toHomeRelative(path.resolve(expandLocalHome(rootPath)));
61
+ updateMeta({ projectRoot: stored });
62
+ return stored;
63
+ }
64
+ /**
65
+ * Infer the projects root from `cwd`: the directory ABOVE the git repo root
66
+ * (cwd inside `~/src/github.com/user/repo` → `~/src/github.com/user`). Returns a
67
+ * home-relative string when under `$HOME`; undefined when `cwd` is not in a repo.
68
+ */
69
+ export async function inferProjectRoot(cwd) {
70
+ try {
71
+ const mainRoot = await getMainRepoRoot(cwd);
72
+ return toHomeRelative(path.dirname(mainRoot));
73
+ }
74
+ catch {
75
+ return undefined;
76
+ }
77
+ }
78
+ /**
79
+ * Resolve the projects root, auto-inferring and caching on first use. Throws an
80
+ * actionable error when it is neither configured nor inferrable from `cwd`.
81
+ */
82
+ export async function ensureProjectRoot(cwd) {
83
+ const existing = getProjectRoot();
84
+ if (existing)
85
+ return existing;
86
+ const inferred = await inferProjectRoot(cwd);
87
+ if (!inferred) {
88
+ throw new Error('Could not determine your projects root. Run once from inside a project ' +
89
+ '(a git repo under your projects dir) so it can be inferred, or set it:\n' +
90
+ ' agents defaults project-root ~/src/github.com/<you>');
91
+ }
92
+ updateMeta({ projectRoot: inferred });
93
+ process.stderr.write(`[project] cached projects root: ${inferred}\n`);
94
+ return inferred;
95
+ }
96
+ /** Parse a `--project` value of the form `<slug>[@<worktree>]`. */
97
+ export function parseProjectRef(ref) {
98
+ const at = ref.indexOf('@');
99
+ if (at === -1)
100
+ return { slug: ref };
101
+ return { slug: ref.slice(0, at), worktree: ref.slice(at + 1) || undefined };
102
+ }
103
+ /**
104
+ * Join a root + `--project` ref into a working directory. Pure (no I/O) so the
105
+ * slug/worktree layout is unit-testable. `forRemote` keeps the path
106
+ * home-relative (`~/…`) for the remote shell to expand; otherwise it is expanded
107
+ * against the local home into an absolute path.
108
+ */
109
+ export function buildProjectPath(root, ref, forRemote) {
110
+ const { slug, worktree } = parseProjectRef(ref);
111
+ if (!slug)
112
+ throw new Error(`Invalid --project value: "${ref}"`);
113
+ let rel = `${root}/${slug}`;
114
+ if (worktree)
115
+ rel += `/.agents/worktrees/${worktree}`;
116
+ return forRemote ? rel : path.resolve(expandLocalHome(rel));
117
+ }
118
+ /**
119
+ * Resolve a `--project` ref to a working directory, inferring/caching the root.
120
+ *
121
+ * `forRemote: true` returns a home-relative path (`~/…`) so the REMOTE login
122
+ * shell expands `~`/`$HOME` to its own home. `forRemote: false` returns an
123
+ * absolute local path and verifies it exists (so a mistyped slug fails loudly).
124
+ */
125
+ export async function resolveProjectRef(ref, opts) {
126
+ const cwd = opts.cwd ?? process.cwd();
127
+ const root = await ensureProjectRoot(cwd);
128
+ const resolved = buildProjectPath(root, ref, opts.forRemote);
129
+ if (!opts.forRemote && !fs.existsSync(resolved)) {
130
+ throw new Error(`Project path not found: ${resolved}`);
131
+ }
132
+ return resolved;
133
+ }
@@ -69,6 +69,8 @@ function getAgentConfigPath(agent, versionHome) {
69
69
  return path.join(versionHome, '.opencode', 'opencode.jsonc');
70
70
  case 'kimi':
71
71
  return path.join(versionHome, '.kimi-code', 'config.toml');
72
+ case 'kiro':
73
+ return path.join(versionHome, '.kiro', 'settings', 'permissions.yaml');
72
74
  default:
73
75
  return null;
74
76
  }
@@ -5,7 +5,7 @@
5
5
  * - Union: All resources from all layers are combined
6
6
  * - Override on name conflict: Higher layer wins (project > user > system)
7
7
  */
8
- export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'antigravity' | 'grok' | 'kimi' | 'hermes' | 'forge';
8
+ export type AgentId = 'claude' | 'codex' | 'gemini' | 'cursor' | 'opencode' | 'openclaw' | 'kiro' | 'antigravity' | 'grok' | 'kimi' | 'hermes' | 'forge';
9
9
  export type Layer = 'system' | 'user' | 'project';
10
10
  export type ResourceKind = 'command' | 'hook' | 'skill' | 'rule' | 'mcp' | 'permission' | 'subagent' | 'workflow' | 'memory';
11
11
  /** A resolved resource with its origin layer. */
@@ -74,23 +74,23 @@ export interface AgentStatusEntry {
74
74
  expiresAt: number;
75
75
  keyCount: number;
76
76
  }
77
- /** True if the launchd plist for the persistent broker is installed. */
77
+ /** True if a legacy standalone-broker launchd plist is still installed. */
78
78
  export declare function secretsAgentServiceInstalled(): boolean;
79
79
  /**
80
- * Install + start the persistent broker as a launchd user service (idempotent).
81
- * Writes the plist, bootstraps it into the GUI domain, and waits for the socket.
82
- * `ProcessType: Interactive` asks launchd to schedule it at foreground priority
83
- * so it can boot even when the machine is loaded. Returns true once reachable.
80
+ * Retire the legacy standalone secrets-agent launchd service: bootout the job
81
+ * (falling back to the legacy `unload`) and remove its plist so the always-on
82
+ * daemon owns the broker socket. Idempotent and best-effort a no-op when no
83
+ * legacy plist is present. Does NOT wipe held bundles: the booted-out process's
84
+ * memory is gone anyway, and the daemon-hosted broker starts fresh.
84
85
  */
85
- export declare function installSecretsAgentService(timeoutMs?: number): Promise<boolean>;
86
+ export declare function retireLegacySecretsAgentService(): void;
86
87
  /**
87
- * Kickstart the already-installed persistent broker so launchd relaunches it
88
- * onto the current on-disk code. Used by postinstall heal-on-upgrade. No-op if
89
- * the service isn't installed; never rewrites the plist or waits, so it's safe
90
- * and fast to call from an installer.
88
+ * Stop the persistent broker for `agents secrets stop`: wipe whatever the broker
89
+ * holds (forces Touch ID again on the next read), then retire any legacy
90
+ * standalone service. The daemon-hosted broker itself is left running it is
91
+ * the always-on backbone, and stopping it would take down unrelated background
92
+ * work (routines, browser IPC, session-sync).
91
93
  */
92
- export declare function kickstartSecretsAgentService(): void;
93
- /** Stop + remove the persistent broker service, and wipe whatever it held. */
94
94
  export declare function uninstallSecretsAgentService(): Promise<void>;
95
95
  export type Request = {
96
96
  cmd: 'ping';
@@ -171,7 +171,30 @@ export declare function shouldWipeOnWatchEvent(chunk: string): boolean;
171
171
  */
172
172
  export declare function runSecretsAgent(opts?: {
173
173
  service?: boolean;
174
- }): Promise<void>;
174
+ }): Promise<{
175
+ close(): void;
176
+ } | null>;
177
+ /**
178
+ * Host the secrets broker inside the always-on daemon (#416).
179
+ *
180
+ * Serves the SAME socket and wire protocol as the standalone `runSecretsAgent`
181
+ * — so every existing client (`agentGetSync`, `agentPing`, `agentAutoLoadSync`)
182
+ * keeps working unchanged, no PROTOCOL_VERSION bump — but it is daemon-safe:
183
+ *
184
+ * - no pid-file single-instance guard (the daemon owns the instance);
185
+ * - no `process.exit`, no SIGTERM/SIGINT handlers, no self-heal/idle-exit
186
+ * (those would kill the daemon — the daemon is the always-on backbone and
187
+ * manages its own version/lifecycle). The sweep only TTL-evicts.
188
+ *
189
+ * The caller (`runDaemon`) normally invokes this only when no broker answers
190
+ * its initial ping. Binding still arbitrates ownership through the same shared
191
+ * path as the standalone service: a live owner wins, while only an unreachable
192
+ * stale socket is reclaimed. Returns a handle the daemon closes on shutdown,
193
+ * or null off-darwin (nothing to broker without biometry).
194
+ */
195
+ export declare function startHostedBroker(): Promise<{
196
+ close(): void;
197
+ } | null>;
175
198
  /** True if a broker socket exists at all. Cheap; gates the sync read so the
176
199
  * never-unlocked path stays a single stat. */
177
200
  export declare function agentSocketExists(): boolean;
@@ -246,14 +269,21 @@ export declare function agentLock(name?: string): Promise<number>;
246
269
  * predates the server-side exclusion, so this keeps the internal entry from
247
270
  * surfacing in `agents secrets status` in that skew window. */
248
271
  export declare function agentStatus(): Promise<AgentStatusEntry[]>;
272
+ /** Ping result: whether a broker is reachable + speaking our protocol, and the
273
+ * version of the code it's running (for staleness detection). */
274
+ export declare function agentPing(): Promise<{
275
+ reachable: boolean;
276
+ cliVersion?: string;
277
+ }>;
249
278
  /**
250
279
  * Ensure a broker is running and reachable. Returns true once the socket answers
251
280
  * a ping. macOS only.
252
281
  *
253
- * Prefers the persistent launchd service: if it isn't installed we install it
254
- * (which makes the broker survive for the whole login session, so subsequent
255
- * reads never cold-start); if it's installed but unreachable we kickstart it.
256
- * Only when the service path can't be used do we fall back to a one-off detached
257
- * broker that's the model that gets starved under heavy load, so it's last.
282
+ * Prefers the always-on daemon, which hosts the broker socket (#416): retire any
283
+ * legacy standalone launchd service so the daemon owns the socket, then bring the
284
+ * daemon up (Path 0) one supervised backbone that survives the whole login
285
+ * session, so subsequent reads never cold-start. Only when the daemon can't be
286
+ * used do we fall back to a one-off detached broker (Path 1) the model that
287
+ * gets starved under heavy load, so it's last.
258
288
  */
259
289
  export declare function ensureAgentRunning(timeoutMs?: number): Promise<boolean>;