mixdog 1.2.5 → 1.3.0

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 (493) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/gate-scheduler.mjs +2 -2
  7. package/scripts/lib/isolated-test-env.mjs +16 -0
  8. package/scripts/lib/run-node-tests.mjs +5 -14
  9. package/scripts/provider-pingpong-live.mjs +9 -9
  10. package/src/agents/advisor/AGENT.md +4 -4
  11. package/src/agents/advisor/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +5 -4
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/defaults/shipped-history.json +17 -0
  15. package/src/defaults/skills/browser-use/SKILL.md +7 -1
  16. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  17. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  18. package/src/defaults/skills/setup/references/actions.md +11 -4
  19. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  20. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  21. package/src/defaults/skills/visualize/SKILL.md +3 -2
  22. package/src/lib/rules-builder.cjs +56 -7
  23. package/src/output-styles/common.md +22 -6
  24. package/src/output-styles/detailed.md +7 -5
  25. package/src/output-styles/minimal.md +7 -3
  26. package/src/output-styles/simple.md +2 -3
  27. package/src/rules/agent/AGENT.md +7 -1
  28. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  29. package/src/rules/agent-mode/MIXY.md +21 -0
  30. package/src/rules/agent-mode/ROUTINE.md +6 -0
  31. package/src/rules/lead/LEAD.md +9 -4
  32. package/src/rules/shared/10-tool-workflow.md +2 -1
  33. package/src/rules/shared/60-verification.md +4 -5
  34. package/src/rules/shared/70-delivery.md +3 -0
  35. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  37. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  39. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  40. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
  41. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  42. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  43. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
  44. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  45. package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
  46. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  49. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  50. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  51. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  53. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  62. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  65. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  66. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  69. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  70. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  84. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  86. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  88. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  92. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  94. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  95. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  96. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  97. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  98. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  99. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  100. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
  101. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  103. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
  104. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  109. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  110. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  111. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  112. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  113. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  114. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  115. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  116. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  117. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  118. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  119. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  120. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  121. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  122. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  123. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  124. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  125. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  126. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  127. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  128. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  129. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  130. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  131. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  132. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  134. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  135. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  140. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  146. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  147. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  148. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  150. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  157. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  158. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  159. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  163. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  166. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  167. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  168. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  169. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  171. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  174. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  175. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  176. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
  177. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
  178. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  179. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  180. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  181. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  182. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  183. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  184. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  185. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  186. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  189. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  191. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  197. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  198. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  199. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  200. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  201. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  203. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  208. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  209. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  210. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  211. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  212. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  213. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  214. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  215. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  216. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  217. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  218. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  219. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  220. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  221. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
  222. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  224. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  225. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  226. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  227. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  228. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  229. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  230. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  231. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  232. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
  233. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
  234. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  235. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
  236. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
  237. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  238. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
  239. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  240. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  241. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  242. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  243. package/src/runtime/attachments/office-extract.mjs +11 -9
  244. package/src/runtime/browser-bridge/client.mjs +24 -0
  245. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  246. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  247. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  248. package/src/runtime/channels/lib/webhook.mjs +0 -1
  249. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  250. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  251. package/src/runtime/computer-bridge/client.mjs +28 -1
  252. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  253. package/src/runtime/connectors/google-mcp.mjs +446 -0
  254. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  255. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  256. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  257. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  258. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  259. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  260. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  261. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  262. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  263. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  264. package/src/runtime/memory/lib/memory.mjs +7 -1
  265. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  266. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  268. package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
  269. package/src/runtime/memory/tool-defs.mjs +5 -0
  270. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  271. package/src/runtime/office/core/tabular.mjs +1 -1
  272. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  273. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  274. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  275. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  276. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  277. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  278. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  279. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  280. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  281. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  282. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  283. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  284. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  285. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  286. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  287. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  288. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  289. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  290. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  291. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  292. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  293. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  294. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  295. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  296. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  297. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  298. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  299. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  300. package/src/runtime/shared/agent-avatar.mjs +320 -0
  301. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  302. package/src/runtime/shared/agent-route-config.mjs +16 -0
  303. package/src/runtime/shared/atomic-file.mjs +36 -14
  304. package/src/runtime/shared/background-tasks.mjs +60 -20
  305. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  306. package/src/runtime/shared/config.mjs +8 -11
  307. package/src/runtime/shared/connector-accounts.mjs +15 -0
  308. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  309. package/src/runtime/shared/connector-catalog.mjs +230 -0
  310. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  311. package/src/runtime/shared/connector-triggers.mjs +43 -0
  312. package/src/runtime/shared/file-lock.mjs +7 -1
  313. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  314. package/src/runtime/shared/file-permissions.d.mts +19 -4
  315. package/src/runtime/shared/file-permissions.mjs +310 -4
  316. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  317. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  318. package/src/runtime/shared/held-request-host.d.mts +12 -0
  319. package/src/runtime/shared/held-request-host.mjs +71 -0
  320. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  321. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  322. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  323. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  324. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  325. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  326. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  327. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  328. package/src/runtime/shared/notify-trace.mjs +8 -1
  329. package/src/runtime/shared/plain-preview.d.mts +1 -0
  330. package/src/runtime/shared/plain-preview.mjs +28 -0
  331. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  332. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  333. package/src/runtime/shared/process-runner.mjs +38 -0
  334. package/src/runtime/shared/provider-accounts.mjs +2 -1
  335. package/src/runtime/shared/provider-api-key.mjs +0 -1
  336. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  337. package/src/runtime/shared/resource-admission.mjs +60 -18
  338. package/src/runtime/shared/routine-limits.d.mts +11 -0
  339. package/src/runtime/shared/routine-limits.mjs +21 -0
  340. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  341. package/src/runtime/shared/routine-trigger.mjs +44 -0
  342. package/src/runtime/shared/run-child.mjs +84 -0
  343. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  344. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  345. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  346. package/src/runtime/shared/singleton-owner.mjs +126 -42
  347. package/src/runtime/shared/slow-span.mjs +4 -12
  348. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  349. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  350. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  351. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  352. package/src/runtime/shared/turn-changes.mjs +0 -8
  353. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  354. package/src/runtime/shared/user-data-guard.mjs +131 -34
  355. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  356. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  357. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  358. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  359. package/src/runtime/tidy/runners/biome.mjs +3 -1
  360. package/src/runtime/tidy/shadow.mjs +2 -1
  361. package/src/runtime/web-search/index.mjs +5 -7
  362. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  363. package/src/runtime/web-search/tool-defs.mjs +6 -1
  364. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  365. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  366. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  367. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  368. package/src/session-runtime/boot/apis.mjs +50 -18
  369. package/src/session-runtime/boot/facade.mjs +5 -8
  370. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  371. package/src/session-runtime/boot/tools.mjs +3 -0
  372. package/src/session-runtime/channel-config-api.mjs +53 -5
  373. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  374. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  375. package/src/session-runtime/held-requests.mjs +228 -0
  376. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  377. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  378. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  379. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  380. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  381. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  382. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  383. package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
  384. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  385. package/src/session-runtime/lifecycle/shared.mjs +25 -2
  386. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  387. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  388. package/src/session-runtime/lifecycle-api.mjs +3 -1
  389. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  390. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  391. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  392. package/src/session-runtime/model-route/set-route.mjs +14 -3
  393. package/src/session-runtime/owned-automation-run.mjs +229 -0
  394. package/src/session-runtime/provider-auth/login.mjs +10 -6
  395. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  396. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  397. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  398. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  399. package/src/session-runtime/provider-models.mjs +76 -10
  400. package/src/session-runtime/provider-readiness.mjs +4 -0
  401. package/src/session-runtime/resource-api.mjs +5 -0
  402. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  403. package/src/session-runtime/resource-skills-api.mjs +12 -0
  404. package/src/session-runtime/room-member-gate.mjs +48 -0
  405. package/src/session-runtime/routine-home-turn.mjs +156 -0
  406. package/src/session-runtime/routine-owner.mjs +48 -0
  407. package/src/session-runtime/schedule-session-run.mjs +30 -64
  408. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  409. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  410. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  411. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  412. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  413. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  414. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  415. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  416. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  417. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  418. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  419. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  420. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  422. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  423. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  424. package/src/session-runtime/services/agent-tool.mjs +20 -0
  425. package/src/session-runtime/services/channel-admin.mjs +93 -24
  426. package/src/session-runtime/services/memory-access.mjs +37 -0
  427. package/src/session-runtime/services/provider-admin.mjs +166 -59
  428. package/src/session-runtime/services/schedule-access.mjs +91 -0
  429. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  430. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  431. package/src/session-runtime/session-create/session-options.mjs +34 -7
  432. package/src/session-runtime/session-hooks.mjs +16 -8
  433. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  434. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  435. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  436. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  437. package/src/session-runtime/skills-api.mjs +16 -1
  438. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  439. package/src/session-runtime/turn/session-ops.mjs +10 -3
  440. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
  441. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  442. package/src/session-runtime/usage-stats-api.mjs +6 -1
  443. package/src/session-runtime/webhook-session-run.mjs +49 -67
  444. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  445. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  446. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  447. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  448. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  449. package/src/standalone/daemon-agent-control.mjs +1 -8
  450. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  451. package/src/standalone/daemon-owner.mjs +117 -25
  452. package/src/standalone/daemon-session-control.mjs +11 -2
  453. package/src/standalone/daemon.mjs +61 -17
  454. package/src/standalone/session-call-cache.mjs +6 -0
  455. package/src/standalone/session-protocol.mjs +28 -1
  456. package/src/standalone/session-service/retention/busy.mjs +7 -2
  457. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  458. package/src/standalone/session-service.mjs +19 -0
  459. package/src/standalone/session-transport/routes.mjs +7 -6
  460. package/src/tui/app/model-options.mjs +39 -2
  461. package/src/tui/app/model-picker.mjs +16 -2
  462. package/src/tui/app/provider-setup-picker.mjs +1 -2
  463. package/src/tui/app/slash-commands.mjs +20 -0
  464. package/src/tui/dist/index.mjs +487 -128
  465. package/src/tui/session/context-state.mjs +6 -1
  466. package/src/tui/session/notice-surface.mjs +35 -4
  467. package/src/tui/session/notice-text.mjs +0 -1
  468. package/src/tui/session/queue-helpers.mjs +2 -1
  469. package/src/tui/session/session-api/extensions.mjs +4 -0
  470. package/src/tui/session/session-api/intake/submission.mjs +12 -13
  471. package/src/tui/session/session-api/integrations.mjs +9 -5
  472. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  473. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  474. package/src/tui/session/session-api/routes.mjs +48 -3
  475. package/src/tui/session/session-flow/drain.mjs +20 -3
  476. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  477. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  478. package/src/tui/session/session-flow/steering.mjs +104 -24
  479. package/src/tui/session/session-flow.mjs +2 -0
  480. package/src/tui/session/tool-approval.mjs +26 -5
  481. package/src/tui/session/transcript-intake.mjs +6 -2
  482. package/src/tui/session/transcript-spill.mjs +5 -1
  483. package/src/tui/session/tui-steering-persist.mjs +65 -42
  484. package/src/tui/session/turn-live-call.mjs +56 -0
  485. package/src/tui/session/turn-stream.mjs +33 -0
  486. package/src/tui/session/turn.mjs +50 -7
  487. package/src/ui/statusline.mjs +1 -6
  488. package/src/ui/streaming-markdown-heal.mjs +24 -7
  489. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  490. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  491. package/src/workflows/default/WORKFLOW.md +9 -13
  492. package/src/session-runtime/automation-agents.mjs +0 -94
  493. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -16,10 +16,17 @@
