@phnx-labs/agents-cli 1.22.56 → 1.22.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 (146) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +4 -4
  3. package/dist/bootstrap.js +11 -2
  4. package/dist/cli/command-registry.d.ts +0 -1
  5. package/dist/cli/command-registry.js +0 -3
  6. package/dist/commands/accounts.js +7 -3
  7. package/dist/commands/apply.js +10 -2
  8. package/dist/commands/exec.js +1 -1
  9. package/dist/commands/fork.d.ts +23 -10
  10. package/dist/commands/fork.js +115 -58
  11. package/dist/commands/hooks.js +4 -4
  12. package/dist/commands/insights.d.ts +7 -5
  13. package/dist/commands/insights.js +16 -9
  14. package/dist/commands/monitors.js +11 -0
  15. package/dist/commands/perf.d.ts +16 -7
  16. package/dist/commands/perf.js +29 -20
  17. package/dist/commands/prune.js +5 -3
  18. package/dist/commands/routines.d.ts +8 -0
  19. package/dist/commands/routines.js +57 -3
  20. package/dist/commands/rules.js +1 -1
  21. package/dist/commands/sessions-picker.d.ts +11 -0
  22. package/dist/commands/sessions-picker.js +16 -0
  23. package/dist/commands/sessions.js +1 -0
  24. package/dist/commands/share.d.ts +14 -0
  25. package/dist/commands/share.js +43 -2
  26. package/dist/commands/ssh.js +24 -14
  27. package/dist/commands/status.js +1 -1
  28. package/dist/commands/sync.js +83 -7
  29. package/dist/commands/traces.js +7 -0
  30. package/dist/commands/trash.d.ts +2 -2
  31. package/dist/commands/trash.js +2 -6
  32. package/dist/commands/versions.d.ts +2 -2
  33. package/dist/commands/versions.js +1 -10
  34. package/dist/commands/view.d.ts +2 -2
  35. package/dist/commands/view.js +7 -6
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +14 -0
  38. package/dist/lib/account-registry.d.ts +5 -1
  39. package/dist/lib/account-registry.js +47 -14
  40. package/dist/lib/accounting/capacity.d.ts +18 -7
  41. package/dist/lib/accounting/capacity.js +19 -8
  42. package/dist/lib/accounting/usage-ingest.d.ts +1 -0
  43. package/dist/lib/accounting/usage-ingest.js +75 -0
  44. package/dist/lib/accounting/usage-sync.d.ts +97 -0
  45. package/dist/lib/accounting/usage-sync.js +203 -0
  46. package/dist/lib/accounting/usage.d.ts +48 -2
  47. package/dist/lib/accounting/usage.js +79 -2
  48. package/dist/lib/agent-spec/agents.js +1 -1
  49. package/dist/lib/analytics/mix-commands.d.ts +8 -7
  50. package/dist/lib/analytics/mix-commands.js +50 -73
  51. package/dist/lib/auth-mint.d.ts +11 -1
  52. package/dist/lib/auth-mint.js +21 -6
  53. package/dist/lib/browser/ipc.d.ts +8 -0
  54. package/dist/lib/browser/ipc.js +87 -0
  55. package/dist/lib/browser/service.d.ts +19 -0
  56. package/dist/lib/browser/service.js +96 -11
  57. package/dist/lib/browser/sessions-list.js +10 -1
  58. package/dist/lib/daemon/daemon.js +5 -0
  59. package/dist/lib/daemon/runner.d.ts +3 -0
  60. package/dist/lib/daemon/runner.js +95 -53
  61. package/dist/lib/daemon/usage-sync-service.d.ts +21 -0
  62. package/dist/lib/daemon/usage-sync-service.js +42 -0
  63. package/dist/lib/daemon-services.d.ts +1 -1
  64. package/dist/lib/daemon-services.js +5 -0
  65. package/dist/lib/device-config.d.ts +17 -6
  66. package/dist/lib/device-config.js +25 -11
  67. package/dist/lib/devices/connect.d.ts +17 -8
  68. package/dist/lib/devices/connect.js +31 -14
  69. package/dist/lib/devices/pool.d.ts +4 -3
  70. package/dist/lib/devices/pool.js +13 -5
  71. package/dist/lib/doctor-diff.js +77 -7
  72. package/dist/lib/exec.d.ts +6 -41
  73. package/dist/lib/exec.js +6 -41
  74. package/dist/lib/fleet/manifest.d.ts +17 -0
  75. package/dist/lib/fleet/manifest.js +26 -0
  76. package/dist/lib/git.d.ts +13 -1
  77. package/dist/lib/git.js +36 -7
  78. package/dist/lib/harness/adapter.d.ts +7 -7
  79. package/dist/lib/harness/adapters/claude.js +3 -2
  80. package/dist/lib/hooks/install.d.ts +27 -11
  81. package/dist/lib/hooks/install.js +42 -17
  82. package/dist/lib/hosts/reconnect.d.ts +52 -203
  83. package/dist/lib/hosts/reconnect.js +64 -284
  84. package/dist/lib/hosts/remote-cmd.d.ts +9 -0
  85. package/dist/lib/hosts/remote-cmd.js +22 -0
  86. package/dist/lib/installations/migrate.d.ts +6 -120
  87. package/dist/lib/installations/migrate.js +27 -259
  88. package/dist/lib/installations/shims.d.ts +13 -95
  89. package/dist/lib/installations/shims.js +22 -139
  90. package/dist/lib/installations/store.js +1 -1
  91. package/dist/lib/installations/versions.d.ts +26 -133
  92. package/dist/lib/installations/versions.js +41 -204
  93. package/dist/lib/perf/db.d.ts +1 -1
  94. package/dist/lib/perf/db.js +1 -1
  95. package/dist/lib/plugins/skills.d.ts +8 -1
  96. package/dist/lib/plugins/skills.js +18 -2
  97. package/dist/lib/refresh.d.ts +9 -0
  98. package/dist/lib/refresh.js +3 -1
  99. package/dist/lib/routine-readiness.d.ts +15 -1
  100. package/dist/lib/routine-readiness.js +41 -0
  101. package/dist/lib/sandbox.d.ts +4 -1
  102. package/dist/lib/sandbox.js +30 -1
  103. package/dist/lib/secrets/agent.d.ts +80 -225
  104. package/dist/lib/secrets/agent.js +139 -401
  105. package/dist/lib/secrets/bundles.d.ts +73 -222
  106. package/dist/lib/secrets/bundles.js +168 -467
  107. package/dist/lib/secrets/reaper.d.ts +28 -70
  108. package/dist/lib/secrets/reaper.js +30 -85
  109. package/dist/lib/secrets/remote.d.ts +42 -129
  110. package/dist/lib/secrets/remote.js +55 -173
  111. package/dist/lib/self-heal/checks/install-staging.d.ts +4 -0
  112. package/dist/lib/self-heal/checks/install-staging.js +96 -0
  113. package/dist/lib/self-heal/registry.js +2 -0
  114. package/dist/lib/self-heal/types.d.ts +1 -1
  115. package/dist/lib/self-update.d.ts +23 -0
  116. package/dist/lib/self-update.js +50 -0
  117. package/dist/lib/session/active.d.ts +16 -32
  118. package/dist/lib/session/active.js +10 -68
  119. package/dist/lib/session/db.d.ts +24 -36
  120. package/dist/lib/session/db.js +143 -44
  121. package/dist/lib/session/discover.d.ts +6 -58
  122. package/dist/lib/session/discover.js +5 -43
  123. package/dist/lib/session/fork.d.ts +45 -26
  124. package/dist/lib/session/fork.js +32 -95
  125. package/dist/lib/session/parse.d.ts +1 -19
  126. package/dist/lib/session/parse.js +2 -15
  127. package/dist/lib/session/tool-calls.d.ts +43 -1
  128. package/dist/lib/session/tool-calls.js +74 -44
  129. package/dist/lib/session/tool-store.d.ts +33 -2
  130. package/dist/lib/session/tool-store.js +56 -3
  131. package/dist/lib/staleness/writers/sources.d.ts +5 -0
  132. package/dist/lib/staleness/writers/sources.js +2 -1
  133. package/dist/lib/startup/command-registry.d.ts +8 -2
  134. package/dist/lib/startup/command-registry.js +12 -4
  135. package/dist/lib/sync-status.d.ts +22 -0
  136. package/dist/lib/sync-status.js +27 -0
  137. package/dist/lib/sync-umbrella.d.ts +9 -0
  138. package/dist/lib/sync-umbrella.js +21 -2
  139. package/dist/lib/traces/insights.d.ts +47 -14
  140. package/dist/lib/traces/insights.js +92 -21
  141. package/dist/lib/traces/phenotype.d.ts +23 -3
  142. package/dist/lib/traces/phenotype.js +72 -24
  143. package/dist/lib/traces/sync.d.ts +15 -0
  144. package/dist/lib/traces/sync.js +104 -19
  145. package/dist/lib/traces/worker-template.js +154 -1
  146. package/package.json +1 -1
