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
@@ -0,0 +1,87 @@
1
+ // What the compaction summary route choice reads: whether the session's
2
+ // prompt cache is still warm (a fork of its last request would read it), and
3
+ // the lighter model of a subscription provider.
4
+ import { cacheCapabilityForProvider, providerTailCacheLifetimeMs } from '../../agent-runtime/cache-strategy.mjs';
5
+ import { getModelMetadataSync } from '../../../../shared/llm/model-catalog.mjs';
6
+ import {
7
+ _compareVersion as compareAnthropicVersion,
8
+ _loadModelCache as loadAnthropicCatalogCache,
9
+ } from '../../providers/anthropic-model-resolve.mjs';
10
+ import { gptVersion } from '../../providers/openai-model-capabilities.mjs';
11
+ import { loadSavedCodexCatalog } from '../../providers/openai-oauth-catalog-store.mjs';
12
+ import { compactionForkRequest } from '../side-question-store.mjs';
13
+
14
+ // A provider outside the Anthropic/OpenAI lifetimes whose catalog marks prompt
15
+ // caching: the shortest common cache lifetime (Gemini's default TTL).
16
+ const CATALOG_PROMPT_CACHE_LIFETIME_MS = 5 * 60_000;
17
+ // The cache is read when the fork is sent, after the route choice: a request
18
+ // this close to its lifetime counts as cold.
19
+ const WARM_MARGIN_MIN_MS = 30_000;
20
+ const WARM_MARGIN_RATIO = 0.1;
21
+
22
+ /** How long the cache a saved request wrote or read stays readable, in ms:
23
+ * Infinity when it does not expire (openai-oauth), 0 when it is not cached.
24
+ * Anthropic: the request's message-tail tier (5m / 1h); OpenAI: the 30m tail
25
+ * that holds the conversation (the Lead's 12h breakpoint covers only the
26
+ * stable tools/instructions prefix). */
27
+ function requestCacheLifetimeMs(request) {
28
+ const name = request?.provider?.name;
29
+ if (name === 'openai-oauth') return Infinity;
30
+ if (name === 'openai' || cacheCapabilityForProvider(name) === 'explicit-breakpoint') {
31
+ return providerTailCacheLifetimeMs(name, request.opts?.cacheStrategy) ?? 0;
32
+ }
33
+ const info = request.provider.getCachedModelInfo?.(request.model) || getModelMetadataSync(request.model, name);
34
+ return info?.supportsPromptCaching ? CATALOG_PROMPT_CACHE_LIFETIME_MS : 0;
35
+ }
36
+
37
+ /** Whether the session's last settled request is still inside the cache
38
+ * lifetime it used, with a safety margin. No forkable request = cold. */
39
+ export function compactionCacheWarm(sessionId, now = Date.now()) {
40
+ const request = compactionForkRequest(sessionId);
41
+ if (!request) return false;
42
+ const lifetime = requestCacheLifetimeMs(request);
43
+ if (lifetime === Infinity) return true;
44
+ const margin = Math.max(WARM_MARGIN_MIN_MS, lifetime * WARM_MARGIN_RATIO);
45
+ return now - Number(request.sentAt) < lifetime - margin;
46
+ }
47
+
48
+ function compareGptVersion(a, b) {
49
+ const [aMajor, aMinor] = gptVersion(a);
50
+ const [bMajor, bMinor] = gptVersion(b);
51
+ // Same version: the undated alias over a dated snapshot.
52
+ return aMajor - bMajor || aMinor - bMinor || b.length - a.length;
53
+ }
54
+
55
+ // The lighter family per subscription provider: its newest catalog model.
56
+ // API-key providers bill per token and get no lighter step. `catalog` reads
57
+ // the provider's saved catalog only (disk cache, no auth or network on the
58
+ // compaction path): Anthropic's within its 24h TTL, Codex's stale or not.
59
+ const LIGHTER_FAMILY = Object.freeze({
60
+ 'anthropic-oauth': {
61
+ catalog: loadAnthropicCatalogCache,
62
+ matches: (row) => row.family === 'haiku',
63
+ compare: compareAnthropicVersion,
64
+ },
65
+ 'openai-oauth': {
66
+ catalog: loadSavedCodexCatalog,
67
+ matches: (row, id) => id.includes('-luna') && gptVersion(id) !== null,
68
+ compare: compareGptVersion,
69
+ },
70
+ });
71
+
72
+ /** The newest lighter-family model the saved catalog of `providerName` lists
73
+ * for this account, or null (not a subscription provider, no saved catalog,
74
+ * none listed). */
75
+ export async function lighterSubscriptionModel(providerName) {
76
+ const family = LIGHTER_FAMILY[providerName];
77
+ if (!family) return null;
78
+ const catalog = await family.catalog();
79
+ if (!Array.isArray(catalog)) return null;
80
+ let best = null;
81
+ for (const row of catalog) {
82
+ const id = String(row?.id || '');
83
+ if (!id || !family.matches(row, id)) continue;
84
+ if (!best || family.compare(id, best) > 0) best = id;
85
+ }
86
+ return best;
87
+ }
@@ -59,6 +59,8 @@ Rules:
59
59
  - Use terse bullets, not prose paragraphs.
