mixdog 1.3.0 → 1.3.2

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 (423) hide show
  1. package/NOTICE.md +52 -9
  2. package/README.md +8 -4
  3. package/package.json +2 -2
  4. package/scripts/native-release-status.mjs +238 -0
  5. package/src/defaults/shipped-history.json +8 -1
  6. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  7. package/src/defaults/skills/setup/SKILL.md +23 -1
  8. package/src/defaults/skills/setup/references/actions.md +54 -4
  9. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  10. package/src/defaults/skills/setup-guide/SKILL.md +17 -10
  11. package/src/defaults/skills/visualize/SKILL.md +9 -6
  12. package/src/defaults/skills/visualize/references/craft.md +8 -1
  13. package/src/help.mjs +1 -1
  14. package/src/lib/keychain-cjs.cjs +4 -1
  15. package/src/lib/rules-builder.cjs +14 -7
  16. package/src/output-styles/common.md +13 -8
  17. package/src/rules/agent-mode/AGENT_MODE.md +9 -6
  18. package/src/rules/agent-mode/MIXY.md +11 -6
  19. package/src/rules/lead/LEAD.md +2 -2
  20. package/src/rules/shared/10-tool-workflow.md +4 -0
  21. package/src/rules/shared/20-research.md +2 -0
  22. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  23. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  24. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  25. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  26. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  27. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  28. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  29. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  31. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  32. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +81 -16
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -4
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  36. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  37. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +80 -31
  38. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +67 -38
  41. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +7 -2
  43. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  44. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +34 -16
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  48. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  49. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +19 -1
  50. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  51. package/src/runtime/agent/orchestrator/providers/lib/tool-call-wire-ids.mjs +65 -0
  52. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  53. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  54. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  55. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  56. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  57. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  58. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +8 -2
  59. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +10 -2
  60. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  61. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  62. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  64. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  65. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +14 -2
  66. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -0
  67. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +17 -0
  68. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  69. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  70. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  71. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  72. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  73. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +5 -5
  74. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +12 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +17 -6
  78. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +41 -10
  79. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +6 -4
  80. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  81. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  83. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  84. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  85. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +21 -2
  86. package/src/runtime/agent/orchestrator/session/agent-room.mjs +149 -45
  87. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  88. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  89. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +14 -11
  90. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  91. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +68 -18
  92. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  93. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  94. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  95. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +5 -1
  96. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +1 -1
  97. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  99. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +230 -0
  100. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +156 -25
  101. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  102. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +21 -9
  103. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  104. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  105. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  106. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +189 -105
  108. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +9 -0
  109. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  110. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +111 -9
  111. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +11 -14
  112. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  113. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +38 -5
  114. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +21 -0
  115. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  116. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +46 -4
  119. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  120. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +18 -8
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +4 -0
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  124. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  125. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +99 -0
  126. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  128. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  129. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  130. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +50 -0
  131. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  132. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  134. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +62 -47
  135. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +6 -8
  136. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +12 -1
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +7 -1
  139. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  140. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  142. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +5 -2
  143. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +42 -10
  144. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  145. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +35 -4
  146. package/src/runtime/agent/orchestrator/session/routine-result.mjs +179 -16
  147. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -1
  148. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  149. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  150. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  151. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  152. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  153. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +8 -3
  154. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +36 -1
  155. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -1
  156. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  157. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  158. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  159. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  160. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  161. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +49 -14
  162. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +134 -17
  163. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +35 -13
  164. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -3
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  167. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  168. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  169. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  170. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +6 -14
  171. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  175. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  176. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +34 -21
  177. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  178. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  179. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  180. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  181. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  182. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  183. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  184. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  185. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  186. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  187. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  188. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  189. package/src/runtime/attachments/limits.mjs +5 -1
  190. package/src/runtime/attachments/office-extract.mjs +3 -6
  191. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  192. package/src/runtime/attachments/store.mjs +55 -30
  193. package/src/runtime/browser-bridge/client.mjs +73 -20
  194. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  195. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  196. package/src/runtime/channels/lib/state-file.mjs +15 -15
  197. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  198. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  199. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  200. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  201. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  202. package/src/runtime/effort-judge/judge-client.mjs +6 -3
  203. package/src/runtime/effort-judge/model-install.mjs +16 -8
  204. package/src/runtime/effort-judge/model-manifest.json +7 -7
  205. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  206. package/src/runtime/local-provider/catalog.mjs +5 -2
  207. package/src/runtime/local-provider/registered-models.mjs +64 -27
  208. package/src/runtime/local-provider/server.mjs +2 -3
  209. package/src/runtime/media/adapters/image-api.mjs +89 -0
  210. package/src/runtime/media/auth.mjs +64 -1
  211. package/src/runtime/media/catalog.mjs +66 -4
  212. package/src/runtime/media/jobs.mjs +4 -0
  213. package/src/runtime/media/lanes.mjs +46 -5
  214. package/src/runtime/media/media-usage.mjs +17 -0
  215. package/src/runtime/media/renditions.mjs +15 -15
  216. package/src/runtime/media/store.mjs +36 -35
  217. package/src/runtime/media/tool.mjs +8 -1
  218. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  219. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  220. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  221. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  222. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  223. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  224. package/src/runtime/office/core/journal.mjs +7 -14
  225. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  226. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  227. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  228. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  229. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  230. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  231. package/src/runtime/office/portable/portable-soffice.mjs +4 -0
  232. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  233. package/src/runtime/shared/agent-kind.mjs +152 -0
  234. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  235. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  236. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  237. package/src/runtime/shared/agent-route-config.d.mts +1 -0
  238. package/src/runtime/shared/agent-route-config.mjs +22 -8
  239. package/src/runtime/shared/agent-store.mjs +149 -0
  240. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  241. package/src/runtime/shared/atomic-file.d.mts +50 -0
  242. package/src/runtime/shared/atomic-file.mjs +394 -132
  243. package/src/runtime/shared/background-tasks.mjs +7 -4
  244. package/src/runtime/shared/bot-hops.mjs +30 -0
  245. package/src/runtime/shared/bot-split-migration.mjs +457 -0
  246. package/src/runtime/shared/bounded-download.mjs +4 -2
  247. package/src/runtime/shared/config-patch.mjs +20 -1
  248. package/src/runtime/shared/config.mjs +39 -9
  249. package/src/runtime/shared/connector-catalog.d.mts +1 -2
  250. package/src/runtime/shared/connector-catalog.mjs +2 -4
  251. package/src/runtime/shared/connector-triggers.mjs +2 -0
  252. package/src/runtime/shared/defaults-separation.mjs +6 -2
  253. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  254. package/src/runtime/shared/err-text.mjs +7 -1
  255. package/src/runtime/shared/error-presentation.mjs +113 -10
  256. package/src/runtime/shared/file-has-data.mjs +28 -0
  257. package/src/runtime/shared/handoff-artifacts.d.mts +20 -0
  258. package/src/runtime/shared/handoff-artifacts.mjs +61 -0
  259. package/src/runtime/shared/llm/provider-model-identities.mjs +6 -1
  260. package/src/runtime/shared/llm/usage-accounting.mjs +7 -1
  261. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  262. package/src/runtime/shared/model-route-fallback.mjs +79 -7
  263. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  264. package/src/runtime/shared/plain-preview.mjs +22 -17
  265. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  266. package/src/runtime/shared/projects.mjs +50 -41
  267. package/src/runtime/shared/provider-accounts.mjs +17 -12
  268. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  269. package/src/runtime/shared/routine-trigger.mjs +14 -6
  270. package/src/runtime/shared/run-child.d.mts +3 -0
  271. package/src/runtime/shared/run-child.mjs +24 -3
  272. package/src/runtime/shared/sensitive-path-rules.d.mts +15 -0
  273. package/src/runtime/shared/sensitive-path-rules.mjs +256 -0
  274. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  275. package/src/runtime/shared/singleton-owner.mjs +7 -6
  276. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  277. package/src/runtime/shared/staged-update.mjs +4 -2
  278. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  279. package/src/runtime/shared/tool-execution-contract.mjs +6 -0
  280. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  281. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  282. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  283. package/src/runtime/shared/turn-snapshot.mjs +44 -2
  284. package/src/runtime/shared/usage-readout-totals.mjs +15 -13
  285. package/src/runtime/shared/user-cwd.mjs +9 -4
  286. package/src/runtime/shared/user-data-guard.mjs +46 -44
  287. package/src/runtime/shared/utf8-cut.mjs +10 -0
  288. package/src/runtime/shared/worktrees/activity-gate.mjs +269 -61
  289. package/src/runtime/shared/worktrees/registry.mjs +26 -26
  290. package/src/runtime/tidy/core-install.mjs +26 -4
  291. package/src/runtime/tidy/install.mjs +91 -23
  292. package/src/runtime/web-search/lib/config.mjs +4 -16
  293. package/src/session-runtime/agent-home-delivery.mjs +859 -136
  294. package/src/session-runtime/agent-room-delivery.mjs +167 -19
  295. package/src/session-runtime/agent-task-sessions.mjs +831 -69
  296. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  297. package/src/session-runtime/boot/apis.mjs +58 -24
  298. package/src/session-runtime/boot/config.mjs +1 -0
  299. package/src/session-runtime/boot/facade.mjs +11 -1
  300. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  301. package/src/session-runtime/boot/providers.mjs +16 -6
  302. package/src/session-runtime/boot/shared.mjs +12 -6
  303. package/src/session-runtime/boot/tools.mjs +2 -2
  304. package/src/session-runtime/bot-status.mjs +432 -0
  305. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  306. package/src/session-runtime/config-lifecycle.mjs +78 -32
  307. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  308. package/src/session-runtime/handoff-attachments.mjs +593 -0
  309. package/src/session-runtime/held-requests.mjs +36 -33
  310. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  311. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  312. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +45 -6
  313. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  314. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  315. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  316. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  317. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  318. package/src/session-runtime/media-api.mjs +5 -2
  319. package/src/session-runtime/model-route/route-persist.mjs +2 -2
  320. package/src/session-runtime/model-route/route-tuning.mjs +11 -2
  321. package/src/session-runtime/model-route/set-route.mjs +82 -2
  322. package/src/session-runtime/native-web-search.mjs +2 -2
  323. package/src/session-runtime/new-session-config.mjs +6 -1
  324. package/src/session-runtime/owned-automation-run.mjs +8 -1
  325. package/src/session-runtime/room-member-gate.mjs +16 -3
  326. package/src/session-runtime/route-resolution.mjs +21 -19
  327. package/src/session-runtime/route-state.mjs +1 -59
  328. package/src/session-runtime/routine-home-turn.mjs +5 -3
  329. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  330. package/src/session-runtime/runtime-paths.mjs +11 -5
  331. package/src/session-runtime/runtime-tunables.mjs +6 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  336. package/src/session-runtime/services/agent-tool/helpers.mjs +24 -5
  337. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  338. package/src/session-runtime/services/agent-tool/send-flow.mjs +36 -24
  339. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -0
  340. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  341. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +12 -6
  342. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +5 -8
  343. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  344. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  345. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  346. package/src/session-runtime/services/agent-tool.mjs +6 -0
  347. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  348. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  349. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  350. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  351. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  352. package/src/session-runtime/services/provider-admin.mjs +14 -4
  353. package/src/session-runtime/services/schedule-access.mjs +33 -4
  354. package/src/session-runtime/services/seeds.mjs +4 -3
  355. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  356. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  357. package/src/session-runtime/session-create/materialize.mjs +2 -3
  358. package/src/session-runtime/session-create/session-options.mjs +15 -2
  359. package/src/session-runtime/settings-builtin-tools-api.mjs +63 -1
  360. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  361. package/src/session-runtime/setup-tool/executor.mjs +20 -3
  362. package/src/session-runtime/setup-tool/extended-actions.mjs +19 -7
  363. package/src/session-runtime/setup-tool/settings-contract.mjs +37 -1
  364. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  365. package/src/session-runtime/skills-api.mjs +12 -5
  366. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +265 -0
  367. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  368. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  369. package/src/session-runtime/tool-policy/policy-stage.mjs +165 -0
  370. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  371. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  372. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  373. package/src/session-runtime/tool-policy-refresh.mjs +47 -358
  374. package/src/session-runtime/tool-surface.mjs +5 -3
  375. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +99 -81
  376. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  377. package/src/session-runtime/turn/turn-run.mjs +2 -1
  378. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  379. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +578 -301
  380. package/src/session-runtime/workflow-agents-api/agent-route.mjs +113 -53
  381. package/src/session-runtime/workflow-agents-api/onboarding.mjs +11 -4
  382. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  383. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  384. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  385. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  386. package/src/standalone/daemon-agent-control.mjs +4 -1
  387. package/src/standalone/daemon-session-control.mjs +60 -11
  388. package/src/standalone/daemon.mjs +34 -4
  389. package/src/standalone/local-session-runtime.mjs +22 -14
  390. package/src/standalone/session-client.mjs +7 -2
  391. package/src/standalone/session-protocol.mjs +7 -0
  392. package/src/standalone/session-service/agent-tree/agent-turns.mjs +46 -3
  393. package/src/standalone/session-service/agent-tree.mjs +4 -3
  394. package/src/standalone/session-service/session-calls/session-turns.mjs +19 -0
  395. package/src/standalone/session-service.mjs +11 -0
  396. package/src/standalone/session-state-projection.mjs +19 -9
  397. package/src/standalone/session-tool-output.mjs +35 -9
  398. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  399. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  400. package/src/tui/app/slash-commands.mjs +1 -1
  401. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  402. package/src/tui/dist/index.mjs +599 -342
  403. package/src/tui/prompt-history-store.mjs +16 -21
  404. package/src/tui/session/context-state.mjs +4 -0
  405. package/src/tui/session/notice-surface.mjs +3 -4
  406. package/src/tui/session/session-api/intake/abort.mjs +19 -1
  407. package/src/tui/session/session-api/media.mjs +2 -1
  408. package/src/tui/session/session-api/routes-agent-definitions.mjs +75 -0
  409. package/src/tui/session/session-api/routes-write.mjs +203 -0
  410. package/src/tui/session/session-api/routes.mjs +23 -236
  411. package/src/tui/session/session-api/settings.mjs +14 -1
  412. package/src/tui/session/session-flow/drain.mjs +28 -3
  413. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  414. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  415. package/src/tui/session/session-flow/steering.mjs +8 -3
  416. package/src/tui/session/tool-approval.mjs +185 -20
  417. package/src/tui/session/transcript-intake.mjs +4 -0
  418. package/src/tui/session/turn-live-call.mjs +11 -3
  419. package/src/tui/session/turn.mjs +36 -17
  420. package/src/tui/transcript-tool-failures.mjs +8 -2
  421. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  422. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  423. package/src/rules/agent-mode/ROUTINE.md +0 -6