@@ -1,24 +1,8 @@
1
1
  /**
2
2
  * Secret bundles — named sets of environment variables backed by a secret store.
3
- *
4
- * Bundle metadata (name, description, vars map) is stored as a JSON blob under
5
- * `agents-cli.bundles.<name>`; secret values live one per item under
6
- * `agents-cli.secrets.<bundle>.<key>`. Two backends carry those items:
7
- *
8
- * - `keychain` (default): the macOS Keychain (device-local, Touch ID / device
9
- * passcode gated) or Linux libsecret — see src/lib/secrets/index.ts.
10
- * - `file`: an AES-256-GCM encrypted-file store keyed by a passphrase
11
- * (src/lib/secrets/filestore.ts). Opt-in, for headless / remote runs where
12
- * no biometry prompt can be satisfied (e.g. a release on a remote Mac over
13
- * SSH). The item-name scheme is identical, so the only difference is where
14
- * bytes land. A file-backed bundle is discovered by the presence of its
15
- * metadata item in the file store.
16
- * - `vault`: a single age-encrypted ~/.agents/vault.age file unlocked by
17
- * `agents secrets vault unlock`; intended for user-managed cross-machine file sync.
18
- *
19
- * Server-backed cross-machine sync is handled by src/lib/secrets/sync.ts via
20
- * an explicit encrypted export/import flow; the bundle layer also supports the
21
- * local vault backend for user-managed file sync.
3
+ * Metadata lives under `agents-cli.bundles.<name>`; values under
4
+ * `agents-cli.secrets.<bundle>.<key>`. Backends: `keychain` (default),
5
+ * `file` (headless/passphrase), and `vault` (age-encrypted, user-synced).
22
6
  */