60
60
  - Preserve exact file paths, commands, error strings, and identifiers when known.
61
61
  - Use the same language as the active user thread when it is clear.
62
+ - Record work as Done or In Progress only when a tool call and its result in the history show it (an agent spawn/send result, an edit, a command outcome). An assistant's stated intent with no matching tool call is not started: list it under Next Steps as "announced, not started".
63
+ - Attribute every decision, answer, and version/choice to its source: the user (message or ask_user answer), a named agent's report, or the assistant. Never credit an agent or the assistant with a user's answer.
62
64
  - Do not mention the summary process or that context was compacted.`;
63
65
 
64
66
  // The final user turn of a summary forked from the session's own cached
@@ -1,6 +1,10 @@
1
1
  // Text/token/message helpers and secret redaction for compaction.
2
2
  import { createHash } from 'node:crypto';
3
- import { attachmentTextForPart, isAttachmentReference, isPermanentAttachmentError } from '../../../../attachments/store.mjs';
3
+ import {
4
+ attachmentTextForPart,
5
+ isAttachmentReference,
6
+ isPermanentAttachmentError,
7
+ } from '../../../../attachments/store.mjs';
4
8
  import { estimateMessagesTokens } from '../context-utils.mjs';
5
9
  import { inlineFileKind } from '../../../../shared/inline-file-kind.mjs';
6
10
 
@@ -5,6 +5,7 @@
5
5
  import { SUMMARY_PREFIX_ANCHOR } from './summary-anchor.mjs';
6
6
  import { isImportedMessage } from '../imported-message.mjs';
7
7
  import {
8
+ CONTROL_MARKER_RE,
8
9
  isInternalRuntimeNotificationText,
9
10
  isModelVisibleToolCompletionWrapper,
10
11
  } from '../../../../shared/tool-execution-contract.mjs';
@@ -132,7 +133,6 @@ export function isActualUserInstructionMessage(message) {
132
133
  // isActualUserInstructionMessage deliberately keeps treating "[truncated]" as
133
134
  // an instruction (compaction tail selection depends on it), so the real-turn
134
135
  // predicate below recognizes it on its own.
135
- const CONTROL_MARKER_RE = /^\[(?:truncated|request interrupted(?: by user(?: for tool use)?| by process restart)?)\]$/i;
136
136
 
137
137
  /** Whether a message stands for real human conversation: a compact summary
138
138
  * (it replaced earlier human turns), imported prose, or the user's own
@@ -226,7 +226,7 @@ const recordExists = (dir, sessionId) => existsSync(join(dir, `${sessionId}.json
226
226
  * since `epoch` was captured wins. Any outcome but a rename removes the temp
227
227
  * file. */
