@phnx-labs/agents-cli 1.20.72 → 1.20.73

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 (219) hide show
  1. package/CHANGELOG.md +299 -0
  2. package/README.md +40 -4
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +12 -0
  5. package/dist/commands/activity.js +68 -0
  6. package/dist/commands/apply.d.ts +13 -0
  7. package/dist/commands/apply.js +28 -3
  8. package/dist/commands/browser.js +1 -1
  9. package/dist/commands/cloud.js +21 -11
  10. package/dist/commands/doctor.js +8 -4
  11. package/dist/commands/events.d.ts +11 -10
  12. package/dist/commands/events.js +30 -21
  13. package/dist/commands/exec.d.ts +34 -0
  14. package/dist/commands/exec.js +337 -39
  15. package/dist/commands/feed.d.ts +13 -0
  16. package/dist/commands/feed.js +130 -29
  17. package/dist/commands/hq.d.ts +2 -0
  18. package/dist/commands/hq.js +58 -0
  19. package/dist/commands/lease.d.ts +19 -0
  20. package/dist/commands/lease.js +138 -11
  21. package/dist/commands/login.d.ts +2 -0
  22. package/dist/commands/login.js +123 -0
  23. package/dist/commands/logs.js +1 -1
  24. package/dist/commands/mine.d.ts +27 -0
  25. package/dist/commands/mine.js +207 -0
  26. package/dist/commands/monitors.js +79 -49
  27. package/dist/commands/plugins.js +1 -0
  28. package/dist/commands/profiles.js +158 -0
  29. package/dist/commands/repo.js +2 -60
  30. package/dist/commands/resources.d.ts +5 -0
  31. package/dist/commands/resources.js +95 -0
  32. package/dist/commands/routines.d.ts +2 -0
  33. package/dist/commands/routines.js +139 -37
  34. package/dist/commands/secrets.js +234 -47
  35. package/dist/commands/send.d.ts +15 -0
  36. package/dist/commands/send.js +62 -0
  37. package/dist/commands/sessions-tail.d.ts +1 -0
  38. package/dist/commands/sessions-tail.js +20 -9
  39. package/dist/commands/sessions.js +18 -3
  40. package/dist/commands/setup-mine.d.ts +18 -0
  41. package/dist/commands/setup-mine.js +106 -0
  42. package/dist/commands/setup-share.js +2 -2
  43. package/dist/commands/setup.js +3 -1
  44. package/dist/commands/share.d.ts +8 -1
  45. package/dist/commands/share.js +122 -37
  46. package/dist/commands/ssh.d.ts +9 -0
  47. package/dist/commands/ssh.js +196 -26
  48. package/dist/commands/sync.js +1 -1
  49. package/dist/commands/teams.js +11 -2
  50. package/dist/index.js +79 -10
  51. package/dist/lib/activity.d.ts +124 -0
  52. package/dist/lib/activity.js +542 -0
  53. package/dist/lib/agents.js +16 -5
  54. package/dist/lib/artifact-actions.d.ts +1 -1
  55. package/dist/lib/artifact-actions.js +1 -1
  56. package/dist/lib/ask-classifier.d.ts +2 -1
  57. package/dist/lib/ask-classifier.js +3 -3
  58. package/dist/lib/auth-health.d.ts +32 -4
  59. package/dist/lib/auth-health.js +40 -1
  60. package/dist/lib/auto-pull.js +8 -1
  61. package/dist/lib/brand.d.ts +40 -0
  62. package/dist/lib/brand.js +122 -0
  63. package/dist/lib/browser/drivers/ssh.js +4 -7
  64. package/dist/lib/browser/service.js +6 -8
  65. package/dist/lib/channels/providers/index.d.ts +2 -0
  66. package/dist/lib/channels/providers/index.js +20 -0
  67. package/dist/lib/channels/providers/mailbox.d.ts +2 -0
  68. package/dist/lib/channels/providers/mailbox.js +26 -0
  69. package/dist/lib/channels/providers/openclaw-telegram.d.ts +2 -0
  70. package/dist/lib/channels/providers/openclaw-telegram.js +34 -0
  71. package/dist/lib/channels/providers/rush.d.ts +6 -0
  72. package/dist/lib/channels/providers/rush.js +55 -0
  73. package/dist/lib/channels/registry.d.ts +42 -0
  74. package/dist/lib/channels/registry.js +20 -0
  75. package/dist/lib/channels/resolve.d.ts +11 -0
  76. package/dist/lib/channels/resolve.js +12 -0
  77. package/dist/lib/cli-resources.d.ts +18 -0
  78. package/dist/lib/cli-resources.js +53 -4
  79. package/dist/lib/cloud/codex.js +6 -3
  80. package/dist/lib/cloud/factory.d.ts +1 -0
  81. package/dist/lib/cloud/factory.js +10 -4
  82. package/dist/lib/cloud/rush.d.ts +3 -1
  83. package/dist/lib/cloud/rush.js +6 -1
  84. package/dist/lib/cloud/types.d.ts +2 -0
  85. package/dist/lib/codex-home.d.ts +35 -0
  86. package/dist/lib/codex-home.js +136 -0
  87. package/dist/lib/crabbox/cli.d.ts +44 -0
  88. package/dist/lib/crabbox/cli.js +120 -20
  89. package/dist/lib/crabbox/lease.d.ts +52 -1
  90. package/dist/lib/crabbox/lease.js +117 -16
  91. package/dist/lib/crabbox/progress.d.ts +31 -0
  92. package/dist/lib/crabbox/progress.js +76 -0
  93. package/dist/lib/crabbox/runtimes.d.ts +4 -0
  94. package/dist/lib/crabbox/runtimes.js +33 -8
  95. package/dist/lib/crabbox/setup-copy.d.ts +87 -0
  96. package/dist/lib/crabbox/setup-copy.js +127 -0
  97. package/dist/lib/daemon.d.ts +5 -5
  98. package/dist/lib/daemon.js +9 -9
  99. package/dist/lib/drive-sync.js +6 -3
  100. package/dist/lib/event-stream.d.ts +36 -0
  101. package/dist/lib/event-stream.js +68 -0
  102. package/dist/lib/events.d.ts +1 -1
  103. package/dist/lib/exec.d.ts +11 -0
  104. package/dist/lib/exec.js +23 -2
  105. package/dist/lib/feed-policy.d.ts +1 -1
  106. package/dist/lib/feed-policy.js +14 -9
  107. package/dist/lib/feed-ranking.d.ts +32 -0
  108. package/dist/lib/feed-ranking.js +224 -0
  109. package/dist/lib/feed.d.ts +35 -2
  110. package/dist/lib/feed.js +66 -3
  111. package/dist/lib/fleet/auth-sync.d.ts +53 -0
  112. package/dist/lib/fleet/auth-sync.js +92 -0
  113. package/dist/lib/fleet/remote-login.d.ts +187 -0
  114. package/dist/lib/fleet/remote-login.js +556 -0
  115. package/dist/lib/format.d.ts +30 -3
  116. package/dist/lib/format.js +34 -5
  117. package/dist/lib/fs-atomic.d.ts +7 -2
  118. package/dist/lib/fs-atomic.js +8 -12
  119. package/dist/lib/git.d.ts +16 -0
  120. package/dist/lib/git.js +79 -2
  121. package/dist/lib/heal.js +11 -3
  122. package/dist/lib/hosts/progress.d.ts +28 -10
  123. package/dist/lib/hosts/progress.js +79 -22
  124. package/dist/lib/hosts/remote-cmd.js +4 -0
  125. package/dist/lib/hq/floor.d.ts +87 -0
  126. package/dist/lib/hq/floor.js +226 -0
  127. package/dist/lib/menubar/install-menubar.js +14 -20
  128. package/dist/lib/notify.d.ts +1 -0
  129. package/dist/lib/notify.js +3 -3
  130. package/dist/lib/open-url.d.ts +2 -0
  131. package/dist/lib/open-url.js +19 -0
  132. package/dist/lib/openclaw-keychain.d.ts +56 -0
  133. package/dist/lib/openclaw-keychain.js +236 -0
  134. package/dist/lib/overdue.js +10 -0
  135. package/dist/lib/permissions.d.ts +44 -1
  136. package/dist/lib/permissions.js +284 -7
  137. package/dist/lib/project-launch.d.ts +6 -12
  138. package/dist/lib/project-launch.js +13 -228
  139. package/dist/lib/project-resources.d.ts +7 -0
  140. package/dist/lib/project-resources.js +291 -0
  141. package/dist/lib/pty-client.d.ts +27 -0
  142. package/dist/lib/pty-client.js +136 -10
  143. package/dist/lib/refresh.js +14 -10
  144. package/dist/lib/resource-profiles.d.ts +26 -0
  145. package/dist/lib/resource-profiles.js +157 -0
  146. package/dist/lib/resources/permissions.js +7 -1
  147. package/dist/lib/resources/types.d.ts +1 -1
  148. package/dist/lib/resources.d.ts +1 -1
  149. package/dist/lib/resources.js +32 -3
  150. package/dist/lib/routines.d.ts +11 -0
  151. package/dist/lib/routines.js +56 -15
  152. package/dist/lib/runner.d.ts +1 -0
  153. package/dist/lib/runner.js +66 -5
  154. package/dist/lib/secrets/bundles.d.ts +16 -3
  155. package/dist/lib/secrets/bundles.js +206 -37
  156. package/dist/lib/secrets/icloud-import.d.ts +2 -2
  157. package/dist/lib/secrets/icloud-import.js +9 -6
  158. package/dist/lib/secrets/mcp.js +1 -1
  159. package/dist/lib/secrets/vault-age-helper.d.ts +1 -0
  160. package/dist/lib/secrets/vault-age-helper.js +34 -0
  161. package/dist/lib/secrets/vault.d.ts +49 -0
  162. package/dist/lib/secrets/vault.js +397 -0
  163. package/dist/lib/self-heal/checks/path.js +3 -1
  164. package/dist/lib/self-heal/checks/shadowing.js +10 -2
  165. package/dist/lib/self-heal/checks/shims.js +19 -11
  166. package/dist/lib/session/cloud.d.ts +2 -2
  167. package/dist/lib/session/cloud.js +2 -2
  168. package/dist/lib/session/db.d.ts +4 -0
  169. package/dist/lib/session/db.js +44 -7
  170. package/dist/lib/session/discover.d.ts +2 -0
  171. package/dist/lib/session/discover.js +114 -3
  172. package/dist/lib/session/stream-render.d.ts +3 -0
  173. package/dist/lib/session/stream-render.js +130 -0
  174. package/dist/lib/session/types.d.ts +6 -0
  175. package/dist/lib/share/analytics.d.ts +13 -0
  176. package/dist/lib/share/analytics.js +45 -0
  177. package/dist/lib/share/config.d.ts +19 -5
  178. package/dist/lib/share/config.js +44 -8
  179. package/dist/lib/share/provision.d.ts +58 -6
  180. package/dist/lib/share/provision.js +97 -22
  181. package/dist/lib/share/publish.d.ts +36 -13
  182. package/dist/lib/share/publish.js +55 -8
  183. package/dist/lib/share/worker-template.js +83 -17
  184. package/dist/lib/shims.d.ts +9 -0
  185. package/dist/lib/shims.js +91 -13
  186. package/dist/lib/ssh-exec.d.ts +14 -0
  187. package/dist/lib/ssh-exec.js +57 -0
  188. package/dist/lib/staleness/detectors/hooks.js +25 -1
  189. package/dist/lib/staleness/detectors/permissions.js +66 -0
  190. package/dist/lib/staleness/detectors/workflows.js +20 -0
  191. package/dist/lib/staleness/writers/hooks.js +58 -4
  192. package/dist/lib/staleness/writers/permissions.js +2 -2
  193. package/dist/lib/staleness/writers/skills.js +1 -1
  194. package/dist/lib/staleness/writers/sources.d.ts +10 -1
  195. package/dist/lib/staleness/writers/sources.js +68 -1
  196. package/dist/lib/star-nudge.d.ts +45 -0
  197. package/dist/lib/star-nudge.js +91 -0
  198. package/dist/lib/startup/command-registry.d.ts +6 -1
  199. package/dist/lib/startup/command-registry.js +17 -3
  200. package/dist/lib/state.d.ts +2 -0
  201. package/dist/lib/state.js +3 -0
  202. package/dist/lib/subagents-registry.d.ts +2 -0
  203. package/dist/lib/subagents-registry.js +3 -0
  204. package/dist/lib/teams/parsers.js +214 -6
  205. package/dist/lib/teams/supervisor.d.ts +7 -0
  206. package/dist/lib/teams/supervisor.js +2 -1
  207. package/dist/lib/template.d.ts +1 -1
  208. package/dist/lib/template.js +1 -1
  209. package/dist/lib/triggers/webhook.js +36 -3
  210. package/dist/lib/types.d.ts +73 -0
  211. package/dist/lib/version.d.ts +40 -0
  212. package/dist/lib/version.js +94 -16
  213. package/dist/lib/versions.d.ts +11 -0
  214. package/dist/lib/versions.js +208 -76
  215. package/dist/lib/workflows.d.ts +2 -0
  216. package/dist/lib/workflows.js +88 -0
  217. package/package.json +2 -1
  218. package/dist/commands/daemon.d.ts +0 -10
  219. package/dist/commands/daemon.js +0 -121