23
7
  import { type BundleValue, type SecretRef } from './index.js';
24
8
  /** Which store carries a bundle's items. */
@@ -26,69 +10,40 @@ export type SecretsBackend = 'keychain' | 'file' | 'vault';
26
10
  /** Disable the broker-only guard for in-memory keychain tests. */
27
11
  export declare function setKeychainAgentOnlyBypassForTest(bypass: boolean): void;
28
12
  /**
29
- * Discover a bundle's backend by location: a file-backed bundle's metadata
30
- * item exists in the encrypted-file store. This is a plain file-existence
31
- * check — no passphrase, no Touch ID — so it sidesteps the chicken-and-egg of
32
- * "read metadata to learn where metadata lives." Absent ⇒ keychain.
13
+ * Discover a bundle's backend by location. File store is checked first (a plain
14
+ * existence test, no passphrase); absent/locked vault falls back to keychain.
33
15
  */
34
16
  export declare function bundleBackend(name: string): SecretsBackend;
35
17
  /** Allowed values for a secret's `type` metadata field. */
36
18
  export declare const SECRET_TYPES: readonly ["api-key", "token", "password", "url", "database-url", "ssh-key", "certificate", "webhook", "note"];
37
19
  export type SecretType = typeof SECRET_TYPES[number];
38
- /** Per-secret metadata. All fields optional; absent ones omitted at write time. */
20
+ /** Per-secret metadata; absent fields are omitted at write time. */
39
21
  export interface VarMeta {
40
22
  type?: SecretType;
41
- /** ISO date 'YYYY-MM-DD'. Always future-dated at write time. */
23
+ /** Future-dated ISO date ('YYYY-MM-DD'). */
42
24
  expires?: string;
43
- /** Singular freeform note. */
44
25
  note?: string;
45
26
  }
46
27
  /**
47
- * A bundle's prompt policy — how often macOS asks for Touch ID to read it:
48
- * - `hold` (default): ask once, then serve it silently for the configured hold
49
- * duration (`secrets.agent.holdMs`, 7d by default). Named for what it does —
50
- * it was called `daily`, which stated a period it never had.
51
- * (Historical name — the window is now a rolling ~1 week, not one calendar day.)
52
- * Eligible for the secrets-agent — the first real keychain read auto-loads it
53
- * (auto-cache is on by default) so concurrent runs read it silently, or `unlock`
54
- * it explicitly. Held from that unlock (not refreshed on use); re-asks sooner
55
- * after sleep, logout, or `agents secrets lock`. A bare screen-lock does NOT
56
- * drop it (the login password already gates a locked screen).
57
- * - `always`: asks every time. Never auto-held — only an explicit `agents
58
- * secrets unlock` ever holds it; every other read pops Touch ID. Opt a
59
- * high-value bundle into this when you want to confirm every single read.
60
- * - `never`: stored WITHOUT the biometry access control — reads are fully
61
- * silent (no Touch ID, no broker). The least-safe tier: any code running as
62
- * the user reads it with no user-presence check. Reserved for low-sensitivity,
63
- * automation-only credentials. Writing a `never` item needs the signed helper's
64
- * `set-no-acl` path (see keychain-helper.swift); an older pinned helper rejects
65
- * it loudly rather than silently downgrading to `always`.
66
- *
67
- * The default is configurable via `secrets.policy` in agents.yaml. Stored on disk
68
- * under the legacy `tier` key (`session` == `hold`, `biometry` == explicit
69
- * `always`, `none` == `never`, absent == inherit the default) so bundles stay
70
- * readable across mixed CLI versions on synced machines. The user-facing
71
- * vocabulary is `policy`/`always`/`hold`/`never`.
28
+ * Bundle prompt policy. `hold` (default): one Touch ID per hold window (~7d),
29
+ * then silent via the secrets-agent. `always`: prompt every read. `never`: no
30
+ * biometry ACL — least-safe, automation-only. Configurable via `secrets.policy`;
31
+ * persisted under the legacy `tier` key for cross-version sync.
72
32
  */
