mixdog 0.9.152 → 0.9.154

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 (300) hide show
  1. package/LICENSES/browser-import-NOTICE.txt +11 -0
  2. package/NOTICE.md +11 -0
  3. package/README.md +8 -10
  4. package/package.json +19 -3
  5. package/scripts/computer-task-cost-report.mjs +144 -0
  6. package/scripts/run-office-live-tests.mjs +32 -0
  7. package/src/agents/front-worker/AGENT.md +13 -0
  8. package/src/agents/front-worker/agent.json +6 -0
  9. package/src/agents/heavy-worker/AGENT.md +10 -11
  10. package/src/agents/heavy-worker/agent.json +1 -1
  11. package/src/agents/maintainer/AGENT.md +0 -2
  12. package/src/agents/reviewer/AGENT.md +9 -13
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/agents/security/AGENT.md +14 -0
  15. package/src/agents/security/agent.json +6 -0
  16. package/src/agents/worker/AGENT.md +8 -8
  17. package/src/agents/worker/agent.json +1 -1
  18. package/src/agents/writer/AGENT.md +14 -0
  19. package/src/agents/writer/agent.json +6 -0
  20. package/src/rules/agent/00-common.md +4 -7
  21. package/src/rules/lead/01-general.md +0 -3
  22. package/src/rules/shared/10-tool-workflow.md +10 -2
  23. package/src/rules/shared/60-verification.md +2 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +183 -26
  25. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +4 -0
  26. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +109 -0
  27. package/src/runtime/agent/orchestrator/config.mjs +7 -2
  28. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +16 -3
  29. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -1
  30. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +30 -9
  31. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +18 -26
  32. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +25 -2
  33. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +103 -10
  34. package/src/runtime/agent/orchestrator/providers/openai-turn-state.mjs +68 -0
  35. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +36 -11
  36. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +140 -86
  37. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +9 -13
  38. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +72 -4
  39. package/src/runtime/agent/orchestrator/session/browser-snapshot-supersession.mjs +61 -0
  40. package/src/runtime/agent/orchestrator/session/compact/handoff.mjs +0 -22
  41. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -2
  42. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +11 -23
  43. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +28 -269
  44. package/src/runtime/agent/orchestrator/session/compact.mjs +0 -6
  45. package/src/runtime/agent/orchestrator/session/context-utils.mjs +86 -11
  46. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +17 -1
  47. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +12 -8
  48. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +110 -2
  49. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +3 -16
  50. package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +80 -18
  51. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +6 -0
  52. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +3 -0
  53. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -0
  54. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +80 -21
  55. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +33 -72
  56. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +21 -2
  57. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +25 -51
  58. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +3 -0
  59. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -110
  60. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +39 -15
  61. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +14 -4
  62. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +41 -8
  63. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +129 -12
  64. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +2 -2
  65. package/src/runtime/agent/orchestrator/session/store/listing.mjs +8 -1
  66. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +15 -1
  67. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +36 -2
  68. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +157 -30
  69. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +57 -0
  70. package/src/runtime/agent/orchestrator/session/store.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/session/tool-batch.mjs +22 -0
  72. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +6 -6
  73. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +4 -0
  74. package/src/runtime/attachments/pdf-extract.mjs +3 -0
  75. package/src/runtime/attachments/pdfjs-runtime.mjs +13 -0
  76. package/src/runtime/browser-bridge/action-schema.mjs +251 -0
  77. package/src/runtime/browser-bridge/client.mjs +75 -26
  78. package/src/runtime/browser-bridge/tool-defs.mjs +132 -15
  79. package/src/runtime/computer-bridge/action-schema.mjs +653 -0
  80. package/src/runtime/computer-bridge/client.mjs +211 -28
  81. package/src/runtime/computer-bridge/tool-defs.mjs +25 -45
  82. package/src/runtime/office/assurance-benchmark.mjs +358 -0
  83. package/src/runtime/office/assurance.mjs +833 -0
  84. package/src/runtime/office/benchmark.mjs +496 -0
  85. package/src/runtime/office/capabilities.mjs +551 -0
  86. package/src/runtime/office/com-adapter.mjs +283 -0
  87. package/src/runtime/office/content-model.mjs +183 -0
  88. package/src/runtime/office/contract-benchmark.mjs +105 -0
  89. package/src/runtime/office/design-library.mjs +1562 -0
  90. package/src/runtime/office/design-system.mjs +1966 -0
  91. package/src/runtime/office/index.mjs +2413 -0
  92. package/src/runtime/office/journal.mjs +82 -0
  93. package/src/runtime/office/office-com-host.ps1 +3781 -0
  94. package/src/runtime/office/office-com-session-host.ps1 +855 -0
  95. package/src/runtime/office/ooxml-validator.mjs +271 -0
  96. package/src/runtime/office/pagination.mjs +132 -0
  97. package/src/runtime/office/pdf-adapter.mjs +591 -0
  98. package/src/runtime/office/pdf-analysis.mjs +372 -0
  99. package/src/runtime/office/pdf-render.mjs +167 -0
  100. package/src/runtime/office/pdf-security.mjs +63 -0
  101. package/src/runtime/office/polish-benchmark.mjs +190 -0
  102. package/src/runtime/office/portable-ooxml.mjs +1867 -0
  103. package/src/runtime/office/quality-live-benchmark.mjs +486 -0
  104. package/src/runtime/office/quality-pipeline.mjs +136 -0
  105. package/src/runtime/office/tabular.mjs +302 -0
  106. package/src/runtime/office/templates/mixdog-executive.pptx +0 -0
  107. package/src/runtime/office/templates/mixdog-executive.pptx.mixdog.json +190 -0
  108. package/src/runtime/office/tool-defs.mjs +113 -0
  109. package/src/runtime/office/visual-diff.mjs +89 -0
  110. package/src/runtime/office/xlsx-assertions.mjs +172 -0
  111. package/src/runtime/office/xlsx-contract.mjs +147 -0
  112. package/src/runtime/shared/agent-route-config.mjs +9 -0
  113. package/src/runtime/shared/tool-execution-contract.mjs +16 -0
  114. package/src/runtime/shared/tool-surface.mjs +9 -0
  115. package/src/session-runtime/context-status.mjs +9 -3
  116. package/src/session-runtime/deferred-tool-delta.mjs +104 -0
  117. package/src/session-runtime/goal-runtime.mjs +617 -197
  118. package/src/session-runtime/lifecycle-api.mjs +50 -8
  119. package/src/session-runtime/provider-request-snapshot.mjs +33 -9
  120. package/src/session-runtime/resource-api.mjs +31 -94
  121. package/src/session-runtime/runtime-core.mjs +78 -26
  122. package/src/session-runtime/session-lifecycle.mjs +34 -12
  123. package/src/session-runtime/session-title.mjs +2 -2
  124. package/src/session-runtime/session-turn-api.mjs +15 -14
  125. package/src/session-runtime/tool-catalog-data.mjs +1 -1
  126. package/src/session-runtime/tool-catalog.mjs +38 -99
  127. package/src/session-runtime/tool-defs.mjs +6 -1
  128. package/src/session-runtime/tool-surface.mjs +18 -8
  129. package/src/session-runtime/workflow-agents-api.mjs +8 -25
  130. package/src/session-runtime/workflow.mjs +8 -6
  131. package/src/standalone/agent-tool/helpers.mjs +2 -2
  132. package/src/standalone/agent-tool/job-views.mjs +3 -1
  133. package/src/standalone/agent-tool/notify.mjs +5 -1
  134. package/src/standalone/agent-tool/render.mjs +3 -0
  135. package/src/standalone/agent-tool/spawn-flow.mjs +61 -16
  136. package/src/standalone/agent-tool/tool-def.mjs +1 -1
  137. package/src/standalone/agent-tool/worker-index.mjs +123 -0
  138. package/src/standalone/agent-tool/worker-rows.mjs +2 -1
  139. package/src/standalone/agent-tool.mjs +57 -10
  140. package/src/standalone/daemon.mjs +105 -6
  141. package/src/standalone/session-runtime-host.mjs +61 -48
  142. package/src/standalone/session-runtime-inline-host.mjs +34 -127
  143. package/src/standalone/session-runtime-worker.mjs +20 -198
  144. package/src/standalone/session-service.mjs +632 -7
  145. package/src/tui/dist/index.mjs +24 -1
  146. package/src/tui/lib/voice-setup.mjs +4 -1
  147. package/src/tui/session/context-state.mjs +2 -13
  148. package/src/tui/session/goal-continuation.mjs +38 -7
  149. package/src/tui/session/session-api-ext.mjs +9 -0
  150. package/src/tui/session/session-api.mjs +15 -1
  151. package/src/tui/session/session-flow.mjs +25 -1
  152. package/src/tui/session/turn.mjs +88 -16
  153. package/src/tui/session-local.mjs +25 -3
  154. package/src/vendor/statusline/src/gateway/route-meta.mjs +31 -0
  155. package/scripts/generate-voice-runtime-manifest.test.mjs +0 -135
  156. package/scripts/native-binary-arch.test.mjs +0 -111
  157. package/scripts/patch-replay.test.mjs +0 -94
  158. package/src/headless-exec.test.mjs +0 -539
  159. package/src/runtime/agent/orchestrator/agent-trace-timing.test.mjs +0 -57
  160. package/src/runtime/agent/orchestrator/context/collect-skills.test.mjs +0 -153
  161. package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +0 -139
  162. package/src/runtime/agent/orchestrator/mcp/features.test.mjs +0 -66
  163. package/src/runtime/agent/orchestrator/mcp/security.test.mjs +0 -80
  164. package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +0 -90
  165. package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +0 -215
  166. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.test.mjs +0 -165
  167. package/src/runtime/agent/orchestrator/providers/media-parity.test.mjs +0 -142
  168. package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +0 -153
  169. package/src/runtime/agent/orchestrator/providers/openai-codex-model.test.mjs +0 -24
  170. package/src/runtime/agent/orchestrator/providers/openai-ws-delta-parity.test.mjs +0 -190
  171. package/src/runtime/agent/orchestrator/providers/provider-replay.test.mjs +0 -235
  172. package/src/runtime/agent/orchestrator/providers/sse-framing.test.mjs +0 -719
  173. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.test.mjs +0 -40
  174. package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +0 -176
  175. package/src/runtime/agent/orchestrator/session/compaction-read-reset.test.mjs +0 -161
  176. package/src/runtime/agent/orchestrator/session/eager-dispatch.test.mjs +0 -115
  177. package/src/runtime/agent/orchestrator/session/evidence-union.test.mjs +0 -228
  178. package/src/runtime/agent/orchestrator/session/image-strip-recovery.test.mjs +0 -245
  179. package/src/runtime/agent/orchestrator/session/lossless-tool-output.test.mjs +0 -125
  180. package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +0 -325
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.test.mjs +0 -70
  182. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.test.mjs +0 -457
  183. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.test.mjs +0 -117
  184. package/src/runtime/agent/orchestrator/session/reduction-metrics.test.mjs +0 -87
  185. package/src/runtime/agent/orchestrator/session/store/serialize.test.mjs +0 -60
  186. package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +0 -583
  187. package/src/runtime/agent/orchestrator/session/task-wait-control.test.mjs +0 -78
  188. package/src/runtime/agent/orchestrator/tools/builtin/absence-absorption.test.mjs +0 -43
  189. package/src/runtime/agent/orchestrator/tools/builtin/absolute-glob-expand.test.mjs +0 -63
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool-cwd-env.test.mjs +0 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +0 -224
  192. package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +0 -224
  193. package/src/runtime/agent/orchestrator/tools/builtin/enoent-outside-project.test.mjs +0 -56
  194. package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +0 -103
  195. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.test.mjs +0 -30
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +0 -390
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.test.mjs +0 -83
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-output-budget.test.mjs +0 -31
  199. package/src/runtime/agent/orchestrator/tools/builtin/grep-single-file-rescue.test.mjs +0 -106
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.test.mjs +0 -102
  201. package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +0 -152
  202. package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +0 -24
  203. package/src/runtime/agent/orchestrator/tools/builtin/native-search-transport.test.mjs +0 -24
  204. package/src/runtime/agent/orchestrator/tools/builtin/noise-dir-visibility.test.mjs +0 -69
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-glob-survey.test.mjs +0 -71
  206. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.test.mjs +0 -56
  207. package/src/runtime/agent/orchestrator/tools/builtin/search-builders.test.mjs +0 -16
  208. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-top-k.test.mjs +0 -39
  209. package/src/runtime/agent/orchestrator/tools/builtin/write-symlink.test.mjs +0 -153
  210. package/src/runtime/agent/orchestrator/tools/code-graph/aggregate-anchor-relocation.test.mjs +0 -55
  211. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.test.mjs +0 -88
  212. package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +0 -72
  213. package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.test.mjs +0 -54
  214. package/src/runtime/agent/orchestrator/tools/code-graph/search-references.test.mjs +0 -20
  215. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +0 -55
  216. package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +0 -268
  217. package/src/runtime/agent/orchestrator/tools/env-scrub.test.mjs +0 -57
  218. package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.test.mjs +0 -19
  219. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +0 -104
  220. package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +0 -661
  221. package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +0 -552
  222. package/src/runtime/agent/orchestrator/tools/patch/patch-symlink.test.mjs +0 -141
  223. package/src/runtime/agent/orchestrator/tools/patch/v4a-pure-move.test.mjs +0 -57
  224. package/src/runtime/agent/orchestrator/tools/patch/v4a-section-coalesce.test.mjs +0 -81
  225. package/src/runtime/agent/orchestrator/tools/spawn-binary-fetcher.test.mjs +0 -52
  226. package/src/runtime/attachments/store.test.mjs +0 -209
  227. package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +0 -17
  228. package/src/runtime/channels/lib/webhook/deliveries.test.mjs +0 -18
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.test.mjs +0 -68
  230. package/src/runtime/media/adapters/codex-image.test.mjs +0 -22
  231. package/src/runtime/media/download.test.mjs +0 -52
  232. package/src/runtime/media/renditions.test.mjs +0 -99
  233. package/src/runtime/media/store.test.mjs +0 -159
  234. package/src/runtime/memory/lib/archive-security.test.mjs +0 -128
  235. package/src/runtime/memory/lib/embedding-model-config.test.mjs +0 -33
  236. package/src/runtime/memory/lib/http-wire.test.mjs +0 -15
  237. package/src/runtime/memory/lib/memory-cycle-packets.test.mjs +0 -49
  238. package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +0 -78
  239. package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +0 -50
  240. package/src/runtime/memory/lib/pg/process.test.mjs +0 -36
  241. package/src/runtime/memory/lib/trace-store.test.mjs +0 -57
  242. package/src/runtime/memory/lib/transcript-ingest.test.mjs +0 -52
  243. package/src/runtime/shared/background-tasks.test.mjs +0 -78
  244. package/src/runtime/shared/bounded-download.test.mjs +0 -41
  245. package/src/runtime/shared/channel-notification-routing.test.mjs +0 -25
  246. package/src/runtime/shared/child-spawn-remote.test.mjs +0 -124
  247. package/src/runtime/shared/remote-intent.test.mjs +0 -40
  248. package/src/runtime/shared/resource-admission.test.mjs +0 -43
  249. package/src/runtime/shared/runtime-root.test.mjs +0 -34
  250. package/src/runtime/shared/schedule-time.test.mjs +0 -29
  251. package/src/runtime/shared/session-runtime-health.test.mjs +0 -83
  252. package/src/runtime/shared/skill-document.test.mjs +0 -81
  253. package/src/runtime/shared/tool-surface.test.mjs +0 -87
  254. package/src/runtime/shared/update-checker.test.mjs +0 -33
  255. package/src/runtime/shared/user-cwd.test.mjs +0 -43
  256. package/src/runtime/shared/webhook-session-run.test.mjs +0 -14
  257. package/src/session-runtime/agent-disable.test.mjs +0 -123
  258. package/src/session-runtime/cwd-plugins.test.mjs +0 -75
  259. package/src/session-runtime/cwd-tool-routing.test.mjs +0 -61
  260. package/src/session-runtime/global-extensions.test.mjs +0 -28
  261. package/src/session-runtime/goal-runtime.test.mjs +0 -108
  262. package/src/session-runtime/lifecycle-api.test.mjs +0 -21
  263. package/src/session-runtime/model-settings-persist.test.mjs +0 -105
  264. package/src/session-runtime/notification-bus.test.mjs +0 -148
  265. package/src/session-runtime/plugin-mcp-standard.test.mjs +0 -177
  266. package/src/session-runtime/prewarm.test.mjs +0 -52
  267. package/src/session-runtime/provider-request-snapshot.test.mjs +0 -37
  268. package/src/session-runtime/resource-api-global.test.mjs +0 -69
  269. package/src/session-runtime/session-lifecycle.test.mjs +0 -125
  270. package/src/session-runtime/session-title.test.mjs +0 -36
  271. package/src/session-runtime/skills-api.test.mjs +0 -99
  272. package/src/session-runtime/tool-policy-surface.test.mjs +0 -331
  273. package/src/standalone/agent-tool/helpers.test.mjs +0 -62
  274. package/src/standalone/agent-tool/notify.test.mjs +0 -37
  275. package/src/standalone/agent-tool/spawn-flow.test.mjs +0 -63
  276. package/src/standalone/channel-restart.test.mjs +0 -145
  277. package/src/standalone/channel-session-router.test.mjs +0 -37
  278. package/src/standalone/channel-worker-heartbeat.test.mjs +0 -30
  279. package/src/standalone/hook-bus/handlers.test.mjs +0 -88
  280. package/src/standalone/plugin-admin.test.mjs +0 -173
  281. package/src/standalone/session-runtime-agent-control-client.test.mjs +0 -71
  282. package/src/standalone/session-runtime-dispatch-cancel.test.mjs +0 -139
  283. package/src/standalone/session-runtime-host-factory.test.mjs +0 -31
  284. package/src/standalone/session-runtime-host-health.test.mjs +0 -160
  285. package/src/standalone/session-runtime-inline-host.test.mjs +0 -83
  286. package/src/standalone/session-runtime-provider-cooldown.test.mjs +0 -80
  287. package/src/standalone/session-runtime-shard-host.test.mjs +0 -569
  288. package/src/standalone/session-runtime-shard-router.test.mjs +0 -101
  289. package/src/tui/app/live-spinner-visibility.test.mjs +0 -44
  290. package/src/tui/app/panel-epoch.test.mjs +0 -1009
  291. package/src/tui/app/panel-handoff.test.mjs +0 -311
  292. package/src/tui/app/prompt-submit.test.mjs +0 -206
  293. package/src/tui/session/completion-card-restore.test.mjs +0 -111
  294. package/src/tui/session/goal-continuation.test.mjs +0 -84
  295. package/src/tui/session/oauth-flows.test.mjs +0 -134
  296. package/src/tui/session/queue-helpers.test.mjs +0 -46
  297. package/src/tui/session/session-action-surface.test.mjs +0 -108
  298. package/src/tui/session/task-wait-submit.test.mjs +0 -80
  299. package/src/tui/session/tool-result-status.test.mjs +0 -131
  300. package/src/tui/session/tui-steering-persist.test.mjs +0 -104
