mixdog 1.4.0 → 1.4.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 (493) hide show
  1. package/package.json +3 -2
  2. package/scripts/build-shipped-history.mjs +115 -33
  3. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  4. package/scripts/gate-local.mjs +183 -63
  5. package/scripts/latest-guard.mjs +21 -9
  6. package/scripts/lib/gate-scheduler.mjs +138 -32
  7. package/scripts/lib/gate-timing-reporter.mjs +16 -0
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +91 -66
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +124 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/lib/test-failure-records.mjs +14 -1
  14. package/scripts/lib/test-failure-summary.mjs +16 -4
  15. package/scripts/lib/test-spawn-ledger.mjs +12 -3
  16. package/scripts/post-release-check.mjs +1 -6
  17. package/scripts/pptx-kit-index.mjs +17 -5
  18. package/scripts/suite-health.mjs +115 -24
  19. package/scripts/test-data-dir.mjs +10 -7
  20. package/scripts/test.mjs +9 -14
  21. package/src/agents/advisor/AGENT.md +4 -4
  22. package/src/agents/advisor/agent.json +1 -1
  23. package/src/agents/explore/AGENT.md +22 -0
  24. package/src/agents/explore/agent.json +6 -0
  25. package/src/app.mjs +4 -0
  26. package/src/cli.mjs +7 -0
  27. package/src/defaults/agents.json +7 -0
  28. package/src/defaults/shipped-history.json +23 -2
  29. package/src/defaults/skills/browser-use/SKILL.md +10 -5
  30. package/src/defaults/skills/code-tidy/SKILL.md +71 -102
  31. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +66 -114
  32. package/src/defaults/skills/code-tidy/references/cleanup-report.md +19 -35
  33. package/src/defaults/skills/code-tidy/references/dead-code.md +3 -9
  34. package/src/defaults/skills/code-tidy/references/test-suite-slop.md +16 -27
  35. package/src/defaults/skills/computer-use/SKILL.md +16 -4
  36. package/src/defaults/skills/goal-management/SKILL.md +1 -1
  37. package/src/defaults/skills/setup/references/actions.md +12 -6
  38. package/src/defaults/skills/setup/references/surfaces.md +2 -2
  39. package/src/defaults/skills/setup-guide/SKILL.md +5 -2
  40. package/src/defaults/skills/setup-guide/model-recommendations.json +13 -0
  41. package/src/defaults/skills/skill-creator/SKILL.md +85 -190
  42. package/src/defaults/skills/skill-creator/references/design.md +70 -162
  43. package/src/defaults/skills/skill-creator/references/evaluation.md +33 -92
  44. package/src/defaults/skills/test-suite/SKILL.md +194 -0
  45. package/src/defaults/skills/test-suite/references/case-management.md +51 -0
  46. package/src/defaults/skills/test-suite/references/flaky-policy.md +39 -0
  47. package/src/defaults/skills/test-suite/references/reliability.md +49 -0
  48. package/src/defaults/skills/test-suite/references/suite-design.md +78 -0
  49. package/src/defaults/skills/test-suite/scripts/inventory.mjs +335 -0
  50. package/src/defaults/skills/visualize/SKILL.md +6 -2
  51. package/src/lib/rules-builder.cjs +35 -42
  52. package/src/output-styles/common.md +2 -2
  53. package/src/output-styles/simple.md +1 -1
  54. package/src/rules/agent/AGENT.md +5 -4
  55. package/src/rules/lead/LEAD.md +12 -7
  56. package/src/rules/shared/10-tool-workflow.md +3 -1
  57. package/src/rules/shared/20-research.md +3 -6
  58. package/src/rules/shared/40-editing.md +1 -0
  59. package/src/rules/shared/45-destructive.md +0 -2
  60. package/src/rules/shared/50-execution.md +2 -0
  61. package/src/rules/shared/60-verification.md +10 -9
  62. package/src/rules/shared/70-delivery.md +8 -9
  63. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/abort-link.mjs +5 -1
  64. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +10 -1
  65. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +156 -24
  66. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +13 -0
  67. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  69. package/src/runtime/agent/orchestrator/config.mjs +12 -7
  70. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +8 -8
  71. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +5 -26
  72. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/internal-agents.mjs +3 -14
  74. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +122 -12
  75. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +5 -3
  76. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +13 -2
  77. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +152 -58
  78. package/src/runtime/agent/orchestrator/mcp/client.mjs +51 -18
  79. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +88 -54
  80. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +42 -26
  81. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +54 -8
  82. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  83. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +37 -31
  84. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  85. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  86. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  87. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +31 -21
  89. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  90. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +14 -3
  91. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +6 -0
  92. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  93. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +3 -1
  94. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -2
  95. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  97. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/providers/cursor.mjs +3 -0
  99. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +40 -0
  100. package/src/runtime/agent/orchestrator/providers/custom-provider-keys.mjs +34 -0
  101. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +3 -0
  102. package/src/runtime/agent/orchestrator/providers/devin-auth.mjs +14 -7
  103. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +3 -2
  104. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  105. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +2 -2
  106. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -1
  107. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +248 -41
  108. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  109. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  110. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  111. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  112. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +0 -18
  113. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +138 -6
  114. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  115. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +1 -1
  116. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +100 -33
  117. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +1 -1
  118. package/src/runtime/agent/orchestrator/providers/oauth-secret-fields.fixtures.mjs +45 -0
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  128. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  129. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  130. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  131. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  132. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  133. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +160 -31
  134. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +22 -12
  135. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  136. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +49 -22
  137. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +1 -1
  138. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +3 -1
  139. package/src/runtime/agent/orchestrator/providers/registry.mjs +45 -2
  140. package/src/runtime/agent/orchestrator/providers/vertex-ai-auth.mjs +622 -0
  141. package/src/runtime/agent/orchestrator/providers/vertex-ai-maas.mjs +185 -0
  142. package/src/runtime/agent/orchestrator/providers/vertex-ai-models.mjs +445 -0
  143. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +567 -0
  144. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +41 -13
  145. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +2 -0
  146. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -6
  147. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +7 -3
  148. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  149. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +6 -1
  150. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +14 -5
  151. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +1 -0
  152. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  153. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -5
  154. package/src/runtime/agent/orchestrator/session/agent-room.mjs +171 -37
  155. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  156. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +102 -12
  157. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  158. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  159. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  160. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  161. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +160 -36
  162. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  163. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +164 -23
  164. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +139 -19
  165. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  166. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  167. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +100 -22
  168. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -3
  169. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -6
  170. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +33 -3
  171. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +9 -2
  172. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  174. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +14 -0
  175. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +13 -1
  176. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +23 -4
  177. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +2 -0
  178. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +23 -1
  179. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +39 -21
  180. package/src/runtime/agent/orchestrator/session/loop/turn-limits.mjs +13 -8
  181. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +50 -28
  182. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  183. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  184. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +2 -2
  185. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +1 -0
  186. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +2 -1
  187. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +30 -13
  188. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +63 -6
  189. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +7 -0
  190. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +15 -0
  191. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +35 -13
  192. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +24 -14
  193. package/src/runtime/agent/orchestrator/session/manager/session-scratch-environment.mjs +70 -0
  194. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +0 -3
  195. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +10 -6
  196. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  197. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +3 -0
  198. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +56 -29
  199. package/src/runtime/agent/orchestrator/session/manager/turn-reap.mjs +125 -0
  200. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +11 -5
  201. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +5 -1
  202. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +30 -7
  203. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  204. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +4 -0
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +37 -8
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +252 -39
  209. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +163 -64
  210. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +164 -4
  211. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +142 -18
  212. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +61 -2
  213. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  214. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +6 -2
  215. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  217. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -1
  218. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -20
  219. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +67 -31
  221. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +25 -13
  222. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  223. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +58 -20
  224. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  225. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  226. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  227. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  228. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  229. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +20 -15
  231. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +96 -20
  232. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +59 -10
  233. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  234. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  235. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  236. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  237. package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +99 -2
  238. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +2 -0
  239. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +103 -13
  240. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +3 -1
  241. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +49 -15
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +45 -27
  243. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +34 -10
  244. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +28 -4
  245. package/src/runtime/agent/orchestrator/tools/lib/wait-until-fixture.mjs +26 -0
  246. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -2
  247. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  248. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  249. package/src/runtime/channels/lib/whisper-port.mjs +29 -11
  250. package/src/runtime/channels/lib/whisper-server.mjs +10 -1
  251. package/src/runtime/computer-bridge/action-schema.mjs +2 -2
  252. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  253. package/src/runtime/computer-bridge/error-recovery.mjs +5 -4
  254. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  255. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  256. package/src/runtime/connectors/connector-accounts-mcp.mjs +236 -103
  257. package/src/runtime/connectors/gmail-app-password.mjs +3 -3
  258. package/src/runtime/explore/explorer.mjs +93 -0
  259. package/src/runtime/explore/tool-defs.mjs +35 -0
  260. package/src/runtime/explore/tool.mjs +123 -0
  261. package/src/runtime/local-provider/server-process.mjs +2 -1
  262. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  263. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  264. package/src/runtime/memory/index.mjs +4 -0
  265. package/src/runtime/memory/lib/embedding-provider.mjs +4 -1
  266. package/src/runtime/memory/lib/embedding-worker.mjs +10 -3
  267. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  268. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  269. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  270. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  271. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  272. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  273. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  274. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  275. package/src/runtime/office/quality/assurance-structure-docx.mjs +4 -2
  276. package/src/runtime/office/quality/design-review.mjs +1 -5
  277. package/src/runtime/office/quality/document-brief.mjs +1 -5
  278. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  279. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  280. package/src/runtime/shared/agent-route-config.mjs +17 -2
  281. package/src/runtime/shared/atomic-file.mjs +103 -2
  282. package/src/runtime/shared/background-tasks.mjs +15 -6
  283. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  284. package/src/runtime/shared/connector-accounts.mjs +5 -5
  285. package/src/runtime/shared/connector-catalog.d.mts +11 -4
  286. package/src/runtime/shared/connector-catalog.mjs +60 -52
  287. package/src/runtime/shared/custom-provider-keys.mjs +20 -0
  288. package/src/runtime/shared/defaults-separation.mjs +1 -4
  289. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  290. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  291. package/src/runtime/shared/file-has-data.mjs +26 -2
  292. package/src/runtime/shared/file-lock.mjs +13 -15
  293. package/src/runtime/shared/git-launch.d.mts +9 -0
  294. package/src/runtime/shared/git-launch.mjs +213 -0
  295. package/src/runtime/shared/git-runner-worker.mjs +2 -1
  296. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  297. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  298. package/src/runtime/shared/idle-auto-clear-gate.mjs +29 -7
  299. package/src/runtime/shared/llm/cost.mjs +11 -2
  300. package/src/runtime/shared/llm/http-agent.mjs +313 -29
  301. package/src/runtime/shared/llm/model-catalog-projection.mjs +3 -2
  302. package/src/runtime/shared/llm/model-catalog.mjs +46 -6
  303. package/src/runtime/shared/llm/provider-catalog-registry.mjs +3 -0
  304. package/src/runtime/shared/llm/system-proxy.mjs +452 -0
  305. package/src/runtime/shared/loopback-listener.mjs +25 -2
  306. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  307. package/src/runtime/shared/model-route-chain.mjs +22 -4
  308. package/src/runtime/shared/open-url.mjs +10 -0
  309. package/src/runtime/shared/provider-accounts.mjs +52 -2
  310. package/src/runtime/shared/session-activity-signal.mjs +14 -0
  311. package/src/runtime/shared/session-scratch.mjs +37 -0
  312. package/src/runtime/shared/staged-install-worker.mjs +5 -0
  313. package/src/runtime/shared/staged-update.mjs +47 -2
  314. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  315. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  316. package/src/runtime/shared/tool-result-summary.mjs +1 -1
  317. package/src/runtime/shared/tool-surface.mjs +113 -36
  318. package/src/runtime/shared/tool-work-units.mjs +6 -0
  319. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  320. package/src/runtime/shared/turn-worktree-snapshot.mjs +414 -54
  321. package/src/runtime/shared/worktrees/activity-gate.mjs +215 -318
  322. package/src/runtime/shared/worktrees/activity-ledger-worker.mjs +56 -0
  323. package/src/runtime/shared/worktrees/activity-ledger.mjs +391 -0
  324. package/src/runtime/shared/worktrees/branch-management.mjs +9 -3
  325. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  326. package/src/runtime/shared/worktrees/git.mjs +72 -0
  327. package/src/runtime/shared/worktrees/manager.mjs +285 -168
  328. package/src/runtime/shared/worktrees/test-support.mjs +6 -10
  329. package/src/runtime/tidy/install.mjs +95 -15
  330. package/src/runtime/tidy/languages.mjs +17 -15
  331. package/src/runtime/tidy/process.mjs +7 -4
  332. package/src/runtime/tidy/resolve.mjs +19 -16
  333. package/src/runtime/tidy/runners/biome.mjs +3 -3
  334. package/src/runtime/tidy/tool.mjs +1 -1
  335. package/src/runtime/tidy/worktree.mjs +10 -6
  336. package/src/runtime/visualize-bridge/client.mjs +6 -1
  337. package/src/runtime/visualize-bridge/tool-defs.mjs +7 -2
  338. package/src/runtime/web-search/index.mjs +1 -1
  339. package/src/session-runtime/agent-home-delivery.mjs +41 -0
  340. package/src/session-runtime/agent-task-sessions.mjs +458 -312
  341. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  342. package/src/session-runtime/boot/tools.mjs +7 -1
  343. package/src/session-runtime/bot-status.mjs +107 -101
  344. package/src/session-runtime/bot-target.mjs +38 -0
  345. package/src/session-runtime/channel-config-api.mjs +6 -1
  346. package/src/session-runtime/connector-merge-migration.mjs +18 -15
  347. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  348. package/src/session-runtime/conversation-import/discovery.mjs +401 -36
  349. package/src/session-runtime/conversation-import/service.mjs +35 -8
  350. package/src/session-runtime/handoff-attachments.mjs +82 -31
  351. package/src/session-runtime/held-requests.mjs +3 -1
  352. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +12 -13
  353. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +1 -0
  354. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -0
  355. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +8 -0
  356. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  357. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +145 -112
  358. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  359. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -1
  360. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -1
  361. package/src/session-runtime/lifecycle/teardown.mjs +1 -1
  362. package/src/session-runtime/mcp-glue/server-catalog.mjs +16 -10
  363. package/src/session-runtime/mcp-glue/server-input.mjs +4 -1
  364. package/src/session-runtime/mixy-heartbeat.mjs +1 -1
  365. package/src/session-runtime/model-route/set-route.mjs +11 -2
  366. package/src/session-runtime/provider-auth/login.mjs +2 -2
  367. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  368. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  369. package/src/session-runtime/provider-models.mjs +4 -2
  370. package/src/session-runtime/resource-mcp-api.mjs +98 -35
  371. package/src/session-runtime/resource-mcp-connector-accounts.mjs +44 -31
  372. package/src/session-runtime/resource-mcp-secrets.mjs +28 -11
  373. package/src/session-runtime/resource-mcp-servers.mjs +5 -4
  374. package/src/session-runtime/resource-recall-api.mjs +6 -1
  375. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  376. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  377. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  378. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  379. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  380. package/src/session-runtime/services/agent-tool/send-flow.mjs +10 -2
  381. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +8 -3
  382. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +4 -1
  383. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +6 -2
  384. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  385. package/src/session-runtime/services/agent-watchdog-registry.mjs +32 -4
  386. package/src/session-runtime/services/channel-worker/daemon-spawn.mjs +2 -0
  387. package/src/session-runtime/services/daemon-candidate.mjs +4 -1
  388. package/src/session-runtime/services/memory-runtime-proxy/daemon-spawn.mjs +4 -0
  389. package/src/session-runtime/services/plugin-admin.mjs +10 -6
  390. package/src/session-runtime/services/provider-admin.mjs +283 -11
  391. package/src/session-runtime/session-turn-api.mjs +2 -0
  392. package/src/session-runtime/settings-builtin-tools-api.mjs +4 -1
  393. package/src/session-runtime/settings-system-api.mjs +1 -1
  394. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +13 -3
  395. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +96 -0
  396. package/src/session-runtime/setup-tool/executor.mjs +6 -2
  397. package/src/session-runtime/setup-tool/tool-defs.mjs +2 -1
  398. package/src/session-runtime/tool-policy/policy-stage.mjs +1 -0
  399. package/src/session-runtime/turn/agent-ops.mjs +23 -0
  400. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +49 -11
  401. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  402. package/src/session-runtime/workflow-agents-api/agent-route.mjs +25 -5
  403. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  404. package/src/session-runtime/workspace/activity-guards.mjs +176 -0
  405. package/src/session-runtime/workspace/session-view.mjs +149 -0
  406. package/src/session-runtime/workspace/side-checkouts.mjs +203 -0
  407. package/src/session-runtime/workspace/transition-runner.mjs +188 -0
  408. package/src/session-runtime/workspace/transitions.mjs +108 -1041
  409. package/src/session-runtime/workspace/workspace-merge.mjs +132 -0
  410. package/src/session-runtime/workspace/worktree-intents.mjs +242 -0
  411. package/src/session-runtime/workspace/worktree-maintenance.mjs +80 -0
  412. package/src/session-runtime/workspace/worktree-setup.mjs +85 -0
  413. package/src/session-runtime/workspace/worktree-tool.mjs +73 -56
  414. package/src/standalone/daemon-desktop-runtime.mjs +2 -0
  415. package/src/standalone/daemon-session-control.mjs +169 -138
  416. package/src/standalone/daemon.mjs +7 -0
  417. package/src/standalone/desktop-service-registry.mjs +9 -0
  418. package/src/standalone/local-session-runtime.mjs +5 -1
  419. package/src/standalone/session-client/daemon-unavailable.mjs +3 -0
  420. package/src/standalone/session-client.mjs +5 -1
  421. package/src/standalone/session-frame-stream.mjs +83 -11
  422. package/src/standalone/session-protocol.mjs +4 -0
  423. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  424. package/src/standalone/session-service/agent-tree.mjs +45 -9
  425. package/src/standalone/session-service/projection/transcript-window.mjs +700 -64
  426. package/src/standalone/session-service/session-calls/session-action.mjs +10 -3
  427. package/src/standalone/session-service/session-calls/session-views.mjs +160 -104
  428. package/src/standalone/session-service/stored-reader.mjs +127 -40
  429. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  430. package/src/tui/App.jsx +154 -164
  431. package/src/tui/app/app-view/floating-panel.jsx +10 -4
  432. package/src/tui/app/app-view/text-entry-labels.mjs +1 -0
  433. package/src/tui/app/clipboard.mjs +21 -10
  434. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  435. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  436. package/src/tui/app/prompt-handlers/use-prompt-history-nav.mjs +1 -1
  437. package/src/tui/app/prompt-handlers/use-prompt-interrupt.mjs +3 -0
  438. package/src/tui/app/prompt-submit/provider-prompts.mjs +3 -1
  439. package/src/tui/app/provider-setup/api-key-actions.mjs +31 -1
  440. package/src/tui/app/provider-setup/oauth-actions.mjs +58 -21
  441. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  442. package/src/tui/app/provider-setup-picker.mjs +29 -18
  443. package/src/tui/app/shell-layout/row-budgets.mjs +12 -10
  444. package/src/tui/app/slash-commands.mjs +1 -0
  445. package/src/tui/app/slash-dispatch/session-commands.mjs +46 -0
  446. package/src/tui/app/slash-dispatch.mjs +1 -1
  447. package/src/tui/app/use-btw-panel.mjs +29 -0
  448. package/src/tui/app/use-context-panel-refresh.mjs +31 -0
  449. package/src/tui/app/use-model-catalog-cache.mjs +13 -10
  450. package/src/tui/app/use-mouse-input.mjs +11 -0
  451. package/src/tui/app/use-onboarding-start.mjs +8 -3
  452. package/src/tui/app/use-panel-transition.mjs +10 -1
  453. package/src/tui/app/use-pasted-buffers.mjs +28 -20
  454. package/src/tui/app/use-prompt-draft-flow.mjs +7 -0
  455. package/src/tui/app/use-prompt-layout-rows.mjs +3 -3
  456. package/src/tui/app/use-prompt-queue-history.mjs +3 -2
  457. package/src/tui/app/use-selection-focus.mjs +11 -2
  458. package/src/tui/app/use-selection-geometry.mjs +18 -9
  459. package/src/tui/app/use-selection-paint.mjs +4 -4
  460. package/src/tui/app/use-selection-stitch.mjs +3 -3
  461. package/src/tui/app/use-slash-palette.mjs +62 -0
  462. package/src/tui/app/use-terminal-chrome.mjs +2 -2
  463. package/src/tui/app/use-transcript-activity.mjs +47 -32
  464. package/src/tui/app/use-transcript-follow.mjs +12 -4
  465. package/src/tui/app/use-transcript-scroll.mjs +46 -17
  466. package/src/tui/app/use-transcript-swap-reset.mjs +27 -0
  467. package/src/tui/app/use-transcript-window/geometry.mjs +19 -5
  468. package/src/tui/app/use-transcript-window/instance-state.mjs +4 -0
  469. package/src/tui/app/use-transcript-window/selection-shift.mjs +5 -1
  470. package/src/tui/app/use-transcript-window.mjs +21 -11
  471. package/src/tui/app/use-tui-ready.mjs +18 -0
  472. package/src/tui/app/use-welcome-prompt-hint.mjs +5 -0
  473. package/src/tui/app/use-workflow-tab-cycle.mjs +5 -3
  474. package/src/tui/components/Picker.jsx +27 -3
  475. package/src/tui/components/QuestionPanel.jsx +13 -4
  476. package/src/tui/components/SignInUrl.jsx +68 -0
  477. package/src/tui/components/TextEntryPanel.jsx +76 -46
  478. package/src/tui/components/sign-in-url-layout.mjs +24 -0
  479. package/src/tui/dist/index.mjs +1963 -1361
  480. package/src/tui/session/goal-turn-state.mjs +6 -2
  481. package/src/tui/session/session-api/controls.mjs +4 -0
  482. package/src/tui/session/session-api/extensions.mjs +8 -0
  483. package/src/tui/session/session-api/intake/abort.mjs +2 -1
  484. package/src/tui/session/session-api/intake.mjs +2 -0
  485. package/src/tui/session/session-api/integrations.mjs +2 -2
  486. package/src/tui/session/session-api/routes-agent-definitions.mjs +1 -0
  487. package/src/tui/session/session-api/settings.mjs +2 -2
  488. package/src/tui/session/session-flow/queue/take-entries.mjs +11 -0
  489. package/src/tui/session/transcript-spill/spill-writer.mjs +15 -0
  490. package/src/tui/session/transcript-spill.mjs +92 -4
  491. package/src/tui/session/transcript-store.mjs +2 -0
  492. package/src/ui/statusline-agents.mjs +1 -3
  493. package/src/defaults/skills/skill-creator/references/source-review.md +0 -94