@@ -4,10 +4,10 @@
4
4
  * consume a retry verdict, and settle a completed send into the loop state.
5
5
  */
6
6
  import { traceCacheBreak } from '../../cache-break-trace.mjs';
7
+ import { anthropicPrefixChanges } from '../../providers/lib/anthropic-prefix-trace.mjs';
7
8
  import { resolveLiveToolCwd } from './tool-exec.mjs';
8
9
  import { notifySkillsApplied, prepareExplicitSkills, recordAppliedSkills } from '../explicit-skills.mjs';
9
10
  import { addUsage, usageDeltaEvent } from './usage.mjs';
10
- import { isVisibleStreamProgress } from '../../../../shared/stream-progress.mjs';
11
11
  import { prepareProviderRequest } from './request-boundary.mjs';
12
12
  import { applyStepEffort } from './step-auto-effort.mjs';
13
13
  import { projectProviderRequest } from './request-projection.mjs';
@@ -182,15 +182,10 @@ export async function sendProviderRequest(state, round) {
182
182
  tools: round.sendTools,
183
183
  opts,
184
184
  });
185
- // The first visible token of this request, for its generation time (queue,
186
- // preflight and time-to-first-token stay out of the speed figures).
187
- let firstVisibleAt = 0;
188
- const callerOnStreamDelta = opts.onStreamDelta;
189
- const observeStreamDelta = (kind, ...rest) => {
190
- if (!firstVisibleAt && isVisibleStreamProgress(kind)) firstVisibleAt = Date.now();
191
- return callerOnStreamDelta?.(kind, ...rest);
192
- };
193
- opts.onStreamDelta = observeStreamDelta;
185
+ // The request's time for the speed figures, the wait for the first token
186
+ // included: the span the usage ledger recorded as `duration_ms` (stamped on
187
+ // the response); wall time only for a response no ledger row timed.
188
+ const requestStartedAt = Date.now();
194
189
  let result;
