@phnx-labs/agents-cli 1.22.83 → 1.22.85

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 (291) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +80 -53
  3. package/dist/bootstrap.d.ts +7 -6
  4. package/dist/bootstrap.js +20 -27
  5. package/dist/cli/command-registry.js +5 -1
  6. package/dist/commands/accounts.d.ts +6 -13
  7. package/dist/commands/accounts.js +330 -143
  8. package/dist/commands/apply.js +1 -1
  9. package/dist/commands/auth-mint.d.ts +7 -3
  10. package/dist/commands/auth-mint.js +21 -9
  11. package/dist/commands/auth.js +2 -3
  12. package/dist/commands/browser.js +10 -10
  13. package/dist/commands/daemon.d.ts +7 -3
  14. package/dist/commands/daemon.js +40 -25
  15. package/dist/commands/doctor.js +28 -2
  16. package/dist/commands/exec.js +87 -39
  17. package/dist/commands/fleet-capture.js +21 -2
  18. package/dist/commands/harness-wizard.js +2 -2
  19. package/dist/commands/lease.js +7 -12
  20. package/dist/commands/profiles.d.ts +2 -2
  21. package/dist/commands/profiles.js +20 -13
  22. package/dist/commands/repo.js +3 -3
  23. package/dist/commands/run-account-picker.d.ts +3 -3
  24. package/dist/commands/run-account-picker.js +29 -30
  25. package/dist/commands/secrets-passthrough.d.ts +20 -0
  26. package/dist/commands/secrets-passthrough.js +43 -0
  27. package/dist/commands/sessions-trace.d.ts +11 -0
  28. package/dist/commands/sessions-trace.js +71 -0
  29. package/dist/commands/setup-accounts.d.ts +3 -2
  30. package/dist/commands/setup-accounts.js +4 -4
  31. package/dist/commands/setup-secrets.d.ts +16 -21
  32. package/dist/commands/setup-secrets.js +43 -224
  33. package/dist/commands/ssh.js +2 -2
  34. package/dist/commands/sync.js +1 -1
  35. package/dist/commands/update.d.ts +10 -0
  36. package/dist/commands/update.js +43 -40
  37. package/dist/commands/versions.d.ts +11 -11
  38. package/dist/commands/versions.js +131 -83
  39. package/dist/commands/view.d.ts +1 -9
  40. package/dist/commands/view.js +41 -55
  41. package/dist/commands/webhook.js +5 -5
  42. package/dist/commands/workflows.js +2 -1
  43. package/dist/index.d.ts +12 -12
  44. package/dist/index.js +13 -42
  45. package/dist/lib/account-capabilities.d.ts +1 -1
  46. package/dist/lib/account-capabilities.js +1 -1
  47. package/dist/lib/account-catalog.d.ts +121 -6
  48. package/dist/lib/account-catalog.js +421 -7
  49. package/dist/lib/account-registry.d.ts +8 -30
  50. package/dist/lib/account-registry.js +36 -79
  51. package/dist/lib/account-schema.d.ts +1 -1
  52. package/dist/lib/account-schema.js +1 -1
  53. package/dist/lib/accounting/account-pool-collect.js +16 -4
  54. package/dist/lib/accounting/rotate.d.ts +20 -7
  55. package/dist/lib/accounting/rotate.js +89 -13
  56. package/dist/lib/accounting/usage.js +11 -16
  57. package/dist/lib/accounts/add.d.ts +138 -0
  58. package/dist/lib/accounts/add.js +651 -0
  59. package/dist/lib/accounts/migrate.d.ts +117 -0
  60. package/dist/lib/accounts/migrate.js +536 -0
  61. package/dist/lib/accounts/slots.d.ts +13 -0
  62. package/dist/lib/accounts/slots.js +100 -0
  63. package/dist/lib/agent-spec/agents.d.ts +16 -0
  64. package/dist/lib/agent-spec/agents.js +32 -4
  65. package/dist/lib/app-bundle-install.js +5 -4
  66. package/dist/lib/auth-health.d.ts +3 -0
  67. package/dist/lib/auth-health.js +11 -0
  68. package/dist/lib/auth-mint.d.ts +52 -16
  69. package/dist/lib/auth-mint.js +117 -39
  70. package/dist/lib/browser/chrome.d.ts +1 -1
  71. package/dist/lib/browser/chrome.js +5 -5
  72. package/dist/lib/byok-usage.js +3 -3
  73. package/dist/lib/claude-account-token.d.ts +47 -3
  74. package/dist/lib/claude-account-token.js +172 -50
  75. package/dist/lib/cloud/antigravity.js +4 -4
  76. package/dist/lib/cloud/cursor.js +4 -4
  77. package/dist/lib/crabbox/cli.d.ts +1 -1
  78. package/dist/lib/crabbox/cli.js +6 -6
  79. package/dist/lib/crabbox/runtimes.d.ts +3 -3
  80. package/dist/lib/crabbox/runtimes.js +5 -5
  81. package/dist/lib/daemon/account-state-daemon-service.d.ts +37 -1
  82. package/dist/lib/daemon/account-state-daemon-service.js +199 -3
  83. package/dist/lib/daemon/auth-sync-service.js +28 -1
  84. package/dist/lib/daemon/daemon.d.ts +6 -16
  85. package/dist/lib/daemon/daemon.js +52 -97
  86. package/dist/lib/daemon/daemon.test-fixture.d.ts +7 -9
  87. package/dist/lib/daemon/daemon.test-fixture.js +22 -40
  88. package/dist/lib/daemon/harness-update-service.d.ts +1 -1
  89. package/dist/lib/daemon/harness-update-service.js +1 -1
  90. package/dist/lib/daemon/runner.d.ts +15 -3
  91. package/dist/lib/daemon/runner.js +49 -24
  92. package/dist/lib/daemon-health.d.ts +1 -2
  93. package/dist/lib/daemon-health.js +7 -6
  94. package/dist/lib/daemon-services.d.ts +1 -1
  95. package/dist/lib/daemon-services.js +1 -11
  96. package/dist/lib/daemon-ticks.js +23 -2
  97. package/dist/lib/daemon-webhooks.d.ts +8 -7
  98. package/dist/lib/daemon-webhooks.js +10 -9
  99. package/dist/lib/device-config.js +1 -1
  100. package/dist/lib/devices/doctor-findings.d.ts +1 -1
  101. package/dist/lib/devices/harness-inventory.d.ts +39 -0
  102. package/dist/lib/devices/harness-inventory.js +126 -4
  103. package/dist/lib/doctor-diff.js +2 -1
  104. package/dist/lib/exec-account-home.d.ts +38 -0
  105. package/dist/lib/exec-account-home.js +164 -0
  106. package/dist/lib/exec.d.ts +30 -0
  107. package/dist/lib/exec.js +101 -31
  108. package/dist/lib/feed/activity-stream.d.ts +60 -0
  109. package/dist/lib/feed/activity-stream.js +271 -0
  110. package/dist/lib/feed/activity.d.ts +7 -0
  111. package/dist/lib/feed/activity.js +10 -4
  112. package/dist/lib/feed/watch.d.ts +9 -3
  113. package/dist/lib/feed/watch.js +72 -36
  114. package/dist/lib/fleet/apply.d.ts +2 -2
  115. package/dist/lib/fleet/apply.js +4 -4
  116. package/dist/lib/fleet/auth-sync.js +4 -3
  117. package/dist/lib/fleet-shared-repo-sync.js +6 -9
  118. package/dist/lib/fleet-shared-state.d.ts +6 -0
  119. package/dist/lib/harness/adapter.d.ts +7 -7
  120. package/dist/lib/harness/adapter.js +10 -6
  121. package/dist/lib/harness/adapters/grok.js +8 -3
  122. package/dist/lib/harness/adapters/muse.js +1 -1
  123. package/dist/lib/harness/adapters/opencode.js +12 -3
  124. package/dist/lib/harness-auth-capabilities.d.ts +61 -0
  125. package/dist/lib/harness-auth-capabilities.js +52 -0
  126. package/dist/lib/helper-versions.d.ts +6 -4
  127. package/dist/lib/helper-versions.js +5 -4
  128. package/dist/lib/hosts/credential-transport.d.ts +10 -0
  129. package/dist/lib/hosts/credential-transport.js +37 -0
  130. package/dist/lib/hosts/dispatch.d.ts +7 -0
  131. package/dist/lib/hosts/dispatch.js +13 -6
  132. package/dist/lib/identity/client.d.ts +3 -3
  133. package/dist/lib/identity/client.js +3 -3
  134. package/dist/lib/installations/index.d.ts +1 -1
  135. package/dist/lib/installations/index.js +1 -1
  136. package/dist/lib/installations/migrate.js +9 -0
  137. package/dist/lib/installations/resolve.js +1 -1
  138. package/dist/lib/installations/shims.d.ts +15 -0
  139. package/dist/lib/installations/shims.js +69 -0
  140. package/dist/lib/installations/store.d.ts +43 -0
  141. package/dist/lib/installations/store.js +75 -7
  142. package/dist/lib/installations/versions.js +1 -1
  143. package/dist/lib/menubar/install-menubar.d.ts +3 -3
  144. package/dist/lib/menubar/install-menubar.js +3 -3
  145. package/dist/lib/native-accounts.d.ts +35 -0
  146. package/dist/lib/native-accounts.js +41 -0
  147. package/dist/lib/net-close.d.ts +11 -0
  148. package/dist/lib/net-close.js +25 -0
  149. package/dist/lib/openclaw-keychain.js +27 -1
  150. package/dist/lib/profiles.js +22 -9
  151. package/dist/lib/project-resources.js +1 -1
  152. package/dist/lib/reserved-stores.d.ts +53 -0
  153. package/dist/lib/reserved-stores.js +120 -0
  154. package/dist/lib/secrets-client.d.ts +191 -0
  155. package/dist/lib/secrets-client.js +710 -0
  156. package/dist/lib/secrets-policy.d.ts +222 -0
  157. package/dist/lib/secrets-policy.js +483 -0
  158. package/dist/lib/secrets-types.d.ts +188 -0
  159. package/dist/lib/secrets-types.js +24 -0
  160. package/dist/lib/service-manifest.js +3 -4
  161. package/dist/lib/session/active.d.ts +38 -4
  162. package/dist/lib/session/active.js +36 -4
  163. package/dist/lib/session/bash-command.js +10 -0
  164. package/dist/lib/session/db.d.ts +65 -2
  165. package/dist/lib/session/db.js +178 -18
  166. package/dist/lib/session/mirror.d.ts +5 -0
  167. package/dist/lib/session/mirror.js +166 -0
  168. package/dist/lib/session/parse.d.ts +49 -0
  169. package/dist/lib/session/parse.js +324 -30
  170. package/dist/lib/session/prompt.d.ts +93 -2
  171. package/dist/lib/session/prompt.js +266 -15
  172. package/dist/lib/session/remote/peer-stream.d.ts +47 -0
  173. package/dist/lib/session/remote/peer-stream.js +142 -0
  174. package/dist/lib/session/remote/watch.d.ts +1 -1
  175. package/dist/lib/session/remote/watch.js +41 -42
  176. package/dist/lib/session/session-cache.d.ts +9 -0
  177. package/dist/lib/session/session-cache.js +40 -2
  178. package/dist/lib/session/sync/config.js +2 -2
  179. package/dist/lib/session/timeline-pass.d.ts +129 -0
  180. package/dist/lib/session/timeline-pass.js +323 -0
  181. package/dist/lib/session/timeline.d.ts +182 -0
  182. package/dist/lib/session/timeline.js +636 -0
  183. package/dist/lib/session/types.d.ts +155 -1
  184. package/dist/lib/sha256-asset.d.ts +11 -16
  185. package/dist/lib/sha256-asset.js +11 -16
  186. package/dist/lib/share/config.js +14 -5
  187. package/dist/lib/signin-badge.d.ts +26 -0
  188. package/dist/lib/signin-badge.js +42 -0
  189. package/dist/lib/staleness/detectors/workflows.js +3 -105
  190. package/dist/lib/staleness/writers/workflows.js +2 -1
  191. package/dist/lib/state.d.ts +7 -4
  192. package/dist/lib/state.js +18 -7
  193. package/dist/lib/summarizer/pass.d.ts +2 -0
  194. package/dist/lib/summarizer/pass.js +14 -1
  195. package/dist/lib/summarizer/summarize.d.ts +7 -0
  196. package/dist/lib/summarizer/summarize.js +3 -0
  197. package/dist/lib/sync-umbrella.js +2 -2
  198. package/dist/lib/teams/agents.js +1 -1
  199. package/dist/lib/types.d.ts +74 -33
  200. package/dist/lib/view-types.d.ts +6 -3
  201. package/dist/lib/workflows-registry.d.ts +71 -0
  202. package/dist/lib/workflows-registry.js +280 -0
  203. package/dist/lib/workflows.d.ts +21 -21
  204. package/dist/lib/workflows.js +12 -327
  205. package/package.json +2 -3
  206. package/scripts/postinstall.js +4 -29
  207. package/dist/commands/secrets-import.d.ts +0 -18
  208. package/dist/commands/secrets-import.js +0 -74
  209. package/dist/commands/secrets-migrate.d.ts +0 -25
  210. package/dist/commands/secrets-migrate.js +0 -334
  211. package/dist/commands/secrets-rotate-passphrase.d.ts +0 -17
  212. package/dist/commands/secrets-rotate-passphrase.js +0 -96
  213. package/dist/commands/secrets-sync.d.ts +0 -11
  214. package/dist/commands/secrets-sync.js +0 -153
  215. package/dist/commands/secrets-vault.d.ts +0 -10
  216. package/dist/commands/secrets-vault.js +0 -130
  217. package/dist/commands/secrets.d.ts +0 -212
  218. package/dist/commands/secrets.js +0 -3030
  219. package/dist/lib/accounts/connect.d.ts +0 -170
  220. package/dist/lib/accounts/connect.js +0 -383
  221. package/dist/lib/daemon/keychain-reap-service.d.ts +0 -17
  222. package/dist/lib/daemon/keychain-reap-service.js +0 -32
  223. package/dist/lib/daemon/secrets-broker-service.d.ts +0 -21
  224. package/dist/lib/daemon/secrets-broker-service.js +0 -51
  225. package/dist/lib/secrets/agent.d.ts +0 -358
  226. package/dist/lib/secrets/agent.js +0 -1291
  227. package/dist/lib/secrets/audit.d.ts +0 -46
  228. package/dist/lib/secrets/audit.js +0 -101
  229. package/dist/lib/secrets/bundles.d.ts +0 -290
  230. package/dist/lib/secrets/bundles.js +0 -1547
  231. package/dist/lib/secrets/download-keychain.d.ts +0 -47
  232. package/dist/lib/secrets/download-keychain.js +0 -70
  233. package/dist/lib/secrets/drivers/rush.d.ts +0 -14
  234. package/dist/lib/secrets/drivers/rush.js +0 -90
  235. package/dist/lib/secrets/fallback.d.ts +0 -48
  236. package/dist/lib/secrets/fallback.js +0 -48
  237. package/dist/lib/secrets/filestore.d.ts +0 -222
  238. package/dist/lib/secrets/filestore.js +0 -1099
  239. package/dist/lib/secrets/headless.d.ts +0 -43
  240. package/dist/lib/secrets/headless.js +0 -56
  241. package/dist/lib/secrets/icloud-import.d.ts +0 -79
  242. package/dist/lib/secrets/icloud-import.js +0 -206
  243. package/dist/lib/secrets/index.d.ts +0 -451
  244. package/dist/lib/secrets/index.js +0 -1568
  245. package/dist/lib/secrets/install-helper.d.ts +0 -72
  246. package/dist/lib/secrets/install-helper.js +0 -245
  247. package/dist/lib/secrets/lease.d.ts +0 -25
  248. package/dist/lib/secrets/lease.js +0 -44
  249. package/dist/lib/secrets/linux.d.ts +0 -77
  250. package/dist/lib/secrets/linux.js +0 -393
  251. package/dist/lib/secrets/list-filter.d.ts +0 -109
  252. package/dist/lib/secrets/list-filter.js +0 -261
  253. package/dist/lib/secrets/mcp.d.ts +0 -93
  254. package/dist/lib/secrets/mcp.js +0 -211
  255. package/dist/lib/secrets/profiles.d.ts +0 -10
  256. package/dist/lib/secrets/profiles.js +0 -13
  257. package/dist/lib/secrets/push.d.ts +0 -133
  258. package/dist/lib/secrets/push.js +0 -273
  259. package/dist/lib/secrets/rc-hygiene.d.ts +0 -63
  260. package/dist/lib/secrets/rc-hygiene.js +0 -143
  261. package/dist/lib/secrets/read-backoff.d.ts +0 -27
  262. package/dist/lib/secrets/read-backoff.js +0 -64
  263. package/dist/lib/secrets/reaper.d.ts +0 -90
  264. package/dist/lib/secrets/reaper.js +0 -243
  265. package/dist/lib/secrets/remote.d.ts +0 -152
  266. package/dist/lib/secrets/remote.js +0 -344
  267. package/dist/lib/secrets/reserved-sync.d.ts +0 -66
  268. package/dist/lib/secrets/reserved-sync.js +0 -147
  269. package/dist/lib/secrets/scope.d.ts +0 -26
  270. package/dist/lib/secrets/scope.js +0 -29
  271. package/dist/lib/secrets/session-store.d.ts +0 -107
  272. package/dist/lib/secrets/session-store.js +0 -342
  273. package/dist/lib/secrets/sync-backend.d.ts +0 -48
  274. package/dist/lib/secrets/sync-backend.js +0 -13
  275. package/dist/lib/secrets/sync-commands.d.ts +0 -21
  276. package/dist/lib/secrets/sync-commands.js +0 -21
  277. package/dist/lib/secrets/sync.d.ts +0 -48
  278. package/dist/lib/secrets/sync.js +0 -237
  279. package/dist/lib/secrets/unlock-hints.d.ts +0 -27
  280. package/dist/lib/secrets/unlock-hints.js +0 -36
  281. package/dist/lib/secrets/usage-db.d.ts +0 -46
  282. package/dist/lib/secrets/usage-db.js +0 -96
  283. package/dist/lib/secrets/vault-age-helper.d.ts +0 -1
  284. package/dist/lib/secrets/vault-age-helper.js +0 -34
  285. package/dist/lib/secrets/vault.d.ts +0 -49
  286. package/dist/lib/secrets/vault.js +0 -399
  287. package/dist/lib/secrets/windows.d.ts +0 -81
  288. package/dist/lib/secrets/windows.js +0 -556
  289. package/scripts/install-helper.js +0 -97
  290. /package/dist/lib/{secrets/sync-passphrase.d.ts → sync-passphrase.d.ts} +0 -0
  291. /package/dist/lib/{secrets/sync-passphrase.js → sync-passphrase.js} +0 -0