@@ -12,6 +12,7 @@ import {
12
12
  textByteLength,
13
13
  } from './text-utils.mjs';
14
14
  import {
15
+ callArguments,
15
16
  contextAckMessage,
16
17
  isCompactConversationRow,
17
18
  isProtectedContextAckMessage,
@@ -25,6 +26,7 @@ import { activeTurnContinuationMessage } from './continuation.mjs';
25
26
  import { EXECUTION_RECOVERY_SOURCE, buildExecutionTail } from './execution-tail.mjs';
26
27
  import { latestSkillBodies, skillMessageText } from '../../context/skill-state.mjs';
27
28
  import { effectiveBudget } from './budget.mjs';
29
+ import { parseMcpToolName } from '../../../../shared/tool-primitives.mjs';
28
30
  import { stripRuntimeUserContext, withRuntimeUserContext } from '../runtime-user-context.mjs';
29
31
  import { carryMessageId, messageIdOf } from '../message-ids.mjs';
30
32
  import { normalizeIngestRole, shouldExcludeIngestMessage } from '../../../../memory/lib/session-ingest.mjs';
@@ -70,12 +72,23 @@ function splitFreshSource(messages) {
70
72
  break;
71
73
  }
72
74
  }
75
+ // Where the previous Compact's skill-use window opens: `live` index after
76
+ // the history it kept (see SKILL_WINDOW_SOURCE). Without a marker the whole
77
+ // transcript is the window.
78
+ const isKept = (message) => !isSummaryMessage(message) && message?.meta?.source !== SKILL_WINDOW_SOURCE;
79
+ const markerAt = conversation.findLastIndex((message) => message?.meta?.source === SKILL_WINDOW_SOURCE);
80
+ const span = conversation[markerAt]?.meta?.span;
81
+ const skillWindowStart =
82
+ markerAt >= 0 && Number.isInteger(span) && span >= 0
83
+ ? conversation.slice(0, markerAt).filter(isKept).length + span
84
+ : 0;
73
85
  const latestUserIndex = latestActualUserInstructionIndex(conversation);