@@ -14,6 +14,10 @@ import { createAbortController } from '../../../../shared/abort-controller.mjs';
14
14
  import { estimateJsonBytes } from '../../../../shared/json-metrics.mjs';
15
15
  import { logLlmCall } from '../../../../shared/llm/usage-log.mjs';
16
16
  import { appendAgentTrace } from '../../agent-trace.mjs';
17
+ import {
18
+ acknowledgePendingDeferredToolDelta,
19
+ snapshotPendingDeferredToolDelta,
20
+ } from '../../../../../session-runtime/deferred-tool-delta.mjs';
17
21
  import { recordStandaloneStatusTelemetry } from './status-telemetry.mjs';
18
22
  import { normalizeStaleCompactingStage } from './compaction-runner.mjs';
19
23
  import { resolveSessionContextMeta, positiveContextWindow } from './context-meta.mjs';
@@ -80,6 +84,37 @@ export async function settleAskCleanup(promise, { timeoutMs } = {}) {
80
84
  return await settleWithin(promise, budget);
81
85
  }
82
86
 
87
+ // Live Agent projection is a per-ask send opt, never a session field.
88
+ // Callers that pass onTextDelta/onAssistantText still need `liveProjection`
89
+ // to un-suppress provider streaming: askSession always wraps those
90
+ // callbacks for interruption tracking, so agent-loop cannot infer intent
91
+ // from `typeof opts.onTextDelta === 'function'`.
92
+ export function resolveAskLiveProjection(askOpts = {}) {
93
+ return askOpts?.liveProjection === true;
94
+ }
95
+
96
+ export function emitAskSessionStart(askOpts, detail) {
97
+ if (typeof askOpts?.onSessionStart !== 'function') return;
98
+ try { askOpts.onSessionStart(detail); } catch { /* best-effort */ }
99
+ }
100
+
101
+ // Replacement is opt-in and transactional: a delta-only consumer cannot
102
+ // retract already exposed bytes, so absence, false, or rejection must
103
+ // preserve the original partial and force the provider's terminal
104
+ // no-replay behavior. Live projection never auto-acks.
105
+ export async function acknowledgeAskTextReset(askOpts, detail, onAcknowledged) {
106
+ if (typeof askOpts?.onTextReset !== 'function') return false;
107
+ let acknowledged = false;
108
+ try {
109
+ acknowledged = await askOpts.onTextReset(detail) === true;
110
+ } catch {
111
+ return false;
112
+ }
113
+ if (!acknowledged) return false;
114
+ if (typeof onAcknowledged === 'function') onAcknowledged(detail);
115
+ return true;
116
+ }
117
+
83
118
  export function persistedAssistantTranscriptMetadata(value, fallbackAt = Date.now()) {
84
119
  if (!value || typeof value !== 'object') return null;
85
120
  const candidateAt = Number(value.assistantAt);
@@ -194,6 +229,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
194
229
  ...(typeof _rawTranscriptMeta.model === 'string' && _rawTranscriptMeta.model ? { model: _rawTranscriptMeta.model } : {}),
195
230
  ...(typeof _rawTranscriptMeta.provider === 'string' && _rawTranscriptMeta.provider ? { provider: _rawTranscriptMeta.provider } : {}),
196
231
  ...(typeof _rawTranscriptMeta.agent === 'string' && _rawTranscriptMeta.agent ? { agent: _rawTranscriptMeta.agent } : {}),
232
+ ...(typeof _rawTranscriptMeta.sender === 'string' && _rawTranscriptMeta.sender ? { sender: _rawTranscriptMeta.sender } : {}),
197
233
  }