@@ -107,11 +107,29 @@ const JOB_DEFAULTS = {
107
107
  timeout: '10m',
108
108
  enabled: true,
109
109
  };
110
+ function overlayUserRoutineDevices(job, userJob) {
111
+ if (job.devices !== undefined)
112
+ return job;
113
+ if (!userJob)
114
+ return job;
115
+ const merged = { ...job };
116
+ if (userJob.devices && userJob.devices.length > 0) {
117
+ merged.devices = userJob.devices;
118
+ }
119
+ else {
120
+ delete merged.devices;
121
+ }
122
+ return merged;
123
+ }
110
124
  /**
111
125
  * List all job configs, scanning project > user > system routine dirs.
112
126
  * Higher layers shadow lower ones of the same name (first-seen wins): a project
113
127
  * routine shadows a user routine, and a user routine shadows a built-in system
114
128
  * routine (`~/.agents/.system/routines/`, shipped via gh:phnx-labs/.agents-system).
129
+ * When a same-name project routine wins for inspection, the user-layer
130
+ * `devices` allowlist is overlaid only if the project routine does not declare
131
+ * its own allowlist, so CWD project discovery cannot hide an operational fleet
132
+ * pin or erase a project-authored one.
115
133
  * Project discovery is opt-in via `cwd`; the daemon (which calls `listJobs()`
116
134
  * with no argument) sees user + system routines, so a built-in routine fires for
117
135
  * every install unless the user overrides or disables it by name.
@@ -120,22 +138,26 @@ export function listJobs(cwd) {
120
138
  ensureAgentsDir();
121
139
  const seen = new Set();
122
140
  const jobs = [];
141
+ const userDir = getRoutinesDir();
123
142
  const dirs = [];
124
143
  if (cwd) {
125
144
  const projectDir = getProjectRoutinesDir(cwd);
126
145
  if (projectDir)
127
- dirs.push(projectDir);
146
+ dirs.push({ scope: 'project', path: projectDir });
128
147
  }
129
- dirs.push(getRoutinesDir());
130
- dirs.push(getSystemRoutinesDir());
131
- for (const dir of dirs) {
148
+ dirs.push({ scope: 'user', path: userDir });
149
+ dirs.push({ scope: 'system', path: getSystemRoutinesDir() });
150
+ for (const { scope, path: dir } of dirs) {
132
151
  if (!fs.existsSync(dir))
133
152
  continue;
134
153
  const files = fs.readdirSync(dir).filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
135
154
  for (const file of files) {
136
- const job = readJobFile(path.join(dir, file));
155
+ let job = readJobFile(path.join(dir, file));
137
156
  if (!job)
138
157
  continue;
158
+ if (scope === 'project') {
159
+ job = overlayUserRoutineDevices(job, readJobFromDir(userDir, job.name));
160
+ }
139
161
  if (seen.has(job.name))
140
162
  continue;
141
163
  seen.add(job.name);
@@ -146,26 +168,39 @@ export function listJobs(cwd) {
146
168
  }
147
169
  /**
148
170
  * Read a single job config by name, checking project > user > system.
171
+ * Same-name project routines keep the user-layer `devices` allowlist only when
172
+ * the project routine does not declare its own allowlist, for the same reason
173
+ * as listJobs().
149
174
  * Project discovery is opt-in via `cwd`; daemon callers pass no argument and
150
175
  * resolve user + system routines (a user routine of the same name shadows a
151
176
  * built-in system routine).
152
177
  */