195
190
  try {
196
191
  result = await runWithProviderRequestToolsScope(round.requestToolScope, () =>
@@ -218,8 +213,6 @@ export async function sendProviderRequest(state, round) {
218
213
  sideQuestion?.discard();
219
214
  if (!(await advanceAgentRoute(state, error))) throw error;
220
215
  result = { action: 'retry_route' };
221
- } finally {
222
- if (opts.onStreamDelta === observeStreamDelta) opts.onStreamDelta = callerOnStreamDelta;
223
216
  }
224
217
  if (result.action === 'proceed') sideQuestion?.settle(result.response);
225
218
  else sideQuestion?.discard();
@@ -230,7 +223,9 @@ export async function sendProviderRequest(state, round) {
230
223
  prefixGuardCandidate,
231
224
  sendStartedAt,
232
225
  sendEndedAt,
233
- generationMs: firstVisibleAt ? Math.max(0, sendEndedAt - firstVisibleAt) : 0,
226
+ requestMs: Number.isFinite(result.response?.usageDurationMs)
227
+ ? Math.max(0, result.response.usageDurationMs)
228
+ : Math.max(0, sendEndedAt - requestStartedAt),
234
229
  preSendMs,
235
230
  toolResumeMs,
236
231
  };
@@ -358,6 +353,8 @@ function traceActualCacheMiss(state, sent, response, { previousGuardState, previ
358
353
  previousCachedPrefixTokens: baseline,
359
354
  idleMs,
360
355
  missingPrefixGuardBaseline: !previousGuardState,
356
+ // Which wire block moved ('none': same bytes, so the cache itself dropped it).
357
+ ...anthropicPrefixChanges(sessionId),
361
358
  },
362
359
  state.cacheBreakTraceOptions
363
360
  );
@@ -443,7 +440,7 @@ export function settleSendResult(state, round, sent) {
443
440
  usage: reportedUsage,
444
441
  sendTools: round.sendTools,
445
442
  toolCalls: response.toolCalls?.length || 0,
446
- generationMs: sent.generationMs,
443
+ requestMs: sent.requestMs,
447
444
  })
448
445
  )
449
446
  );
@@ -1,6 +1,7 @@
1
1
  // Steering-message normalization/merge helpers.
2
2
  // Merges queued steering entries into a single content payload + display text.
3
3
  import { stableMessageId } from '../../../../shared/transcript-metadata.mjs';
4
+ import { raiseTurnHop } from '../../../../shared/bot-hops.mjs';
4
5
 