198
234
  : null;
199
235
  const _takeAssistantTranscriptMetadata = () => {
@@ -229,6 +265,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
229
265
  // can compare against the last turn's generation.
230
266
  let askGeneration = 0;
231
267
  let _crashRecoveryChecked = false;
268
+ let _sessionStartEmitted = false;
232
269
  try {
233
270
  // Turn loop (pendingMessages pattern): run the current prompt, then drain
234
271
  // any `agent type=send` messages that were queued while this turn was in
@@ -238,6 +275,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
238
275
  for (;;) {
239
276
  let _pwstTurnDrained = null;
240
277
  let _turnPendingEntries = [];
278
+ let _turnDeferredToolDelta = null;
241
279
  // After the first turn, the next prompt comes from the drained queue.
242
280
  // (On the first iteration _pendingTail is empty and `prompt` is the
243
281
  // caller's original message.)
@@ -333,6 +371,16 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
333
371
  linkParentSignalToSession(sessionId, _linkedParentSignal);
334
372
  }
335
373
  markSessionAskStart(sessionId);
374
+ if (!_sessionStartEmitted) {
375
+ _sessionStartEmitted = true;
376
+ emitAskSessionStart(askOpts, {
377
+ sessionId,
378
+ agent: preSession.agent || null,
379
+ model: preSession.model || null,
380
+ provider: preSession.provider || null,
381
+ owner: preSession.owner || null,
382
+ });
383
+ }
336
384
  // Preprocessing is inside try so provider-not-available / trim failures
337
385
  // fall into the catch and mark the session as errored rather than
338
386
  // leaving stage='connecting' forever.
@@ -540,9 +588,13 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
540
588
  process.stderr.write(`[session] Warning: prompt is very large (est. ${Math.round(promptTokenEstimate)} tokens vs ${softBudget} soft budget)\n`);
541
589
  }
542
590
  const _currentTimeBlock = buildCurrentTimeBlock(prompt);
543
- const _turnReminderBlock = _currentTimeBlock
544
- ? `<system-reminder>\n# Current Time\n${_currentTimeBlock}\n</system-reminder>`
545
- : '';
591
+ _turnDeferredToolDelta = snapshotPendingDeferredToolDelta(session);
592
+ const _turnReminderBlock = [
593
+ _currentTimeBlock
594
+ ? `<system-reminder>\n# Current Time\n${_currentTimeBlock}\n</system-reminder>`
595
+ : '',
596
+ _turnDeferredToolDelta?.content || '',
597
+ ].filter(Boolean).join('\n\n');
546
598
  const _baseUserTurnContent = prefixUserTurnContent(prompt, _contextBlock);
547
599
  const _userTurnContent = prefixSessionStartContent(_baseUserTurnContent, _turnReminderBlock);
548
600
  cancelledUserTurnContent = _userTurnContent;
@@ -623,23 +675,10 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
623
675
  _scheduleTurnCheckpoint();
624
676
  if (typeof askOpts?.onTextDelta === 'function') askOpts.onTextDelta(chunk);
625
677
  };