153
178
  export function readJob(name, cwd) {
154
179
  ensureAgentsDir();
180
+ const userDir = getRoutinesDir();
155
181
  const dirs = [];
156
182
  if (cwd) {
157
183
  const projectDir = getProjectRoutinesDir(cwd);
158
184
  if (projectDir)
159
- dirs.push(projectDir);
160
- }
161
- dirs.push(getRoutinesDir());
162
- dirs.push(getSystemRoutinesDir());
163
- for (const dir of dirs) {
164
- for (const ext of ['.yml', '.yaml']) {
165
- const filePath = safeJoin(dir, name + ext);
166
- if (fs.existsSync(filePath)) {
167
- return readJobFile(filePath);
168
- }
185
+ dirs.push({ scope: 'project', path: projectDir });
186
+ }
187
+ dirs.push({ scope: 'user', path: userDir });
188
+ dirs.push({ scope: 'system', path: getSystemRoutinesDir() });
189
+ for (const { scope, path: dir } of dirs) {
190
+ const job = readJobFromDir(dir, name);
191
+ if (job) {
192
+ if (scope === 'project')
193
+ return overlayUserRoutineDevices(job, readJobFromDir(userDir, name));
194
+ return job;
195
+ }
196
+ }
197
+ return null;
198
+ }
199
+ function readJobFromDir(dir, name) {
200
+ for (const ext of ['.yml', '.yaml']) {
201
+ const filePath = safeJoin(dir, name + ext);
202
+ if (fs.existsSync(filePath)) {
203
+ return readJobFile(filePath);
169
204
  }
170
205
  }
171
206
  return null;
@@ -440,6 +475,12 @@ export function validateTrigger(trigger) {
440
475
  if (github.branch !== undefined && typeof github.branch !== 'string') {
441
476
  errors.push('trigger.branch must be a string');
442
477
  }
478
+ if (github.action !== undefined && typeof github.action !== 'string') {
479
+ errors.push('trigger.action must be a string');
480
+ }
481
+ if (github.label !== undefined && typeof github.label !== 'string') {
482
+ errors.push('trigger.label must be a string');
483
+ }
443
484
  return errors;
444
485
  }
445
486
  const linear = t;
@@ -25,6 +25,7 @@ export interface RunResult {
25
25
  }
26
26
  /** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
27
27
  export declare function buildJobCommand(config: JobConfig, resolvedPrompt: string): string[];
28
+ export declare function archiveRoutineTranscripts(meta: Pick<RunMeta, 'jobName' | 'runId' | 'agent'>, runDir: string, overlayHome?: string): void;
28
29
  /** Pre-flight version/account selection for a routine job. */
29
30
  export interface RoutineLaunchPlan {
30
31
  /** Ordered attempts: primary first, then same-agent failover accounts. */
@@ -19,13 +19,14 @@ import * as path from 'path';
19
19
  import * as os from 'os';
20
20
  import { resolveJobPrompt, parseTimeout, writeRunMeta, getRunDir, checkJobDeviceEligibility, finalizeRunMeta, } from './routines.js';
21
21
  import { getRunsDir } from './state.js';
22
- import { prepareJobHome, buildSpawnEnv } from './sandbox.js';
22
+ import { prepareJobHome, buildSpawnEnv, getJobHomePath } from './sandbox.js';
23
23
  import { resolveModel, buildReasoningFlags } from './models.js';
24
24
  import { createTimer, maybeRotate, redactPrompt } from './events.js';
25
25
  import { normalizeMode, resolveHeadlessMode, buildExecEnv, detectRateLimit, } from './exec.js';
26
26
  import { loadTask as loadHostTask } from './hosts/tasks.js';
27
27
  import { reconcileTask as reconcileHostTask } from './hosts/reconcile.js';
28
28
  import { backgroundSpawnOptions } from './platform/process.js';
29
+ import { walkForFiles } from './fs-walk.js';
29
30
  import { getBinaryPath, isVersionInstalled, resolveVersion } from './versions.js';
30
31
  import { getConfiguredRunStrategy, resolveRunVersion, rotationFailoverChain, readinessFromCandidate, } from './rotate.js';
31
32
  /** CLI command templates per agent, with {prompt} as a placeholder. */
@@ -36,6 +37,10 @@ const AGENT_COMMANDS = {
36
37
  kimi: ['kimi', '--prompt', '{prompt}', '--output-format', 'stream-json'],
37
38
  droid: ['droid', 'exec', '{prompt}', '-o', 'stream-json'],
38
39
  };
40
+ const ROUTINE_TRANSCRIPT_SPECS = {
41
+ claude: [{ root: ['.claude', 'projects'], ext: '.jsonl' }],
42
+ codex: [{ root: ['.codex', 'sessions'], ext: '.jsonl' }],
43
+ };
39
44
  /** Build the full CLI argv for executing a job, applying mode, model, and permission flags. */
40
45
  export function buildJobCommand(config, resolvedPrompt) {
41
46
  // Workflow branch: delegate to `agents run <workflow>` which handles subagent
@@ -184,6 +189,35 @@ function appendModelAndReasoning(cmd, config) {
184
189
  function generateRunId() {
185
190
  return new Date().toISOString().replace(/[:.]/g, '-');
186
191
  }
192
+ export function archiveRoutineTranscripts(meta, runDir, overlayHome) {
193
+ if (!meta.agent)
194
+ return;
195
+ const specs = ROUTINE_TRANSCRIPT_SPECS[meta.agent];
196
+ if (!specs)
197
+ return;
198
+ const home = overlayHome ?? getJobHomePath(meta.jobName);
199
+ for (const spec of specs) {
200
+ const sourceRoot = path.join(home, ...spec.root);
201
+ if (!fs.existsSync(sourceRoot))
202
+ continue;
203
+ const destRoot = path.join(runDir, 'sessions', meta.agent, spec.root[spec.root.length - 1]);
204
+ for (const sourcePath of walkForFiles(sourceRoot, spec.ext, 100_000)) {
205
+ const rel = path.relative(sourceRoot, sourcePath);
206
+ if (rel.startsWith('..') || path.isAbsolute(rel))
207
+ continue;
208
+ const destPath = path.join(destRoot, rel);
209
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
210
+ try {
211
+ fs.copyFileSync(sourcePath, destPath);
212
+ fs.chmodSync(destPath, 0o600);
213
+ }
214
+ catch {
215
+ // The process already reached a terminal state; a concurrently removed
216
+ // transcript should not rewrite that outcome.
217
+ }
218
+ }
219
+ }
220
+ }
187
221
  /**
188
222
  * Build the argv for a command-mode routine: run the shell string directly
189
223
  * through the platform shell. No agent binary, no rotation, no sandbox.
@@ -533,6 +567,7 @@ export async function executeJob(config, deps) {
533
567
  finalizeRunMeta(meta, loopFailed ? 'failed' : 'completed', loopFailed ? 1 : 0, loopFailed ? { errorMessage: `loop stopped: ${loopResult.stoppedBy}` } : undefined);
534
568
  writeRunMeta(meta);
535
569
  timer.end({ status: meta.status, exitCode: meta.exitCode ?? undefined, runId });
570
+ archiveRoutineTranscripts(meta, runDir, overlayHome);
536
571
  return { meta, reportPath: null };
537
572
  }
538
573
  // Single-shot path: build the command once, then walk the launch chain on
@@ -577,6 +612,7 @@ export async function executeJob(config, deps) {
577
612
  writeRunMeta(meta);
578
613
  timer.end({ status: 'timeout', runId });
579
614
  const reportPath = extractAndSaveReport(stdoutPath, effectiveAgent, runDir);
615
+ archiveRoutineTranscripts(meta, runDir, overlayHome);
580
616
  return { meta, reportPath };
581
617
  }
582
618
  if (attempt.status === 'completed') {
@@ -584,6 +620,7 @@ export async function executeJob(config, deps) {
584
620
  writeRunMeta(meta);
585
621
  timer.end({ status: 'completed', exitCode: 0, runId });
586
622
  const reportPath = extractAndSaveReport(stdoutPath, effectiveAgent, runDir);
623
+ archiveRoutineTranscripts(meta, runDir, overlayHome);
587
624
  return { meta, reportPath };
588
625
  }
589
626
  // Failed — cascade only on rate/usage limit when more chain entries remain.
@@ -608,6 +645,7 @@ export async function executeJob(config, deps) {
608
645
  ...(attempt.error ? { error: attempt.error } : {}),
609
646
  });
610
647
  const reportPath = extractAndSaveReport(stdoutPath, effectiveAgent, runDir);
648
+ archiveRoutineTranscripts(meta, runDir, overlayHome);
611
649
  return { meta, reportPath };
612
650
  }
613
651
  // Unreachable: chain is always non-empty, but keep a safe fallback.
@@ -830,13 +868,32 @@ export async function executeJobDetached(config) {
830
868
  ...backgroundSpawnOptions({ fdStdio: true }),
831
869
  env: spawnEnv,
832
870
  });
871
+ let settled = false;
872
+ const settle = (status, exitCode, errorMessage) => {
873
+ if (settled)
874
+ return;
875
+ settled = true;
876
+ finalizeRunMeta(meta, status, exitCode, errorMessage ? { errorMessage } : undefined);
877
+ writeRunMeta(meta);
878
+ archiveRoutineTranscripts(meta, runDir, overlayHome);
879
+ if (status !== 'timeout')
880
+ extractAndSaveReport(stdoutPath, effectiveAgent, runDir);
881
+ };
882
+ child.on('exit', (code) => {
883
+ const inferred = inferFinalStatusFromLog(stdoutPath, effectiveAgent);
884
+ if (inferred) {
885
+ settle(inferred.status, inferred.exitCode);
886
+ }
887
+ else {
888
+ settle(code === 0 ? 'completed' : 'failed', code ?? 1);
889
+ }
890
+ });
833
891
  child.on('error', (err) => {
834
892
  try {
835
893
  fs.closeSync(stdoutFd);
836
894
  }
837
895
  catch { /* fd already closed */ }
838
- finalizeRunMeta(meta, 'failed', 1, { errorMessage: err.message });
839
- writeRunMeta(meta);
896
+ settle('failed', 1, err.message);
840
897
  process.stderr.write(`[agents] daemon: spawn failed for job "${config.name}": ${err.message}\n`);
841
898
  });
842
899
  child.unref();
@@ -1098,8 +1155,10 @@ export function monitorRunningJobs() {
1098
1155
  if (Number.isFinite(wallClockMs) && wallClockMs > MAX_WALL_CLOCK_MS) {
1099
1156
  finalizeRunMeta(meta, 'timeout', null, { errorMessage: 'exceeded max wall clock' });
1100
1157
  writeRunMeta(meta);
1101
- if (!isCommandRun)
1158
+ if (!isCommandRun) {
1102
1159
  extractAndSaveReport(stdoutPath, meta.agent, runDirPath);
1160
+ archiveRoutineTranscripts(meta, runDirPath);
1161
+ }
1103
1162
  continue;
1104
1163
  }
1105
1164
  if (!isPidOurs(meta.pid, meta.spawnedAt)) {
@@ -1122,8 +1181,10 @@ export function monitorRunningJobs() {
1122
1181
  }
1123
1182
  }
1124
1183
  writeRunMeta(meta);
1125
- if (!isCommandRun)
1184
+ if (!isCommandRun) {
1126
1185
  extractAndSaveReport(stdoutPath, meta.agent, runDirPath);
1186
+ archiveRoutineTranscripts(meta, runDirPath);
1187
+ }
1127
1188
  }
1128
1189
  }
1129
1190
  catch { /* corrupt or unreadable meta.json */ }
@@ -13,13 +13,16 @@
13
13
  * SSH). The item-name scheme is identical, so the only difference is where