5
6
  function steeringContentText(content) {
6
7
  if (typeof content === 'string') return content;
@@ -140,6 +141,8 @@ function injectSteeringEntry(merged, { messages, opts, stage, onSkillPrompt }) {
140
141
  // gets it back (onSteerMessage) for the row it draws. A steered prompt
141
142
  // keeps the id the surface gave it, else gets one.
142
143
  const messageId = stableMessageId(merged.transcriptMeta?.messageId);
144
+ // A bot's message at a higher hop makes the running turn bot-descended at that hop.
145
+ raiseTurnHop(opts.session, merged.transcriptMeta);
143
146
  const steeringTranscriptMeta =
144
147
  merged.transcriptMeta && typeof merged.transcriptMeta === 'object'
145
148
  ? { at: submittedAt ?? injectedAt, ...merged.transcriptMeta, messageId }
@@ -6,15 +6,20 @@ import { resolvePreToolAskApproval } from '../tool-helpers.mjs';
6
6
  import { runAbortable, throwIfAborted } from '../../../../../shared/abort-race.mjs';
7
7
  import { evaluateExecPolicy } from '../../../tools/bash-policy-scan.mjs';
8
8
  import { beginSessionApprovalWait } from '../../manager/runtime-liveness.mjs';
9
- import { agentMcpToolPermission } from '../../../mcp/agent-connectors.mjs';
10
- import { humanGateDecision, recordHumanDecision } from '../../human-gate/gate.mjs';
9
+ import { agentMcpToolPermission, writeAgentMcpToolPermission } from '../../../mcp/agent-connectors.mjs';
10
+ import {
11
+ actingContactBotId,
12
+ humanGateDecision,
13
+ recordHumanDecision,
14
+ rememberSurfaceOf,
15
+ } from '../../human-gate/gate.mjs';
11
16
  import { applyEditedArgs } from '../../human-gate/requests.mjs';
12
17
 
13
18
  const isArgsObject = (value) => value && typeof value === 'object' && !Array.isArray(value);
14
19
 
15
20
  async function askApproval(
16
21
  call,
17
- { callerSessionId, executeOpts, toolApprovalHook, decision, request = null, gate = null }
22
+ { callerSessionId, executeOpts, toolApprovalHook, decision, request = null, gate = null, onApproved = null }
18
23
  ) {
19
24
  const askReason = String(decision?.reason || 'approval requested by hook').trim();
20
25
  // While the user decides, the session is waiting on them, not working.
@@ -46,26 +51,52 @@ async function askApproval(
46
51
  }
47
52
  // The user approved this very call on the richer outbound card.
48
53
  if (request?.kind === 'outbound') call.outboundApproved = true;
54
+ await onApproved?.(approval);
49
55
  return null;
50
56
  }
51
57
 
58
+ /** The renderer translates this exact text (desktop-toasts.tsx ALLOW_NOT_SAVED_TEXT). */
59
+ export const ALLOW_NOT_SAVED_NOTICE = 'Allowed this once. The setting could not be saved.';
52
60
  export const CONNECTOR_APPROVAL_REASON = 'This connector tool is set to ask before it runs for this agent.';
53
61
 
54
62
  /**
55
63
  * A connector tool the running agent set to `needs_approval` asks through the
56
64
  * ordinary approval hook, whatever the PreToolUse hooks decided. Run after
57
65
  * them, on the final call: a renamed call is judged under its final name.
58
- * A call the outbound card already got approved is not asked a second time.
66
+ * A call the outbound gate owns (its card approved it, or a remembered
67
+ * outbound grant answered it) is not asked a second time.
59
68
  * @returns {Promise<{ denial: string } | null>} the denial, or null to run
60
69
  */
61
70
  export function applyConnectorApproval(call, { agentId, callerSessionId, executeOpts, toolApprovalHook }) {
62
71
  if (call.outboundApproved) return null;
63
72
  if (agentMcpToolPermission(agentId, call.name) !== 'needs_approval') return null;
73
+ // "Always allow" is offered only to the contact bot that is acting, for its
74
+ // own policy entry for this one tool: never another bot, Mixy or the Lead.
75
+ const botId = actingContactBotId(callerSessionId);
76
+ const rememberable = Boolean(botId) && botId === agentId;
64
77
  return askApproval(call, {
65
78
  callerSessionId,
66
79
  executeOpts,
67
80
  toolApprovalHook,
68
81
  decision: { reason: CONNECTOR_APPROVAL_REASON },
82
+ request: rememberable
83
+ ? {
84
+ kind: 'approval',
85
+ agentId: botId,
86
+ remember: ['once', 'agent'],
87
+ rememberAt: rememberSurfaceOf(callerSessionId),
88
+ }
89
+ : null,
90
+ onApproved: async (approval) => {
91
+ if (!rememberable || approval?.remember !== 'agent') return;
92
+ try {
93
+ if (await writeAgentMcpToolPermission(botId, call.name, 'auto')) return;
94
+ } catch {
95
+ // fall through to the notice below
96
+ }
97
+ // Not saved: this call still runs once, and the next one asks again.
98
+ toolApprovalHook?.notice?.(ALLOW_NOT_SAVED_NOTICE, 'warn');
99
+ },
69
100
  });
70
101
  }
71
102
 
@@ -120,7 +151,7 @@ export async function applyBeforeToolHook({
120
151
  // Judged as the call will run: only a rewriting decision changes name/args.
121
152
  const hookAction = String(hookDecision?.action || hookDecision?.decision || '').toLowerCase();
122
153
  const rewritten = hookAction === 'ask' || hookAction === 'modify' || hookAction === 'rewrite';
123
- const { decision, request, gate } = await humanGateDecision(
154
+ const { decision, request, gate, remembered } = await humanGateDecision(
124
155
  {
125
156
  name:
126
157
  rewritten && typeof hookDecision?.name === 'string' && hookDecision.name.trim()
@@ -132,6 +163,8 @@ export async function applyBeforeToolHook({
132
163
  { sessionId: callerSessionId, mcpScopeId }
133
164
  );
134
165
  const action = String(decision?.action || decision?.decision || '').toLowerCase();
166
+ // A remembered outbound grant answered this call: the connector ask is not repeated.
167
+ if (remembered) call.outboundApproved = true;
135
168
  if (action === 'deny' || action === 'block') {
136
169
  const reason = decision?.reason ? `: ${decision.reason}` : '';
137
170
  return { denial: `Error: tool "${name}" denied by hook${reason}` };
@@ -17,6 +17,26 @@ import { isOnDeferredToolSurface } from '../deferred-call-through.mjs';
17
17
  import { _executeCodeGraphToolLazy } from '../../manager/runtime-loaders.mjs';
18
18
  import { requiresReadBody } from '../tool-classify.mjs';
19
19
  import { executeCursorNativeBuiltin } from '../../../tools/builtin/external-tool-adapters.mjs';
20
+ import { providerNativePdf, providerReadsImages } from '../../../providers/registry.mjs';
21
+ import { getModelMetadataSync } from '../../../../../shared/llm/model-catalog.mjs';
22
+
23
+ /**
24
+ * How read returns a PDF to this session's model. Native PDF support decides
25
+ * only whether a short PDF may arrive whole as a document block. A scanned
26
+ * page arrives as a page image whenever the model sees images — the provider
27
+ * sends image input (only the text-only local runtime does not) and the
28
+ * catalog does not say the model lacks it — and as OCR text otherwise.
29
+ */
30
+ export function pdfReadModes(
31
+ sessionRef,
32
+ { nativePdf = providerNativePdf, readsImages = providerReadsImages, catalogRow = getModelMetadataSync } = {}
33
+ ) {
34
+ const provider = sessionRef?.provider;
35
+ const model = sessionRef?.model;
36
+ const seesImages =
37
+ readsImages(provider) && (model ? catalogRow(model, provider)?.supportsVision : undefined) !== false;
38
+ return { pdfPageMode: seesImages ? 'image' : 'ocr', pdfNativeDocument: nativePdf(provider) };
39
+ }
20
40
 
21
41
  function dispatchMcp(name, args, { cwd, callerSessionId, sessionRef, executeOpts }) {
22
42
  const mcpScopeId = sessionRef?.mcpScopeId || null;
@@ -107,6 +127,7 @@ export function dispatchToolCall({ name, args }, ctx) {
107
127
  : {}),
108
128
  // Runtime-granted only; any value inherited from executeOpts is overridden.
109
129
  elevationApproved: ctx.elevationApproved === true,
130
+ ...pdfReadModes(sessionRef),
110
131
  };
111
132
  return executeCursorNativeBuiltin(
112
133
  name,
@@ -6,6 +6,7 @@
6
6
  // tool-exec/after-hook.mjs — PostToolUse result override
7
7
  // tool-exec/read-only-deadline.mjs — bounded read-only I/O with partial-output grace
8
8
  import { isMcpTool } from '../../mcp/client.mjs';
9
+ import { connectorPolicyAgentOf } from '../../mcp/agent-connectors.mjs';
9
10
  import { prepareDeferredToolCallThrough } from './deferred-call-through.mjs';
10
11
  import { refreshDeferredMcpToolCatalog } from '../../runtime-core/tool-catalog.mjs';
11
12
  import { bridgeTargetUnavailable } from '../../runtime-core/tool-bridge.mjs';
@@ -54,7 +55,7 @@ async function executeToolOwned(name, args, cwd, callerSessionId, sessionRef, ex
54
55
  });
55
56
  if (hooked.denial) return hooked.denial;
56
57
  const connectorDenied = await applyConnectorApproval(hooked, {
57
- agentId: sessionRef?.agent,
58
+ agentId: connectorPolicyAgentOf(sessionRef),
58
59
  callerSessionId,
59
60
  executeOpts,
60
61
  toolApprovalHook,
@@ -8,6 +8,7 @@ import {
8
8
  bridgeSurfaceOutdated,
9
9
  sessionToolExposure,
10
10
  TOOL_EXPOSURE_BRIDGE,
11
+ TOOL_EXPOSURE_NATIVE,
11
12
  TOOL_SURFACE_MIGRATION_INTENT,
12
13
  toolExposureMigrationNeeded,
13
14
  toolExposureModeForProvider,
@@ -122,6 +123,17 @@ export function applyAgentToolExposure(session, { existing = false } = {}) {
122
123
  const bridge =
123
124
  !allowlisted && toolExposureModeForProvider(session.provider, session.wireRoute) === TOOL_EXPOSURE_BRIDGE;
124
125
  if (!bridge && before === null) return false;
126
+ // A native deferred surface (the session runtime builds one) stays one:
127
+ // re-selected over the tools just resolved, never stripped to the full
128
+ // array. Native → native is no exposure change, so nothing is tagged.
129
+ if (!bridge && before === TOOL_EXPOSURE_NATIVE) {
130
+ session.tools = session.tools.slice();
131
+ applyDeferredToolSurface(session, session.deferredSurfaceMode || 'full', [], {
132
+ provider: session.provider,
133
+ model: session.model,
134
+ });
135
+ return false;
136
+ }
125
137
  // A surface an earlier bridge revision built is rebuilt now.
126
138
  const outdated = bridgeSurfaceOutdated(session);
127
139
  // The tools just resolved are the whole authority: no earlier catalog, MCP
@@ -3,6 +3,7 @@
3
3
  // native tool-search payload parsing, bash-session routing, and approval.
4
4
  import { isMcpTool } from '../../mcp/client.mjs';
5
5
  import { unattendedApprovalPort } from '../human-gate/gate.mjs';
6
+ import { isGrantedRun, runAskOutcome } from '../human-gate/run-policy.mjs';
6
7
  import { isBuiltinTool } from '../../tools/builtin.mjs';
7
8
  import { isInternalTool } from '../../internal-tools.mjs';
8
9
  import { loadSkillToolDependencies } from '../../runtime-core/skill-tool-loading.mjs';
@@ -212,12 +213,13 @@ export async function resolvePreToolAskApproval({
212
213
  }) {
213
214
  const name = String(toolName || 'tool');
214
215
  const reason = String(askReason || 'approval requested by hook').trim();
215
- // Unattended runs never auto-approve and have no card: a schedule or webhook
216
- // run always holds the request for its owner; a room member does when no
216
+ // A schedule or webhook run never asks: saving it was the user's grant
217
+ // (human-gate/run-policy.mjs). A room member is held for its owner when no
217
218
  // approval UI is attached (session-runtime/held-requests.mjs).
218
219
  const unattended = unattendedApprovalPort();
219
220
  const run = unattended?.runOf?.(sessionId) || null;
220
- const held = run && (run.kind !== 'room' || typeof toolApprovalHook !== 'function');
221
+ if (isGrantedRun(run)) return runAskOutcome({ name, request });
222
+ const held = run && typeof toolApprovalHook !== 'function';
221
223
  const ask = held ? unattended.hold : toolApprovalHook;
222
224
  if (typeof ask !== 'function') {
223
225
  return { denial: formatMissingToolApprovalUiDenial(name, reason) };
@@ -238,8 +240,12 @@ export async function resolvePreToolAskApproval({
238
240
  return { denial: `Error: tool "${name}" denied by hook: ${detail}` };
239
241
  }
240
242
  if (!approvalGranted(approval)) {
241
- const detail = approvalReason(approval, reason || 'not approved');
242
- return { denial: `Error: tool "${name}" denied by hook: ${detail}`, approval };
243
+ // A person (or the hold for one) said no: never a policy hook to work around.
244
+ const detail = String(approvalReason(approval, reason || 'not approved')).replace(/[.\s]+$/, '');
245
+ return {
246
+ denial: `Error: tool "${name}" was not approved: ${detail}. Do not retry it or reach the same result another way; report it.`,
247
+ approval,
248
+ };
243
249
  }
244
250
  return { approval };
245
251
  }
@@ -41,7 +41,7 @@ export function usageDeltaEvent({
41
41
  usage,
42
42
  sendTools,
43
43
  toolCalls = 0,
44
- generationMs = 0,
44
+ requestMs = 0,
45
45
  }) {
46
46
  return {
47
47
  sessionId,
@@ -57,9 +57,10 @@ export function usageDeltaEvent({
57
57
  deltaToolCalls: toolCalls,
58
58
  ...reasoningUsage(usage),
59
59
  deltaPrompt: usage.promptTokens || 0,
60
- // First visible token → end of this response; 0 when the call was not
61
- // timed (nothing visible streamed). Only timed output enters speed figures.
62
- generationMs,
60
+ // Request start → end of this response (first-token wait included, as the
61
+ // ledger's duration_ms); 0 when the call was not timed. Only timed output
62
+ // enters speed figures.
63
+ requestMs,
63
64
  // A provider that cannot measure the prompt or its cache split says so;
64
65
  // its zeros are unknowns, not readings (no cache hit can be derived).
65
66
  promptKnown: usage.inputTokensKnown !== false && usage.cacheTokensKnown !== false,
@@ -79,6 +80,47 @@ export function usageDeltaEvent({
79
80
  };
80
81
  }
81
82
 
83
+ const SUMMED_USAGE_FIELDS = [
84
+ 'inputTokens',
85
+ 'outputTokens',
86
+ 'cachedTokens',
87
+ 'cacheWriteTokens',
88
+ 'cacheWrite1hTokens',
89
+ 'promptTokens',
90
+ ];
91
+
92
+ /** What `total` added after the running total `earlier` (both from addUsage); null when nothing. */
93
+ export function usageSince(total, earlier) {
94
+ if (!total) return null;
95
+ if (!earlier) return { ...total };
96
+ const delta = {};
97
+ for (const field of SUMMED_USAGE_FIELDS) delta[field] = Math.max(0, (total[field] || 0) - (earlier[field] || 0));
98
+ if (total.reasoningTokens != null) {
99
+ delta.reasoningTokens = Math.max(0, total.reasoningTokens - (earlier.reasoningTokens ?? 0));
100
+ delta.reasoningTokensComplete = total.reasoningTokensComplete === true;
101
+ }
102
+ if (total.costUsd != null) delta.costUsd = Math.max(0, total.costUsd - (earlier.costUsd || 0));
103
+ if (total.contextTokens) delta.contextTokens = total.contextTokens;
104
+ return Object.values(delta).some((value) => typeof value === 'number' && value > 0) ? delta : null;
105
+ }
106
+
107
+ /**
108
+ * The turn's usage split per model when the route switched mid-turn:
109
+ * `hops` holds, per left model, the running total when it was left. Each
110
+ * segment carries only its own tokens so it is priced at its own rates.
111
+ */
112
+ export function routeUsageSegments(hops, total, current) {
113
+ if (!Array.isArray(hops) || !hops.length) return null;
114
+ const boundaries = [...hops, { ...current, usage: total }];
115
+ return boundaries
116
+ .map((segment, index) => ({
117
+ provider: segment.provider,
118
+ model: segment.model,
119
+ usage: usageSince(segment.usage, index ? boundaries[index - 1].usage : null),
120
+ }))
121
+ .filter((segment) => segment.usage);
122
+ }
123
+
82
124
  export function addUsage(total, usage) {
83
125
  const delta = normalizeUsage(usage);
84
126
  if (!delta) return total;
@@ -11,6 +11,7 @@ import { autoEffortModel, turnEffortConfigurationMode } from '../../providers/ef
11
11
  import {
12
12
  autoEffortApplicable,
13
13
  autoEffortBase,
14
+ clampAutoEffortBase,
14
15
  normalizeAutoEffortMode,
15
16
  resolveAutoEffort,
16
17
  } from '../../providers/auto-effort.mjs';
@@ -58,6 +59,15 @@ export function autoEffortMode() {
58
59
  }
59
60
  }
60
61
 
62
+ /** The global "Auto reasoning base" setting; absent or unreadable is medium. */
63
+ export function autoEffortBaseSetting() {
64
+ try {
65
+ return autoEffortBase(readSection('agent')?.autoEffortBase);
66
+ } catch {
67
+ return autoEffortBase(undefined);
68
+ }
69
+ }
70
+
61
71
  /**
62
72
  * Starts the judge worker when a session is created or opened on a route that
63
73
  * Auto effort can judge, so the first judged turn does not wait for the cold
@@ -102,7 +112,9 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
102
112
  if (!request || request.startsWith('<')) return null;
103
113
  if (!turnEffortConfigurationMode(session, provider)) return null;
104
114
  const chosen = String(session.effort || '').toLowerCase();
105
- const base = autoEffortBase(chosen);
115
+ const options = effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model));
116
+ const setting = autoEffortBaseSetting();
117
+ const base = clampAutoEffortBase({ base: setting, options }) ?? autoEffortBase(setting);
106
118
  const record = {
107
119
  at: new Date().toISOString(),
108
120
  sessionId,
@@ -113,7 +125,6 @@ export async function resolveTurnAutoEffort({ sessionId, session, provider, inpu
113
125
  base,
114
126
  requestChars: request.length,
115
127
  };
116
- const options = effortOptionsFor(session.provider, autoEffortModel(session.provider, provider, session.model));
117
128
  // No judged outcome can apply when the base is off the model's ladder, so
118
129
  // the judge is not asked (in observe mode too: nothing would be logged).
119
130
  if (!autoEffortApplicable({ base, options })) {
@@ -120,14 +120,24 @@ function recordAgentRuntimeCall(session, result) {
120
120
  export async function recordAskUsage({ session, result, askStartedAt, turnSignal }) {
121
121
  const prefixHashForLog = recordAgentRuntimeCall(session, result);
122
122
  if (!result.usage) return;
123
- await logSessionLlmUsage({
124
- session,
125
- usage: result.usage,
126
- startedAt: askStartedAt,
127
- signal: turnSignal,
128
- prefixHash: prefixHashForLog,
129
- transportTiming: result.transportTiming || null,
130
- });
123
+ // A turn that switched models mid-way books one row per model, each priced
124
+ // at that model's rates; otherwise one row for the whole turn.
125
+ const segments = result.usageSegments?.length
126
+ ? result.usageSegments
127
+ : [{ provider: session.provider, model: session.model, usage: result.usage }];
128
+ for (const segment of segments) {
129
+ await logSessionLlmUsage({
130
+ session:
131
+ segment.provider === session.provider && segment.model === session.model
132
+ ? session
133
+ : { ...session, provider: segment.provider, model: segment.model },
134
+ usage: segment.usage,
135
+ startedAt: askStartedAt,
136
+ signal: turnSignal,
137
+ prefixHash: prefixHashForLog,
138
+ transportTiming: result.transportTiming || null,
139
+ });
140
+ }
131
141
  recordStandaloneStatusTelemetry(session, result, Date.now() - askStartedAt);
132
142
  }
133
143
 
@@ -19,12 +19,15 @@ import { retainedFailedTurnInput, rewindUnansweredPrompt } from './failed-turn-r
19
19
  import { buildAgentLoopOptions, trackedToolCallHook } from './ask-turn-loop-options.mjs';
20
20
  import { _api_call_with_interrupt } from './ask-call-interrupt.mjs';
21
21
  import { backfillFallbackSessionTitle } from './session-fallback-title.mjs';
22
+ import { stampTurnHop } from '../../../../shared/bot-hops.mjs';
22
23
 
23
24
  /** Resolves the provider, builds the user turn and pre-commits it; returns
24
25
  * what the loop and the commit need from this preparation. */
25
26
  export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, transcriptMeta }) {
26
27
  const { turn, runtime, turnSignal, askGeneration, checkpoint, turnToken, startedAt, codexTurnId } = opened;
27
28
  const session = turn.session;
29
+ // Where the turn sits in a bot-to-bot chain (session-runtime/bot-hops.mjs).
30
+ stampTurnHop(session, transcriptMeta, input.promptSource);
28
31
  const retained = input.retryTurnId
29
32
  ? retainedFailedTurnInput(session.messages, input.retryTurnId, input.retryAttemptId)
30
33
  : null;
@@ -187,6 +190,7 @@ export async function prepareAskTurn({ sessionId, opened, input, cwdOverride, tr
187
190
  deferredToolDelta: userTurn.deferredToolDelta,
188
191
  goalReminder: userTurn.goalReminder,
189
192
  workflowNotice: userTurn.workflowNotice,
193
+ orchestrationModeNotice: userTurn.orchestrationModeNotice,
190
194
  workspaceNotice: userTurn.workspaceNotice,
191
195
  setupNotice: userTurn.setupNotice,
192
196
  };
@@ -20,6 +20,7 @@ import {
20
20
  snapshotPendingSetupNotice,
21
21
  } from './prompt-utils.mjs';
22
22
  import { snapshotPendingWorkflowNotice } from './workflow-switch-notice.mjs';
23
+ import { snapshotPendingOrchestrationModeNotice } from './orchestration-mode-switch-notice.mjs';
23
24
  import { codexWireSendOpts } from './session-id.mjs';
24
25
  import { _tryBridgeExplicitPrefetch } from './prefetch-bridge.mjs';
25
26
  import { filterModelVisibleSessionMessages } from './message-sanitize.mjs';
@@ -184,8 +185,12 @@ export async function buildUserTurn({
184
185
  // A mid-conversation workflow switch: the prompt and tools were rebuilt
185
186
  // for the new workflow; this turn tells the model once.
186
187
  const workflowNotice = snapshotPendingWorkflowNotice(session);
188
+ // A mid-conversation orchestration mode switch, after the workflow notice
189
+ // so the mode it names wins over the one a workflow reminder carries.
190
+ const orchestrationModeNotice = snapshotPendingOrchestrationModeNotice(session);
187
191
  const reminderBlock = [
188
192
  workflowNotice?.content || '',
193
+ orchestrationModeNotice?.content || '',
189
194
  workspaceNotice?.content || '',
190
195
  setupNotice?.content || '',
191
196
  currentTimeBlock ? `<system-reminder>\n# Current Time\n${currentTimeBlock}\n</system-reminder>` : '',
@@ -215,6 +220,7 @@ export async function buildUserTurn({
215
220
  deferredToolDelta,
216
221
  goalReminder,
217
222
  workflowNotice,
223
+ orchestrationModeNotice,
218
224
  workspaceNotice,
219
225
  setupNotice,
220
226
  userTurnMessage,
@@ -10,6 +10,7 @@ import { acknowledgePendingDeferredToolDelta } from '../../runtime-core/deferred
10
10
  import { acknowledgePendingGoalReminder } from '../../runtime-core/goal-reminder.mjs';
11
11
  import { acknowledgePendingSetupNotice, acknowledgePendingWorkspaceNotice } from './prompt-utils.mjs';
12
12
  import { acknowledgePendingWorkflowNotice } from './workflow-switch-notice.mjs';
13
+ import { acknowledgePendingOrchestrationModeNotice } from './orchestration-mode-switch-notice.mjs';
13
14
  import {
14
15
  finalizePendingMessageDelivery,
15
16
  drainPendingMessages,
@@ -141,6 +142,9 @@ export async function commitAskTurn({ sessionId, opened, prepared, result, rawTr
141
142
  acknowledgePendingGoalReminder(session, prepared.goalReminder.revision);
142
143
  }
143
144
  if (prepared.workflowNotice) acknowledgePendingWorkflowNotice(session, prepared.workflowNotice.revision);
145
+ if (prepared.orchestrationModeNotice) {
146
+ acknowledgePendingOrchestrationModeNotice(session, prepared.orchestrationModeNotice.revision);
147
+ }
144
148
  if (prepared.workspaceNotice) {
145
149
  acknowledgePendingWorkspaceNotice(session, prepared.workspaceNotice.revision);
146
150
  }
@@ -110,17 +110,12 @@ function withoutLegacyCompactFields(value) {
110
110
  delete next[key];
111
111
  return next;
112
112
  }
113
- // Handoff-summary timeout scales with transcript size (clear/manual path):
114
- // default max(60s, ~10s per 25k estimated message tokens) capped at 300s.
115
- // The summary may run on a slow reasoning model — the session's own model is
116
- // used whenever the maintenance route is unavailable — where even a small
117
- // transcript outlives the old 30s floor and a large one the old 120s cap.
118
- // session.compaction.timeoutMs still overrides.
119
- function handoffSummaryTimeoutMs(session, messageTokens) {
120
- const override = positiveInt(session?.compaction?.timeoutMs);
121
- if (override) return override;
122
- const scaled = Math.ceil((messageTokens || 0) / 25_000) * 10_000;
123
- return Math.min(HANDOFF_TIMEOUT_MAX_MS, Math.max(60_000, scaled));
113
+ // Handoff-summary timeout (clear/manual path): the same 300s as automatic
114
+ // compaction, whatever the transcript size — the summary may run on a slow
115
+ // reasoning model or a stalled provider connection, and a timeout fails the
116
+ // compaction. session.compaction.timeoutMs still overrides.
117
+ function handoffSummaryTimeoutMs(session) {
118
+ return positiveInt(session?.compaction?.timeoutMs) || HANDOFF_TIMEOUT_MAX_MS;
124
119
  }
125
120
  /**
126
121
  * ONE wiring for the fresh-context handoff pass: provider, summary model,
@@ -144,13 +139,8 @@ export async function runHandoffCompaction({
144
139
  provider = null,
145
140
  model = null,
146
141
  config,
147
- messageTokensEst = null,
148
142
  requireReduction = false,
149
143
  } = {}) {
150
- const messageList = Array.isArray(messages) ? messages : [];
151
- const transcriptTokens = Number.isFinite(Number(messageTokensEst))
152
- ? Number(messageTokensEst)
153
- : estimateMessagesTokens(messageList);
154
144
  return runFreshContextCompact({
155
145
  sessionRef: session,
156
146
  messages,
@@ -159,7 +149,7 @@ export async function runHandoffCompaction({
159
149
  reserveTokens,
160
150
  contextWindow: positiveInt(contextWindow) || positiveInt(session?.contextWindow) || boundaryTokens,
161
151
  boundaryTokens,
162
- handoffTimeoutMs: handoffSummaryTimeoutMs(session, transcriptTokens),
152
+ handoffTimeoutMs: handoffSummaryTimeoutMs(session),
163
153
  },
164
154
  sessionId,
165
155
  signal,
@@ -336,7 +326,6 @@ async function runSessionHandoff(plan, opts) {
336
326
  provider: opts.provider || getProvider(session.provider) || null,
337
327
  model: opts.model,
338
328
  config: opts.config,
339
- messageTokensEst: plan.beforeMessageTokens,
340
329
  requireReduction: plan.requireReduction,
341
330
  });
342
331
  if (Array.isArray(run.freshContextResult?.messages)) {
@@ -609,7 +598,43 @@ function withAppendedMessages(plan, numbers, appended) {
609
598
  const TRANSCRIPT_MOVED_ERROR =
610
599
  'compact: the conversation changed while compacting; compaction not applied, run it again';
611
600
 
601
+ // Why a compaction took as long as it did: persisted next to lastSummaryModel
602
+ // and echoed as one daemon.log line. Wall time runs start → end of commit; the
603
+ // summary call's own time comes from the handoff pipeline, so the remainder is
604
+ // planning, transcript comparison and the commit below. The archive write is
605
+ // scheduled by the commit and runs later, off it.
606
+ function recordCompactionTiming(plan, freshContextResult, { startedAt, commitStartedAt, afterTokens }) {
607
+ const { session, resolvedSessionId } = plan;
608
+ const committedAt = Date.now();
609
+ const pipeline = freshContextResult?.diagnostics?.pipeline;
610
+ const summaryMs = pipeline?.generated?.durationMs ?? null;
611
+ // How the summary ran: a fork of the cached request or the isolated
612
+ // request, and on which route (session / maintenance / lighter).
613
+ let summaryRun = null;
614
+ if (pipeline?.summaryRoute) {
615
+ const kind = pipeline.generated?.mode === 'shared-prefix' ? 'fork' : 'isolated';
616
+ summaryRun = `${kind}:${pipeline.summaryRoute}`;
617
+ }
618
+ const timing = {
619
+ lastStartedAt: startedAt,
620
+ lastCommittedAt: committedAt,
621
+ lastDurationMs: committedAt - startedAt,
622
+ lastPipelineMs: pipeline?.totalMs ?? null,
623
+ lastSummaryMs: summaryMs,
624
+ lastCommitMs: committedAt - commitStartedAt,
625
+ lastCompactMode: summaryRun ? `${pipeline.mode}:${summaryRun}` : pipeline?.mode || null,
626
+ };
627
+ Object.assign(session.compaction, timing);
628
+ writeStderrBestEffort(
629
+ `compaction session=${resolvedSessionId || 'unknown'} source=${freshContextResult?.handoffSource || 'session-local'} ` +
630
+ `model=${freshContextResult?.summaryModel || '-'} mode=${summaryRun || '-'} total=${timing.lastDurationMs}ms ` +
631
+ `summary=${summaryMs ?? 0}ms commit=${timing.lastCommitMs}ms ` +
632
+ `in=${plan.beforeTokens} out=${afterTokens}\n`
633
+ );
634
+ }
635
+
612
636
  async function commitSessionCompaction(plan, run, compactStartedAt) {
637
+ const commitStartedAt = Date.now();
613
638
  const { session, messages, mode, force, resolvedSessionId, reserveTokens } = plan;
614
639
  const { freshContextResult } = run;
615
640
  const numbers = await afterCompactionNumbers(plan, run.compacted);
@@ -688,6 +713,7 @@ async function commitSessionCompaction(plan, run, compactStartedAt) {
688
713
  if (changed) {
689
714
  session.pendingCacheBreakIntent = mode === 'auto' ? 'post_turn_compaction' : 'manual_compaction';
690
715
  }
716
+ recordCompactionTiming(plan, freshContextResult, { startedAt: compactStartedAt, commitStartedAt, afterTokens });
691
717
  return compactionResult(
692
718
  plan,
693
719
  { changed, reason: unchangedReason },