mixdog 1.1.2 → 1.2.1

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 (424) hide show
  1. package/README.md +8 -5
  2. package/package.json +2 -1
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/test-direct.mjs +4 -2
  6. package/src/defaults/shipped-history.json +10 -0
  7. package/src/defaults/skills/pptx/SKILL.md +6 -6
  8. package/src/defaults/skills/pptx/references/review.md +6 -4
  9. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  10. package/src/defaults/skills/setup/references/actions.md +18 -5
  11. package/src/defaults/skills/visualize/SKILL.md +160 -0
  12. package/src/defaults/skills/visualize/references/craft.md +88 -0
  13. package/src/headless-command.mjs +56 -3
  14. package/src/headless-exec.mjs +17 -1
  15. package/src/help.mjs +2 -0
  16. package/src/rules/shared/60-verification.md +3 -1
  17. package/src/rules/shared/70-delivery.md +4 -2
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
  21. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  22. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  24. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +19 -5
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
  33. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  35. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  36. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  43. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  45. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  50. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
  51. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  52. package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
  53. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  55. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  56. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  57. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  59. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  60. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  61. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
  62. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  64. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  66. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
  67. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  68. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
  78. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  79. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  80. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
  81. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  82. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  83. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
  85. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  86. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  87. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  88. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  90. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  91. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  92. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  93. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
  94. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  95. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  96. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  97. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
  98. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  99. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  100. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  101. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  102. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  103. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  104. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  105. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  106. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  107. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  108. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  109. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  112. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  114. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
  115. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
  116. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
  124. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  125. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  126. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
  128. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  131. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  133. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  136. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  137. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  138. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
  139. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  140. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  141. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  142. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  143. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  144. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
  145. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
  146. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
  147. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  148. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
  149. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  150. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  151. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
  153. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  157. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  158. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  159. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  160. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  161. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  162. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
  163. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  164. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  165. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  166. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  167. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
  168. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  169. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  175. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  176. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  177. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  179. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  180. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  181. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  182. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  184. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  185. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  186. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  187. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  188. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  189. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  190. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  191. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  192. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  193. package/src/runtime/attachments/store.d.mts +3 -0
  194. package/src/runtime/attachments/store.mjs +38 -0
  195. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  196. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  197. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  199. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  200. package/src/runtime/memory/lib/memory.mjs +1 -1
  201. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  202. package/src/runtime/shared/atomic-file.mjs +33 -6
  203. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  204. package/src/runtime/shared/code-graph-state.mjs +12 -0
  205. package/src/runtime/shared/defaults-separation.mjs +2 -0
  206. package/src/runtime/shared/extension-scopes.mjs +5 -3
  207. package/src/runtime/shared/file-lock.mjs +37 -11
  208. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  209. package/src/runtime/shared/git-runner.mjs +4 -3
  210. package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
  211. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  212. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  213. package/src/runtime/shared/llm/model-catalog.mjs +240 -130
  214. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  215. package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
  216. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  217. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  218. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  219. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  220. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  221. package/src/runtime/shared/pristine-execution.mjs +28 -4
  222. package/src/runtime/shared/provider-accounts.mjs +125 -21
  223. package/src/runtime/shared/provider-api-key.mjs +33 -1
  224. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  225. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  226. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  227. package/src/runtime/shared/shell-display.mjs +9 -3
  228. package/src/runtime/shared/singleton-owner.mjs +36 -6
  229. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  230. package/src/runtime/shared/skill-mentions.mjs +133 -0
  231. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  232. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  233. package/src/runtime/shared/tool-primitives.mjs +35 -2
  234. package/src/runtime/shared/tool-surface.mjs +7 -0
  235. package/src/runtime/shared/tool-work-units.mjs +14 -0
  236. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  237. package/src/runtime/shared/turn-changes.mjs +791 -0
  238. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  239. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  240. package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
  241. package/src/runtime/shared/worker-requests.mjs +15 -2
  242. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  243. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  244. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  245. package/src/runtime/shared/worktrees/git.mjs +247 -0
  246. package/src/runtime/shared/worktrees/include.mjs +480 -0
  247. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  248. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  249. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  250. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  251. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  252. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  253. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  254. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  255. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  256. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  257. package/src/runtime/visualize-bridge/client.mjs +181 -0
  258. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  259. package/src/session-runtime/automation-agents.mjs +8 -2
  260. package/src/session-runtime/boot/apis.mjs +90 -0
  261. package/src/session-runtime/boot/config.mjs +7 -2
  262. package/src/session-runtime/boot/facade.mjs +21 -0
  263. package/src/session-runtime/boot/infrastructure.mjs +30 -18
  264. package/src/session-runtime/boot/session.mjs +1 -0
  265. package/src/session-runtime/boot/tools.mjs +4 -0
  266. package/src/session-runtime/config-lifecycle.mjs +10 -1
  267. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  268. package/src/session-runtime/conversation-import/service.mjs +369 -0
  269. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
  270. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  271. package/src/session-runtime/global-extensions.mjs +2 -2
  272. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  273. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  274. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  275. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  276. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  277. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  278. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  279. package/src/session-runtime/mcp-glue/connect.mjs +68 -16
  280. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  281. package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
  282. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  283. package/src/session-runtime/mcp-glue.mjs +22 -2
  284. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  285. package/src/session-runtime/model-route/set-route.mjs +15 -2
  286. package/src/session-runtime/new-session-config.mjs +3 -2
  287. package/src/session-runtime/notification-bus.mjs +18 -3
  288. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  289. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  290. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  291. package/src/session-runtime/provider-readiness.mjs +16 -10
  292. package/src/session-runtime/resource-extension-sync.mjs +45 -7
  293. package/src/session-runtime/resource-mcp-api.mjs +121 -84
  294. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  295. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  296. package/src/session-runtime/resource-skills-api.mjs +23 -2
  297. package/src/session-runtime/route-resolution.mjs +10 -1
  298. package/src/session-runtime/route-state.mjs +16 -2
  299. package/src/session-runtime/runtime-core.mjs +5 -0
  300. package/src/session-runtime/runtime-facade.mjs +8 -0
  301. package/src/session-runtime/runtime-review-api.mjs +9 -0
  302. package/src/session-runtime/schedule-session-run.mjs +1 -1
  303. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  304. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  305. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  306. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  307. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  308. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  310. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  311. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  312. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  313. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  314. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  315. package/src/session-runtime/services/agent-tool.mjs +1 -0
  316. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  317. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  318. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  319. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  320. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  321. package/src/session-runtime/services/provider-admin.mjs +64 -18
  322. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  323. package/src/session-runtime/session-create/session-options.mjs +10 -1
  324. package/src/session-runtime/session-turn-api.mjs +1 -0
  325. package/src/session-runtime/settings-api.mjs +4 -2
  326. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  327. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  328. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  329. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  330. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
  331. package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
  332. package/src/session-runtime/skills-api.mjs +7 -3
  333. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  334. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
  335. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  336. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  337. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  338. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  339. package/src/session-runtime/turn/turn-run.mjs +6 -7
  340. package/src/session-runtime/webhook-session-run.mjs +16 -4
  341. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  342. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  343. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  344. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  345. package/src/session-runtime/workspace/transitions.mjs +651 -0
  346. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  347. package/src/skill-script-entry.mjs +36 -0
  348. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  349. package/src/standalone/channel-transport.mjs +13 -1
  350. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  351. package/src/standalone/daemon-owner.mjs +372 -0
  352. package/src/standalone/daemon-stored-session-views.mjs +15 -4
  353. package/src/standalone/daemon.mjs +179 -8
  354. package/src/standalone/desktop-service-registry.mjs +20 -0
  355. package/src/standalone/local-session-runtime.mjs +11 -0
  356. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  357. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  358. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  359. package/src/standalone/session-client.mjs +104 -13
  360. package/src/standalone/session-protocol.mjs +21 -0
  361. package/src/standalone/session-proxy.mjs +3 -0
  362. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  363. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  364. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  365. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  366. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  367. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  368. package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
  369. package/src/standalone/session-transport/call-route.mjs +13 -0
  370. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  371. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  372. package/src/standalone/session-transport/client-registry.mjs +20 -3
  373. package/src/standalone/session-transport/routes.mjs +79 -2
  374. package/src/standalone/session-transport/stack.mjs +2 -0
  375. package/src/standalone/session-transport.mjs +24 -1
  376. package/src/tui/App.jsx +9 -1
  377. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  378. package/src/tui/app/app-view.jsx +6 -0
  379. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  380. package/src/tui/app/message-selector.mjs +2 -1
  381. package/src/tui/app/model-options.mjs +37 -5
  382. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  383. package/src/tui/app/model-picker.mjs +4 -2
  384. package/src/tui/app/skill-mention-palette.mjs +88 -0
  385. package/src/tui/app/slash-palette.mjs +23 -7
  386. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  387. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  388. package/src/tui/app/use-prompt-state.mjs +5 -0
  389. package/src/tui/components/Message.jsx +22 -1
  390. package/src/tui/components/PromptInput.jsx +21 -3
  391. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  392. package/src/tui/components/TranscriptItem.jsx +9 -1
  393. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  394. package/src/tui/components/user-message-skills.mjs +38 -0
  395. package/src/tui/dist/index.mjs +839 -207
  396. package/src/tui/index.jsx +10 -2
  397. package/src/tui/session/compaction-view.mjs +147 -0
  398. package/src/tui/session/context-state.mjs +6 -0
  399. package/src/tui/session/initial-state.mjs +3 -0
  400. package/src/tui/session/queue-helpers.mjs +7 -2
  401. package/src/tui/session/quota-resume.mjs +2 -1
  402. package/src/tui/session/session-api/commands.mjs +10 -2
  403. package/src/tui/session/session-api/controls.mjs +4 -0
  404. package/src/tui/session/session-api/extensions.mjs +4 -2
  405. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  406. package/src/tui/session/session-api/integrations.mjs +17 -0
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  408. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  409. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  410. package/src/tui/session/session-api/routes.mjs +10 -1
  411. package/src/tui/session/session-api/settings.mjs +9 -0
  412. package/src/tui/session/session-api/worktree.mjs +60 -0
  413. package/src/tui/session/session-api.mjs +2 -0
  414. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  415. package/src/tui/session/session-flow/drain.mjs +27 -3
  416. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  417. package/src/tui/session/session-flow.mjs +2 -0
  418. package/src/tui/session/transcript-intake.mjs +14 -5
  419. package/src/tui/session/tui-steering-persist.mjs +25 -9
  420. package/src/tui/session/turn-stream.mjs +32 -4
  421. package/src/tui/session/turn-tool-cards.mjs +10 -2
  422. package/src/tui/session/turn.mjs +56 -10
  423. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  424. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Display-only archive of the conversation a compaction dropped.