14
14
  * bytes land. A file-backed bundle is discovered by the presence of its
15
15
  * metadata item in the file store.
16
+ * - `vault`: a single age-encrypted ~/.agents/vault.age file unlocked by
17
+ * `agents login`; intended for user-managed cross-machine file sync.
16
18
  *
17
- * Cross-machine sync is handled by src/lib/secrets/sync.ts via an explicit
18
- * encrypted export/import flow; the bundle layer is sync-agnostic.
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.
19
22
  */
20
23
  import { type BundleValue, type SecretRef } from './index.js';
21
24
  /** Which store carries a bundle's items. */
22
- export type SecretsBackend = 'keychain' | 'file';
25
+ export type SecretsBackend = 'keychain' | 'file' | 'vault';
23
26
  /**
24
27
  * Discover a bundle's backend by location: a file-backed bundle's metadata
25
28
  * item exists in the encrypted-file store. This is a plain file-existence
@@ -91,10 +94,12 @@ export interface LegacyBundleCandidate {
91
94
  }
92
95
  export declare const BUNDLE_NAME_PATTERN: RegExp;
93
96
  export declare const ENV_KEY_PATTERN: RegExp;
97
+ export declare const BUNDLE_KEY_PATTERN: RegExp;
94
98
  export declare const BUNDLE_META_PREFIX = "agents-cli.bundles.";
95
99
  export declare const RESERVED_ENV_NAMES: Set<string>;
96
100
  export declare function bundleToEnvPrefix(name: string): string;
97
101
  export declare function isReservedEnvName(key: string): boolean;
102
+ export declare function bundleKeyToEnvKey(key: string): string;
98
103
  export declare function isLoaderOrInterpreterEnv(name: string): boolean;
99
104
  export declare function sanitizeProcessEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
100
105
  /** Validate a bundle name against the allowed pattern. Throws on invalid input. */