228
228
  async function publishArchive({ dir, path, sessionId, text, epoch }) {
229
- const tmp = join(dir, `.${basename(path)}.${randomBytes(8).toString('hex')}.tmp`);
229
+ const tmp = join(dir, `${basename(path)}.mixdog-tmp-${randomBytes(4).toString('hex')}`);
230
230
  const removeTmp = () => {
231
231
  try {
232
232
  unlinkSync(tmp);
@@ -260,24 +260,55 @@ function messageImageAllowance(m) {
260
260
  if (!m || typeof m !== 'object') return 0;
261
261
  return messageImageDescriptors(m).reduce((sum, descriptor) => sum + imageDescriptorAllowance(descriptor), 0);
262
262
  }
263
- // Inline document (PDF) allowance: Anthropic bills ~1,500-3,000 tokens per
264
- // page and a typical PDF page is ~50-100KB, so ~bytes/16 with a floor and a
265
- // cap. The base64 payload itself is excluded from text estimates (see
263
+ // Inline document (PDF) allowance. A native PDF reader turns every page into
264
+ // one image AND sends the text it extracts from that page (the provider PDF
265
+ // docs: each page is billed as an image plus its text, typically
266
+ // 1,500-3,000 text tokens for a dense page). The page image is bounded like
267
+ // any image — the reader downscales it to the vision ceiling — so a page costs
268
+ // at most IMAGE_TOKEN_ALLOWANCE for its picture whatever its file size. A
269
+ // scanned page has no text layer and costs only its picture.
270
+ //
271
+ // The base64 payload itself is excluded from text estimates (see
266
272
  // jsonFallbackFromPart), so this allowance is the document's entire cost.
267
273
  const FILE_TOKEN_ALLOWANCE_FLOOR = 1_500;
268
274
  const FILE_MAX_TOKEN_ALLOWANCE = 300_000;
269
- // A PDF that records its page count (intake and the read tool do) is priced per
270
- // page; the size-based figure stays as the floor, so a dense scan still costs
271
- // at least its bytes. Inline and stored parts carry the same count.
272
- const FILE_TOKENS_PER_PAGE = 2_500;
275
+ // Extracted PDF text is priced at two characters per token: the low end of
276
+ // the documented per-page range for Latin text, and close to the measured
277
+ // density of Korean/CJK text, so neither is read low.
278
+ const PDF_TEXT_CHARS_PER_TOKEN = 2;
279
+ // A PDF that records its page count but not its text layer (older parts, an
280
+ // inline part from a path that did not measure it) keeps the documented
281
+ // mid-range per-page figure.
282
+ const FILE_TOKENS_PER_PAGE_UNMEASURED = 2_500;
273
283
  // A PDF that lowers to text costs at most its extraction budget (~4 bytes/token).
274
284
  const PDF_TEXT_MAX_TOKENS = Math.ceil(PDF_TEXT_MAX_BYTES / 4);
285
+ // Without a page count only the file size is known: ~bytes/16 (a typical page
286
+ // is 50-100KB) with a floor and a cap. Never used once pages are known — a
287
+ // scan's bytes are its image data, which the reader downscales, so bytes/16
288
+ // priced a 5 MB, 37-page scan at ~330k tokens instead of ~58k.
275
289
  function fileDescriptorAllowance(descriptor, nativePdf = true) {
276
- const bySize = Math.ceil((descriptor.sizeBytes || 0) / 16);
277
- const byPage = (descriptor.pageCount || 0) * FILE_TOKENS_PER_PAGE;
278
- const native = Math.min(FILE_MAX_TOKEN_ALLOWANCE, Math.max(FILE_TOKEN_ALLOWANCE_FLOOR, bySize, byPage));
290
+ const pages = descriptor.pageCount || 0;
291
+ let native;
292
+ if (pages > 0 && Number.isFinite(descriptor.textChars)) {
293
+ native = pages * IMAGE_TOKEN_ALLOWANCE + Math.ceil(descriptor.textChars / PDF_TEXT_CHARS_PER_TOKEN);
294
+ } else if (pages > 0) {
295
+ native = pages * FILE_TOKENS_PER_PAGE_UNMEASURED;
296
+ } else {
297
+ native = Math.max(FILE_TOKEN_ALLOWANCE_FLOOR, Math.ceil((descriptor.sizeBytes || 0) / 16));
298
+ }
299
+ native = Math.min(FILE_MAX_TOKEN_ALLOWANCE, native);
279
300
  return nativePdf ? native : Math.min(native, PDF_TEXT_MAX_TOKENS);
280
301
  }
302
+
303
+ /** The tokens one PDF part costs inline (native reader unless `nativePdf` is false). */
304
+ export function pdfPartAllowance(part, { nativePdf = true } = {}) {
305
+ return contentFileDescriptors([part]).reduce((sum, d) => sum + fileDescriptorAllowance(d, nativePdf), 0);
306
+ }
307
+
308
+ /** The tokens one image part costs inline. */
309
+ export function imagePartAllowance(part) {
310
+ return contentImageDescriptors([part]).reduce((sum, d) => sum + imageDescriptorAllowance(d), 0);
311
+ }
281
312
  /**
282
313
  * How many tokens the native-PDF meter overstates `messages` for a provider
283
314
  * that takes PDFs as text (nativePdf === false); subtract it from the
@@ -0,0 +1,230 @@
1
+ // session/human-gate/delegation.mjs
2
+ // Who a delegated session works for, recorded when it is created and re-bound
3
+ // only when another session sends it work (rebindDelegation): a sub-agent (any
4
+ // depth) and a task session a bot created carry `delegation` on their session
5
+ // record —
6
+ // { rootSessionId, rootAgentId, via, run?, path }
7
+ // `rootSessionId` is the top-level conversation (a bot's home chat, a room
8
+ // member session, a coding Lead's session); `rootAgentId` the bot or Mixy it
9
+ // acts for ('' under a coding Lead); `via` how that root was acting when the
10
+ // work started: 'home' (the bot's own attended chat), 'room', 'run' (a
11
+ // schedule/webhook turn; `run` = { kind, name }) or 'coding'; `path` the steps
12
+ // from the root down ({ kind: 'task' } | { kind: 'agent', agent }).
13
+ // It is copied from the parent's record (or bound from the parent itself when
14
+ // the parent is top-level) by the host at creation — never from tool input —
15
+ // so no parent walk happens at gate time. It follows the session that drives
16
+ // the work: when a session sends an existing worker (or task session) a new
17
+ // request, the record is re-bound to what that sender is doing now, so work an
18
+ // unattended run started asks when the attended chat later reuses it, and the
19
+ // reverse. A background wake of the worker is no new request and keeps it. A
20
+ // record from before this field existed is
21
+ // backfilled once, from its parent by the same rules (backfillOf), and kept on
22
+ // the in-memory record, which its next save persists. A session that has a
23
+ // parent link but no valid or derivable root is UNRESOLVED: the gate fails
24
+ // closed on it. `via: 'unknown'` — a legacy task session that names its bot
25
+ // but not where it was created: acts as that bot, never remembers.
26
+ import { agentHomeSessionId, agentIdOfHomeSession } from '../agent-home.mjs';
27
+ import { roomMemberOfSession } from '../agent-room.mjs';
28
+ import { loadSession } from '../store/load-session.mjs';
29
+ import { _liveSessions } from '../store/live-state.mjs';
30
+ import { saveSessionAsync } from '../store/save-worker.mjs';
31
+ import { isTurnInFlight, runtimeState } from '../manager/runtime-state.mjs';
32
+ import { unattendedApprovalPort } from './unattended-port.mjs';
33
+
34
+ const VIA = new Set(['home', 'room', 'run', 'coding', 'unknown']);
35
+ const AGENT_ID = /^[A-Za-z0-9_-]+$/;
36
+ const MAX_PATH = 32;
37
+ const clean = (value) => (typeof value === 'string' ? value.trim() : '');
38
+ const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
39
+
40
+ const agentOwned = (record) =>
41
+ clean(record?.owner).toLowerCase() === 'agent' || clean(record?.visibility).toLowerCase() === 'agent-only';
42
+
43
+ /** The session that started `record` (its spawn parent, or the chat that
44
+ * created a task session), or '' for a top-level session. */
45
+ function parentOf(record) {
46
+ if (agentOwned(record)) return clean(record.parentSessionId) || clean(record.ownerSessionId);
47
+ return clean(record?.taskOrigin?.sessionId);
48
+ }
49
+
50
+ /** The step a new session adds to its parent's path. */
51
+ function stepOf(record) {
52
+ return agentOwned(record) ? { kind: 'agent', agent: clean(record.agent) } : { kind: 'task' };
53
+ }
54
+
55
+ function validDelegation(value) {
56
+ if (!isRecord(value) || !clean(value.rootSessionId) || !VIA.has(value.via)) return null;
57
+ if (!Array.isArray(value.path) || value.path.length === 0 || value.path.length > MAX_PATH) return null;
58
+ if (value.via === 'run' && !isRecord(value.run)) return null;
59
+ return value;
60
+ }
61
+
62
+ /** A failed rebind leaves `{ unresolved: true }` instead of a delegation: an
63
+ * explicit "no root", so the legacy backfill cannot derive one again from a
64
+ * parent that still holds a run. */
65
+ const UNRESOLVED = Object.freeze({ unresolved: true });
66
+ const isUnresolvedMark = (value) => isRecord(value) && value.unresolved === true;
67
+
68
+ /** The recorded delegation of `record`, else its legacy backfill; null when
69
+ * it has none or was explicitly left unresolved. */
70
+ function delegationOfRecord(id, record, depth) {
71
+ if (isUnresolvedMark(record.delegation)) return null;
72
+ return validDelegation(record.delegation) || backfillOf(id, record, depth);
73
+ }
74
+
75
+ function loadRecord(sessionId) {
76
+ try {
77
+ return loadSession(sessionId);
78
+ } catch {
79
+ return null;
80
+ }
81
+ }
82
+
83
+ /** How a top-level session `sessionId` is acting right now (no record needed
84
+ * for a home or room member id, which name their agent themselves). */
85
+ function bindingOf(sessionId, record, { legacy = false } = {}) {
86
+ // A legacy record is classified from what was recorded, never from what the
87
+ // parent is doing now (a run going or ended since says nothing about it).
88
+ const run = legacy ? null : unattendedApprovalPort()?.runOf?.(sessionId);
89
+ if (run && run.kind !== 'room') {
90
+ return { rootAgentId: clean(run.agentId), via: 'run', run: { kind: run.kind, name: String(run.name || '') } };
91
+ }
92
+ const member = roomMemberOfSession(sessionId);
93
+ if (member) return { rootAgentId: member.agentId, via: 'room' };
94
+ const home = agentIdOfHomeSession(sessionId);
95
+ if (home) return { rootAgentId: home, via: 'home' };
96
+ if (!record || agentOwned(record)) return null;
97
+ // A Lead with host-set bot provenance (session-options taskOriginSuffix) is
98
+ // a bot's task session, never coding work: its root is that bot's chat.
99
+ const source = sourceTaskOf(record);
100
+ if (source === false) return null;
101
+ if (source) {
102
+ const task = { rootSessionId: agentHomeSessionId(source.bot), rootAgentId: source.bot, task: true };
103
+ if (source.from === 'home') return { ...task, via: 'home' };
104
+ if (source.from === 'run') return { ...task, via: 'run', run: { kind: 'schedule', name: '' } };
105
+ if (source.from === undefined) return { ...task, via: 'unknown' };
106
+ return null; // a room (which one is not recorded) or anything else: ambiguous
107
+ }
108
+ return { rootAgentId: '', via: 'coding' };
109
+ }
110
+
111
+ /** A Lead's host-set bot provenance `agent:<bot>[@from]`: `{ bot, from }`,
112
+ * null when it has none, false when it is malformed. */
113
+ function sourceTaskOf(record) {
114
+ const source = clean(record?.sourceName);
115
+ if (agentOwned(record) || clean(record?.sourceType) !== 'lead' || !source.startsWith('agent:')) return null;
116
+ const [bot, from, ...rest] = source.slice(6).split('@');
117
+ return AGENT_ID.test(bot) && !rest.length ? { bot, from } : false;
118
+ }
119
+
120
+ /** The root of a top-level `parent` for a child taking `step`. */
121
+ function rootFrom(parent, record, step, options) {
122
+ const binding = bindingOf(parent, record, options);
123
+ if (!binding) return null;
124
+ const { task, ...root } = binding;
125
+ return { rootSessionId: parent, ...root, path: task ? [{ kind: 'task' }, step] : [step] };
126
+ }
127
+
128
+ /** The delegation a legacy `record` (id `id`, a parent link, none recorded)
129
+ * is given, derived now from what was recorded — its own host-set `@run`
130
+ * provenance, else its parent's record, which must exist — and kept on the
131
+ * live record; null when it cannot be derived. Never `via: 'home'`: whether
132
+ * an old record was started by the attended chat (or by a run that has
133
+ * ended since) is not recorded, so home ancestry becomes 'unknown' — that
134
+ * bot, never remembered. Only a session created with its `delegation` can be
135
+ * 'home'. */
136
+ function backfillOf(id, record, depth = 0) {
137
+ const parent = parentOf(record);
138
+ if (!parent || parent === id || depth >= MAX_PATH) return null;
139
+ const own = sourceTaskOf(record);
140
+ let delegation = null;
141
+ if (own && own.from === 'run') {
142
+ // A run's task session stays the run's, whatever its origin chat is doing now.
143
+ delegation = {
144
+ rootSessionId: agentHomeSessionId(own.bot),
145
+ rootAgentId: own.bot,
146
+ via: 'run',
147
+ run: { kind: 'schedule', name: '' },
148
+ path: [{ kind: 'task' }],
149
+ };
150
+ } else {
151
+ const parentRecord = loadRecord(parent);
152
+ if (!parentRecord) return null;
153
+ const step = stepOf(record);
154
+ if (parentOf(parentRecord)) {
155
+ const inherited = delegationOfRecord(parent, parentRecord, depth + 1);
156
+ if (!inherited || inherited.path.length >= MAX_PATH) return null;
157
+ delegation = { ...inherited, path: [...inherited.path, step] };
158
+ } else {
159
+ delegation = rootFrom(parent, parentRecord, step, { legacy: true });
160
+ }
161
+ if (delegation?.via === 'home') delegation = { ...delegation, via: 'unknown' };
162
+ }
163
+ if (delegation && _liveSessions.get(id) === record) record.delegation = delegation;
164
+ return delegation;
165
+ }
166
+
167
+ /**
168
+ * The `delegation` to record on a session being created with `opts` (its
169
+ * owner/visibility, parentSessionId/ownerSessionId, taskOrigin and agent), or
170
+ * null: none for a top-level session, none for one whose parent's root cannot
171
+ * be established (the record then stays unresolved and the gate fails closed).
172
+ */
173
+ export function delegationForNewSession(opts = {}) {
174
+ const parent = parentOf(opts);
175
+ if (!parent || parent === clean(opts.id)) return null;
176
+ return delegationUnder(parent, stepOf(opts));
177
+ }
178
+
179
+ /** The delegation of a session taking `step` under the session `parent`. */
180
+ function delegationUnder(parent, step) {
181
+ const record = loadRecord(parent);
182
+ if (record && parentOf(record)) {
183
+ // The parent is delegated itself: its recorded root, one step deeper.
184
+ const inherited = delegationOfRecord(parent, record);
185
+ if (!inherited || inherited.path.length >= MAX_PATH) return null;
186
+ return { ...inherited, path: [...inherited.path, step] };
187
+ }
188
+ return rootFrom(parent, record, step);
189
+ }
190
+
191
+ /**
192
+ * Session `callerSessionId` sends the delegated session `sessionId` new work
193
+ * (`agent send`, a spawn of its tag, a task-session send): from now on it
194
+ * acts as that caller does — the delegation it would have been created with
195
+ * under the caller — and the record is saved, so a restart keeps it. A worker
196
+ * a run started is attended when the chat sends it work, and the other way
197
+ * round; only the requests of the session that sends it count, never the
198
+ * session that created it. A turn already running keeps its attended status
199
+ * against a run (the run's request queues behind it and asks): it is never
200
+ * loosened mid-turn. Resolves true when the record changed; false when
201
+ * nothing applies (no record, not delegated, no or the same caller, the same
202
+ * delegation) or the running turn kept its status. A caller whose root cannot
203
+ * be established leaves the record without one, which the gate fails closed on.
204
+ */
205
+ export async function rebindDelegation(sessionId, callerSessionId) {
206
+ const id = clean(sessionId);
207
+ const caller = clean(callerSessionId);
208
+ const record = id ? loadRecord(id) : null;
209
+ if (!record || record.closed === true || !caller || caller === id || !parentOf(record)) return false;
210
+ const current = validDelegation(record.delegation);
211
+ const next = delegationUnder(caller, stepOf(record));
212
+ if (next ? JSON.stringify(current) === JSON.stringify(next) : isUnresolvedMark(record.delegation)) return false;
213
+ if (next?.via === 'run' && current?.via !== 'run' && isTurnInFlight(runtimeState.get(id))) return false;
214
+ record.delegation = next || { ...UNRESOLVED };
215
+ await saveSessionAsync(record, { expectedGeneration: record.generation });
216
+ return true;
217
+ }
218
+
219
+ /** `{ state: 'top' }` for a top-level session (or one with no record),
220
+ * `{ state: 'resolved', ...delegation }` for a delegated one, `{ state:
221
+ * 'unresolved' }` for one with a parent link and no valid recorded root. */
222
+ export function delegationOf(sessionId) {
223
+ const id = clean(sessionId);
224
+ const record = id ? loadRecord(id) : null;
225
+ if (!record) return { state: 'top' };
226
+ const parent = parentOf(record);
227
+ if (!parent) return { state: 'top' };
228
+ const delegation = parent === id ? null : delegationOfRecord(id, record);
229
+ return delegation ? { state: 'resolved', ...delegation } : { state: 'unresolved' };
230
+ }