3
+ *
4
+ * One JSONL sidecar per session next to its record
5
+ * (`<id>.compaction-archive.jsonl`), one line per compaction segment, oldest
6
+ * first. A segment holds the transcript ITEMS (the pane's display shape, never
7
+ * model messages) its compaction removed from the model context, followed by
8
+ * the "Conversation compacted" divider. Each line opens with a fixed header
9
+ * (`{"v":1,"seq":N,"id":"segN","prev":…,"at":…,"count":…,"bytes":…,"items":[…]}`)
10
+ * so a reader can pick segments by number without parsing their items.
11
+ *
12
+ * Nothing here ever reaches `session.messages` or a provider payload: the
13
+ * archive is read only by the stored-transcript projection and the paged
14
+ * history loader. Leaf module (no store.mjs, no config) so the cold catalog
15
+ * path can read it; the writer lives in compaction-archive-capture.mjs.
16
+ */
17
+ import { unlinkSync } from 'node:fs';
18
+ import { open } from 'node:fs/promises';
19
+ import { join } from 'node:path';
20
+
21
+ export const COMPACTION_ARCHIVE_SUFFIX = '.compaction-archive.jsonl';
22
+ /** The one label every compaction boundary row carries (persisted and live),
23
+ * translated by the desktop as the app's compaction wording. */
24
+ export const COMPACTION_DIVIDER_LABEL = 'Conversation compacted';
25
+ /** Per-session cap; the oldest segments are dropped past it. */
26
+ export const COMPACTION_ARCHIVE_MAX_BYTES = 32 * 1024 * 1024;
27
+
28
+ const SESSION_ID = /^[A-Za-z0-9_-]+$/;
29
+ const SEGMENT_HEAD = /^\{"v":1,"seq":(\d+),"id":"[^"]*","prev":(null|"[^"]*"),"at":(\d+),"count":(\d+),"bytes":(\d+),/;
30
+
31
+ /** The archive sidecar of `sessionId` inside a sessions directory, or null
32
+ * for an id that is not a stored session id. */
33
+ export function compactionArchivePath(storeDir, sessionId) {
34
+ const id = String(sessionId || '');
35
+ if (!SESSION_ID.test(id)) return null;
36
+ return join(storeDir, `${id}${COMPACTION_ARCHIVE_SUFFIX}`);
37
+ }
38
+
39
+ /** The visible archive a session record names: segments numbered in
40
+ * (after, before). A clear hides everything archived before it. Null when
41
+ * nothing is visible. */
42
+ export function compactionArchiveRange(session) {
43
+ const archive = session?.compaction?.archive;
44
+ if (!archive || typeof archive !== 'object') return null;
45
+ const before = Number(archive.nextSeq);
46
+ const after = Math.max(0, Math.floor(Number(archive.clearedThrough) || 0));
47
+ if (!Number.isInteger(before) || before - 1 <= after) return null;
48
+ return { after, before };
49
+ }
50
+
51
+ /** A segment line's header fields, or null for a line that is not one. */
52
+ export function compactionSegmentHead(line) {
53
+ const match = SEGMENT_HEAD.exec(String(line || ''));
54
+ if (!match) return null;
55
+ return {
56
+ seq: Number(match[1]),
57
+ id: `seg${match[1]}`,
58
+ prev: match[2] === 'null' ? null : match[2].slice(1, -1),
59
+ at: Number(match[3]),
60
+ count: Number(match[4]),
61
+ bytes: Number(match[5]),
62
+ };
63
+ }
64
+
65
+ /** One segment as its JSONL line (no trailing newline). */
66
+ export function compactionSegmentLine({ seq, prev, at, items }) {
67
+ const body = JSON.stringify(items);
68
+ return `{"v":1,"seq":${seq},"id":"seg${seq}","prev":${prev == null ? 'null' : JSON.stringify(`seg${prev}`)},"at":${Math.max(0, Math.floor(Number(at) || 0))},"count":${items.length},"bytes":${Buffer.byteLength(body)},"items":${body}}`;
69
+ }
70
+
71
+ function segmentItems(text) {
72
+ try {
73
+ const items = JSON.parse(text)?.items;
74
+ return Array.isArray(items) ? items : [];
75
+ } catch {
76
+ return [];
77
+ }
78
+ }
79
+
80
+ /**
81
+ * One page from a segment list (oldest → newest heads; `load(entry)` yields a
82
+ * segment's items): segments numbered in (after, before), newest first,
83
+ * skipping the `skip` newest items a reader already holds and returning at
84
+ * most `limit` items above them, in display order. Item counts come from
85
+ * the segment headers: a segment wholly inside the skipped rows or past the
86
+ * page is never read or parsed. `hasOlder` says whether older items remain.
87
+ */
88
+ async function pageFromSegments(
89
+ entries,
90
+ load,
91
+ { after = 0, before = Number.POSITIVE_INFINITY, skip = 0, limit = 0 } = {}
92
+ ) {
93
+ const held = Math.max(0, Math.floor(Number(skip) || 0));
94
+ const wanted = Math.max(0, Math.floor(Number(limit) || 0));
95
+ const parts = [];
96
+ // Items newer than this entry, counted from the newest.
97
+ let newer = 0;
98
+ let more = false;
99
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
100
+ const { head } = entries[index];
101
+ if (head.seq <= after || head.seq >= before || head.count < 1) continue;
102
+ if (newer >= held + wanted) {
103
+ more = true;
104
+ break;
105
+ }
106
+ const count = head.count;
107
+ if (newer + count > held) {
108
+ // This segment holds rows of the page: newest-first offsets
109
+ // [max(held, newer), min(held + wanted, newer + count)) of it.
110
+ const items = await load(entries[index]);
111
+ const fromEnd = Math.max(0, held - newer);
112
+ const take = Math.min(held + wanted, newer + count) - Math.max(held, newer);
113
+ const end = items.length - fromEnd;
114
+ parts.push(items.slice(Math.max(0, end - take), Math.max(0, end)));
115
+ if (newer + count > held + wanted) more = true;
116
+ }
117
+ newer += count;
118
+ }
119
+ return { items: parts.reverse().flat(), hasOlder: more };
120
+ }
121
+
122
+ /** pageFromSegments over archive text (tests and in-memory callers). */
123
+ export function compactionArchivePageFromText(text, range = {}) {
124
+ const entries = [];
125
+ for (const line of String(text || '').split('\n')) {
126
+ const head = compactionSegmentHead(line);
127
+ if (head) entries.push({ head, line });
128
+ }
129
+ return pageFromSegments(entries, (entry) => segmentItems(entry.line), range);
130
+ }
131
+
132
+ // Per-sidecar segment index (byte offsets + headers), keyed by the file
133
+ // identity it was built from: a page reads only the segments it reaches, and
134
+ // the headers are indexed once per change rather than once per page.
135
+ const INDEX_CACHE_LIMIT = 16;
136
+ const archiveIndexes = new Map();
137
+
138
+ const HEAD_PROBE_BYTES = 512;
139
+ const ITEMS_KEY = '"items":';
140
+ const SCAN_CHUNK_BYTES = 64 * 1024;
141
+
142
+ /** Offset of the first newline at or after `from` (or `size`), scanning. */
143
+ async function nextNewline(handle, from, size) {
144
+ const chunk = Buffer.alloc(SCAN_CHUNK_BYTES);
145
+ for (let at = from; at < size; at += SCAN_CHUNK_BYTES) {
146
+ const { bytesRead } = await handle.read(chunk, 0, Math.min(SCAN_CHUNK_BYTES, size - at), at);
147
+ const hit = chunk.subarray(0, bytesRead).indexOf(0x0a);
148
+ if (hit >= 0) return at + hit;
149
+ }
150
+ return size;
151
+ }
152
+
153
+ /** Segment offsets of an archive file. Each header names its items' byte
154
+ * length, so the index reads only the headers and jumps over the bodies; a
155
+ * line whose header does not account for its length is scanned past. */
156
+ async function indexArchive(handle, size) {
157
+ const entries = [];
158
+ const probe = Buffer.alloc(HEAD_PROBE_BYTES);
159
+ let start = 0;
160
+ while (start < size) {
161
+ const { bytesRead } = await handle.read(probe, 0, Math.min(HEAD_PROBE_BYTES, size - start), start);
162
+ const text = probe.toString('latin1', 0, bytesRead);
163
+ const match = SEGMENT_HEAD.exec(text);
164
+ let end = -1;
165
+ if (match && text.startsWith(ITEMS_KEY, match[0].length)) {
166
+ // `…"items":<body>}` — the header is ASCII, so chars are bytes.
167
+ const candidate = start + match[0].length + ITEMS_KEY.length + Number(match[5]) + 1;
168
+ if (candidate === size) end = candidate;
169
+ else if (candidate < size) {
170
+ const tail = Buffer.alloc(1);
171
+ await handle.read(tail, 0, 1, candidate);
172
+ if (tail[0] === 0x0a) end = candidate;
173
+ }
174
+ }
175
+ if (end >= 0) entries.push({ head: compactionSegmentHead(text), start, length: end - start });
176
+ else end = await nextNewline(handle, start, size);
177
+ start = end + 1;
178
+ }
179
+ return entries;
180
+ }
181
+
182
+ async function archiveIndexFor(path, handle) {
183
+ const info = await handle.stat();
184
+ const stamp = `${info.dev}:${info.ino}:${info.size}:${info.mtimeMs}`;
185
+ const cached = archiveIndexes.get(path);
186
+ if (cached?.stamp === stamp) {
187
+ archiveIndexes.delete(path);
188
+ archiveIndexes.set(path, cached);
189
+ return cached.entries;
190
+ }
191
+ const entries = await indexArchive(handle, info.size);
192
+ archiveIndexes.set(path, { stamp, entries });
193
+ while (archiveIndexes.size > INDEX_CACHE_LIMIT) archiveIndexes.delete(archiveIndexes.keys().next().value);
194
+ return entries;
195
+ }
196
+
197
+ /** One page of a session's archive sidecar (see pageFromSegments). The
198
+ * open handle pins the file a concurrent atomic replace may swap out, so
199
+ * the index and the segment reads describe the same bytes. */
200
+ export async function readCompactionArchivePage(storeDir, sessionId, range = {}) {
201
+ const path = compactionArchivePath(storeDir, sessionId);
202
+ if (!path) return { items: [], hasOlder: false };
203
+ let handle;
204
+ try {
205
+ handle = await open(path, 'r');
206
+ } catch {
207
+ // Display-only history: an absent or unreadable archive shows nothing.
208
+ archiveIndexes.delete(path);
209
+ return { items: [], hasOlder: false };
210
+ }
211
+ try {
212
+ const entries = await archiveIndexFor(path, handle);
213
+ return await pageFromSegments(
214
+ entries,
215
+ async (entry) => {
216
+ const buffer = Buffer.alloc(entry.length);
217
+ await handle.read(buffer, 0, entry.length, entry.start);
218
+ return segmentItems(buffer.toString('utf8'));
219
+ },
220
+ range
221
+ );
222
+ } catch {
223
+ return { items: [], hasOlder: false };
224
+ } finally {
225
+ await handle.close().catch(() => {});
226
+ }
227
+ }
228
+
229
+ // Delete fence: every deletion bumps the session's epoch. A writer captures
230
+ // the epoch before it starts and publishes only if it is unchanged at the
231
+ // (synchronous) rename, so a delete that lands while a segment is being
232
+ // written is never undone by that write.
233
+ const deleteEpochs = new Map();
234
+
235
+ export function compactionArchiveDeleteEpoch(sessionId) {
236
+ return deleteEpochs.get(String(sessionId || '')) || 0;
237
+ }
238
+
239
+ /** Remove a session's archive (session delete) and fence every write that
240
+ * started before it. Best-effort: true when the sidecar is gone afterwards. */
241
+ export function deleteCompactionArchive(storeDir, sessionId) {
242
+ const path = compactionArchivePath(storeDir, sessionId);
243
+ if (!path) return false;
244
+ const id = String(sessionId);
245
+ deleteEpochs.set(id, compactionArchiveDeleteEpoch(id) + 1);
246
+ archiveIndexes.delete(path);
247
+ try {
248
+ unlinkSync(path);
249
+ return true;
250
+ } catch (error) {
251
+ return error?.code === 'ENOENT';
252
+ }
253
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Claude Code project transcript (`projects/<dir>/<session>.jsonl`) → raw
3
+ * import turns. The active branch is followed from the last main-chain entry
4
+ * back through `parentUuid` (and `logicalParentUuid` across a compaction), so
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.
10
+ */
11
+ import { IMAGE_OMITTED_TEXT, IMPORT_WARNING_IMAGE_OMITTED, isWholeElement, textPart, toolNote } from './turns.mjs';
12
+
13
+ const CLAUDE_ENVELOPE_TAGS = [
14
+ 'local-command-stdout',
15
+ 'local-command-stderr',
16
+ 'local-command-caveat',
17
+ 'task-notification',
18
+ 'system-reminder',
19
+ 'user-prompt-submit-hook',
20
+ 'bash-input',
21
+ 'bash-stdout',
22
+ 'bash-stderr',
23
+ ];
24
+ const SLASH_COMMAND_RE = /^(?:\s*<(command-name|command-message|command-args)>[\s\S]*?<\/\1>)+\s*$/;
25
+ const INTERRUPTED_RE = /^\[Request interrupted by user[^\]\n]*\]$/;
26
+ // Claude Code's text annotation that accompanies a pasted image block.
27
+ const IMAGE_ANNOTATION_RE = /^\[Image\b[^\n]*\]$/;
28
+
29
+ export function isClaudeEnvelopeText(text) {
30
+ const trimmed = String(text || '').trim();
31
+ if (!trimmed) return true;
32
+ return (
33
+ isWholeElement(trimmed, CLAUDE_ENVELOPE_TAGS) ||
34
+ SLASH_COMMAND_RE.test(trimmed) ||
35
+ INTERRUPTED_RE.test(trimmed) ||
36
+ IMAGE_ANNOTATION_RE.test(trimmed)
37
+ );
38
+ }
39
+
40
+ const isMessageEntry = (record) => record?.type === 'user' || record?.type === 'assistant';
41
+ const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
42
+ const uuidOrNull = (value) => (typeof value === 'string' ? value : null);
43
+ const timestampOrNull = (value) => (typeof value === 'string' || typeof value === 'number' ? value : null);
44
+ // Rough fixed cost of one retained entry object, so tiny entries still count.
45
+ const ENTRY_OVERHEAD_BYTES = 64;
46
+ const metadataLength = (entry) =>
47
+ (entry.uuid?.length || 0) +
48
+ (entry.parentUuid?.length || 0) +
49
+ (entry.logicalParentUuid?.length || 0) +
50
+ (typeof entry.at === 'string' ? entry.at.length : 0);
51
+
52
+ /**
53
+ * 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).
56
+ */
57
+ function activeChain(entries) {
58
+ const main = entries.filter((entry) => entry.main);
59
+ const leaf = main[main.length - 1];
60
+ if (!leaf || leaf.uuid === null) return { chain: main, crossedCompaction: false };
61
+ const byUuid = new Map();
62
+ for (const entry of entries) {
63
+ if (entry.uuid !== null) byUuid.set(entry.uuid, entry);
64
+ }
65
+ const chain = [];
66
+ const seen = new Set();
67
+ let crossedCompaction = false;
68
+ let current = leaf;
69
+ while (current && !seen.has(current.uuid)) {
70
+ seen.add(current.uuid);
71
+ chain.push(current);
72
+ 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;
75
+ current = parent || logical;
76
+ }
77
+ return { chain: chain.reverse().filter((entry) => entry.main), crossedCompaction };
78
+ }
79
+
80
+ function entryParts(role, content, warnings) {
81
+ const blocks = typeof content === 'string' ? [{ type: 'text', text: content }] : content;
82
+ if (!Array.isArray(blocks)) return [];
83
+ const parts = [];
84
+ for (const block of blocks) {
85
+ if (!block || typeof block !== 'object') continue;
86
+ if (block.type === 'text' && typeof block.text === 'string') {
87
+ if (role === 'user' && isClaudeEnvelopeText(block.text)) continue;
88
+ parts.push(textPart(block.text));
89
+ } else if (block.type === 'image') {
90
+ warnings.add(IMPORT_WARNING_IMAGE_OMITTED);
91
+ parts.push(textPart(IMAGE_OMITTED_TEXT));
92
+ } else if (block.type === 'tool_use' && role === 'assistant') {
93
+ parts.push(toolNote(block.name));
94
+ }
95
+ }
96
+ return parts;
97
+ }
98
+
99
+ function skippedEntry(record) {
100
+ // Claude Code flags its compaction summary transcript-only as well.
101
+ if (record.isCompactSummary === true) return false;
102
+ return record.isMeta === true || record.isVisibleInTranscriptOnly === true || record.isApiErrorMessage === true;
103
+ }
104
+
105
+ /**
106
+ * Incremental reducer: `push(record)` keeps a compact chain entry (uuid links
107
+ * plus the plain-text parts of a main-chain message) and drops the record
108
+ * itself, so tool results, thinking and image data are never retained.
109
+ * `chars` is the kept text across every branch so far, `entries` the chain
110
+ * entries retained and `metadataBytes` their link/timestamp bookkeeping.
111
+ */
112
+ export function createClaudeReducer() {
113
+ const entries = [];
114
+ let recognized = false;
115
+ let foreignSessionId = null;
116
+ let cwd = null;
117
+ let chars = 0;
118
+ let metadataBytes = 0;
119
+ return {
120
+ get chars() {
121
+ return chars;
122
+ },
123
+ get entries() {
124
+ return entries.length;
125
+ },
126
+ get metadataBytes() {
127
+ return metadataBytes;
128
+ },
129
+ push(record) {
130
+ const message = isMessageEntry(record);
131
+ if (message) recognized = true;
132
+ const main = message && record.isSidechain !== true;
133
+ if (main) {
134
+ foreignSessionId ||= stringOrNull(record.sessionId);
135
+ cwd ||= stringOrNull(record.cwd);
136
+ }
137
+ const uuid = uuidOrNull(record?.uuid);
138
+ if (!main && uuid === null) return;
139
+ const entry = {
140
+ uuid,
141
+ parentUuid: uuidOrNull(record.parentUuid),
142
+ logicalParentUuid: uuidOrNull(record.logicalParentUuid),
143
+ main,
144
+ role: null,
145
+ parts: null,
146
+ warnings: null,
147
+ at: timestampOrNull(record.timestamp),
148
+ };
149
+ metadataBytes += ENTRY_OVERHEAD_BYTES + metadataLength(entry);
150
+ if (main && !skippedEntry(record)) {
151
+ const body = record.message && typeof record.message === 'object' ? record.message : null;
152
+ entry.role = body?.role === 'user' || body?.role === 'assistant' ? body.role : record.type;
153
+ const warnings = new Set();
154
+ entry.parts = entryParts(entry.role, body ? body.content : null, warnings);
155
+ if (record.isCompactSummary === true) {
156
+ entry.summary = true;
157
+ entry.parts = entry.parts.map((part) => ({ ...part, summary: true }));
158
+ }
159
+ if (warnings.size) entry.warnings = warnings;
160
+ for (const part of entry.parts) chars += part.text.length;
161
+ }
162
+ entries.push(entry);
163
+ },
164
+ result() {
165
+ const warnings = new Set();
166
+ const rawTurns = [];
167
+ const { chain, crossedCompaction } = activeChain(entries);
168
+ for (const entry of chain) {
169
+ 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 });
173
+ for (const code of entry.warnings || []) warnings.add(code);
174
+ }
175
+ return { recognized, foreignSessionId, cwd, rawTurns, warnings };
176
+ },
177
+ };
178
+ }
179
+
180
+ export function convertClaudeRecords(records) {
181
+ const reducer = createClaudeReducer();
182
+ for (const record of records) reducer.push(record);
183
+ return reducer.result();
184
+ }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Codex CLI rollout (`rollout-*.jsonl`) → raw import turns. Kept: the first
3
+ * `session_meta` (foreign id, cwd), user/assistant `message` items as text and
4
+ * tool calls as `[ran tool: <name>]` notes. Dropped: developer/system
5
+ * messages, Codex's own wrapper user messages, tool outputs, reasoning,
6
+ * event_msg, token counts and turn_context.
7
+ */
8
+ import { IMAGE_OMITTED_TEXT, IMPORT_WARNING_IMAGE_OMITTED, isWholeElement, textPart, toolNote } from './turns.mjs';
9
+
10
+ // User-role envelopes Codex injects itself (environment, instructions, abort
11
+ // notices, plugin hints, shell-command echoes, image wrappers).
12
+ const CODEX_ENVELOPE_TAGS = [
13
+ 'environment_context',
14
+ 'user_instructions',
15
+ 'turn_aborted',
16
+ 'recommended_plugins',
17
+ 'user_shell_command',
18
+ 'subagent_notification',
19
+ 'skills_instructions',
20
+ ];
21
+ const TOOL_CALL_TYPES = new Set(['function_call', 'custom_tool_call', 'local_shell_call']);
22
+
23
+ export function isCodexEnvelopeText(text) {
24
+ const trimmed = String(text || '').trim();
25
+ if (!trimmed) return true;
26
+ if (/^# AGENTS\.md instructions\b/.test(trimmed) && trimmed.includes('<INSTRUCTIONS>')) return true;
27
+ if (isWholeElement(trimmed, CODEX_ENVELOPE_TAGS)) return true;
28
+ // The `<image name=…>` / `</image>` text parts that wrap an input_image.
29
+ return /^<image\b[^>]*>$/.test(trimmed) || trimmed === '</image>';
30
+ }
31
+
32
+ function messageParts(payload, warnings) {
33
+ const content = typeof payload.content === 'string' ? [{ type: 'text', text: payload.content }] : payload.content;
34
+ if (!Array.isArray(content)) return [];
35
+ const parts = [];
36
+ for (const item of content) {
37
+ if (!item || typeof item !== 'object') continue;
38
+ if (item.type === 'input_image' || item.type === 'image') {
39
+ warnings.add(IMPORT_WARNING_IMAGE_OMITTED);
40
+ parts.push(textPart(IMAGE_OMITTED_TEXT));
41
+ continue;
42
+ }
43
+ if (typeof item.text !== 'string') continue;
44
+ if (payload.role === 'user' && isCodexEnvelopeText(item.text)) continue;
45
+ parts.push(textPart(item.text));
46
+ }
47
+ return parts;
48
+ }
49
+
50
+ function toolName(payload) {
51
+ if (typeof payload.name === 'string' && payload.name.trim()) return payload.name;
52
+ return payload.type === 'local_shell_call' ? 'local_shell' : '';
53
+ }
54
+
55
+ const stringOrNull = (value) => (typeof value === 'string' && value.trim() ? value.trim() : null);
56
+ // Rough fixed cost of one retained turn object, so tiny turns still count.
57
+ const ENTRY_OVERHEAD_BYTES = 64;
58
+
59
+ /**
60
+ * Incremental reducer: `push(record)` keeps only the plain-text parts of one
61
+ * record (the record itself is not retained), `chars` is the kept text so far,
62
+ * `entries` the raw turns kept and `metadataBytes` their timestamp bookkeeping.
63
+ */
64
+ export function createCodexReducer() {
65
+ const warnings = new Set();
66
+ const rawTurns = [];
67
+ let recognized = false;
68
+ let foreignSessionId = null;
69
+ let cwd = null;
70
+ let metaSeen = false;
71
+ let chars = 0;
72
+ let metadataBytes = 0;
73
+ const keep = (role, parts, at) => {
74
+ for (const part of parts) chars += part.text.length;
75
+ const stamp = typeof at === 'string' || typeof at === 'number' ? at : null;
76
+ metadataBytes += ENTRY_OVERHEAD_BYTES + (typeof stamp === 'string' ? stamp.length : 0);
77
+ rawTurns.push({ role, parts, at: stamp });
78
+ };
79
+ return {
80
+ get chars() {
81
+ return chars;
82
+ },
83
+ get entries() {
84
+ return rawTurns.length;
85
+ },
86
+ get metadataBytes() {
87
+ return metadataBytes;
88
+ },
89
+ push(record) {
90
+ const payload = record?.payload && typeof record.payload === 'object' ? record.payload : null;
91
+ if (!payload) return;
92
+ if (record.type === 'session_meta') {
93
+ recognized = true;
94
+ if (!metaSeen) {
95
+ metaSeen = true;
96
+ foreignSessionId = stringOrNull(payload.id) || stringOrNull(payload.session_id);
97
+ cwd = stringOrNull(payload.cwd);
98
+ }
99
+ return;
100
+ }
101
+ if (record.type !== 'response_item') return;
102
+ recognized = true;
103
+ if (payload.type === 'message' && (payload.role === 'user' || payload.role === 'assistant')) {
104
+ const parts = messageParts(payload, warnings);
105
+ if (parts.length) keep(payload.role, parts, record.timestamp);
106
+ } else if (TOOL_CALL_TYPES.has(payload.type)) {
107
+ keep('assistant', [toolNote(toolName(payload))], record.timestamp);
108
+ }
109
+ },
110
+ result() {
111
+ return { recognized, foreignSessionId, cwd, rawTurns, warnings };
112
+ },
113
+ };
114
+ }
115
+
116
+ export function convertCodexRecords(records) {
117
+ const reducer = createCodexReducer();
118
+ for (const record of records) reducer.push(record);
119
+ return reducer.result();
120
+ }
121
+
122
+ /** id/cwd of a `session_meta` first line cut off by a bounded prefix read. */
123
+ export function codexMetaFromTruncatedLine(line) {
124
+ const text = String(line || '');
125
+ if (!/"type"\s*:\s*"session_meta"/.test(text)) return null;
126
+ const field = (name) => {
127
+ const match = new RegExp(`"${name}"\\s*:\\s*("(?:[^"\\\\]|\\\\.)*")`).exec(text);
128
+ if (!match) return null;
129
+ try {
130
+ return stringOrNull(JSON.parse(match[1]));
131
+ } catch {
132
+ return null;
133
+ }
134
+ };
135
+ return { foreignSessionId: field('id') || field('session_id'), cwd: field('cwd') };
136
+ }