73
33
  export type SecretsPolicy = 'always' | 'hold' | 'never';
74
- /** A named set of environment variable definitions backed by various secret providers. */
34
+ /** A named set of environment variable definitions backed by secret stores. */
75
35
  export interface SecretsBundle {
76
36
  name: string;
77
37
  description?: string;
78
38
  allow_exec?: boolean;
79
- /** Which store carries this bundle's items. Absent ⇒ `keychain` (the default). */
39
+ /** Absent ⇒ `keychain`. */
80
40
  backend?: SecretsBackend;
81
- /** Prompt policy. Absent ⇒ the configured default (`hold`). Serialized under
82
- * the legacy `tier` key — see SecretsPolicy. */
41
+ /** Absent ⇒ configured default (`hold`). */
83
42
  policy?: SecretsPolicy;
84
- /** ISO 8601 UTC timestamp. Set once on the first writeBundle() for a bundle. */
85
43
  created_at?: string;
86
- /** ISO 8601 UTC timestamp. Refreshed on every writeBundle(). */
87
44
  updated_at?: string;
88
- /** ISO 8601 UTC timestamp. Stamped by resolveBundleEnv (throttled). */
89
45
  last_used?: string;
90
46
  vars: Record<string, BundleValue>;
91
- /** Optional per-var metadata, keyed by var name (parallel to `vars`). */
92
47
  meta?: Record<string, VarMeta>;
93
48
  }