74
86
  return {
75
87
  protectedPrefix,
76
88
  previousSummary: previousSummaryMessage?.content || null,
77
89
  previousSummaryMessage,
78
90
  previousAck,
91
+ skillWindowStart,
79
92
  // Budgeting, AI input and rule-only retention use this same projection.
80
93
  // Keep current-turn reminders; remove only proven runtime additions from
81
94
  // older requests. Unmarked legacy text remains part of the summary budget.
@@ -86,7 +99,7 @@ function splitFreshSource(messages) {
86
99
  .map((message, index) =>
87
100
  index === latestUserIndex ? message : referenceMessageMedia(stripRuntimeUserContext(message))
88
101
  )
89
- .filter((message) => !isSummaryMessage(message)),
102
+ .filter(isKept),
90
103
  sanitized,
91
104
  };
92
105
  }
@@ -135,9 +148,11 @@ function toolCallSummaryLine(call, result) {
135
148
  // Arguments arrive redacted (splitFreshSource); the outcome is redacted
136
149
  // here, before any cut can split a secret from its key.
137
150
  const outcome = redactRawSecretString(contentText(result?.content)).trim();
138
- // An ask_user result is the user's own answer: never cut.
151
+ // An ask_user or handoff_user result is the user's own answer: never cut.
139
152
  const shown =
140
- call.name === 'ask_user' ? outcome : boundedSummaryText(outcome.replace(/\s+/g, ' '), SUMMARY_TOOL_OUTCOME_CHARS);
153
+ call.name === 'ask_user' || call.name === 'handoff_user'
154
+ ? outcome
155
+ : boundedSummaryText(outcome.replace(/\s+/g, ' '), SUMMARY_TOOL_OUTCOME_CHARS);
141
156
  return `[tool call] ${call.name}(${args}) → ${result ? shown || '(empty result)' : '(no result)'}`;
142
157
  }