16
16
  * field name and row key is a consumed contract and is used verbatim.
17
17
  */
18
18
  import { readFileSync } from 'node:fs';
19
+ import { readFile, stat } from 'node:fs/promises';
19
20
  import { join } from 'node:path';
20
- import { probePath, PROBE_PRESENT } from './store/fs-probe.mjs';
21
+ import {
22
+ isSettledSessionStamp,
23
+ sameSessionStamp,
24
+ sessionStampOf,
25
+ statSessionStamp,
26
+ } from './store/canonical-reader.mjs';
21
27
  import { readJsonProjection } from '../../../shared/pending-json-projection.mjs';
22
28
  import { listStoredAgentWorkers as assembleStoredAgentWorkers } from './store-agent-worker-pool.mjs';
29
+ import { readTopLevelFields } from './store-agent-worker-header.mjs';
23
30
  import {
24
31
  TAG_TOMBSTONE_TTL_MS,
25
32
  agentPaneVisible,
@@ -166,16 +173,83 @@ function cancelStage(cancel, declaredStage) {
166
173
  return /^cancel/i.test(declaredStage) || CANCELLED_AGENT_STATUS.test(declaredStage) ? declaredStage : cancel.status;
167
174
  }
168
175
 
169
- function storedAgentWorkerIndex() {
170
- const path = storedAgentWorkerIndexPath();
176
+ // The worker indexes were re-parsed in full on every pool listing and ancestry
177
+ // lookup, changed or not. Each keeps its last parse beside the bytes it came
178
+ // from: the file is still read every time (a few hundred KB), but an
179
+ // unchanged one — byte for byte, so no timestamp collision can serve a stale
180
+ // document — reuses that parse.
181
+ // Before the read, the full file stamp is checked under the same canonical
182
+ // policy the session headers use: a file that still has the exact settled
183
+ // { dev, ino, size, mtimeNs, ctimeNs } its bytes were proven to come from is
184
+ // not read again (each pool listing re-read the whole index synchronously).
185
+ const workerIndexCache = new Map(); // path -> { bytes, doc, stamp|null }
186
+ // The index documents an asynchronous listing read before its (synchronous)
187
+ // assembly; live only during that assembly.
188
+ let passIndexes = null; // path -> doc | null
189
+
190
+ function readWorkerIndexFile(path) {
191
+ if (passIndexes?.has(path)) return passIndexes.get(path);
171
192
  // This process's own queued (not yet persisted) writes win over the file.
172
193
  const projected = readJsonProjection(path);
173
194
  if (projected) return projected;
195
+ const before = observeStamp(path);
196
+ const cached = workerIndexCache.get(path);
197
+ if (before && cached?.stamp && sameSessionStamp(cached.stamp, before)) return cached.doc;
198
+ let bytes;
174
199
  try {
175
- return JSON.parse(readFileSync(path, 'utf8'));
200
+ bytes = readFileSync(path);
176
201
  } catch {
202
+ workerIndexCache.delete(path);
177
203
  return null;
178
204
  }
205
+ const stamp = before && isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path)) ? before : null;
206
+ if (cached?.bytes.equals(bytes)) {
207
+ cached.stamp = stamp;
208
+ return cached.doc;
209
+ }
210
+ let doc = null;
211
+ try {
212
+ doc = JSON.parse(bytes.toString('utf8'));
213
+ } catch {
214
+ /* malformed: no rows */
215
+ }
216
+ workerIndexCache.set(path, { bytes, doc, stamp });
217
+ return doc;
218
+ }
219
+
220
+ /** readWorkerIndexFile with every stat and read awaited, under the same
221
+ * stamp and byte-equality cache. */
222
+ async function readWorkerIndexFileAsync(path) {
223
+ const projected = readJsonProjection(path);
224
+ if (projected) return projected;
225
+ const before = await observeStampAsync(path);
226
+ const cached = workerIndexCache.get(path);
227
+ if (before && cached?.stamp && sameSessionStamp(cached.stamp, before)) return cached.doc;
228
+ let bytes;
229
+ try {
230
+ bytes = await readFile(path);
231
+ } catch {
232
+ workerIndexCache.delete(path);
233
+ return null;
234
+ }
235
+ const settled = before && isSettledSessionStamp(before);
236
+ const stamp = settled && sameSessionStamp(before, await observeStampAsync(path)) ? before : null;
237
+ if (cached?.bytes.equals(bytes)) {
238
+ cached.stamp = stamp;
239
+ return cached.doc;
240
+ }
241
+ let doc = null;
242
+ try {
243
+ doc = JSON.parse(bytes.toString('utf8'));
244
+ } catch {
245
+ /* malformed: no rows */
246
+ }
247
+ workerIndexCache.set(path, { bytes, doc, stamp });
248
+ return doc;
249
+ }
250
+
251
+ function storedAgentWorkerIndex() {
252
+ return readWorkerIndexFile(storedAgentWorkerIndexPath());
179
253
  }