626
- const _trackTextReset = async (detail) => {
627
- // Replacement is opt-in and transactional: a delta-only
628
- // consumer cannot retract already exposed bytes, so absence,
629
- // false, or rejection must preserve the original partial and
630
- // force the provider's terminal no-replay behavior.
631
- if (typeof askOpts?.onTextReset !== 'function') return false;
632
- let acknowledged = false;
633
- try {
634
- acknowledged = await askOpts.onTextReset(detail) === true;
635
- } catch {
636
- return false;
637
- }
638
- if (!acknowledged) return false;
639
- _turnInterruption.tombstoneText(detail?.chars);
678
+ const _trackTextReset = async (detail) => acknowledgeAskTextReset(askOpts, detail, (resetDetail) => {
679
+ _turnInterruption.tombstoneText(resetDetail?.chars);
640
680
  _scheduleTurnCheckpoint(true);
641
- return true;
642
- };
681
+ });
643
682
  const _trackReasoningDelta = (chunk) => {
644
683
  _turnInterruption.recordReasoningDelta(chunk);
645
684
  _scheduleTurnCheckpoint();
@@ -679,9 +718,13 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
679
718
  onReasoningDelta: _trackReasoningDelta,
680
719
  onAssistantText: _trackAssistantText,
681
720
  takeAssistantTranscriptMetadata: _takeAssistantTranscriptMetadata,
682
- onAssistantMessageCommitted: () => {
721
+ // Send-opt only. Never copy onto `session` — a transient
722
+ // interactive/live flag must not survive this ask.
723
+ liveProjection: resolveAskLiveProjection(askOpts),
724
+ onAssistantMessageCommitted: (message) => {
683
725
  _turnInterruption.markAssistantMessageCommitted();
684
726
  _scheduleTurnCheckpoint(true);
727
+ try { askOpts?.onAssistantMessageCommitted?.(message); } catch {}
685
728
  },
686
729
  onAssistantToolCallObserved: (call, detail) => {
687
730
  _turnInterruption.recordToolCalls([call], detail);
@@ -1008,6 +1051,7 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
1008
1051
  promptTokens,
1009
1052
  prefixHash: prefixHashForLog,
1010
1053
  costUsd,
1054
+ transportTiming: result.transportTiming || null,
1011
1055
  });
1012
1056
  recordStandaloneStatusTelemetry(session, result, Date.now() - _askStartedAt);
1013
1057
  }