@@ -43,7 +43,13 @@ export declare function sessionDisplayAgent(session: {
43
43
  }): string;
44
44
  /** A single normalized event within a session (message, tool call, thinking, etc.). */
45
45
  export interface SessionEvent {
46
- type: 'message' | 'tool_use' | 'tool_result' | 'thinking' | 'error' | 'init' | 'result' | 'usage' | 'attachment' | 'hook' | 'interrupt';
46
+ /**
47
+ * `file_change` is the harness's own file ledger (Claude `file-history-delta`,
48
+ * Codex `FileChange`, OpenCode `patch`) rather than a tool call. It is emitted
49
+ * only on request (`includeFileHistory`) or by the Codex item reader, never in
50
+ * the default published stream — see `ParseSessionOptions.includeFileHistory`.
51
+ */
52
+ type: 'message' | 'tool_use' | 'tool_result' | 'thinking' | 'error' | 'init' | 'result' | 'usage' | 'attachment' | 'hook' | 'interrupt' | 'file_change';
47
53
  agent: SessionAgentId;
48
54
  timestamp: string;
49
55
  role?: 'user' | 'assistant';
@@ -57,6 +63,13 @@ export interface SessionEvent {
57
63
  success?: boolean;
58
64
  /** Structured harness outcome; never inferred from free-text output. */
59
65
  outcome?: 'ok' | 'error' | 'unknown';
66
+ /**
67
+ * The call never RAN: a permission rule or hook denied it (Claude
68
+ * `toolDenialKind`), or the turn was aborted. Distinct from a failure — a
69
+ * blocked call says nothing about whether the work would have succeeded, so
70
+ * the timeline counts the two separately.
71
+ */
72
+ blocked?: boolean;
60
73
  exitCode?: number;
61
74
  statusCode?: number;
62
75
  errorCode?: string;
@@ -93,6 +106,35 @@ export interface SessionEvent {
93
106
  * programmatically, not the user). Undefined for every other event.
94
107
  */
95
108
  slashCommand?: string;
109
+ /**
110
+ * The label the HARNESS itself wrote for this call — Claude's Bash
111
+ * `input.description` ("Locate the prior session transcript on disk"), Kimi's
112
+ * `tool.call.description`, OpenCode's `state.title` / `state.input.description`,
113
+ * Codex's `CommandExecution.command`. Human-written, one per call, and exactly
114
+ * what a now-line needs; `summarizeToolUse` renders the mechanical form
115
+ * instead, which is why this is kept separately rather than folded into it.
116
+ */
117
+ label?: string;
118
+ /**
119
+ * Verb class the HARNESS assigned (Codex `parsed_cmd.type`, a typed tool
120
+ * name). Absent when the harness does not classify its own call — then the
121
+ * consumer derives one from `command`.
122
+ */
123
+ verbClass?: SessionVerbClass;
124
+ /**
125
+ * Per-path file operations the harness recorded for this call (Codex
126
+ * `FileChange.changes`, OpenCode `patch.files`). The exact edit ledger, as
127
+ * opposed to inferring one from Edit/Write arguments.
128
+ */
129
+ changes?: Array<{
130
+ path: string;
131
+ op: 'created' | 'modified' | 'deleted';
132
+ }>;
133
+ /**
134
+ * Codex tags an `AgentMessage` with the role it played in the turn
135
+ * (`commentary` is the narration between tool calls). Absent elsewhere.
136
+ */
137
+ phase?: string;
96
138
  }
97
139
  /** A displayable file attachment discovered in a session transcript. */
98
140
  export interface SessionAttachment {
@@ -148,6 +190,110 @@ export interface SessionChecklistItem {
148
190
  * left no summary; the row deliberately carries no goal/checkpoints.
149
191
  */
150
192
  export type SummaryState = 'pending' | 'ready' | 'skipped';
193
+ /**
194
+ * How a tool call is bucketed on a timeline step (PHNX-3939). Coarse on purpose
195
+ * — a step renders as "6 run · 2 blocked", not as a tool histogram. Taken from
196
+ * the harness when it classifies its own call (Codex `parsed_cmd.type`, a
197
+ * harness tool name) and derived from the command otherwise.
198
+ */
199
+ export type SessionVerbClass = 'read' | 'edit' | 'run' | 'git' | 'test' | 'browser' | 'agent' | 'other';
200
+ /**
201
+ * One step of the narration-anchored timeline (PHNX-3939): the agent's own
202
+ * short line about what it is doing, plus the tool calls that ran under it.
203
+ *
204
+ * The text is never model-written — it is the harness's own narration (Claude
205
+ * assistant text, Codex `AgentMessage` commentary, Gemini thought subject), a
206
+ * thinking block when there is no narration, the user's tidied turn, or a
207
+ * mix-derived line when tools ran with nothing said. `source` says which.
208
+ */
209
+ export interface SessionStep {
210
+ /** Headline: the first sentence of the narration, capped. */
211
+ text: string;
212
+ /** ISO time this step opened. */
213
+ at: string;
214
+ /** ISO time of the last event folded into it. */
215
+ endedAt?: string;
216
+ source: 'narration' | 'thinking' | 'derived' | 'user';
217
+ /** Tool calls attached to this step. */
218
+ tools: number;
219
+ /** Calls that RAN and failed (non-zero exit, `is_error`, harness error status). */
220
+ failed: number;
221
+ /** Calls that never ran: a permission rule, a hook denial, an aborted turn. */
222
+ blocked: number;
223
+ /** Per-verb-class tally of the attached calls. */
224
+ mix?: Partial<Record<SessionVerbClass, number>>;
225
+ /** Milestones recognized under this step ("worktree created", "PR opened", "files changed"). */
226
+ marks?: string[];
227
+ /** True on the newest step while the session is working. */
228
+ live?: boolean;
229
+ /** Harness label of the running tool on a live step — the now-line. */
230
+ now?: string;
231
+ }
232
+ /**
233
+ * The bounded projection of a session's timeline that rides the row (PHNX-3939).
234
+ *
235
+ * `steps` is the tail; everything before it collapses into `earlier` so the
236
+ * payload stays flat as a session grows. `state` is honest about harnesses that
237
+ * cannot supply one: `partial` when an event cap was hit, `unavailable` with a
238
+ * `reason` when the harness writes no parseable transcript (OpenClaw) — never
239
+ * an empty step list presented as "nothing happened".
240
+ */
241
+ export interface SessionTimeline {
242
+ /** Newest last, at most `keep` (8). */
243
+ steps: SessionStep[];
244
+ earlier: {
245
+ steps: number;
246
+ tools: number;
247
+ failed: number;
248
+ };
249
+ /** Totals across the WHOLE session, not just `steps`. */
250
+ tools: number;
251
+ failed: number;
252
+ blocked: number;
253
+ spanMs: number;
254
+ state: 'ready' | 'partial' | 'unavailable';
255
+ reason?: string;
256
+ }
257
+ /**
258
+ * The session's operative request: the latest genuine user turn, tidied but
259
+ * never rewritten (PHNX-3939). `turns` counts every genuine user turn so a
260
+ * consumer can say "latest of 6"; `continues` names the session a `/continue`
261
+ * picked up.
262
+ */
263
+ export type SessionRequest = import('./prompt.js').TidyRequest & {
264
+ /**
265
+ * Genuine user turns in the session so far, so a consumer can say "latest of
266
+ * 6". Present only when the daemon's timeline fold counted them; a request
267
+ * tidied inline from the indexed turn alone does not know the count and says
268
+ * nothing rather than claiming 1.
269
+ */
270
+ turns?: number;
271
+ continues?: {
272
+ id: string;
273
+ label?: string;
274
+ };
275
+ };
276
+ /** One file the session created, modified or deleted. */
277
+ export interface SessionFileChange {
278
+ path: string;
279
+ op: 'created' | 'modified' | 'deleted';
280
+ /** How many times the session touched it. */
281
+ edits: number;
282
+ /** ISO time of the last touch. */
283
+ at: string;
284
+ }
285
+ /**
286
+ * The files a session changed (PHNX-3939). `source: 'harness'` means the
287
+ * harness recorded the change itself (Codex `FileChange`, OpenCode `patch`,
288
+ * Claude `file-history-delta`); `'tools'` means it was derived from Edit/Write
289
+ * calls because the harness records no file ledger. `changes` is bounded to 8
290
+ * rows; `total` is the real count.
291
+ */
292
+ export interface SessionFiles {
293
+ changes: SessionFileChange[];
294
+ total: number;
295
+ source: 'harness' | 'tools';
296
+ }
151
297
  /** Metadata attached when a session was spawned by `agents teams`. */
152
298
  export interface TeamOrigin {
153
299
  /** Teammate name if set, otherwise first 8 chars of the agent UUID. */
@@ -256,6 +402,14 @@ export interface SessionMeta {
256
402
  topic?: string;
257
403
  /** Full, cleaned first genuine user turn; distinct from the one-line topic. */
258
404
  firstUserMessage?: string;
405
+ /**
406
+ * Full, cleaned LATEST genuine user turn. For a `/continue`, a redirect, or an
407
+ * interrupted-and-restated session the operative request is the last thing the
408
+ * user said, not the first — which is what the session row and the sidebar's
409
+ * Request card show (PHNX-3939). Equal to {@link firstUserMessage} for a
410
+ * single-turn session; absent for rows indexed before this field shipped.
411
+ */
412
+ lastUserMessage?: string;
259
413
  /**
260
414
  * The session's human-readable name — one field, several sources with a plain
261
415
  * priority: an agent-generated title / Claude `/rename` wins; else the launch
@@ -3,22 +3,17 @@
3
3
  *
4
4
  * These live in their own LEAF module — importing only `node:crypto` and
5
5
  * `node:fs` — on purpose. They used to sit in `computer/ssh-tunnel.ts`, whose
6
- * own import graph reaches `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
- * `secrets/*`. `helper-download.ts` needs nothing from that graph but these two
8
- * pure functions, and importing them from there closed a module-initialization
9
- * cycle:
10
- *
11
- * helper-download.ts
12
- * -> computer/ssh-tunnel.ts (evaluated BEFORE `EXPECTED_TEAM_ID`)
13
- * -> browser/drivers/ssh.ts -> browser/chrome.ts
14
- * -> secrets/bundles.ts -> secrets/index.ts -> secrets/install-helper.ts
15
- * -> secrets/download-keychain.ts
16
- * -> helper-download.ts (still evaluating; const not yet bound)
17
- *
18
- * which threw `ReferenceError: Cannot access 'EXPECTED_TEAM_ID' before
19
- * initialization` at `secrets/download-keychain.ts:45` for any entry point that
20
- * reached `helper-download.ts` first (RUSH-3113). Keep this module a leaf: adding
21
- * a local import here can reintroduce that cycle.
6
+ * own import graph reached `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
+ * the in-repo secrets engine's own keychain-helper downloader, which imported
8
+ * back into `helper-download.ts` while it was still evaluating — before
9
+ * `EXPECTED_TEAM_ID` was bound, throwing `ReferenceError: Cannot access
10
+ * 'EXPECTED_TEAM_ID' before initialization` for any entry point that reached
11
+ * `helper-download.ts` first (RUSH-3113). The secrets engine that closed that
12
+ * cycle is gone now (PHNX-3989 — the standalone `secrets` CLI downloads its own
13
+ * helper), but the discipline that fixed it still holds: keep this module a
14
+ * leaf, since `helper-download.ts` needs nothing beyond these two pure
15
+ * functions and adding a local import here can reintroduce a cycle with
16
+ * whatever imports it next.
22
17
  */
23
18
  /** Pull the digest out of a `<sha256> <filename>` release asset. */
24
19
  export declare function parseSha256Asset(text: string): string;
@@ -3,22 +3,17 @@
3
3
  *
4
4
  * These live in their own LEAF module — importing only `node:crypto` and
5
5
  * `node:fs` — on purpose. They used to sit in `computer/ssh-tunnel.ts`, whose
6
- * own import graph reaches `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
- * `secrets/*`. `helper-download.ts` needs nothing from that graph but these two
8
- * pure functions, and importing them from there closed a module-initialization
9
- * cycle:
10
- *
11
- * helper-download.ts
12
- * -> computer/ssh-tunnel.ts (evaluated BEFORE `EXPECTED_TEAM_ID`)
13
- * -> browser/drivers/ssh.ts -> browser/chrome.ts
14
- * -> secrets/bundles.ts -> secrets/index.ts -> secrets/install-helper.ts
15
- * -> secrets/download-keychain.ts
16
- * -> helper-download.ts (still evaluating; const not yet bound)
17
- *
18
- * which threw `ReferenceError: Cannot access 'EXPECTED_TEAM_ID' before
19
- * initialization` at `secrets/download-keychain.ts:45` for any entry point that
20
- * reached `helper-download.ts` first (RUSH-3113). Keep this module a leaf: adding
21
- * a local import here can reintroduce that cycle.
6
+ * own import graph reached `browser/drivers/ssh.ts` -> `browser/chrome.ts` ->
7
+ * the in-repo secrets engine's own keychain-helper downloader, which imported
8
+ * back into `helper-download.ts` while it was still evaluating — before
9
+ * `EXPECTED_TEAM_ID` was bound, throwing `ReferenceError: Cannot access
10
+ * 'EXPECTED_TEAM_ID' before initialization` for any entry point that reached
11
+ * `helper-download.ts` first (RUSH-3113). The secrets engine that closed that
12
+ * cycle is gone now (PHNX-3989 — the standalone `secrets` CLI downloads its own
13
+ * helper), but the discipline that fixed it still holds: keep this module a
14
+ * leaf, since `helper-download.ts` needs nothing beyond these two pure
15
+ * functions and adding a local import here can reintroduce a cycle with
16
+ * whatever imports it next.
22
17
  */
23
18
  import { createHash } from 'node:crypto';
24
19
  import * as fs from 'node:fs';
@@ -9,8 +9,7 @@
9
9
  // `cloudflare` bundle.
10
10
  import { randomBytes } from 'node:crypto';
11
11
  import { readMeta, updateMeta } from '../state.js';
12
- import { bundleExists, bundleItemStore, bundlePolicy, keychainRef, readAndResolveBundleEnv, readBundle, writeBundle, } from '../secrets/bundles.js';
13
- import { secretsKeychainItem } from '../secrets/index.js';
12
+ import { bundleExistsSync as bundleExists, keychainRef, readAndResolveBundleEnvSync as readAndResolveBundleEnv, readBundleSync as readBundle, secretsKeychainItem, writeBundleWithItemsSync as writeBundleWithItems, } from '../secrets-client.js';
14
13
  export const SHARE_BUNDLE = 'share';
15
14
  export const SHARE_TOKEN_KEY = 'WRITE_TOKEN';
16
15
  export const SHARE_TOKEN_ENV_KEY = 'SHARE_WRITE_TOKEN';
@@ -113,10 +112,12 @@ export function storeWriteToken(token) {
113
112
  vars: {},
114
113
  };
115
114
  }
116
- const store = bundleItemStore(bundle.backend, { noAcl: bundlePolicy(bundle) === 'never' });
117
- store.set(secretsKeychainItem(bundle.name, SHARE_TOKEN_KEY), token);
115
+ // writeBundleWithItems decides the ACL (keychain backend) purely from
116
+ // `bundle.policy === 'never'` — the fresh-bundle case above sets it
117
+ // explicitly, and an existing bundle already carries its own explicit tier.
118
118
  bundle.vars[SHARE_TOKEN_KEY] = keychainRef(SHARE_TOKEN_KEY);
119
- writeBundle(bundle);
119
+ const item = secretsKeychainItem(bundle.name, SHARE_TOKEN_KEY);
120
+ writeBundleWithItems(bundle, new Map([[item, token]]));
120
121
  }
121
122
  /** Read the raw write token from the `share` secrets bundle. */
122
123
  export function readWriteTokenFromBundle() {
@@ -178,6 +179,14 @@ export function readCloudflareCreds(bundle = DEFAULT_CF_BUNDLE, override) {
178
179
  if (override?.apiToken) {
179
180
  return { apiToken: override.apiToken, accountId: override.accountId ?? '' };
180
181
  }
182
+ // Check existence first (same guard as readShareToken above): resolving a
183
+ // missing bundle through the process client surfaces an opaque transport code
184
+ // (e.g. LOCKED), never naming the bundle the user typed — so name it here.
185
+ if (!bundleExists(bundle)) {
186
+ throw new Error(`The '${bundle}' bundle does not exist. ` +
187
+ `Pass credentials directly with --token <t> [--account <id>], or create it: ` +
188
+ `agents secrets add ${bundle} CLOUDFLARE_API_TOKEN`);
189
+ }
181
190
  const { env } = readAndResolveBundleEnv(bundle, {
182
191
  caller: 'share',
183
192
  // Setup is still a read; only `agents secrets unlock` may authenticate.
@@ -1,5 +1,8 @@
1
1
  import type { AccountInfo } from './agents.js';
2
+ import type { AuthVerdict } from './auth-health.js';
2
3
  import type { AgentId } from './types.js';
4
+ export type AccountVerdict = Exclude<AuthVerdict, 'unconfigured' | 'error'> | 'missing' | 'per-device' | 'ready';
5
+ export type AccountProvisioning = 'portable' | 'per-device';
3
6
  /**
4
7
  * The exact command that logs a given agent in — for warn banners and nudges.
5
8
  * Driven off the registry `cliCommand` with the per-agent subcommand overrides
@@ -8,6 +11,29 @@ import type { AgentId } from './types.js';
8
11
  * remaining agents (kimi, gemini, …) start their device/oauth flow on launch.
9
12
  */
10
13
  export declare function loginHint(agentId: AgentId): string;
14
+ /**
15
+ * Exact action shown beside a non-live account. Every emitted command exists
16
+ * today — never a planned surface and never a hidden verb:
17
+ * - Per-device harnesses repair per box via `agents devices login`.
18
+ * - Named accounts re-auth through `agents accounts login <harness>#<name>`.
19
+ * A known account with no slot on a headed device is onboarded with
20
+ * `agents accounts add <harness> <name>`. A worker never runs an
21
+ * interactive login — the hint is `add.ts`'s worker refusal (add on the
22
+ * personal device; this box is provisioned from the durable credential).
23
+ * - Unnamed legacy homes use the same version-targeted command shape as
24
+ * doctor, so the hint never logs a different/default home in by accident.
25
+ * - `unverified` emits nothing: the probe could not confirm state (e.g. a
26
+ * worker whose token lacks the usage scope), so there is nothing to repair.
27
+ */
28
+ export declare function fixFor(input: {
29
+ agent: AgentId;
30
+ verdict: AccountVerdict;
31
+ name?: string | null;
32
+ version?: string | null;
33
+ provisioning?: AccountProvisioning;
34
+ /** When false, this named account has no slot on this device yet. */
35
+ hasSlot?: boolean;
36
+ }): string | null;
11
37
  /**
12
38
  * Whether `agents run` should probe login state before launching. True only for
13
39
  * a launch that actually opens the interactive TUI — where discovering a logged-out
@@ -9,7 +9,9 @@
9
9
  * on it (the run preflight) WARN and continue; they never block.
10
10
  */
11
11
  import chalk from 'chalk';
12
+ import { addWorkerRefusal } from './accounts/add.js';
12
13
  import { AGENTS } from './agents.js';
14
+ import { CONFIG_ENV_ISOLATED_AGENTS } from './installations/shims.js';
13
15
  /**
14
16
  * The exact command that logs a given agent in — for warn banners and nudges.
15
17
  * Driven off the registry `cliCommand` with the per-agent subcommand overrides
@@ -34,6 +36,46 @@ export function loginHint(agentId) {
34
36
  return cli;
35
37
  }
36
38
  }
39
+ /**
40
+ * Exact action shown beside a non-live account. Every emitted command exists
41
+ * today — never a planned surface and never a hidden verb:
42
+ * - Per-device harnesses repair per box via `agents devices login`.
43
+ * - Named accounts re-auth through `agents accounts login <harness>#<name>`.
44
+ * A known account with no slot on a headed device is onboarded with
45
+ * `agents accounts add <harness> <name>`. A worker never runs an
46
+ * interactive login — the hint is `add.ts`'s worker refusal (add on the
47
+ * personal device; this box is provisioned from the durable credential).
48
+ * - Unnamed legacy homes use the same version-targeted command shape as
49
+ * doctor, so the hint never logs a different/default home in by accident.
50
+ * - `unverified` emits nothing: the probe could not confirm state (e.g. a
51
+ * worker whose token lacks the usage scope), so there is nothing to repair.
52
+ */
53
+ export function fixFor(input) {
54
+ const { agent, verdict } = input;
55
+ if (verdict === 'live' || verdict === 'rate_limited' || verdict === 'unverified' || verdict === 'ready')
56
+ return null;
57
+ if (input.provisioning === 'per-device' || verdict === 'per-device') {
58
+ return `agents devices login --agents ${agent}`;
59
+ }
60
+ if (input.name) {
61
+ const worker = addWorkerRefusal(agent, input.name);
62
+ if (worker)
63
+ return worker;
64
+ if (input.hasSlot === false)
65
+ return `agents accounts add ${agent} ${input.name}`;
66
+ return `agents accounts login ${agent}#${input.name}`;
67
+ }
68
+ const version = input.version ?? null;
69
+ if (!version || !CONFIG_ENV_ISOLATED_AGENTS.includes(agent))
70
+ return loginHint(agent);
71
+ if (agent === 'claude')
72
+ return `agents run ${agent}@${version}, then /login`;
73
+ if (agent === 'codex' || agent === 'grok')
74
+ return `agents run ${agent}@${version} -- login`;
75
+ if (agent === 'opencode')
76
+ return `agents run ${agent}@${version} -- auth login`;
77
+ return `agents run ${agent}@${version}`;
78
+ }
37
79
  /**
38
80
  * Whether `agents run` should probe login state before launching. True only for
39
81
  * a launch that actually opens the interactive TUI — where discovering a logged-out
@@ -1,115 +1,13 @@
1
- /**
2
- * Workflows detector — scans the agent-specific on-disk layout for synced
3
- * workflow names (Claude WORKFLOW.md trees, Kimi flow skills, Antigravity
4
- * global_workflows, Goose recipes, OpenClaw Lobster, Grok Rhai).
5
- */
6
- import * as fs from 'fs';
7
- import * as os from 'os';
8
- import * as path from 'path';
9
- import * as yaml from 'yaml';
10
1
  import { capableAgents } from '../../capabilities.js';
11
- import { grokWorkflowMarker } from '../../workflows.js';
2
+ import { workflowTarget } from '../../workflows-registry.js';
12
3
  import { lazyAgentMap } from '../writers/lazy-map.js';
13
4
  function buildWorkflowsDetector(agent) {
14
5
  return {
15
6
  kind: 'workflows',
16
7
  agent,
17
8
  list({ versionHome }) {
18
- if (agent === 'kimi') {
19
- const skillsDir = path.join(versionHome, '.kimi-code', 'skills');
20
- if (!fs.existsSync(skillsDir))
21
- return [];
22
- return fs.readdirSync(skillsDir, { withFileTypes: true })
23
- .filter(d => d.isDirectory() && fs.existsSync(path.join(skillsDir, d.name, 'SKILL.md')))
24
- .filter(d => {
25
- try {
26
- const skill = fs.readFileSync(path.join(skillsDir, d.name, 'SKILL.md'), 'utf-8');
27
- const lines = skill.split('\n');
28
- if (lines[0] !== '---')
29
- return false;
30
- const endIndex = lines.slice(1).findIndex(l => l === '---');
31
- if (endIndex < 0)
32
- return false;
33
- const parsed = yaml.parse(lines.slice(1, endIndex + 1).join('\n'));
34
- return parsed?.type === 'flow' && parsed.agents_workflow === d.name;
35
- }
36
- catch {
37
- return false;
38
- }
39
- })
40
- .map(d => d.name);
41
- }
42
- if (agent === 'antigravity') {
43
- // Antigravity user workflows are HOME-global and shared across versions,
44
- // not version-isolated — see workflows.ts:antigravityWorkflowsDir(). agy
45
- // scans the real ~/.gemini/config/global_workflows/, so the detector reads
46
- // the same shared dir the writer targets (versionHome is intentionally unused).
47
- const dir = path.join(process.env.HOME ?? os.homedir(), '.gemini', 'config', 'global_workflows');
48
- if (!fs.existsSync(dir))
49
- return [];
50
- return fs.readdirSync(dir, { withFileTypes: true })
51
- .filter(d => d.isFile() && d.name.endsWith('.md') && !d.name.startsWith('.'))
52
- .filter(d => {
53
- try {
54
- const content = fs.readFileSync(path.join(dir, d.name), 'utf-8');
55
- const lines = content.split('\n');
56
- if (lines[0] !== '---')
57
- return false;
58
- const endIndex = lines.slice(1).findIndex(l => l === '---');
59
- if (endIndex < 0)
60
- return false;
61
- const parsed = yaml.parse(lines.slice(1, endIndex + 1).join('\n'));
62
- return parsed?.agents_workflow === d.name.slice(0, -'.md'.length);
63
- }
64
- catch {
65
- return false;
66
- }
67
- })
68
- .map(d => d.name.slice(0, -'.md'.length));
69
- }
70
- if (agent === 'goose') {
71
- const recipesDir = path.join(versionHome, '.config', 'goose', 'recipes');
72
- if (!fs.existsSync(recipesDir))
73
- return [];
74
- return fs.readdirSync(recipesDir, { withFileTypes: true })
75
- .filter(d => d.isFile() && d.name.endsWith('.yaml') && !d.name.startsWith('.'))
76
- .map(d => d.name.slice(0, -'.yaml'.length));
77
- }
78
- if (agent === 'openclaw') {
79
- const dir = path.join(versionHome, '.openclaw', 'workflows');
80
- if (!fs.existsSync(dir))
81
- return [];
82
- return fs.readdirSync(dir, { withFileTypes: true })
83
- .filter(d => d.isFile() && d.name.endsWith('.lobster') && !d.name.startsWith('.'))
84
- .filter(d => {
85
- try {
86
- const parsed = yaml.parse(fs.readFileSync(path.join(dir, d.name), 'utf-8'));
87
- const env = parsed && typeof parsed === 'object' && parsed.env && typeof parsed.env === 'object' && !Array.isArray(parsed.env)
88
- ? parsed.env
89
- : {};
90
- return env.AGENTS_CLI_WORKFLOW === d.name.slice(0, -'.lobster'.length);
91
- }
92
- catch {
93
- return false;
94
- }
95
- })
96
- .map(d => d.name.slice(0, -'.lobster'.length));
97
- }
98
- if (agent === 'grok') {
99
- const workflowsDir = path.join(versionHome, '.grok', 'workflows');
100
- if (!fs.existsSync(workflowsDir))
101
- return [];
102
- return fs.readdirSync(workflowsDir, { withFileTypes: true })
103
- .filter(d => d.isFile() && d.name.endsWith('.rhai') && !d.name.startsWith('.'))
104
- .filter(d => grokWorkflowMarker(path.join(workflowsDir, d.name)) === d.name.slice(0, -'.rhai'.length))
105
- .map(d => d.name.slice(0, -'.rhai'.length));
106
- }
107
- const workflowsDir = path.join(versionHome, 'workflows');
108
- if (!fs.existsSync(workflowsDir))
109
- return [];
110
- return fs.readdirSync(workflowsDir, { withFileTypes: true })
111
- .filter(d => d.isDirectory() && fs.existsSync(path.join(workflowsDir, d.name, 'WORKFLOW.md')))
112
- .map(d => d.name);
9
+ const target = workflowTarget(agent);
10
+ return target.names(target.dir(versionHome));
113
11
  },
114
12
  };
115
13
  }
@@ -1,5 +1,6 @@
1
1
  import { capableAgents } from '../../capabilities.js';
2
- import { listInstalledWorkflows, syncWorkflowToVersion } from '../../workflows.js';
2
+ import { listInstalledWorkflows } from '../../workflows.js';
3
+ import { syncWorkflowToVersion } from '../../workflows-registry.js';
3
4
  import { lazyAgentMap } from './lazy-map.js';
4
5
  function buildWorkflowsWriter(agent) {
5
6
  return {
@@ -123,10 +123,13 @@ export declare function getUserSecretsDir(): string;
123
123
  * AGENTS_SECRETS_DB without racing the module-load capture of USER_SECRETS_DIR —
124
124
  * mirrors the AGENTS_EVENTS_PATH / AGENTS_DEVICES_DIR escape hatches. Holds only
125
125
  * value-free usage telemetry (which bundle was created/imported/exported/viewed/
126
- * accessed/unlocked, when, by whom), never a secret value. It is a derived index
127
- * fed FROM the emitSecretAudit chokepoint alongside the append-only
128
- * ~/.agents/.history/events/YYYY-MM-DD audit log — the same way sessions.db indexes session
129
- * metadata off the real session flow — not a second write path.
126
+ * accessed/unlocked, when, by whom), never a secret value. It used to be a
127
+ * derived index fed FROM the in-repo secrets engine's own emitSecretAudit
128
+ * chokepoint — not a second write path — the same way sessions.db indexes
129
+ * session metadata off the real session flow. That engine (and its audit
130
+ * emission) moved out of this repo entirely with the standalone `secrets`
131
+ * engine (PHNX-3989), so nothing writes this DB from agents-cli today; the
132
+ * read-side queries in `analytics/usage-db.ts` have no current caller.
130
133
  */
131
134
  export declare function getSecretsDbPath(): string;
132
135
  /**
package/dist/lib/state.js CHANGED
@@ -396,10 +396,13 @@ export function getUserSecretsDir() { return USER_SECRETS_DIR; }
396
396
  * AGENTS_SECRETS_DB without racing the module-load capture of USER_SECRETS_DIR —
397
397
  * mirrors the AGENTS_EVENTS_PATH / AGENTS_DEVICES_DIR escape hatches. Holds only
398
398
  * value-free usage telemetry (which bundle was created/imported/exported/viewed/
399
- * accessed/unlocked, when, by whom), never a secret value. It is a derived index
400
- * fed FROM the emitSecretAudit chokepoint alongside the append-only
401
- * ~/.agents/.history/events/YYYY-MM-DD audit log — the same way sessions.db indexes session
402
- * metadata off the real session flow — not a second write path.
399
+ * accessed/unlocked, when, by whom), never a secret value. It used to be a
400
+ * derived index fed FROM the in-repo secrets engine's own emitSecretAudit
401
+ * chokepoint — not a second write path — the same way sessions.db indexes
402
+ * session metadata off the real session flow. That engine (and its audit
403
+ * emission) moved out of this repo entirely with the standalone `secrets`
404
+ * engine (PHNX-3989), so nothing writes this DB from agents-cli today; the
405
+ * read-side queries in `analytics/usage-db.ts` have no current caller.
403
406
  */
404
407
  export function getSecretsDbPath() {
405
408
  return process.env.AGENTS_SECRETS_DB ?? path.join(USER_SECRETS_DIR, 'secrets.db');
@@ -1332,7 +1335,9 @@ export function writeMetaUnlocked(meta) {
1332
1335
  // In-flight connect attempts (PHNX-3940), device-scoped like homes.
1333
1336
  const accountsPending = deviceAccounts?.pendingConnects && Object.keys(deviceAccounts.pendingConnects).length > 0
1334
1337
  ? deviceAccounts.pendingConnects : undefined;
1335
- if (accountsNative || accountsBindings || accountsHomes || accountsPending) {
1338
+ const accountsSlots = deviceAccounts?.slots && Object.keys(deviceAccounts.slots).length > 0
1339
+ ? deviceAccounts.slots : undefined;
1340
+ if (accountsNative || accountsBindings || accountsHomes || accountsPending || accountsSlots) {
1336
1341
  const da = {};
1337
1342
  if (accountsNative)
1338
1343
  da.native = accountsNative;
@@ -1342,6 +1347,8 @@ export function writeMetaUnlocked(meta) {
1342
1347
  da.homes = accountsHomes;
1343
1348
  if (accountsPending)
1344
1349
  da.pendingConnects = accountsPending;
1350
+ if (accountsSlots)
1351
+ da.slots = accountsSlots;
1345
1352
  doc.accounts = da;
1346
1353
  }
1347
1354
  else if (writesDeviceAccounts)
@@ -1487,12 +1494,16 @@ function overlayMachineLocal(meta) {
1487
1494
  if (acc.pendingConnects !== undefined && !isMap(acc.pendingConnects)) {
1488
1495
  throw new Error(`Device config corrupted at ${devicePath}: accounts.pendingConnects must be a map.`);
1489
1496
  }
1497
+ if (acc.slots !== undefined && !isMap(acc.slots)) {
1498
+ throw new Error(`Device config corrupted at ${devicePath}: accounts.slots must be a map.`);
1499
+ }
1490
1500
  const native = acc.native;
1491
1501
  const bindings = acc.bindings;
1492
1502
  const homes = acc.homes;
1493
1503
  const pendingConnects = acc.pendingConnects;
1494
- if (native || bindings || homes || pendingConnects)
1495
- meta.deviceAccounts = { ...(native ? { native } : {}), ...(bindings ? { bindings } : {}), ...(homes ? { homes } : {}), ...(pendingConnects ? { pendingConnects } : {}) };
1504
+ const slots = acc.slots;
1505
+ if (native || bindings || homes || pendingConnects || slots)
1506
+ meta.deviceAccounts = { ...(native ? { native } : {}), ...(bindings ? { bindings } : {}), ...(homes ? { homes } : {}), ...(pendingConnects ? { pendingConnects } : {}), ...(slots ? { slots } : {}) };
1496
1507
  }
1497
1508
  if (Object.prototype.hasOwnProperty.call(dm, 'routines')) {
1498
1509
  if (!Array.isArray(dm.routines) || dm.routines.some((name) => typeof name !== 'string')) {
@@ -15,6 +15,8 @@ import { type SummarizerConfig } from './config.js';
15
15
  import { summarize as defaultSummarize } from './summarize.js';
16
16
  /** Max sessions summarized per tick — a debounce ceiling on local-model calls. */
17
17
  export declare const SUMMARIZER_MAX_PER_TICK = 8;
18
+ /** Narration headlines handed to the model per session — the recent tail, not the history. */
19
+ export declare const SUMMARIZER_STEP_INPUT = 12;
18
20
  export interface SummarizerPassOptions {
19
21
  now?: number;
20
22
  config?: SummarizerConfig;
@@ -17,6 +17,8 @@ import { resolveSummarizerConfig, isSummarizerRunnable } from './config.js';
17
17
  import { summarize as defaultSummarize } from './summarize.js';
18
18
  /** Max sessions summarized per tick — a debounce ceiling on local-model calls. */
19
19
  export const SUMMARIZER_MAX_PER_TICK = 8;
20
+ /** Narration headlines handed to the model per session — the recent tail, not the history. */
21
+ export const SUMMARIZER_STEP_INPUT = 12;
20
22
  /**
21
23
  * Merge model checkpoint TEXTS with any prior stored checkpoints so a line that
22
24
  * already existed keeps its original `at`, and only genuinely new lines get
@@ -87,7 +89,18 @@ export async function runSummarizerPass(opts = {}) {
87
89
  }
88
90
  let computed;
89
91
  try {
90
- computed = await runSummarize(prompt, { todos: s.todos, plan: s.plan, phase: s.phase }, { baseUrl: config.baseUrl, model: config.model, ...(opts.signal ? { signal: opts.signal } : {}) });
92
+ computed = await runSummarize(prompt, {
93
+ todos: s.todos,
94
+ plan: s.plan,
95
+ phase: s.phase,
96
+ // The agent's own narration headlines, when the timeline pass has
97
+ // folded them (PHNX-3939) — the model's progress input was previously
98
+ // only the checklist, which many sessions never write. Optional by
99
+ // construction: a row with no timeline passes nothing extra.
100
+ ...(s.timeline?.steps.length
101
+ ? { steps: s.timeline.steps.slice(-SUMMARIZER_STEP_INPUT).map((step) => step.text) }
102
+ : {}),
103
+ }, { baseUrl: config.baseUrl, model: config.model, ...(opts.signal ? { signal: opts.signal } : {}) });
91
104
  }
92
105
  catch {
93
106
  computed = undefined;