180
254
 
181
255
  function storedAgentWorkerIndexRows() {
@@ -211,7 +285,8 @@ export function listStoredAgentWorkerLinks() {
211
285
 
212
286
  // The agent pool only reads top-level session fields, yet each refresh used to
213
287
  // JSON.parse every active worker's whole transcript (megabytes per worker,
214
- // every catalog publish). Cache the header per file fingerprint instead.
288
+ // every catalog publish). Cache the header per full file stamp instead (see
289
+ // probeWorkerSessionHeader).
215
290
  // The cache holds exactly the headers the latest pool assembly read: a fixed
216
291
  // LRU cap below the index size (263 headers against 256 slots) evicted every
217
292
  // entry just before its reuse, so each refresh re-parsed every worker
@@ -250,30 +325,120 @@ const WORKER_HEADER_FIELDS = [
250
325
  ...SESSION_CANCEL_FIELDS,
251
326
  ];
252
327
 
328
+ // One assembly reads a session's header up to three times (reap check, row
329
+ // projection, orphan prune); each read used to stat the file again. Within an
330
+ // assembly the first answer stands, absent or unreadable included.
331
+ let passHeaders = null; // path -> header | null, live only during an assembly
332
+
333
+ function workerSessionHeaderPath(sessionId) {
334
+ return join(dataDir(), 'sessions', `${sessionId}.json`);
335
+ }
336
+
253
337
  function readWorkerSessionHeader(sessionId) {
254
- const path = join(dataDir(), 'sessions', `${sessionId}.json`);
255
- const probe = probePath(path);
256
- if (probe.state !== PROBE_PRESENT) return null;
338
+ const path = workerSessionHeaderPath(sessionId);
339
+ if (passHeaders?.has(path)) return passHeaders.get(path);
340
+ const header = probeWorkerSessionHeader(path);
341
+ passHeaders?.set(path, header);
342
+ return header;
343
+ }
344
+
345
+ let workerHeaderReads = 0;
346
+
347
+ function observeStamp(path) {
348
+ try {
349
+ return statSessionStamp(path);
350
+ } catch {
351
+ return null;
352
+ }
353
+ }
354
+
355
+ // A header is reused only under the canonical full-stamp policy: the file
356
+ // still has the exact { dev, ino, size, mtimeNs, ctimeNs } its bytes were
357
+ // proven to come from (stat → read → stat), and that stamp was past the racy
358
+ // window when read. Size + mtime alone let an equal-length rewrite that kept
359
+ // its mtime serve stale ownership, status and cancel fields.
360
+ function probeWorkerSessionHeader(path) {
361
+ // Absent and unreadable alike: no header.
362
+ const before = observeStamp(path);
363
+ if (!before) return null;
257
364
  const cached = workerSessionHeaderCache.get(path);
258
- if (cached && cached.mtimeMs === probe.mtimeMs && cached.size === probe.size) {
365
+ if (cached && sameSessionStamp(cached.stamp, before)) {
259
366
  cached.pass = headerPass;
260
367
  return cached.header;
261
368
  }
262
- let session = null;
369
+ workerSessionHeaderCache.delete(path);
370
+ // A rewrite re-reads only this record, and only its top-level fields are
371
+ // decoded: the transcript bytes are validated, never materialized.
372
+ let header = null;
373
+ try {
374
+ header = readTopLevelFields(readFileSync(path), WORKER_HEADER_FIELDS);
375
+ } catch {
376
+ return null;
377
+ }
378
+ if (!header) return null;
379
+ workerHeaderReads += 1;
380
+ if (isSettledSessionStamp(before) && sameSessionStamp(before, observeStamp(path))) {
381
+ workerSessionHeaderCache.set(path, { stamp: before, header, pass: headerPass });
382
+ }
383
+ return header;
384
+ }
385
+
386
+ async function observeStampAsync(path) {
387
+ try {
388
+ return sessionStampOf(await stat(path, { bigint: true }));
389
+ } catch {
390
+ return null;
391
+ }
392
+ }
393
+
394
+ /** probeWorkerSessionHeader with every stat and read awaited, under the
395
+ * same full-stamp cache policy. */
396
+ async function probeWorkerSessionHeaderAsync(path) {
397
+ const before = await observeStampAsync(path);
398
+ if (!before) return null;
399
+ const cached = workerSessionHeaderCache.get(path);
400
+ if (cached && sameSessionStamp(cached.stamp, before)) return cached.header;
401
+ workerSessionHeaderCache.delete(path);
402
+ let header = null;
263
403
  try {
264
- session = JSON.parse(readFileSync(path, 'utf8'));
404
+ header = readTopLevelFields(await readFile(path), WORKER_HEADER_FIELDS);
265
405
  } catch {
266
406
  return null;
267
407
  }
268
- if (!session || typeof session !== 'object') return null;
269
- const header = {};
270
- for (const field of WORKER_HEADER_FIELDS) {
271
- if (Object.hasOwn(session, field)) header[field] = session[field];
408
+ if (!header) return null;
409
+ workerHeaderReads += 1;
410
+ if (isSettledSessionStamp(before) && sameSessionStamp(before, await observeStampAsync(path))) {
411
+ workerSessionHeaderCache.set(path, { stamp: before, header, pass: headerPass });
272
412
  }
273
- workerSessionHeaderCache.set(path, { mtimeMs: probe.mtimeMs, size: probe.size, header, pass: headerPass });
274
413
  return header;
275
414
  }
276
415
 
416
+ // Header reads in flight at once: each is a stat (and a read only when the
417
+ // file changed) on the shared libuv pool, which other file I/O also needs.
418
+ const HEADER_READ_CONCURRENCY = 4;
419
+
420
+ async function prefetchWorkerHeaders(sessionIds, headers) {
421
+ const paths = [...new Set([...sessionIds].map(workerSessionHeaderPath))].filter((path) => !headers.has(path));
422
+ let next = 0;
423
+ const drain = async () => {
424
+ while (next < paths.length) {
425
+ const path = paths[next++];
426
+ headers.set(path, await probeWorkerSessionHeaderAsync(path));
427
+ }
428
+ };
429
+ await Promise.all(Array.from({ length: Math.min(HEADER_READ_CONCURRENCY, paths.length) }, drain));
430
+ }
431
+
432
+ /** Test/diagnostic seam: cached headers and the records this process read. */
433
+ export function storedAgentWorkerHeaderStats() {
434
+ return {
435
+ entries: workerSessionHeaderCache.size,
436
+ reads: workerHeaderReads,
437
+ stickyIdle: stickyIdleSince.size,
438
+ indexes: workerIndexCache.size,
439
+ };
440
+ }
441
+
277
442
  /** Owner record of a child row that is a Lead CONVERSATION. Only such an owner
278
443
  * lets a missing pool row mean "the Lead's lease expired"; an Agent-owned or
279
444
  * unreadable owner keeps its child rows visible exactly as before. */
@@ -377,16 +542,8 @@ function projectChildWorkerRow(row, context) {
377
542
  }
378
543
 
379
544
  function storedLeadWorkerRows() {
380
- const path = storedLeadWorkerIndexPath();
381
- let parsed = readJsonProjection(path);
382
- if (!parsed) {
383
- try {
384
- parsed = JSON.parse(readFileSync(path, 'utf8'));
385
- } catch {
386
- /* no resident Lead pool */
387
- }
388
- }
389
- return workerRows(parsed);
545
+ // Absent or unreadable: no resident Lead pool.
546
+ return workerRows(readWorkerIndexFile(storedLeadWorkerIndexPath()));
390
547
  }
391
548
 
392
549
  /** A Lead-index row whose durable session turned out to be an agent's.
@@ -464,6 +621,72 @@ function pruneOrphanChildRows(bySessionId, { liveLeadSessionIds }) {
464
621
  * never projected into either pool. `runningSessionIds` is the daemon's set of
465
622
  * sessions whose turn is in flight; omitted, no row is shown as running. */
466
623
  export function listStoredAgentWorkers({ runningSessionIds = null } = {}) {
624
+ headerPass += 1;
625
+ passHeaders = new Map();
626
+ try {
627
+ return assembleListedWorkers(runningSessionIds);
628
+ } finally {
629
+ passHeaders = null;
630
+ forgetUnreadHeaders();
631
+ }
632
+ }
633
+
634
+ // Keep what the latest assembly read, whatever the index size; forget the rest.
635
+ function forgetUnreadHeaders() {
636
+ for (const [path, entry] of workerSessionHeaderCache) {
637
+ if (entry.pass !== headerPass) workerSessionHeaderCache.delete(path);
638
+ }
639
+ }
640
+
641
+ /** listStoredAgentWorkers without blocking the event loop on the store: both
642
+ * indexes and every header the assembly can consult (each row's session,
643
+ * each Lead row's, and each child's owner) are read first with awaited
644
+ * stats and reads; the assembly then runs over what was read, with no file
645
+ * access left in it (any header it still asks for falls back to a read).
646
+ * `runningSessionIds` may be a function, asked once the reads are done. */
647
+ export async function listStoredAgentWorkersAsync({ runningSessionIds = null } = {}) {
648
+ const agentIndexPath = storedAgentWorkerIndexPath();
649
+ const leadIndexPath = storedLeadWorkerIndexPath();
650
+ const [agentIndex, leadIndex] = await Promise.all([
651
+ readWorkerIndexFileAsync(agentIndexPath),
652
+ readWorkerIndexFileAsync(leadIndexPath),
653
+ ]);
654
+ const headers = new Map();
655
+ const childRows = workerRows(agentIndex);
656
+ const sessionIds = new Set(childRows.map((row) => cleanValue(row?.sessionId)));
657
+ for (const row of workerRows(leadIndex)) {
658
+ const sessionId = cleanValue(row?.sessionId);
659
+ if (sessionId && isStoredSessionId(sessionId)) sessionIds.add(sessionId);
660
+ }
661
+ await prefetchWorkerHeaders(sessionIds, headers);
662
+ const owners = new Set();
663
+ for (const row of childRows) {
664
+ const header = headers.get(workerSessionHeaderPath(cleanValue(row?.sessionId)));
665
+ const owner = row && typeof row === 'object' ? workerOwnerSessionId(row, header) : null;
666
+ if (owner) owners.add(owner);
667
+ }
668
+ await prefetchWorkerHeaders(owners, headers);
669
+ const running = typeof runningSessionIds === 'function' ? runningSessionIds() : runningSessionIds;
670
+ headerPass += 1;
671
+ for (const path of headers.keys()) {
672
+ const entry = workerSessionHeaderCache.get(path);
673
+ if (entry) entry.pass = headerPass;
674
+ }
675
+ passHeaders = headers;
676
+ passIndexes = new Map([
677
+ [agentIndexPath, agentIndex],
678
+ [leadIndexPath, leadIndex],
679
+ ]);
680
+ try {
681
+ return assembleListedWorkers(running);
682
+ } finally {
683
+ passHeaders = null;
684
+ passIndexes = null;
685
+ forgetUnreadHeaders();
686
+ }
687
+ }
688
+
689
+ function assembleListedWorkers(runningSessionIds) {
467
690
  const now = Date.now();
468
691
  const index = storedAgentWorkerIndex();
469
692
  const rows = workerRows(index);
@@ -487,21 +710,13 @@ export function listStoredAgentWorkers({ runningSessionIds = null } = {}) {
487
710
  };
488
711
  return tombstoneBlocksWork(work, findTagTombstone(work, tombstones));
489
712
  };
490
- headerPass += 1;
491
- try {
492
- return assembleStoredAgentWorkers({
493
- now,
494
- runningSessionIds,
495
- storedAgentWorkerIndexRows: () => rows.filter((row) => !isReaped(cleanValue(row.sessionId))),
496
- projectChildWorkerRow,
497
- storedLeadWorkerRows,
498
- projectLeadWorkerRow,
499
- pruneOrphanChildRows,
500
- });
501
- } finally {
502
- // Keep what this assembly read, whatever the index size; forget the rest.
503
- for (const [path, entry] of workerSessionHeaderCache) {
504
- if (entry.pass !== headerPass) workerSessionHeaderCache.delete(path);
505
- }
506
- }
713
+ return assembleStoredAgentWorkers({
714
+ now,
715
+ runningSessionIds,
716
+ storedAgentWorkerIndexRows: () => rows.filter((row) => !isReaped(cleanValue(row.sessionId))),
717
+ projectChildWorkerRow,
718
+ storedLeadWorkerRows,
719
+ projectLeadWorkerRow,
720
+ pruneOrphanChildRows,
721
+ });
507
722
  }
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Strict session-record read that decodes only what its caller needs.
3
+ *
4
+ * `readTopLevelLifecycleRecord` (lifecycle-scan.mjs) is the authority: a full
5
+ * `JSON.parse`, then a top-level duplicate-key walk. On a transcript that
6
+ * materializes every message object — about 2.5 ms per MB, most of it for
7
+ * tool output nobody reads. This reader reaches the SAME verdict and the same
8
+ * values while decoding only:
9
+ * - top-level values whose key is not in `skip`;
10
+ * - of `messages` (when it is an array): each element whose `role` is in
11
+ * `fullMessageRoles` in full; every other object element as `{ role }`
12
+ * (or `{}` without one); non-object elements in full.
13
+ * Everything else is validated, never decoded.
14
+ *
15
+ * The fast path accepts a document only when it has PROVEN it valid:
16
+ * - no raw byte below 0x20 anywhere (one native regex test; JSON forbids
17
+ * them inside strings, so a document with whitespace other than spaces,
18
+ * i.e. a pretty-printed one, simply takes the authoritative path);
19
+ * - no invalid escape anywhere (one native regex test; a backslash outside
20
+ * a string fails the grammar walk below);
21
+ * - the complete grammar, walked over every byte outside strings: values,
22
+ * separators, exact number and literal spellings, nesting, and nothing
23
+ * but whitespace after the root object;
24
+ * - a root object without any repeated top-level key.
25
+ * Anything it cannot prove — malformed, ambiguous, or merely unusual — is
26
+ * handed to `readTopLevelLifecycleRecord` itself, so every rejection is the
27
+ * authority's own. Decoded values are `JSON.parse` of their exact UTF-8
28
+ * slices, which equal the full parse's sub-values (slices start and end at
29
+ * ASCII delimiters, so UTF-8 decoding agrees too).
30
+ */
31
+ import { lifecycleRecordOf, readTopLevelLifecycleRecord } from './lifecycle-scan.mjs';
32
+
33
+ const QUOTE = 0x22;
34
+ const BACKSLASH = 0x5c;
35
+ const SPACE = 0x20;
36
+ const COMMA = 0x2c;
37
+ const COLON = 0x3a;
38
+ const OPEN_OBJECT = 0x7b;
39
+ const CLOSE_OBJECT = 0x7d;
40
+ const OPEN_ARRAY = 0x5b;
41
+ const CLOSE_ARRAY = 0x5d;
42
+ const MINUS = 0x2d;
43
+ const PLUS = 0x2b;
44
+ const DOT = 0x2e;
45
+ const ZERO = 0x30;
46
+ const NINE = 0x39;
47
+ const EMPTY = new Set();
48
+
49
+ // String-content faults, one native pass each (an alternation of the two is
50
+ // slower than both). Any raw control byte: inside a string JSON.parse rejects
51
+ // it; outside one only \t \n \r are whitespace, and those documents take the
52
+ // full path.
53
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: control bytes are exactly what this detects.
54
+ const CONTROL_BYTE = /[\x00-\x1f]/;
55
+ // An escape that is not one of \" \\ \/ \b \f \n \r \t \uXXXX. The run before
56
+ // it starts after a non-backslash, so escapes pair from the run's start.
57
+ const INVALID_ESCAPE = /(?<!\\)(?:\\\\)*\\(?:[^"\\/bfnrtu]|u(?![0-9a-fA-F]{4}))/;
58
+
59
+ const isDigit = (code) => code >= ZERO && code <= NINE;
60
+
61
+ // Only spaces can remain: CONTROL_BYTE already sent every \t \n \r away.
62
+ function skipSpaces(text, at) {
63
+ let i = at;
64
+ while (text.charCodeAt(i) === SPACE) i++;
65
+ return i;
66
+ }
67
+
68
+ /** Index after the string opening at `at`, or -1. */
69
+ function stringEnd(text, at) {
70
+ let quote = at;
71
+ for (;;) {
72
+ quote = text.indexOf('"', quote + 1);
73
+ if (quote === -1) return -1;
74
+ let run = 0;
75
+ for (let k = quote - 1; text.charCodeAt(k) === BACKSLASH; k--) run++;
76
+ if ((run & 1) === 0) return quote + 1;
77
+ }
78
+ }
79
+
80
+ /** Index after the JSON number at `at`, or -1. */
81
+ function numberEnd(text, at) {
82
+ let i = at;
83
+ if (text.charCodeAt(i) === MINUS) i++;
84
+ if (text.charCodeAt(i) === ZERO) i++;
85
+ else if (isDigit(text.charCodeAt(i))) {
86
+ while (isDigit(text.charCodeAt(i))) i++;
87
+ } else return -1;
88
+ if (text.charCodeAt(i) === DOT) {
89
+ i++;
90
+ if (!isDigit(text.charCodeAt(i))) return -1;
91
+ while (isDigit(text.charCodeAt(i))) i++;
92
+ }
93
+ const exponent = text.charCodeAt(i);
94
+ if (exponent === 0x65 || exponent === 0x45) {
95
+ i++;
96
+ if (text.charCodeAt(i) === PLUS || text.charCodeAt(i) === MINUS) i++;
97
+ if (!isDigit(text.charCodeAt(i))) return -1;
98
+ while (isDigit(text.charCodeAt(i))) i++;
99
+ }
100
+ return i;
101
+ }
102
+
103
+ /** Index after `"key" :` starting at `at`, or -1. */
104
+ function memberValueStart(text, at) {
105
+ if (text.charCodeAt(at) !== QUOTE) return -1;
106
+ const end = stringEnd(text, at);
107
+ if (end === -1) return -1;
108
+ const colon = skipSpaces(text, end);
109
+ return text.charCodeAt(colon) === COLON ? colon + 1 : -1;
110
+ }
111
+
112
+ /** Index after one valid JSON value starting at or after `at`, or -1.
113
+ * Iterative, so nesting depth costs no stack. */
114
+ function valueEnd(text, at) {
115
+ const closers = [];
116
+ let i = at;
117
+ for (;;) {
118
+ i = skipSpaces(text, i);
119
+ const code = text.charCodeAt(i);
120
+ if (code === QUOTE) i = stringEnd(text, i);
121
+ else if (code === OPEN_OBJECT || code === OPEN_ARRAY) {
122
+ const closer = code === OPEN_OBJECT ? CLOSE_OBJECT : CLOSE_ARRAY;
123
+ i = skipSpaces(text, i + 1);
124
+ if (text.charCodeAt(i) === closer) i++;
125
+ else {
126
+ closers.push(closer);
127
+ if (closer === CLOSE_OBJECT) {
128
+ i = memberValueStart(text, i);
129
+ if (i === -1) return -1;
130
+ }
131
+ continue;
132
+ }
133
+ } else if (code === 0x74) i = text.startsWith('true', i) ? i + 4 : -1;
134
+ else if (code === 0x66) i = text.startsWith('false', i) ? i + 5 : -1;
135
+ else if (code === 0x6e) i = text.startsWith('null', i) ? i + 4 : -1;
136
+ else i = numberEnd(text, i);
137
+ if (i === -1) return -1;
138
+ // After a value: close containers or move to the next member/element.
139
+ for (;;) {
140
+ if (closers.length === 0) return i;
141
+ i = skipSpaces(text, i);
142
+ const next = text.charCodeAt(i);
143
+ const closer = closers[closers.length - 1];
144
+ if (next === closer) {
145
+ closers.pop();
146
+ i++;
147
+ continue;
148
+ }
149
+ if (next !== COMMA) return -1;
150
+ i = skipSpaces(text, i + 1);
151
+ if (closer === CLOSE_OBJECT) {
152
+ i = memberValueStart(text, i);
153
+ if (i === -1) return -1;
154
+ }
155
+ break;
156
+ }
157
+ }
158
+ }
159
+
160
+ const decodeSlice = (bytes, start, end) => JSON.parse(bytes.toString('utf8', start, end));
161
+
162
+ /** The key of the string token [start, end): its own bytes unless escaped. */
163
+ function decodeKey(bytes, text, start, end) {
164
+ for (let k = start + 1; k < end - 1; k++) {
165
+ if (text.charCodeAt(k) === BACKSLASH) return decodeSlice(bytes, start, end);
166
+ }
167
+ return bytes.toString('utf8', start + 1, end - 1);
168
+ }
169
+
170
+ function defineMember(target, key, value) {
171
+ // JSON.parse creates own data properties, `__proto__` included.
172
+ Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
173
+ }
174
+
175
+ // "role" spelled with escapes is at most 6 escapes of 6 chars plus quotes.
176
+ const MAX_ESCAPED_ROLE_KEY = 26;
177
+
178
+ /** One `messages` element [start, ...): `{ end, item }`, or null. */
179
+ function messageElement(bytes, text, start, fullRoles) {
180
+ if (text.charCodeAt(start) !== OPEN_OBJECT) {
181
+ const end = valueEnd(text, start);
182
+ return end === -1 ? null : { end, item: decodeSlice(bytes, start, end) };
183
+ }
184
+ let i = skipSpaces(text, start + 1);
185
+ let role;
186
+ let hasRole = false;
187
+ if (text.charCodeAt(i) === CLOSE_OBJECT) return { end: i + 1, item: {} };
188
+ for (;;) {
189
+ if (text.charCodeAt(i) !== QUOTE) return null;
190
+ const keyEnd = stringEnd(text, i);
191
+ if (keyEnd === -1) return null;
192
+ const isRole =
193
+ (keyEnd - i === 6 && text.startsWith('"role"', i)) ||
194
+ (keyEnd - i <= MAX_ESCAPED_ROLE_KEY && keyEnd - i > 6 && decodeKey(bytes, text, i, keyEnd) === 'role');
195
+ const colon = skipSpaces(text, keyEnd);
196
+ if (text.charCodeAt(colon) !== COLON) return null;
197
+ const valueStart = skipSpaces(text, colon + 1);
198
+ const end = valueEnd(text, valueStart);
199
+ if (end === -1) return null;
200
+ if (isRole) {
201
+ // Duplicate members resolve last-wins, as in the full parse.
202
+ role = decodeSlice(bytes, valueStart, end);
203
+ hasRole = true;
204
+ }
205
+ i = skipSpaces(text, end);
206
+ if (text.charCodeAt(i) === COMMA) {
207
+ i = skipSpaces(text, i + 1);
208
+ continue;
209
+ }
210
+ if (text.charCodeAt(i) !== CLOSE_OBJECT) return null;
211
+ const elementEnd = i + 1;
212
+ let item = {};
213
+ if (hasRole && fullRoles.has(role)) item = decodeSlice(bytes, start, elementEnd);
214
+ else if (hasRole) defineMember(item, 'role', role);
215
+ return { end: elementEnd, item };
216
+ }
217
+ }
218
+
219
+ /** Decode `messages` from the end in full, newest first, until `tailUntil`
220
+ * accepts one; `spans` holds each element's [start, end). */
221
+ function decodeTail(bytes, messages, spans, fullRoles, tailUntil) {
222
+ for (let k = messages.length - 1; k >= 0; k--) {
223
+ let item = messages[k];
224
+ if (item && typeof item === 'object' && !Array.isArray(item) && !fullRoles.has(item.role)) {
225
+ item = decodeSlice(bytes, spans[2 * k], spans[2 * k + 1]);
226
+ messages[k] = item;
227
+ }
228
+ if (tailUntil(item)) return;
229
+ }
230
+ }
231
+
232
+ /** The `messages` array [start, ...): `{ end, messages }`, or null. */
233
+ function messagesValue(bytes, text, start, fullRoles, tailUntil) {
234
+ const messages = [];
235
+ const spans = tailUntil ? [] : null;
236
+ let i = skipSpaces(text, start + 1);
237
+ if (text.charCodeAt(i) === CLOSE_ARRAY) return { end: i + 1, messages };
238
+ for (;;) {
239
+ const element = messageElement(bytes, text, i, fullRoles);
240
+ if (!element) return null;
241
+ messages.push(element.item);
242
+ spans?.push(i, element.end);
243
+ i = skipSpaces(text, element.end);
244
+ if (text.charCodeAt(i) === COMMA) {
245
+ i = skipSpaces(text, i + 1);
246
+ continue;
247
+ }
248
+ if (text.charCodeAt(i) !== CLOSE_ARRAY) return null;
249
+ if (tailUntil) decodeTail(bytes, messages, spans, fullRoles, tailUntil);
250
+ return { end: i + 1, messages };
251
+ }
252
+ }
253
+
254
+ /**
255
+ * The proven-valid, duplicate-free root object of `bytes`, or null when
256
+ * validity could not be proven (or the document is not such an object). Of
257
+ * it only the members `decode(key)` accepts are decoded — `messages`, when an
258
+ * array, as described in the module comment with `fullMessageRoles`; every
259
+ * other member is validated and left out. With `fullTailUntil`, `messages`
260
+ * is also decoded in full from its end, newest first, through the first
261
+ * element the predicate accepts.
262
+ */
263
+ export function provenTopLevelObject(bytes, decode, fullMessageRoles = EMPTY, fullTailUntil = null) {
264
+ try {
265
+ return provenDocument(bytes, decode, fullMessageRoles, fullTailUntil);
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
270
+
271
+ function provenDocument(bytes, decode, fullRoles, tailUntil) {
272
+ const text = bytes.toString('latin1');
273
+ if (CONTROL_BYTE.test(text) || INVALID_ESCAPE.test(text)) return null;
274
+ let i = skipSpaces(text, 0);
275
+ if (text.charCodeAt(i) !== OPEN_OBJECT) return null;
276
+ const doc = {};
277
+ const seen = new Set();
278
+ i = skipSpaces(text, i + 1);
279
+ if (text.charCodeAt(i) === CLOSE_OBJECT) i++;
280
+ else {
281
+ for (;;) {
282
+ if (text.charCodeAt(i) !== QUOTE) return null;
283
+ const keyEnd = stringEnd(text, i);
284
+ if (keyEnd === -1) return null;
285
+ const key = decodeKey(bytes, text, i, keyEnd);
286
+ if (seen.has(key)) return null;
287
+ seen.add(key);
288
+ const colon = skipSpaces(text, keyEnd);
289
+ if (text.charCodeAt(colon) !== COLON) return null;
290
+ const start = skipSpaces(text, colon + 1);
291
+ const wanted = decode(key);
292
+ let end;
293
+ if (wanted && key === 'messages' && text.charCodeAt(start) === OPEN_ARRAY) {
294
+ const value = messagesValue(bytes, text, start, fullRoles, tailUntil);
295
+ if (!value) return null;
296
+ end = value.end;
297
+ defineMember(doc, key, value.messages);
298
+ } else {
299
+ end = valueEnd(text, start);
300
+ if (end === -1) return null;
301
+ if (wanted) defineMember(doc, key, decodeSlice(bytes, start, end));
302
+ }
303
+ i = skipSpaces(text, end);
304
+ if (text.charCodeAt(i) === COMMA) {
305
+ i = skipSpaces(text, i + 1);
306
+ continue;
307
+ }
308
+ if (text.charCodeAt(i) !== CLOSE_OBJECT) return null;
309
+ i++;
310
+ break;
311
+ }
312
+ }
313
+ return skipSpaces(text, i) === text.length ? doc : null;
314
+ }
315
+
316
+ /**
317
+ * `readTopLevelLifecycleRecord` of the UTF-8 document in `bytes`, with `doc`
318
+ * decoded as described above — only for a proven document; a record from the
319
+ * authoritative fallback carries the complete document. Consumers must read
320
+ * nothing but non-skipped fields and, of non-full messages, `role`.
321
+ */
322
+ export function readStrictRecordFields(bytes, { skip = EMPTY, fullMessageRoles = EMPTY, fullTailUntil = null } = {}) {
323
+ const doc = provenTopLevelObject(bytes, (key) => !skip.has(key), fullMessageRoles, fullTailUntil);
324
+ return doc ? lifecycleRecordOf(doc) : readTopLevelLifecycleRecord(bytes.toString('utf8'));
325
+ }