@@ -140,6 +145,7 @@ export interface WriteBundleOptions {
140
145
  */
141
146
  export declare function shouldEvictAfterBundleWrite(skipRequested: boolean, noAgentEnv: string | undefined, backendOverridden: boolean): boolean;
142
147
  export declare function writeBundle(bundle: SecretsBundle, opts?: WriteBundleOptions): void;
148
+ export declare function writeBundleWithItems(bundle: SecretsBundle, items: Map<string, string>, opts?: WriteBundleOptions): void;
143
149
  export declare function deleteBundle(name: string): boolean;
144
150
  export declare function listBundles(): SecretsBundle[];
145
151
  export interface BundleEntryInfo {
@@ -185,7 +191,14 @@ export interface ResolveBundleOptions {
185
191
  * bundle-level expiry has passed) aborts the run before Touch ID is popped.
186
192
  */
187
193
  allowExpired?: boolean;
194
+ /**
195
+ * `process` projects dotted account keys like `GITHUB_USERNAME.personal` to
196
+ * the shell-safe base env name (`GITHUB_USERNAME`). Direct value lookups and
197
+ * backup/export flows use `storage` to preserve the exact bundle key names.
198
+ */
199
+ keyMode?: 'process' | 'storage';
188
200
  }
201
+ export declare function canCacheResolvedEnv(bundle: SecretsBundle, selectedKeys: Set<string>, keyMode: ResolveBundleOptions['keyMode']): boolean;
189
202
  /**
190
203
  * Apply the --keys subset + expiry gate to an already-resolved snapshot from
191
204
  * the secrets-agent fast-path. The agent stores the FULL bundle env, so a