143
158
 
@@ -564,44 +579,128 @@ function currentSkillBodyMessage(message, current) {
564
579
 
565
580
  const SKILL_RELOAD_SOURCE = 'compact-skill-reload';
566
581
 
567
- function skillReloadReminder(name) {
582
+ // How many Compacts in a row a skill may go unused before even its reload
583
+ // line is dropped: a skill untouched that long belongs to finished work, and
584
+ // the model can still load it by name from the skill manifest.
585
+ export const SKILL_RELOAD_MAX_IDLE = 3;
586
+
587
+ const SKILL_TOOL_NAMES = new Set(['Skill', 'skill_view']);
588
+
589
+ function skillReloadReminder(name, idle = 0) {
568
590
  return {
569
591
  role: 'user',
570
- content: `[mixdog-runtime] Skill "${name}" was active, but its instructions did not fit after Compact. Reload it with the Skill tool before relying on them.`,
571
- meta: { source: SKILL_RELOAD_SOURCE, synthetic: true, skill: name },
592
+ content: `[mixdog-runtime] Skill "${name}" was active earlier, but Compact did not keep its instructions. Reload it with the Skill tool before relying on them.`,
593
+ meta: { source: SKILL_RELOAD_SOURCE, synthetic: true, skill: name, idle },
572
594
  };
573
595
  }
574
596
 
575
- // Restore the skills active in the transcript, each as the Skill tool would
576
- // deliver it now. `currentSkill(name)` (the caller's resolver) answers
577
- // `{ body, native, summary }` for a skill that still loads in the session, or null for
578
- // one switched off, outside its scope or removed: such a skill is restored
579
- // neither as a body nor as a reload reminder, since its instructions are no
580
- // longer in force. Without a resolver the transcript copies are kept.
597
+ // Every Compact leaves this marker just ahead of the skills it restores, with
598
+ // `span`: how many messages follow it (restored skills and retained tail).
599
+ // All of them happened before that Compact, so the next one reads the skill
600
+ // use window from after them, whether or not a summary exists. The marker is
601
+ // not conversation: the next Compact removes it and writes a new one.
602
+ const SKILL_WINDOW_SOURCE = 'compact-skill-window';
603
+
604
+ function skillWindowMarker(span = 0) {
605
+ return {
606
+ role: 'user',
607
+ content: '[mixdog-runtime] Compact boundary.',
608
+ meta: { source: SKILL_WINDOW_SOURCE, synthetic: true, span },
609
+ };
610
+ }
611
+
612
+ // Restore the skills the session used since the previous Compact, each as the
613
+ // Skill tool would deliver it now. A skill is used when it was loaded or one of
614
+ // its linked tools ran in that window (`windowStart` onward); its linked
615
+ // tools are the native tool references of its loader pair and, when the
616
+ // resolver gives them, `tools`. `currentSkill(name)` (the caller's resolver)
617
+ // answers `{ body, native, summary, tools? }` for a skill that still loads in
618
+ // the session, or null for one switched off, outside its scope or removed:
619
+ // such a skill is restored neither as a body nor as a reload line, since its
620
+ // instructions are no longer in force. Without a resolver the transcript
621
+ // copies are kept.
581
622
  // Never summarize/truncate operating instructions or displace the handoff
582
- // and current task. An omitted body leaves a one-line reload reminder, which
583
- // stands through later Compacts until the skill is loaded again. Reminders
584
- // are charged to the same budget: every skill's reminder is reserved before
585
- // any body is restored, and restoring a body releases its own reminder.
586
- function restoreSkillBodies(live, skillBudget, retainedTail, currentSkill = null) {
623
+ // and current task. Every other active skill, and a used one whose body does
624
+ // not fit, is left as a one-line reload line. The line counts the Compacts
625
+ // the skill went unused (`meta.idle`) and is dropped past SKILL_RELOAD_MAX_IDLE.
626
+ // Reload lines are charged to the same budget: every possible line is
627
+ // reserved before any body is restored, and restoring a body releases its own.
628
+ function restoreSkillBodies(live, skillBudget, retainedTail, currentSkill = null, windowStart = 0) {
587
629
  const retainedCallIds = new Set(retainedTail.filter((m) => m?.role === 'tool').map((m) => m.toolCallId));
588
630
  const current = (name) => (currentSkill ? currentSkill(name) : undefined);
589
631
  const loads = (name) => current(name) !== null;
590
632
  const transcriptBodies = latestSkillBodies(live);
591
633
  const loaded = new Set(transcriptBodies.map((body) => body.name));
592
634
  const bodies = transcriptBodies.filter((body) => loads(body.name));
593
- const reload = live
594
- .filter((m) => m?.meta?.source === SKILL_RELOAD_SOURCE && !loaded.has(m.meta.skill) && loads(m.meta.skill))
595
- .map((m) => m.meta.skill);
596
- const reminderCost = (name) => estimateMessagesTokens([skillReloadReminder(name)]);
597
- let reserved = [...new Set([...reload, ...bodies.map((body) => body.name)])].reduce(
598
- (sum, name) => sum + reminderCost(name),
599
- 0
600
- );
601
- const dropped = [];
635
+ // Reload lines from earlier Compacts: skill -> Compacts it went unused.
636
+ const earlier = new Map();
637
+ for (const m of live) {
638
+ if (m?.meta?.source !== SKILL_RELOAD_SOURCE || loaded.has(m.meta.skill) || !loads(m.meta.skill)) continue;
639
+ earlier.delete(m.meta.skill);
640
+ earlier.set(m.meta.skill, Number.isFinite(m.meta.idle) ? m.meta.idle : 0);
641
+ }
642
+
643
+ // A loader pair can sit anywhere in the transcript (a restored body stays
644
+ // ahead of the retained tail's pair), so read them all.
645
+ const results = new Map(live.filter((m) => m?.role === 'tool').map((m) => [m.toolCallId, m]));
646
+ const loaderTools = new Map();
647
+ for (const message of live) {
648
+ for (const call of message?.role === 'assistant' ? message.toolCalls || [] : []) {
649
+ const references = results.get(call.id)?.nativeToolSearch?.toolReferences;
650
+ if (call.name !== 'Skill' || !references) continue;
651
+ const name = String(callArguments(call).name || '').trim();
652
+ loaderTools.set(name, [...(loaderTools.get(name) || []), ...references]);
653
+ }
654
+ }
655
+ const skillsOfTool = new Map();
656
+ for (const name of new Set([...earlier.keys(), ...bodies.map((body) => body.name)])) {
657
+ const skill = current(name);
658
+ const tools = [...(skill?.native?.toolReferences || []), ...(skill?.tools || []), ...(loaderTools.get(name) || [])];
659
+ for (const tool of tools) skillsOfTool.set(tool, (skillsOfTool.get(tool) || new Set()).add(name));
660
+ }
661
+ // Use since the previous Compact (from `windowStart`): the newest transcript
662
+ // position of a load or of a linked tool's call.
663
+ const lastUse = new Map();
664
+ const noteUse = (name, index) => lastUse.set(name, Math.max(lastUse.get(name) ?? -1, index));
665
+ live.forEach((message, index) => {
666
+ if (index < windowStart || message?.role !== 'assistant') return;
667
+ for (const call of message.toolCalls || []) {
668
+ const args = callArguments(call);
669
+ if (SKILL_TOOL_NAMES.has(call.name)) noteUse(String(args.name || '').trim(), index);
670
+ else {
671
+ const tool = call.name === 'call_tool' ? args.name : call.name;
672
+ const mcpServer = `mcp:${parseMcpToolName(tool)?.server}`;
673
+ for (const name of [...(skillsOfTool.get(tool) || []), ...(skillsOfTool.get(mcpServer) || [])]) {
674
+ noteUse(name, index);
675
+ }
676
+ }
677
+ }
678
+ });
679
+ for (const { name, message } of bodies) {
680
+ const index = live.lastIndexOf(message);
681
+ if (index >= windowStart) noteUse(name, index);
682
+ }
683
+
684
+ // Every active skill without a body ends as a reload line: an unused one
685
+ // ages by a Compact and goes once past the bound; one used (or whose body
686
+ // did not fit) starts over.
687
+ const reminders = new Map();
688
+ for (const [name, idle] of earlier) {
689
+ if (lastUse.has(name)) reminders.set(name, 0);
690
+ else if (idle < SKILL_RELOAD_MAX_IDLE) reminders.set(name, idle + 1);
691
+ }
692
+ const used = bodies.filter(({ name }) => lastUse.has(name)).sort((a, b) => lastUse.get(b.name) - lastUse.get(a.name));
693
+ const unused = bodies.filter(({ name }) => !lastUse.has(name));
694
+
695
+ const reminderCost = (name, idle = 0) => estimateMessagesTokens([skillReloadReminder(name, idle)]);
696
+ let reserved =
697
+ [...reminders].reduce((sum, [name, idle]) => sum + reminderCost(name, idle), 0) +
698
+ unused.reduce((sum, { name }) => sum + reminderCost(name, 1), 0) +
699
+ used.reduce((sum, { name }) => sum + reminderCost(name), 0);
700
+ const dropped = new Set();
602
701
  const restoredSkills = [];
603
702
  let skillTokens = 0;
604
- for (const { name, message } of bodies.reverse()) {
703
+ for (const { name, message } of used) {
605
704
  const skill = current(name);
606
705
  const restored = [
607
706
  ...skillLoaderPair(live, live.lastIndexOf(message), name, retainedCallIds, skill),
@@ -610,24 +709,31 @@ function restoreSkillBodies(live, skillBudget, retainedTail, currentSkill = null
610
709
  const cost = estimateMessagesTokens(restored);
611
710
  const own = reminderCost(name);
612
711
  if (skillTokens + cost + reserved - own > skillBudget) {
613
- dropped.unshift(name);
712
+ dropped.add(name);
614
713
  continue;
615
714
  }
616
715
  restoredSkills.unshift(...restored);
617
716
  skillTokens += cost;
618
717
  reserved -= own;
619
718
  }
620
- // Newest skill first; only a budget too small for the reminders themselves
719
+ // In transcript order, after the lines of earlier Compacts.
720
+ for (const { name } of bodies) {
721
+ if (!lastUse.has(name)) reminders.set(name, 1);
722
+ else if (dropped.has(name)) reminders.set(name, 0);
723
+ }
724
+ // Newest skill first; only a budget too small for the lines themselves
621
725
  // leaves one out.
622
- const pending = [...new Set([...reload, ...dropped])];
726
+ const pending = [...reminders];
623
727
  const reminded = new Set();
624
- for (const name of pending.toReversed()) {
625
- const cost = reminderCost(name);
728
+ for (const [name, idle] of pending.toReversed()) {
729
+ const cost = reminderCost(name, idle);
626
730
  if (skillTokens + cost > skillBudget) continue;
627
731
  reminded.add(name);
628
732
  skillTokens += cost;
629
733
  }
630
- restoredSkills.push(...pending.filter((name) => reminded.has(name)).map(skillReloadReminder));
734
+ restoredSkills.push(
735
+ ...pending.filter(([name]) => reminded.has(name)).map(([name, idle]) => skillReloadReminder(name, idle))
736
+ );
631
737
  return { restoredSkills, skillTokens };
632
738
  }
633
739
 
@@ -864,15 +970,33 @@ export function freshContextCompactMessages(messages, budgetTokens, options = {}
864
970
  const summaryContent = String(summaryMessage?.content || '');
865
971
  assertHandoffFits(summaryMessage, handoff.room);
866
972
  const skillBudget = freshSkillBudget(targetBudget, budget, [...stableHead, ...tail.volatileTail]);
973
+ // Restored skills carry the window marker, which is charged to their budget.
974
+ const markerCost = estimateMessagesTokens([skillWindowMarker()]);
867
975
  const skills = {
868
- ...restoreSkillBodies(source.live, skillBudget, tail.volatileTail, opts.currentSkill),
976
+ ...restoreSkillBodies(
977
+ source.live,
978
+ Math.max(0, skillBudget - markerCost),
979
+ tail.volatileTail,
980
+ opts.currentSkill,
981
+ source.skillWindowStart
982
+ ),
869
983
  skillBudget,
870
984
  };
871
- const stablePrefixMessages = [...stableHead, ...skills.restoredSkills];
872
- const result = rebaseCompactedEffortConfiguration(
985
+ // A session with no skill state has no use to window: no marker.
986
+ const marker = skills.restoredSkills.length ? [skillWindowMarker()] : [];
987
+ const stablePrefixMessages = [...stableHead, ...marker, ...skills.restoredSkills];
988
+ const compacted = rebaseCompactedEffortConfiguration(
873
989
  baseSanitized,
874
990
  reconcileDedupStubs(sanitizeToolPairs([...stablePrefixMessages, ...tail.volatileTail]))
875
991
  );
992
+ // The next Compact reads everything after the marker as history, not as use.
993
+ const markerAt = compacted.findLastIndex((message) => message?.meta?.source === SKILL_WINDOW_SOURCE);
994
+ const result =
995
+ markerAt < 0
996
+ ? compacted
997
+ : compacted.map((message, index) =>
998
+ index === markerAt ? skillWindowMarker(compacted.length - markerAt - 1) : message
999
+ );
876
1000
  const finalTokens = estimateMessagesTokens(result);
877
1001
  if (finalTokens > budget) {
878
1002
  throw new Error(`freshContextCompactMessages: compacted result exceeds budget=${budget} (result=${finalTokens})`);
@@ -581,8 +581,20 @@ function reasoningBreakdown(message, fullTextTokens) {
581
581
  };
582
582
  }
583
583
  }
584
+ // Price each reasoning item that is actually sent (a non-empty replay
585
+ // replaces content) at its own cost; the whole-minus-visible difference only
586
+ // caps the sum so the rows never exceed the total.
587
+ const sent = message.providerReplay?.items?.length ? message.providerReplay.items.filter(isReasoningBlock) : blocks;
584
588
  return {
585
- tokens: blocks.length ? Math.max(0, fullTextTokens() - messageTextTokens(visible)) : 0,
589
+ tokens: blocks.length
590
+ ? Math.max(
591
+ 0,
592
+ Math.min(
593
+ sent.reduce((sum, block) => sum + estimateTokens(nativeBlocksEstimateText(block)), 0),
594
+ fullTextTokens() - messageTextTokens(visible)
595
+ )
596
+ )
597
+ : 0,
586
598
  blocks,
587
599
  message: visible,
588
600
  };
@@ -3,12 +3,22 @@
3
3
  * import turns. The active branch is followed from the last main-chain entry
4
4
  * back through `parentUuid` (and `logicalParentUuid` across a compaction), so
5
5
  * replayed or abandoned branches never duplicate. Kept: text blocks and
6
- * `tool_use` as `[ran tool: <name>]` notes; images become an omission marker;
7
- * a compaction summary only when the history it replaced is no longer linked.
8
- * Dropped: sidechains, meta entries, tool results, thinking, signatures and
9
- * Claude Code's own command/notification envelopes.
6
+ * `tool_use` as tool notes; images become an omission marker; a compaction
7
+ * summary (as an assistant recap) only when the history it replaced is gone;
8
+ * a slash command as `/name args` unless the app answered it locally; pasted
9
+ * text without the app's paste envelope. Dropped: sidechains, meta entries,
10
+ * tool results, thinking, signatures and Claude Code's own
11
+ * command-output/notification envelopes.
10
12
  */
11
- import { IMAGE_OMITTED_TEXT, IMPORT_WARNING_IMAGE_OMITTED, isWholeElement, textPart, toolNote } from './turns.mjs';
13
+ import {
14
+ IMAGE_OMITTED_TEXT,
15
+ IMPORT_WARNING_IMAGE_OMITTED,
16
+ isWholeElement,
17
+ stringOrNull,
18
+ textPart,
19
+ titleFromText,
20
+ toolNote,
21
+ } from './turns.mjs';
12
22
 
13
23
  const CLAUDE_ENVELOPE_TAGS = [
14
24
  'local-command-stdout',
@@ -20,11 +30,54 @@ const CLAUDE_ENVELOPE_TAGS = [
20
30
  'bash-input',
21
31
  'bash-stdout',
22
32
  'bash-stderr',
33
+ // The app's own instructions behind its "create PR" action.
34
+ 'create-pr-command',
23
35
  ];
24
- const SLASH_COMMAND_RE = /^(?:\s*<(command-name|command-message|command-args)>[\s\S]*?<\/\1>)+\s*$/;
36
+ const LOCAL_OUTPUT_TAGS = ['local-command-stdout', 'local-command-stderr'];
37
+ const SLASH_COMMAND_RE = /^(?:\s*<(command-name|command-message|command-args|skill-format)>[\s\S]*?<\/\1>)+\s*$/;
25
38
  const INTERRUPTED_RE = /^\[Request interrupted by user[^\]\n]*\]$/;
26
39
  // Claude Code's text annotation that accompanies a pasted image block.
27
40
  const IMAGE_ANNOTATION_RE = /^\[Image\b[^\n]*\]$/;
41
+ // The desktop app's paste envelope: `<pasted_content id="hhhh">\n` body
42
+ // `\n</pasted_content id="hhhh">`, one four-hex id on both tags.
43
+ const PASTE_OPEN = '<pasted_content id="';
44
+ const PASTE_ID_RE = /^[0-9a-f]{4}$/;
45
+
46
+ /**
47
+ * Text with the app's paste envelopes unwrapped the way the app displays it:
48
+ * each paste body in place, the text around it trimmed toward it, pieces on
49
+ * their own lines. Only a complete envelope with a matching id is unwrapped; any
50
+ * other `<pasted_content…>` is the user's own text and stays as typed.
51
+ */
52
+ export function unwrapClaudePastes(text) {
53
+ const pieces = [];
54
+ let cursor = 0;
55
+ let search = 0;
56
+ for (;;) {
57
+ const open = text.indexOf(PASTE_OPEN, search);
58
+ if (open < 0) break;
59
+ const idStart = open + PASTE_OPEN.length;
60
+ const id = text.slice(idStart, idStart + 4);
61
+ const bodyStart = idStart + 7; // past `hhhh">\n`
62
+ if (!PASTE_ID_RE.test(id) || text.slice(idStart + 4, bodyStart) !== '">\n') {
63
+ search = idStart;
64
+ continue;
65
+ }
66
+ const closeTag = `\n</pasted_content id="${id}">`;
67
+ // The line feed ending the open tag may also start the close tag (an empty paste).
68
+ const close = text.indexOf(closeTag, bodyStart - 1);
69
+ if (close < 0) break;
70
+ // Trimmed only toward a paste: the first fragment keeps its leading text.
71
+ const before = text.slice(cursor, open);
72
+ pieces.push(cursor ? before.trim() : before.trimEnd(), text.slice(bodyStart, Math.max(close, bodyStart)));
73
+ cursor = close + closeTag.length;
74
+ search = cursor;
75
+ }
76
+ if (!pieces.length) return text;
77
+ pieces.push(text.slice(cursor).trimStart());
78
+ const unwrapped = pieces.filter((piece, index) => index % 2 === 1 || piece).join('\n');
79
+ return unwrapped.trim() ? unwrapped : text;
80
+ }
28
81
 
29
82
  export function isClaudeEnvelopeText(text) {
30
83
  const trimmed = String(text || '').trim();
@@ -37,8 +90,18 @@ export function isClaudeEnvelopeText(text) {
37
90
  );
38
91
  }
39
92
 
93
+ const tagValue = (text, tag) => new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`).exec(text)?.[1].trim() || '';
94
+
95
+ /** A slash-command envelope as Claude Code shows it (`/name args`), else null. */
96
+ function claudeCommandText(text) {
97
+ const trimmed = String(text || '').trim();
98
+ if (!SLASH_COMMAND_RE.test(trimmed)) return null;
99
+ const name = (tagValue(trimmed, 'command-name') || tagValue(trimmed, 'command-message')).replace(/^\//, '');
100
+ if (!name) return null;
101
+ return [`/${name}`, tagValue(trimmed, 'command-args')].filter(Boolean).join(' ');
102
+ }
103
+
40
104
  const isMessageEntry = (record) => record?.type === 'user' || record?.type === 'assistant';
41
- const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
42
105
  const uuidOrNull = (value) => (typeof value === 'string' ? value : null);
43
106
  const timestampOrNull = (value) => (typeof value === 'string' || typeof value === 'number' ? value : null);
44
107
  // Rough fixed cost of one retained entry object, so tiny entries still count.
@@ -49,42 +112,69 @@ const metadataLength = (entry) =>
49
112
  (entry.logicalParentUuid?.length || 0) +
50
113
  (typeof entry.at === 'string' ? entry.at.length : 0);
51
114
 
115
+ // The main-chain message written last before file position `seq`: the leaf of
116
+ // the history a compaction boundary replaced.
117
+ function precedingMain(entries, seq) {
118
+ for (let index = seq - 1; index >= 0; index--) {
119
+ if (entries[index].main && entries[index].uuid !== null) return entries[index];
120
+ }
121
+ return null;
122
+ }
123
+
52
124
  /**
53
125
  * The active branch, root first: walked back from the last main-chain entry.
54
- * `crossedCompaction` tells whether a compaction boundary led back into the
55
- * history it replaced (Claude Code often no longer keeps that history).
126
+ * A compaction boundary links back to the history it replaced through its
127
+ * logical parent, or, when Claude Code never wrote that message, to the last
128
+ * message before the boundary. `replaced` holds the compaction summaries whose
129
+ * history the walk crossed into (that history is imported instead).
56
130
  */
57
131
  function activeChain(entries) {
58
132
  const main = entries.filter((entry) => entry.main);
59
133
  const leaf = main[main.length - 1];
60
- if (!leaf || leaf.uuid === null) return { chain: main, crossedCompaction: false };
134
+ if (!leaf || leaf.uuid === null) return { chain: main, replaced: new Set() };
61
135
  const byUuid = new Map();
62
136
  for (const entry of entries) {
63
137
  if (entry.uuid !== null) byUuid.set(entry.uuid, entry);
64
138
  }
65
139
  const chain = [];
66
140
  const seen = new Set();
67
- let crossedCompaction = false;
141
+ const replaced = new Set();
142
+ let summary = null;
68
143
  let current = leaf;
69
144
  while (current && !seen.has(current.uuid)) {
70
145
  seen.add(current.uuid);
71
146
  chain.push(current);
147
+ if (current.summary) summary = current;
72
148
  const parent = current.parentUuid !== null ? byUuid.get(current.parentUuid) : null;
73
- const logical = !parent && current.logicalParentUuid !== null ? byUuid.get(current.logicalParentUuid) : null;
74
- if (logical) crossedCompaction = true;
149
+ const logical =
150
+ !parent && current.logicalParentUuid !== null
151
+ ? byUuid.get(current.logicalParentUuid) || precedingMain(entries, current.seq)
152
+ : null;
153
+ if (logical && summary) {
154
+ replaced.add(summary);
155
+ summary = null;
156
+ }
75
157
  current = parent || logical;
76
158
  }
77
- return { chain: chain.reverse().filter((entry) => entry.main), crossedCompaction };
159
+ return { chain: chain.reverse().filter((entry) => entry.main), replaced };
78
160
  }
79
161
 
80
- function entryParts(role, content, warnings) {
162
+ /** Kept parts of a message; a slash command is a `command` part, a local command's output marks `localOutput`. */
163
+ function entryParts(role, content, warnings, marks) {
81
164
  const blocks = typeof content === 'string' ? [{ type: 'text', text: content }] : content;
82
165
  if (!Array.isArray(blocks)) return [];
83
166
  const parts = [];
84
167
  for (const block of blocks) {
85
168
  if (!block || typeof block !== 'object') continue;
86
169
  if (block.type === 'text' && typeof block.text === 'string') {
87
- if (role === 'user' && isClaudeEnvelopeText(block.text)) continue;
170
+ if (role === 'user') {
171
+ const command = claudeCommandText(block.text);
172
+ if (command) parts.push({ ...textPart(command), command: true });
173
+ if (isWholeElement(block.text, LOCAL_OUTPUT_TAGS)) marks.localOutput = true;
174
+ if (isClaudeEnvelopeText(block.text)) continue;
175
+ parts.push(textPart(unwrapClaudePastes(block.text)));
176
+ continue;
177
+ }
88
178
  parts.push(textPart(block.text));
89
179
  } else if (block.type === 'image') {
90
180
  warnings.add(IMPORT_WARNING_IMAGE_OMITTED);
@@ -96,6 +186,31 @@ function entryParts(role, content, warnings) {
96
186
  return parts;
97
187
  }
98
188
 
189
+ /**
190
+ * The title Claude Code's resume list shows from the records it appends to a
191
+ * transcript: the last `custom-title` (a `/rename`), else the last `ai-title`,
192
+ * then the last `summary`, then the last `last-prompt`. A blank custom title
193
+ * (a cleared rename) ends the name tier without falling back to the AI title.
194
+ * Other record types, such as `task-summary` or `agent-name`, never name it.
195
+ */
196
+ function createClaudeTitleScan() {
197
+ let custom;
198
+ let ai;
199
+ let summary;
200
+ let lastPrompt;
201
+ return {
202
+ push(record) {
203
+ if (record.type === 'custom-title' && typeof record.customTitle === 'string') custom = record.customTitle;
204
+ else if (record.type === 'ai-title' && typeof record.aiTitle === 'string') ai = record.aiTitle;
205
+ else if (record.type === 'summary' && typeof record.summary === 'string') summary = record.summary;
206
+ else if (record.type === 'last-prompt' && typeof record.lastPrompt === 'string') lastPrompt = record.lastPrompt;
207
+ },
208
+ title() {
209
+ return titleFromText(custom ?? ai) || titleFromText(summary) || titleFromText(lastPrompt);
210
+ },
211
+ };
212
+ }
213
+
99
214
  function skippedEntry(record) {
100
215
  // Claude Code flags its compaction summary transcript-only as well.
101
216
  if (record.isCompactSummary === true) return false;
@@ -111,9 +226,12 @@ function skippedEntry(record) {
111
226
  */
112
227
  export function createClaudeReducer() {
113
228
  const entries = [];
229
+ const titleScan = createClaudeTitleScan();
114
230
  let recognized = false;
115
231
  let foreignSessionId = null;
116
232
  let cwd = null;
233
+ let sidechainSeen = false;
234
+ let mainSeen = false;
117
235
  let chars = 0;
118
236
  let metadataBytes = 0;
119
237
  return {
@@ -126,10 +244,23 @@ export function createClaudeReducer() {
126
244
  get metadataBytes() {
127
245
  return metadataBytes;
128
246
  },
247
+ /** Cheap running check: messages seen so far are all sidechain (a verdict only at the end of the file). */
248
+ get sidechainOnly() {
249
+ return sidechainSeen && !mainSeen;
250
+ },
251
+ /** Feed a record for its stored title only (a transcript's tail window). */
252
+ pushTitle(record) {
253
+ titleScan.push(record);
254
+ },
129
255
  push(record) {
256
+ titleScan.push(record);
130
257
  const message = isMessageEntry(record);
131
258
  if (message) recognized = true;
132
259
  const main = message && record.isSidechain !== true;
260
+ if (message) {
261
+ if (main) mainSeen = true;
262
+ else sidechainSeen = true;
263
+ }
133
264
  if (main) {
134
265
  foreignSessionId ||= stringOrNull(record.sessionId);
135
266
  cwd ||= stringOrNull(record.cwd);
@@ -145,13 +276,14 @@ export function createClaudeReducer() {
145
276
  parts: null,
146
277
  warnings: null,
147
278
  at: timestampOrNull(record.timestamp),
279
+ seq: entries.length,
148
280
  };
149
281
  metadataBytes += ENTRY_OVERHEAD_BYTES + metadataLength(entry);
150
282
  if (main && !skippedEntry(record)) {
151
283
  const body = record.message && typeof record.message === 'object' ? record.message : null;
152
284
  entry.role = body?.role === 'user' || body?.role === 'assistant' ? body.role : record.type;
153
285
  const warnings = new Set();
154
- entry.parts = entryParts(entry.role, body ? body.content : null, warnings);
286
+ entry.parts = entryParts(entry.role, body ? body.content : null, warnings, entry);
155
287
  if (record.isCompactSummary === true) {
156
288
  entry.summary = true;
157
289
  entry.parts = entry.parts.map((part) => ({ ...part, summary: true }));
@@ -164,15 +296,24 @@ export function createClaudeReducer() {
164
296
  result() {
165
297
  const warnings = new Set();
166
298
  const rawTurns = [];
167
- const { chain, crossedCompaction } = activeChain(entries);
168
- for (const entry of chain) {
299
+ const { chain, replaced } = activeChain(entries);
300
+ for (const [index, entry] of chain.entries()) {
169
301
  if (!entry.parts?.length) continue;
170
- // The summary stands in for history only when that history is gone.
171
- if (entry.summary && crossedCompaction) continue;
172
- rawTurns.push({ role: entry.role, parts: entry.parts, at: entry.at });
302
+ // The summary stands in for history only when that history is gone,
303
+ // and then as the model's recap, never as something the user wrote.
304
+ if (entry.summary && replaced.has(entry)) continue;
305
+ const role = entry.summary ? 'assistant' : entry.role;
306
+ // A local command (`/model`, `/rename`…) answered by the app itself is not part of the conversation.
307
+ const local =
308
+ entry.parts.some((part) => part.command) &&
309
+ chain.slice(index + 1).find((next) => next.parts?.length || next.localOutput)?.localOutput;
310
+ const parts = local ? entry.parts.filter((part) => !part.command) : entry.parts;
311
+ rawTurns.push({ role, parts, at: entry.at });
173
312
  for (const code of entry.warnings || []) warnings.add(code);
174
313
  }
175
- return { recognized, foreignSessionId, cwd, rawTurns, warnings };
314
+ // A transcript whose every message is a sidechain is a subagent's own, not a conversation.
315
+ const internalKind = this.sidechainOnly ? 'subagent' : null;
316
+ return { recognized, foreignSessionId, cwd, internalKind, rawTurns, warnings, storedTitle: titleScan.title() };
176
317
  },
177
318
  };
178
319
  }