@@ -1020,6 +1064,9 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
1020
1064
  } else if (result.providerStateUpdated === true || result.providerState !== undefined) {
1021
1065
  session.providerState = result.providerState;
1022
1066
  }
1067
+ if (_turnDeferredToolDelta) {
1068
+ acknowledgePendingDeferredToolDelta(session, _turnDeferredToolDelta.revision);
1069
+ }
1023
1070
  const terminalResultPreview = {
1024
1071
  ...result,
1025
1072
  trimmed: messagesDropped > 0,
@@ -1214,13 +1261,25 @@ export async function askSession(sessionId, prompt, context, onToolCall, cwdOver
1214
1261
  // renderer's ephemeral failure toast.
1215
1262
  try {
1216
1263
  const _status = Number(err?.httpStatus || err?.status || err?.response?.status || 0) || 0;
1264
+ const _errorDetails = err?.details && typeof err.details === 'object'
1265
+ ? err.details
1266
+ : null;
1217
1267
  const _parts = [
1218
1268
  `session=${sessionId}`,
1219
1269
  `name=${err?.name || 'Error'}`,
1220
1270
  _status ? `status=${_status}` : null,
1221
1271
  err?.code ? `code=${err.code}` : null,
1222
1272
  err?.providerErrorType ? `type=${err.providerErrorType}` : null,
1223
- `kind=${classifyError(err)}`,
1273
+ `kind=${_errorDetails?.kind || classifyError(err)}`,
1274
+ _errorDetails?.reason ? `reason=${_errorDetails.reason}` : null,
1275
+ _errorDetails?.source ? `source=${_errorDetails.source}` : null,
1276
+ Number.isFinite(Number(_errorDetails?.index)) ? `index=${_errorDetails.index}` : null,
1277
+ Number.isFinite(Number(_errorDetails?.previousCount))
1278
+ ? `previousCount=${_errorDetails.previousCount}`
1279
+ : null,
1280
+ Number.isFinite(Number(_errorDetails?.nextCount))
1281
+ ? `nextCount=${_errorDetails.nextCount}`
1282
+ : null,
1224
1283
  Number.isFinite(Number(err?.attempts)) ? `attempts=${err.attempts}` : null,
1225
1284
  Number.isFinite(Number(err?.midstreamRetries)) ? `midstreamRetries=${err.midstreamRetries}` : null,
1226
1285
  err?.midstreamClassifier ? `midstream=${err.midstreamClassifier}` : null,
@@ -16,6 +16,8 @@ import {
16
16
  import { estimateMessagesTokens, estimateRequestReserveTokens, estimateTranscriptContextUsage, resolveCompactBufferRatio } from '../context-utils.mjs';
17
17
  import { executeInternalTool } from '../../internal-tools.mjs';
18
18
  import {
19
+ callMemoryColdStart,
20
+ recallMemoryTimeoutMs,
19
21
  runRecallFastTrackCompact,
20
22
  } from '../loop/recall-fasttrack.mjs';
21
23
  import {
@@ -34,9 +36,11 @@ import {
34
36
  compactTargetBudget as compactTargetBudgetForPolicy,
35
37
  currentContextEstimateTokens,
36
38
  invalidateProviderContextBaseline,
39
+ recordContextUsageSnapshot,
37
40
  resolveGaugeContextTokens,
38
41
  resolveWorkerCompactPolicy,
39
42
  } from '../loop/compact-policy.mjs';
43
+ import { snapshotProviderRequestTools } from '../../../../../session-runtime/tool-catalog.mjs';
40
44
 
41
45
  // 'compacting' is a transient in-flight stage written just before semantic /
42
46
  // recall-fasttrack compaction runs. If the process crashes or only partially
@@ -58,12 +62,19 @@ export function normalizeStaleCompactingStage(session) {
58
62
 
59
63
  // Manual/auto-clear compaction needs the same threshold and post-compact
60
64
  // target math as the loop, even when automatic compaction is disabled.
61
- export function resolveSessionCompactionPolicy(session) {
65
+ export function resolveSessionCompactionPolicy(session, messages = session?.messages) {
62
66
  if (!session) return null;
67
+ const requestTools = snapshotProviderRequestTools({
68
+ provider: session.provider,
69
+ tools: session.tools || [],
70
+ nativeTools: [],
71
+ messages: Array.isArray(messages) ? messages : [],
72
+ session,
73
+ });
63
74
  return resolveWorkerCompactPolicy({
64
75
  ...session,
65
76
  compaction: { ...(session.compaction || {}), auto: true },
66
- }, session.tools || []);
77
+ }, requestTools);
67
78
  }
68
79
  function addCompactUsageToSession(session, usage) {
69
80
  if (!session || !usage) return;
@@ -79,44 +90,9 @@ function addCompactUsageToSession(session, usage) {
79
90
  session.totalUncachedInputTokens = (session.totalUncachedInputTokens || 0) + uncachedInputTokens;
80
91
  session.tokensCumulative = (session.tokensCumulative || 0) + inputTokens + outputTokens;
81
92
  }
82
- // A stalled memory call must NEVER wedge the
83
- // compact//clear path. Bound every recall-fasttrack memory call with a short
84
- // local timeout: on timeout we abort (best-effort cancel via a chained signal)
85
- // and treat it exactly like an RPC failure, so compaction proceeds WITHOUT
86
- // recall-fasttrack instead of hanging.
87
- const RECALL_MEMORY_CALL_TIMEOUT_MS = Math.max(
88
- 250,
89
- Number(process.env.MIXDOG_AGENT_COMPACT_RECALL_TIMEOUT_MS) || 4000,
90
- );
91
- function recallMemoryTimeoutMs(session) {
92
- const configured = positiveContextWindow(session?.compaction?.recallMemoryTimeoutMs);
93
- // Clamp ALL sources (session-config included) to the 250ms floor so a
94
- // misconfigured tiny value can't turn the bound into a busy no-wait.
95
- return Math.max(250, configured || RECALL_MEMORY_CALL_TIMEOUT_MS);
96
- }
97
- // Cold-start allowance (clear/manual path only): a booting memory runtime can
98
- // miss the tight first bound (waitForPort + first-RPC warmup ~2-10s). On a
99
- // timeout we retry ONCE with a longer bound before honoring the bail-to-
100
- // semantic contract, so a rebooting runtime succeeds instead of instantly
101
- // failing. Non-timeout errors and outer aborts propagate immediately.
102
- // 15s: memory boot is ~2-4s warm-cache / ~10s worst since the PG fast-start
103
- // fix; keeping this tight bounds the clear path's worst case (2 memory calls
104
- // retried + 120s semantic) under the TUI auto-clear watchdog (180s).
105
- const RECALL_COLD_START_TIMEOUT_MS = 15_000;
106
- function isTimeoutError(err) {
107
- return typeof err?.message === 'string' && err.message.includes('timed out after');
108
- }
109
- async function callMemoryColdStart(args, callerCtx, timeoutMs, executeMemory) {
110
- try {
111
- return await callMemoryBounded(args, callerCtx, timeoutMs, executeMemory);
112
- } catch (err) {
113
- if (!isTimeoutError(err) || callerCtx?.signal?.aborted) throw err;
114
- const coldMs = Math.max(timeoutMs, RECALL_COLD_START_TIMEOUT_MS);
115
- if (coldMs <= timeoutMs) throw err;
116
- try { process.stderr.write(`[session] recall-fasttrack ${args?.action || 'call'} cold-start retry (${timeoutMs}ms -> ${coldMs}ms)\n`); } catch {}
117
- return await callMemoryBounded(args, callerCtx, coldMs, executeMemory);
118
- }
119
- }
93
+ // Recall-fasttrack memory bounds live with the pipeline itself
94
+ // (loop/recall-fasttrack.mjs) so every caller — this one and the pre-send
95
+ // compaction — shares one timeout contract instead of each wiring its own.
120
96
  // Semantic-compact timeout scales with transcript size (clear/manual path):
121
97
  // default max(30s, ~10s per 25k estimated message tokens) capped at 120s, so a
122
98
  // large (~100k-token) transcript no longer dies on a fixed 30s bound.
@@ -127,34 +103,6 @@ function semanticCompactTimeoutMs(session, messageTokens) {
127
103
  const scaled = Math.ceil((messageTokens || 0) / 25_000) * 10_000;
128
104
  return Math.min(120_000, Math.max(30_000, scaled));
129
105
  }
130
- async function callMemoryBounded(args, callerCtx, timeoutMs, executeMemory = executeInternalTool) {
131
- const ac = new AbortController();
132
- const outer = callerCtx?.signal;
133
- const onOuterAbort = () => { try { ac.abort(); } catch {} };
134
- if (outer) {
135
- if (outer.aborted) ac.abort();
136
- else { try { outer.addEventListener?.('abort', onOuterAbort, { once: true }); } catch {} }
137
- }
138
- let timer = null;
139
- const timeout = new Promise((_, reject) => {
140
- timer = setTimeout(() => {
141
- try { ac.abort(); } catch {}
142
- reject(new Error(`memory ${args?.action || 'call'} timed out after ${timeoutMs}ms`));
143
- }, timeoutMs);
144
- try { timer.unref?.(); } catch {}
145
- });
146
- try {
147
- return await Promise.race([
148
- executeMemory('memory', args, { ...callerCtx, signal: ac.signal }),
149
- timeout,
150
- ]);
151
- } finally {
152
- if (timer) clearTimeout(timer);
153
- // Drop the chained-abort listener when the call settles first, so a
154
- // later outer abort can't fire into a dead controller / leak.
155
- try { outer?.removeEventListener?.('abort', onOuterAbort); } catch {}
156
- }
157
- }
158
106
  // Element-identity change detection (same approach as loop.mjs messagesArrayChanged): two
159
107
  // arrays are "unchanged" only when same length AND every slot is the same object
160
108
  // reference. Used to reject a no-op prune (which returns a fresh array whose
@@ -438,11 +386,12 @@ export async function runSessionCompaction(session, opts = {}) {
438
386
  try { beforeEncoded = JSON.stringify(messages); } catch { beforeEncoded = ''; }
439
387
  try { afterEncoded = JSON.stringify(compacted); } catch { afterEncoded = ''; }
440
388
  const afterMessageTokens = estimateMessagesTokens(compacted);
389
+ const postCompactPolicy = resolveSessionCompactionPolicy(session, compacted) || alignedPolicy;
441
390
  // Same scale as beforeTokens: compaction invalidates the provider baseline,
442
391
  // so the gauge's post-compact number is the calibrated transcript estimate
443
392
  // plus the request reserve. The raw sum reported roughly half of that.
444
- const afterTokens = alignedPolicy
445
- ? currentContextEstimateTokens(afterMessageTokens, alignedPolicy)
393
+ const afterTokens = postCompactPolicy
394
+ ? currentContextEstimateTokens(afterMessageTokens, postCompactPolicy)
446
395
  : afterMessageTokens + reserveTokens;
447
396
  const changed = beforeEncoded && afterEncoded
448
397
  ? beforeEncoded !== afterEncoded
@@ -471,7 +420,8 @@ export async function runSessionCompaction(session, opts = {}) {
471
420
  triggerTokens,
472
421
  bufferTokens,
473
422
  bufferRatio,
474
- reserveTokens,
423
+ requestReserveTokens: postCompactPolicy?.requestReserveTokens || 0,
424
+ reserveTokens: postCompactPolicy?.reserveTokens ?? reserveTokens,
475
425
  type: compactType,
476
426
  compactType,
477
427
  lastCompactType: compactType,
@@ -503,7 +453,18 @@ export async function runSessionCompaction(session, opts = {}) {
503
453
  } : null,
504
454
  compactCount: (session.compaction?.compactCount || 0) + (changed ? 1 : 0),
505
455
  };
506
- if (changed) invalidateProviderContextBaseline(session);
456
+ if (changed) {
457
+ invalidateProviderContextBaseline(session);
458
+ if (postCompactPolicy) {
459
+ recordContextUsageSnapshot(session, postCompactPolicy, {
460
+ messages: compacted,
461
+ usedTokens: afterTokens,
462
+ messageTokensEst: afterMessageTokens,
463
+ source: 'post_compact',
464
+ updatedAt: now,
465
+ });
466
+ }
467
+ }
507
468
  // Observability parity with the loop's pre-send pass: record the
508
469
  // out-of-loop mutation as compact_meta and park a one-shot intent so the
509
470
  // next turn's first send tags its cache break instead of logging an
@@ -6,7 +6,12 @@ import { loadSession, saveSessionAsync, setLiveSession, evictLiveSession, listSt
6
6
  import { estimateMessagesTokens, estimateTranscriptContextUsage } from '../context-utils.mjs';
7
7
  import { normalizeCompactType, DEFAULT_COMPACT_TYPE, SUMMARY_PREFIX } from '../compact.mjs';
8
8
  import { runSessionCompaction, resolveSessionCompactionPolicy } from './compaction-runner.mjs';
9
- import { resolveGaugeContextTokens } from '../loop/compact-policy.mjs';
9
+ import {
10
+ currentContextEstimateTokens,
11
+ invalidateContextUsageSnapshot,
12
+ recordContextUsageSnapshot,
13
+ resolveGaugeContextTokens,
14
+ } from '../loop/compact-policy.mjs';
10
15
  import { hasUserConversationMessage, promptContentText, resetSessionBp3Environment } from './prompt-utils.mjs';
11
16
  import { getProvider } from '../../providers/registry.mjs';
12
17
  import { isSessionCompactionBlocked, getSessionAbortSignal, _runtimeEntries } from './runtime-liveness.mjs';
@@ -161,7 +166,10 @@ export async function clearSessionMessages(sessionId, options = {}) {
161
166
  const beforeTokens = (clearPolicy
162
167
  ? resolveGaugeContextTokens(beforeMessageTokens, clearPolicy, { messages, sessionRef: session })
163
168
  : 0) || estimateTranscriptContextUsage(messages, session.tools || [], { provider: session.provider });
164
- const afterTokens = estimateTranscriptContextUsage(keep, session.tools || [], { provider: session.provider });
169
+ const postClearPolicy = resolveSessionCompactionPolicy(session, keep);
170
+ const afterTokens = postClearPolicy
171
+ ? currentContextEstimateTokens(afterMessageTokens, postClearPolicy)
172
+ : estimateTranscriptContextUsage(keep, session.tools || [], { provider: session.provider });
165
173
  const now = Date.now();
166
174
  // --- Fork the outgoing transcript to a separate resumable session BEFORE
167
175
  // the wipe below. Runs for every clear path (plain /clear, auto-clear,
@@ -264,6 +272,17 @@ export async function clearSessionMessages(sessionId, options = {}) {
264
272
  lastClearCompactType: clearCompactType || session.compaction?.compactType || null,
265
273
  lastClearCompactError: clearCompactError?.message || null,
266
274
  };
275
+ if (summaryCarriedForward && postClearPolicy) {
276
+ recordContextUsageSnapshot(session, postClearPolicy, {
277
+ messages: keep,
278
+ usedTokens: afterTokens,
279
+ messageTokensEst: afterMessageTokens,
280
+ source: 'post_clear',
281
+ updatedAt: now,
282
+ });
283
+ } else {
284
+ invalidateContextUsageSnapshot(session);
285
+ }
267
286
  session.updatedAt = now;
268
287
  await saveSessionAsync(session, { expectedGeneration: session.generation });
269
288
  return session;
@@ -21,7 +21,6 @@ import {
21
21
  _buildLeadMetaContext,
22
22
  } from './rules-cache.mjs';
23
23
  import {
24
- applyToolPermissionNarrowing,
25
24
  finalizeSessionToolList,
26
25
  resolveSessionTools,
27
26
  permissionFromToolSpec,
@@ -213,11 +212,8 @@ export function createSession(opts) {
213
212
  const resolvedAgent = opts.agent || opts.role || profile?.taskType || null;
214
213
  const hiddenAgent = getHiddenAgent(resolvedAgent);
215
214
  const isRetrievalAgent = hiddenAgent?.kind === 'retrieval';
216
- // Skill schema is fixed for public agent sessions, but hidden retrieval /
217
- // maintenance roles are deliberately narrowed away from the Skill tool.
218
- // Do not leak a Skill manifest into those hidden prompts when no Skill()
219
- // loader is available.
220
- const skills = (opts.skipSkills || hiddenAgent) ? [] : collectPromptSkillsCached(opts.cwd);
215
+ // Lead and Agent share the same cwd-scoped Skill inventory.
216
+ const skills = opts.skipSkills ? [] : collectPromptSkillsCached(opts.cwd);
221
217
 
222
218
  // BP1 is shared tool policy. BP2 holds persistent profile/tool catalogs;
223
219
  // BP3 holds workflow/role and session/project environment.
@@ -230,7 +226,6 @@ export function createSession(opts) {
230
226
  // override role-inapplicable entries.
231
227
  const sessionDeny = [
232
228
  ...(Array.isArray(opts.disallowedTools) ? opts.disallowedTools : []),
233
- ...(hiddenAgent ? ['Skill'] : []),
234
229
  ...(!ownerIsAgent && workflowDisallowsAgentTool(opts.workflow) ? ['agent'] : []),
235
230
  ];
236
231
  // The edit dialect this model never receives is omitted from the rules as
@@ -245,60 +240,31 @@ export function createSession(opts) {
245
240
  ? _buildAgentRules(agentRulesProfile)
246
241
  : _buildLeadRules({ includeLeadBrief: !delegationFree }));
247
242
  const metaContext = skipAgentRules ? '' : (ownerIsAgent ? '' : _buildLeadMetaContext());
248
- // Prompt permission is metadata for the write bundle, but a read-only role
249
- // is stamped BEFORE the toolSpec decision so its schema ships the narrowed
250
- // bundle. Resolve toolPermission (with profile/preset fallbacks) first, and
251
- // let the stored/logged `permission` reflect that resolved value — not just
252
- // opts.permission — so diagnostics show the effective read/write class.
243
+ // Role permission is prompt/diagnostic metadata only. Resolve and persist
244
+ // it without shaping the provider-visible Agent schema.
253
245
  const toolPermission = opts.permission
254
246
  || profile?.permission
255
247
  || permissionFromToolSpec(toolPreset)
256
248
  || null;
257
249
  const permission = toolPermission;
258
250
 
259
- // Agent sessions do not inherit arbitrary role/profile/preset tool
260
- // narrowing — that would shatter provider prefix reuse into one shard per
261
- // role. Instead they collapse onto exactly TWO stable, bit-identical
262
- // bundles, one cache group each:
263
- // - read-only roles (reviewer / hidden retrieval, i.e. any
264
- // session resolving to permission 'read') -> 'readonly' bundle:
265
- // read builtins (code_graph/find/glob/list/grep/read) + retrieval
266
- // (web_search/web_fetch/Skill) + shell/task for self-verification
267
- // (agent-owned readonly bundle only), no apply_patch, no MCP-write.
268
- // applyToolPermissionNarrowing('read') below trims the
269
- // bundle to AGENT_STRING_PERMISSION_READ_ALLOW so the final surface is
270
- // bit-identical across these roles regardless of MCP registry state.
271
- // - write roles (worker / heavy-worker / maintainer / …) -> 'full'
272
- // bundle: the historical full schema.
273
- // Call-time permission enforcement below is UNCHANGED (defense in depth):
274
- // applyToolPermissionNarrowing still runs so the bundle choice never
275
- // widens effective access.
276
- const isReadOnlyAgentBundle = ownerIsAgent && toolPermission === 'read';
251
+ // Exactly two schema surfaces exist: Lead and Agent. Every Agent role gets
252
+ // the full Lead-capable base bundle; the recursive `agent` control tool is
253
+ // removed later at the owner boundary. Role permission remains prompt/
254
+ // diagnostic metadata and does not fragment the provider-visible schema.
277
255
  const toolSpec = ownerIsAgent
278
- ? (isReadOnlyAgentBundle ? 'readonly' : 'full')
256
+ ? 'full'
279
257
  : (Array.isArray(profile?.tools) ? profile.tools : toolPreset);
280
258
  let toolsForRouting = resolveSessionTools(toolSpec, skills, {
281
259
  ownerIsAgentSession: ownerIsAgent,
282
260
  mcpScopeId: opts.mcpScopeId || null,
283
261
  modelName,
284
262
  });
285
- // Fail-closed permission intersection: when a session declares an explicit
286
- // object-form permission, intersect the
287
- // resolved tool list with the permission's allow/deny lists. If the
288
- // intersection produces an empty set the permission config is broken —
289
- // fail closed (zero tools) rather than silently falling back to the full
290
- // preset, which would grant the role more surface than declared.
291
- if (ownerIsAgent) {
292
- // Pass the RESOLVED agent (opts.agent || opts.role): narrowing keys
293
- // retrieval/locator roles off this name to strip
294
- // shell/task; verifying read roles (reviewer) keep them.
295
- toolsForRouting = applyToolPermissionNarrowing(toolsForRouting, toolPermission, resolvedAgent);
296
- }
297
263
 
298
264
  const workflowMeta = toSessionWorkflowMeta(opts.workflow);
299
265
  const hasCallerAllow = Array.isArray(opts.schemaAllowedTools);
300
266
  const tools = finalizeSessionToolList(toolsForRouting, {
301
- schemaAllowedTools: hasCallerAllow ? opts.schemaAllowedTools : null,
267
+ schemaAllowedTools: ownerIsAgent ? null : (hasCallerAllow ? opts.schemaAllowedTools : null),
302
268
  disallowedTools: sessionDeny,
303
269
  ownerIsAgent,
304
270
  resolvedAgent,
@@ -478,7 +444,9 @@ export function createSession(opts) {
478
444
  profileId: profile?.id || null,
479
445
  permissionMode: opts.permissionMode ?? null,
480
446
  providerCacheOpts: providerCacheOpts || null,
447
+ parentSessionId: opts.parentSessionId || null,
481
448
  ownerSessionId: opts.ownerSessionId || null,
449
+ visibility: opts.visibility || null,
482
450
  clientHostPid: opts.clientHostPid || null,
483
451
  };
484
452
  refreshSessionBp3Environment(session, opts.cwd);
@@ -643,7 +611,7 @@ const _preparedResumes = new Map();
643
611
 
644
612
  function _prepareResumeTools(session, preset) {
645
613
  const ownerIsAgent = isAgentOwner(session);
646
- const skills = ownerIsAgent ? [] : collectPromptSkillsCached(session.cwd);
614
+ const skills = collectPromptSkillsCached(session.cwd);
647
615
  let toolSpec = ownerIsAgent ? 'full' : (preset || session.preset || 'full');
648
616
  const agentRuntime = getAgentRuntimeSync();
649
617
  if (session.profileId && agentRuntime?.getProfile) {
@@ -657,19 +625,17 @@ function _prepareResumeTools(session, preset) {
657
625
  mcpScopeId: session.mcpScopeId || null,
658
626
  modelName: session.model,
659
627
  });
660
- if (ownerIsAgent) {
661
- toolsForRouting = applyToolPermissionNarrowing(toolsForRouting, session.toolPermission, session.agent || null);
662
- }
663
628
  return {
664
629
  session,
665
630
  preset,
666
631
  toolSpec,
667
632
  ownerIsAgent,
668
633
  tools: finalizeSessionToolList(toolsForRouting, {
669
- schemaAllowedTools: Array.isArray(session.schemaAllowedTools) ? session.schemaAllowedTools : null,
634
+ schemaAllowedTools: ownerIsAgent
635
+ ? null
636
+ : (Array.isArray(session.schemaAllowedTools) ? session.schemaAllowedTools : null),
670
637
  disallowedTools: [
671
638
  ...(Array.isArray(session.disallowedTools) ? session.disallowedTools : []),
672
- ...(getHiddenAgent(session.agent || null) ? ['Skill'] : []),
673
639
  ...(!isAgentOwner(session) && workflowDisallowsAgentTool(session.workflow) ? ['agent'] : []),
674
640
  ],
675
641
  ownerIsAgent,
@@ -715,6 +681,14 @@ export function prefetchSession(sessionId, preset = 'full') {
715
681
  export function prepareSessionProjection(session, preset = 'full') {
716
682
  if (!session?.id) return null;
717
683
  const prepared = _preparedResumeForSession(session, preset);
684
+ // This read-only projection describes the last provider-visible session,
685
+ // not the transient bootstrap bundle that resumeSession installs before
686
+ // session-runtime reapplies deferred selections. Keeping the persisted
687
+ // active surface preserves its provider baseline/tool signature while the
688
+ // cached `prepared.tools` remains available for the real resume.
689
+ const projectionTools = Array.isArray(session.tools)
690
+ ? session.tools
691
+ : prepared.tools;
718
692
  let contextMeta = null;
719
693
  try {
720
694
  const provider = session.provider ? getProvider(session.provider) : null;
@@ -727,7 +701,7 @@ export function prepareSessionProjection(session, preset = 'full') {
727
701
  return {
728
702
  ...session,
729
703
  toolSpec: prepared.toolSpec,
730
- tools: prepared.tools,
704
+ tools: projectionTools,
731
705
  ...(contextMeta ? {
732
706
  contextWindow: contextMeta.contextWindow,
733
707
  rawContextWindow: contextMeta.rawContextWindow,
@@ -58,9 +58,12 @@ export function recordStandaloneStatusTelemetry(session, result, durationMs) {
58
58
  ...summarizeGatewayUsage(routeInfo, providerOut, _compactArg, durationMs),
59
59
  requestKind: 'chat',
60
60
  sessionId: session.id || null,
61
+ sourceType: session.sourceType || 'lead',
62
+ sourceName: session.sourceName || session.agent || null,
61
63
  toolCount: result.toolCallsTotal ?? null,
62
64
  messageCount: Array.isArray(session.messages) ? session.messages.length : null,
63
65
  cacheStrategy: session.providerCacheOpts?.cacheStrategy || null,
66
+ transportTiming: result.transportTiming || null,
64
67
  };
65
68
  recordGatewayUsageEvent(summary);
66
69
  } catch {