94
49
  export interface LegacyBundleCandidate {
@@ -102,11 +57,8 @@ export declare const BUNDLE_KEY_PATTERN: RegExp;
102
57
  export declare const BUNDLE_META_PREFIX = "agents-cli.bundles.";
103
58
  export declare const RESERVED_ENV_NAMES: Set<string>;
104
59
  /**
105
- * The reserved FILE-BACKED bundle that holds long-lived Claude setup-tokens.
106
- * Usage/probe reads authenticate with these instead of the ACL-bound login item,
107
- * so they never pop Touch ID and they can cross the fleet. A keychain- or
108
- * vault-backed bundle of this name is a misconfiguration: the consumer used to
109
- * return null (SEC-GAP-3) and silently fall through to Touch ID.
60
+ * Reserved FILE-BACKED bundle for long-lived setup tokens. Must be file-backed;
61
+ * a keychain/vault `auth` bundle is a misconfiguration and is ignored.
110
62
  */
111
63
  export declare const AUTH_BUNDLE_NAME = "auth";
112
64
  export declare const AUTH_BUNDLE_BACKEND: SecretsBackend;
@@ -118,11 +70,17 @@ export declare class ReservedBundleWrongBackendError extends Error {
118
70
  readonly backend: SecretsBackend;
119
71
  constructor(bundle: string, backend: SecretsBackend);
120
72
  }
121
- /** Fail loud when `name` is reserved and `backend` is not the required one. */
73
+ /** Fail loud when a reserved bundle is on the wrong backend. */
122
74
  export declare function assertReservedBundleBackend(name: string, backend: SecretsBackend): void;
123
75
  /**
124
- * Presence + backend of the reserved `auth` bundle. `ok` is true when the
125
- * bundle is absent (nothing to fix) or present and file-backed.
76
+ * Check the reserved `auth` bundle. `ok` is true when absent or file-backed.
77
+ *
78
+ * Read-only status probe (`agents doctor`, `agents fleet apply`) — unlike the
79
+ * destructive-write guards `bundleExists()`/`hasKeychainToken()` document
80
+ * themselves as failing loud for, a diagnostic MUST NOT crash the whole
81
+ * command because ONE optional finding could not reach the keychain (e.g. the
82
+ * macOS Keychain helper source is unavailable — PHNX-3385). Treat an
83
+ * unreachable keychain the same as "bundle absent": nothing to warn about.
126
84
  */
127
85
  export declare function inspectReservedAuthBundle(): {
128
86
  exists: boolean;
@@ -130,10 +88,9 @@ export declare function inspectReservedAuthBundle(): {
130
88
  ok: boolean;
131
89
  };
132
90
  /**
133
- * After a file-backed import, actually decrypt the keys and fail if any are
134
- * unreadable. Import used to print "Imported N key(s)" from the write tally
135
- * alone — ciphertext sealed under a forwarded AGENTS_SECRETS_PASSPHRASE that
136
- * the destination daemon does not hold still counted as success.
91
+ * After a file-backed import, verify the keys actually decrypt. Import used to
92
+ * report success for ciphertext sealed under a forwarded passphrase this process
93
+ * does not hold.
137
94
  */
138
95
  export declare function assertFileBundleDecryptable(name: string, keys: string[]): void;
139
96
  export declare function bundleToEnvPrefix(name: string): string;
@@ -147,67 +104,42 @@ export declare function validateEnvKey(key: string): void;
147
104
  /** Assert that `t` is one of the known SECRET_TYPES. Throws with the allowed list otherwise. */
148
105
  export declare function validateSecretType(t: string): asserts t is SecretType;
149
106
  /**
150
- * Validate an `expires` value. Accepts strict 'YYYY-MM-DD' only and rejects
151
- * any date <= now. We compare against end-of-day UTC for the chosen date so
152
- * "today" is treated as past (per spec).
107
+ * Validate a future `expires` date. Accepts strict 'YYYY-MM-DD'; end-of-day UTC
108
+ * means "today" is past.
153
109
  */
154
110
  export declare function validateExpiresFutureDated(iso: string): void;
155
111
  export declare function bundleExists(name: string): boolean;
156
112
  /**
157
- * Thrown by `readBundle` for the one state `readBundleIfDecryptable` may treat
158
- * as "present but permanently unreadable": file-store ciphertext on disk that
159
- * will not decrypt with the passphrase in effect (lost/rotated key or a tampered
160
- * store). It is deliberately narrow — a bundle that is merely *locked for this
161
- * run* (headless macOS without `AGENTS_SECRETS_PASSPHRASE`, or a vault that is
162
- * not logged in) is recoverable and must not be collapsed into this.
113
+ * Thrown for file-store ciphertext that will not decrypt (lost/rotated key or
114
+ * tampered store). Narrow: temporarily-locked bundles are recoverable and must
115
+ * not be collapsed here.
163
116
  */
164
117
  export declare class BundleUndecryptableError extends Error {
165
118
  constructor(message: string);
166
119
  }
167
120
  /**
168
- * Read a bundle, or return null when its metadata is present but genuinely
169
- * cannot be decrypted — a lost or rotated file-store passphrase, or a tampered
170
- * file store (signalled by `BundleUndecryptableError`).
171
- *
172
- * Deleting such a bundle is the only way out of that state, and deletion needs
173
- * no plaintext, so it must not be gated behind a successful decrypt. Every other
174
- * failure rethrows: a genuinely missing bundle ("not found"), and — critically —
175
- * a bundle that is only *temporarily locked* for this run (headless macOS with no
176
- * `AGENTS_SECRETS_PASSPHRASE`, or a not-logged-in vault). Collapsing that
177
- * recoverable "set the env / log in" state into "unreadable, safe to delete"
178
- * would let `secrets delete <name> --yes` silently destroy a perfectly healthy
179
- * bundle from a cron/launchd run that merely forgot to export the passphrase.
121
+ * Read a bundle, returning null only when metadata is present but permanently
122
+ * unreadable (`BundleUndecryptableError`). Other failures — including temporary
123
+ * lockout — rethrow so a healthy bundle is never deleted by mistake.
180
124
  */
181
125
  export declare function readBundleIfDecryptable(name: string): SecretsBundle | null;
182
126
  export declare function readBundle(name: string): SecretsBundle;
183
- /** The default prompt policy applied to bundles without an explicit per-bundle
184
- * policy. Configurable via `secrets.policy` in agents.yaml; `hold` (one Touch ID
185
- * per hold window — `secrets.agent.holdMs`, 7d by default) unless the user
186
- * explicitly opts back into prompt-every-time with `always`. Best-effort: an
187
- * unreadable config falls back to the `hold` default. */
127
+ /** Default policy for bundles without an explicit one (`secrets.policy`). */
188
128
  export declare function secretsDefaultPolicy(): SecretsPolicy;
189
- /** The effective prompt policy of a bundle (absent ⇒ the configured default). */
129
+ /** Effective prompt policy of a bundle. */
190
130
  export declare function bundlePolicy(bundle: SecretsBundle): SecretsPolicy;
191
131
  /** Options for writeBundle. */
192
132
  export interface WriteBundleOptions {
193
133
  /**
194
- * Skip evicting the bundle from the secrets-agent broker after the write.
195
- * Only for writers that change nothing the broker serves — today that is
196
- * stampLastUsed (a usage-telemetry timestamp, fired on every broker HIT):
197
- * evicting there would make the cache destroy itself on first use. Every
198
- * mutating writer (add / rotate / remove / rename / policy / import) must
199
- * leave this unset so a broker-held copy never serves stale values for up
200
- * to the ~7d hold.
134
+ * Skip evicting the broker-held copy. Only for no-op writers such as
135
+ * stampLastUsed; mutating writes must evict so stale values aren't served.
201
136
  */
202
137
  skipBrokerEviction?: boolean;
203
138
  }
204
139
  /**
205
- * Whether a bundle write should evict the broker-held copy. Pure + exported
206
- * for regression coverage. Skips when the writer opted out (stampLastUsed),
207
- * when the broker integration is disabled (AGENTS_SECRETS_NO_AGENT — the same
208
- * kill-switch the read fast-path honors), or when a test keychain backend is
209
- * installed (an in-memory backend has no real keychain behind it, and a test
210
- * writing bundle 'prod' must never evict the user's real 'prod' unlock).
140
+ * Whether a bundle write should evict the broker-held copy. Exported for tests.
141
+ * Skips when the writer opted out, the broker is disabled, or a test backend is
142
+ * active (so tests don't evict the user's real unlocks).
211
143
  */
212
144
  export declare function shouldEvictAfterBundleWrite(skipRequested: boolean, noAgentEnv: string | undefined, backendOverridden: boolean): boolean;
213
145
  export declare function writeBundle(bundle: SecretsBundle, opts?: WriteBundleOptions): void;
@@ -225,18 +157,14 @@ export declare const __metaIndexForTest: {
225
157
  remove: typeof removeBundleFromMetaIndex;
226
158
  };
227
159
  /**
228
- * Re-write already-read keychain bundle metadata items WITHOUT the biometry ACL.
229
- * `metaJsonByName` maps bundle name → the exact metadata JSON listBundles just
230
- * batch-read, so writing it back only flips the ACL (via the helper's
231
- * delete-then-add `set-no-acl`) — contents and updated_at are preserved and no
232
- * extra keychain read is issued. Exported for tests. Returns the count healed.
160
+ * Re-write keychain metadata items without the biometry ACL. The caller supplies
161
+ * the JSON already read, so contents are preserved and no extra read is issued.
162
+ * Exported for tests.
233
163
  */
234
164
  export declare function healKeychainBundleMetadata(metaJsonByName: Map<string, string>): number;
235
165
  /**
236
- * One-time driver around healKeychainBundleMetadata (RUSH-1759). macOS + real
237
- * keychain only — libsecret/CredMan have no biometry ACL to shed, and a test
238
- * backend has no real keychain — and gated by a sentinel so it runs at most
239
- * once. Best-effort: a heal failure never breaks bundle listing.
166
+ * One-time driver for healKeychainBundleMetadata. macOS + real keychain only;
167
+ * gated by a sentinel. Best-effort: a heal failure never breaks listing.
240
168
  */
241
169
  export declare function healKeychainBundleMetadataAclOnce(metaJsonByName: Map<string, string>): void;
242
170
  export declare function listBundles(): SecretsBundle[];
@@ -249,64 +177,28 @@ export declare function describeBundle(bundle: SecretsBundle): BundleEntryInfo[]
249
177
  export declare function stampLastUsed(bundle: SecretsBundle): void;
250
178
  /** Options for resolveBundleEnv. */
251
179
  export interface ResolveBundleOptions {
252
- /**
253
- * Human-readable label for who is requesting the secrets. Currently
254
- * informational only — the helper's Touch ID prompt is set by the OS and
255
- * cannot be reliably customized once we drop the per-batch reason path,
256
- * but we keep this in the API so call sites stay explicit about who's
257
- * about to read the bundle.
258
- */
259
180
  caller?: string;
260
- /** Harness type whose unlock may be reused (claude, codex, kimi, ...). */
181
+ /** Harness type whose unlock may be reused. */
261
182
  agent?: string;
262
- /** Human duration rendered in the Touch ID prompt. */
183
+ /** Duration shown in the Touch ID prompt. */
263
184
  duration?: string;
264
- /** Explicitly permit this agent request to raise interactive authentication. */
185
+ /** Allow this call to raise an interactive biometric prompt. */
265
186
  interactiveUnlock?: boolean;
266
- /**
267
- * Skip the secrets-agent fast-path and read straight from the keychain
268
- * (popping Touch ID). Set by callers that must NOT serve a cached snapshot —
269
- * `unlock` (which populates the agent in the first place) and any flow that
270
- * needs live values. Also honored via AGENTS_SECRETS_NO_AGENT=1.
271
- */
187
+ /** Skip the broker fast-path and read from the keychain directly. */
272
188
  noAgent?: boolean;
273
- /**
274
- * Resolve only from an already-unlocked secrets-agent snapshot. If the
275
- * broker has no snapshot, fail before touching Keychain or any other store.
276
- * Background processes use this to guarantee they never surface a biometric
277
- * prompt that nobody can answer.
278
- */
189
+ /** Resolve only from an already-unlocked broker snapshot; fail before prompting. */
279
190
  agentOnly?: boolean;
280
- /**
281
- * Inject only this subset of keys from the bundle. Keys not in this list are
282
- * silently excluded from the returned env map. An error is thrown if any
283
- * requested key is absent from the bundle (fail-loud, never silent skip).
284
- * When absent or empty, all keys are injected (original behaviour).
285
- */
191
+ /** Inject only these keys. Errors if any requested key is absent. */
286
192
  keys?: string[];
287
- /**
288
- * When true, skip the pre-run expiry check and inject keys even if their
289
- * `expires` date is in the past. By default any expired key (or a key whose
290
- * bundle-level expiry has passed) aborts the run before Touch ID is popped.
291
- */
193
+ /** Skip the per-key expiry gate. */
292
194
  allowExpired?: boolean;
293
- /**
294
- * `process` projects dotted account keys like `GITHUB_USERNAME.personal` to
295
- * the shell-safe base env name (`GITHUB_USERNAME`). Direct value lookups and
296
- * backup/export flows use `storage` to preserve the exact bundle key names.
297
- */
195
+ /** `process` projects dotted keys to shell-safe env names; `storage` preserves them. */
298
196
  keyMode?: 'process' | 'storage';
299
197
  }
300
198
  export declare function canCacheResolvedEnv(bundle: SecretsBundle, selectedKeys: Set<string>, keyMode: ResolveBundleOptions['keyMode']): boolean;
301
199
  /**
302
- * Apply the --keys subset + expiry gate to an already-resolved snapshot from
303
- * the secrets-agent fast-path. The agent stores either a full unlock or a
304
- * scoped lease env, so a naive fast-path return could silently defeat --keys
305
- * and inject expired values. Mirrors the slow-path pre-checks in `resolveBundleEnv` /
306
- * `readAndResolveBundleEnv` and returns a new env whose keys match the subset.
307
- *
308
- * Exported for tests; production callers reach it via the fast-path branch in
309
- * `readAndResolveBundleEnv`.
200
+ * Apply --keys and --allow-expired to a broker snapshot so the fast path
201
+ * mirrors the slow path's gates. Exported for tests.
310
202
  */
311
203
  export declare function filterAgentHitBySubsetAndExpiry(hit: {
312
204
  bundle: SecretsBundle;
@@ -316,14 +208,8 @@ export declare function filterAgentHitBySubsetAndExpiry(hit: {
316
208
  env: Record<string, string>;
317
209
  };
318
210
  /**
319
- * Guard for remote-bundle callers (`bundle@host` / `--device`) — the SSH
320
- * resolver in `remoteResolveEnv` does not thread --keys or --allow-expired
321
- * yet. Silently applying them would inject the full remote env or an expired
322
- * value, defeating the least-privilege intent, so we fail loud.
323
- *
324
- * Exported so `agents run --secrets bundle@host` and `agents secrets exec
325
- * --device` share the exact same error text; the tests exercise this helper
326
- * directly instead of driving the whole CLI.
211
+ * Fail loud when remote bundle resolution is asked for flags the SSH resolver
212
+ * does not yet thread. Exported so callers share the same error text.
327
213
  */
328
214
  export declare function assertRemoteBundleFlagsUnsupported(bundleName: string, host: string, opts: {
329
215
  keys?: string[];
@@ -343,16 +229,8 @@ export declare function resolveBundleEnv(bundle: SecretsBundle, _opts?: ResolveB
343
229
  export { isHeadlessSecretsContext, isAgentInvocationContext } from './headless.js';
344
230
  /**
345
231
  * Read a bundle's metadata AND resolve its env in a single Touch ID prompt.
346
- *
347
- * `readBundle` + `resolveBundleEnv` issued two separate `LAContext` calls
348
- * (metadata read via `get-auth`, then secret values via `get-batch`) which
349
- * surfaced as two consecutive Touch ID prompts. macOS does not honor
350
- * "Always Allow" for items protected with `kSecAttrAccessControl`+biometry,
351
- * so caching at the OS level was never an option. This collapses both reads
352
- * into one `get-batch` call: we enumerate the bundle's secret items first
353
- * (silent — `list` returns attrs only and does not trigger biometry) and
354
- * include the metadata item in the same batch. One prompt, correctly scoped
355
- * to the bundle name and caller.
232
+ * `readBundle` + `resolveBundleEnv` used to issue two LAContext calls (two
233
+ * prompts). This collapses them into one batch that includes the metadata item.
356
234
  */
357
235
  export declare function readAndResolveBundleEnv(name: string, opts?: ResolveBundleOptions): {
358
236
  bundle: SecretsBundle;
@@ -370,57 +248,30 @@ export interface RotateOptions {
370
248
  meta?: Partial<VarMeta>;
371
249
  }
372
250
  /**
373
- * Rotate a keychain-backed secret in `bundle`. Errors if `key` is not present
374
- * in the bundle (use `add` to introduce a new key). Preserves existing meta
375
- * unless `clearMeta` or a `meta` patch is supplied.
251
+ * Rotate a keychain-backed secret. Errors if the key is absent; preserves meta
252
+ * unless cleared or patched.
376
253
  */
377
254
  export declare function rotateBundleSecret(bundle: SecretsBundle, key: string, opts: RotateOptions): void;
378
255
  /**
379
- * Reconcile a bundle's keychain-backed VALUE items to its CURRENT policy, then
380
- * write the (always no-ACL) metadata.
381
- *
382
- * `writeBundle` only rewrites the metadata item, so a policy change alone leaves
383
- * every value item carrying the ACL it was created with — and macOS gates each
384
- * read on the ITEM's ACL, not the bundle's declared tier (spec SEC-19). Without
385
- * this reconcile, `agents secrets policy <b> never` reports "silent" while the
386
- * still-ACL'd value keeps popping Touch ID on every read, forever.
387
- *
388
- * hold/always -> never strips the biometry ACL (helper `set-no-acl`: delete+add)
389
- * never -> hold/always re-attaches it (helper `set`)
390
- *
391
- * The current values are read in ONE batch, so the reconcile costs at most a
392
- * single Touch ID — the last prompt a hold->never bundle will ever raise (a
393
- * never->* flip reads silently, since the items are already no-ACL). No-op on the
394
- * ACL to write for non-keychain backends (file/vault have no biometry concept),
395
- * and a metadata-only write when the bundle has no keychain-backed values.
256
+ * Reconcile keychain value items to the bundle's current policy. macOS gates
257
+ * reads on each item's ACL, not the bundle's declared policy, so a policy change
258
+ * alone would leave stale ACLs. hold/always → never strips ACL; never → *
259
+ * re-attaches it. Non-keychain backends no-op.
396
260
  */
397
261
  export declare function reAclBundleItems(bundle: SecretsBundle): void;
398
262
  /** Options for renameBundle. */
399
263
  export interface RenameOptions {
400
- /** When true, overwrite an existing destination bundle (purges its keychain items first). */
264
+ /** Overwrite an existing destination bundle. */
401
265
  force?: boolean;
402
266
  }
403
267
  /**
404
- * Rename a bundle: move metadata + every keychain-backed value to a new name.
405
- *
406
- * Sequence is ordered so the source stays intact if anything in the copy
407
- * phase fails:
408
- * 1) read source, validate dest
409
- * 2) purge dest if --force, refuse otherwise
410
- * 3) copy each keychain value source -> dest
411
- * 4) write new bundle metadata
412
- * 5) delete the old per-key keychain items + old metadata
413
- *
414
- * Steps 1-4 are reversible. If 5 partially fails, running `rename` again is
415
- * a safe no-op for the source items.
268
+ * Rename a bundle: copy metadata + keychain values to the new name, then delete
269
+ * the source. Steps are ordered so a copy-phase failure leaves the source intact.
416
270
  */
417
271
  export declare function renameBundle(oldName: string, newName: string, opts?: RenameOptions): void;
418
272
  /**
419
- * The store (keychain or encrypted file) that carries a bundle's items. The
420
- * CLI uses this to read/write/delete per-key items (built with
421
- * secretsKeychainItem) in the same store as the bundle's metadata, for `add` /
422
- * `import` / `remove` / `delete`. Pass the bundle's resolved backend
423
- * (`bundle.backend ?? 'keychain'`).
273
+ * The item store (keychain or encrypted file) for a bundle's per-key secrets.
274
+ * Pass the resolved backend (`bundle.backend ?? 'keychain'`).
424
275
  */
425
276
  export declare function bundleItemStore(backend: SecretsBackend | undefined, opts?: {
426
277
  noAcl?: boolean;