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,337 @@
1
+ // Command Code browser sign-in. The Studio CLI auth page mints a Provider API
2
+ // key and POSTs it (with our one-shot state) as JSON to a loopback callback.
3
+ // A pasted key or pasted callback JSON completes the same login by hand. The
4
+ // first sign-in on a machine adopts an existing Command Code CLI login instead.
5
+ import { createServer } from 'node:http';
6
+ import { randomBytes } from 'node:crypto';
7
+ import { readFileSync } from 'node:fs';
8
+ import { homedir } from 'node:os';
9
+ import { join } from 'node:path';
10
+ import { openLoginUrl } from './lib/open-login-url.mjs';
11
+ import { readProviderAccountPool } from '../../../shared/provider-accounts.mjs';
12
+ import { withProviderAccount } from '../../../shared/provider-auth-binding.mjs';
13
+ import {
14
+ LOGIN_TIMEOUT_MS,
15
+ PROVIDER_ID,
16
+ STUDIO_ORIGIN,
17
+ fetchCommandCodeIdentity,
18
+ isWellFormedApiKey,
19
+ loadTokens,
20
+ saveTokens,
21
+ } from './command-code-oauth-tokens.mjs';
22
+
23
+ export const CALLBACK_PORT = 5959;
24
+ const CALLBACK_PATH = '/callback';
25
+ const MAX_CALLBACK_BYTES = 16 * 1024;
26
+ const CALLBACK_FIELDS = Object.freeze(['apiKey', 'userId', 'userName', 'keyName']);
27
+
28
+ const CORS_HEADERS = Object.freeze({
29
+ 'Access-Control-Allow-Origin': STUDIO_ORIGIN,
30
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
31
+ 'Access-Control-Allow-Headers': 'Content-Type',
32
+ // The Studio page is public and the callback is loopback: Chromium asks first.
33
+ 'Access-Control-Allow-Private-Network': 'true',
34
+ Vary: 'Origin',
35
+ });
36
+
37
+ export const defaultLocalAuthPath = () => join(homedir(), '.commandcode', 'auth.json');
38
+
39
+ /** Validate the Studio callback body against this login's state. */
40
+ export function parseCallbackPayload(value, expectedState) {
41
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
42
+ throw new Error('[command-code-oauth] callback body must be an object');
43
+ }
44
+ if (typeof value.state !== 'string' || !expectedState || value.state !== expectedState) {
45
+ throw new Error('[command-code-oauth] callback state mismatch');
46
+ }
47
+ for (const field of CALLBACK_FIELDS) {
48
+ if (typeof value[field] !== 'string' || !value[field].trim()) {
49
+ throw new Error(`[command-code-oauth] callback is missing ${field}`);
50
+ }
51
+ }
52
+ const apiKey = value.apiKey.trim();
53
+ if (!isWellFormedApiKey(apiKey)) throw new Error('[command-code-oauth] callback carried a malformed API key');
54
+ return { apiKey, userId: value.userId.trim(), userName: value.userName.trim(), keyName: value.keyName.trim() };
55
+ }
56
+
57
+ /** A paste is either the callback JSON (state-checked) or a bare API key. */
58
+ export function parsePastedInput(input, expectedState) {
59
+ const pasted = String(input ?? '').trim();
60
+ if (!pasted) throw new Error('[command-code-oauth] API key is required');
61
+ if (pasted.startsWith('{')) {
62
+ let doc;
63
+ try {
64
+ doc = JSON.parse(pasted);
65
+ } catch {
66
+ throw new Error('[command-code-oauth] pasted callback is not valid JSON');
67
+ }
68
+ return parseCallbackPayload(doc, expectedState);
69
+ }
70
+ if (!isWellFormedApiKey(pasted)) throw new Error('[command-code-oauth] pasted API key is malformed');
71
+ return { apiKey: pasted, userId: '', userName: '', keyName: '' };
72
+ }
73
+
74
+ // No account of this provider is connected yet: no roster entry and no
75
+ // credential in the default slot. Adding a second account or reconnecting an
76
+ // existing one must pick a fresh browser identity, never the local CLI login.
77
+ function isFirstSignIn() {
78
+ try {
79
+ if (readProviderAccountPool(PROVIDER_ID).accounts.length) return false;
80
+ return !withProviderAccount(PROVIDER_ID, 'default', () => loadTokens());
81
+ } catch {
82
+ // An unreadable roster cannot prove this is the first account.
83
+ return false;
84
+ }
85
+ }
86
+
87
+ function readLocalAuth(path) {
88
+ try {
89
+ const doc = JSON.parse(readFileSync(path, 'utf-8'));
90
+ const apiKey = typeof doc?.apiKey === 'string' ? doc.apiKey.trim() : '';
91
+ const userId = typeof doc?.userId === 'string' ? doc.userId.trim() : '';
92
+ return apiKey && userId && isWellFormedApiKey(apiKey) ? { apiKey, userId } : null;
93
+ } catch {
94
+ return null;
95
+ }
96
+ }
97
+
98
+ function listenOn(server, port, host) {
99
+ return new Promise((resolve, reject) => {
100
+ server.once('error', reject);
101
+ server.listen(port, host, () => {
102
+ server.off('error', reject);
103
+ resolve();
104
+ });
105
+ });
106
+ }
107
+
108
+ function readBody(req) {
109
+ return new Promise((resolve, reject) => {
110
+ const chunks = [];
111
+ let size = 0;
112
+ req.on('data', (chunk) => {
113
+ size += chunk.length;
114
+ if (size > MAX_CALLBACK_BYTES) {
115
+ reject(new Error('[command-code-oauth] callback body too large'));
116
+ req.destroy();
117
+ return;
118
+ }
119
+ chunks.push(chunk);
120
+ });
121
+ req.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
122
+ req.on('error', reject);
123
+ });
124
+ }
125
+
126
+ function reply(res, status, body) {
127
+ res.writeHead(status, { ...CORS_HEADERS, ...(body ? { 'Content-Type': 'application/json' } : {}) });
128
+ res.end(body ? JSON.stringify(body) : undefined);
129
+ }
130
+
131
+ function authorizeUrl(port, state) {
132
+ const url = new URL('/studio/auth/cli', STUDIO_ORIGIN);
133
+ url.searchParams.set('callback', `http://127.0.0.1:${port}${CALLBACK_PATH}`);
134
+ url.searchParams.set('state', state);
135
+ return url.toString();
136
+ }
137
+
138
+ export async function beginOAuthLogin({
139
+ openBrowser = true,
140
+ timeoutMs = LOGIN_TIMEOUT_MS,
141
+ fetchImpl = fetch,
142
+ port: preferredPort = CALLBACK_PORT,
143
+ importLocal = isFirstSignIn(),
144
+ localAuthPath = defaultLocalAuthPath(),
145
+ printUrl = false,
146
+ } = {}) {
147
+ // Validate and store a key under its whoami identity.
148
+ const store = async (candidate, signal) => {
149
+ const identity = await fetchCommandCodeIdentity(candidate.apiKey, { fetchImpl, signal });
150
+ return { apiKey: candidate.apiKey, userId: identity.userId, userName: identity.userName || candidate.userName };
151
+ };
152
+
153
+ if (importLocal) {
154
+ const local = readLocalAuth(localAuthPath);
155
+ if (local) {
156
+ let tokens = null;
157
+ try {
158
+ tokens = saveTokens(await store(local));
159
+ } catch {
160
+ /* a stale or rejected CLI login falls through to the browser */
161
+ }
162
+ if (tokens) {
163
+ return {
164
+ provider: PROVIDER_ID,
165
+ url: '',
166
+ manualUrl: null,
167
+ imported: true,
168
+ waitForCallback: Promise.resolve(tokens),
169
+ cancel: () => {},
170
+ };
171
+ }
172
+ }
173
+ }
174
+
175
+ const state = randomBytes(32).toString('base64url');
176
+ const servers = [];
177
+ // Open client connections, and the ones still owed a reply by a request
178
+ // that is completing the login.
179
+ const sockets = new Set();
180
+ const replying = new Set();
181
+ const work = new AbortController();
182
+ let timer = null;
183
+ let finish = null;
184
+ let settled = false;
185
+ const waitForCallback = new Promise((resolve, reject) => {
186
+ finish = (value, error = null) => {
187
+ if (settled) return;
188
+ settled = true;
189
+ if (timer) clearTimeout(timer);
190
+ work.abort();
191
+ for (const server of servers) {
192
+ try {
193
+ server.close();
194
+ } catch {
195
+ /* already closed */
196
+ }
197
+ }
198
+ // close() only stops accepting: drop every connection (a stalled
199
+ // partial POST included) except those about to get their reply.
200
+ for (const socket of sockets) if (!replying.has(socket)) socket.destroy();
201
+ if (error) reject(error);
202
+ else resolve(value);
203
+ };
204
+ timer = setTimeout(
205
+ () => finish(null, new Error(`[command-code-oauth] login timed out after ${Math.round(timeoutMs / 1000)}s`)),
206
+ timeoutMs
207
+ );
208
+ });
209
+ // A caller may only use the paste path; a later timeout/cancel must not be an unhandled rejection.
210
+ waitForCallback.catch(() => {});
211
+
212
+ // The credential is written only while the login is still open, and the
213
+ // login completes in the same synchronous step.
214
+ const settle = async (candidate) => {
215
+ const tokens = await store(candidate, work.signal);
216
+ if (settled) throw new Error('[command-code-oauth] login is no longer active');
217
+ const saved = saveTokens(tokens);
218
+ finish(saved);
219
+ return saved;
220
+ };
221
+
222
+ const handleRequest = async (req, res) => {
223
+ const origin = req.headers.origin;
224
+ if (origin && origin !== STUDIO_ORIGIN) return reply(res, 403, { success: false, error: 'Forbidden origin' });
225
+ if (req.method === 'OPTIONS') return reply(res, 204, null);
226
+ let pathname;
227
+ try {
228
+ pathname = new URL(req.url || '/', 'http://127.0.0.1').pathname;
229
+ } catch {
230
+ return reply(res, 400, { success: false, error: 'Bad request' });
231
+ }
232
+ if (pathname !== CALLBACK_PATH || settled) {
233
+ return reply(res, 404, { success: false, error: 'Not found' });
234
+ }
235
+ if (req.method !== 'POST') return reply(res, 405, { success: false, error: 'Method not allowed' });
236
+ // Anything that is not this login's well-formed callback is refused and
237
+ // the login keeps waiting.
238
+ let doc;
239
+ try {
240
+ doc = JSON.parse(await readBody(req));
241
+ } catch {
242
+ return reply(res, 400, { success: false, error: 'Invalid JSON body' });
243
+ }
244
+ let candidate;
245
+ try {
246
+ candidate = parseCallbackPayload(doc, state);
247
+ } catch (err) {
248
+ return reply(res, 400, { success: false, error: String(err.message).replace(/^\[command-code-oauth\]\s*/, '') });
249
+ }
250
+ replying.add(req.socket);
251
+ try {
252
+ await settle(candidate);
253
+ reply(res, 200, { success: true });
254
+ } catch (err) {
255
+ reply(res, 400, { success: false, error: 'API key validation failed' });
256
+ finish(null, err instanceof Error ? err : new Error(String(err)));
257
+ }
258
+ };
259
+
260
+ // No request can fail outside this handler; once the login is over, a
261
+ // connection closes as soon as its reply is flushed.
262
+ const onRequest = (req, res) => {
263
+ res.on('finish', () => {
264
+ replying.delete(req.socket);
265
+ if (settled) req.socket.destroy();
266
+ });
267
+ handleRequest(req, res).catch(() => {
268
+ replying.delete(req.socket);
269
+ if (res.headersSent) res.destroy();
270
+ else reply(res, 400, { success: false, error: 'Bad request' });
271
+ });
272
+ };
273
+ const trackSockets = (server) => {
274
+ server.on('connection', (socket) => {
275
+ if (settled) {
276
+ socket.destroy();
277
+ return;
278
+ }
279
+ sockets.add(socket);
280
+ socket.on('close', () => {
281
+ sockets.delete(socket);
282
+ replying.delete(socket);
283
+ });
284
+ });
285
+ return server;
286
+ };
287
+
288
+ // The advertised callback is 127.0.0.1; ::1 is bound on the same port when
289
+ // possible because `localhost` may resolve to it first.
290
+ let primary = trackSockets(createServer(onRequest));
291
+ try {
292
+ try {
293
+ await listenOn(primary, preferredPort, '127.0.0.1');
294
+ } catch (err) {
295
+ // The fixed port is taken (another login, another app): any free port.
296
+ if (!preferredPort) throw err;
297
+ primary = trackSockets(createServer(onRequest));
298
+ await listenOn(primary, 0, '127.0.0.1');
299
+ }
300
+ } catch (err) {
301
+ finish(null, err);
302
+ throw err;
303
+ }
304
+ const port = primary.address().port;
305
+ servers.push(primary);
306
+ const secondary = trackSockets(createServer(onRequest));
307
+ try {
308
+ await listenOn(secondary, port, '::1');
309
+ servers.push(secondary);
310
+ } catch {
311
+ /* no IPv6 loopback: the IPv4 listener is enough */
312
+ }
313
+ for (const server of servers) {
314
+ server.on('error', (err) =>
315
+ finish(null, new Error(`[command-code-oauth] callback server failed: ${err?.message || err}`))
316
+ );
317
+ }
318
+
319
+ const url = authorizeUrl(port, state);
320
+ if (printUrl) process.stderr.write(`\n[command-code-oauth] Open this URL to sign in:\n${url}\n\n`);
321
+ if (openBrowser) await openLoginUrl(PROVIDER_ID, url);
322
+
323
+ return {
324
+ provider: PROVIDER_ID,
325
+ url,
326
+ manualUrl: null,
327
+ port,
328
+ waitForCallback,
329
+ completeCode: async (input) => settle(parsePastedInput(input, state)),
330
+ cancel: () => finish(null),
331
+ };
332
+ }
333
+
334
+ export async function loginOAuth() {
335
+ const login = await beginOAuthLogin({ printUrl: true });
336
+ return await login.waitForCallback;
337
+ }
@@ -0,0 +1,159 @@
1
+ // Command Code credentials: a non-expiring Provider API key issued by the
2
+ // Studio CLI sign-in. There is no refresh grant — a 401 means the user signs
3
+ // in again. Stored in Mixdog's own token file (command-code-oauth.json), one
4
+ // file per connected account.
5
+ import { readFileSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
6
+ import { join, resolve } from 'node:path';
7
+ import { getPluginData } from '../config.mjs';
8
+ import { writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
9
+ import { boundProviderAuthPath } from '../../../shared/provider-auth-binding.mjs';
10
+ import { accountIdentityFields, fileMtimeMs, scrubOAuthSecrets } from './lib/oauth-token-utils.mjs';
11
+ import { modelProxyClientMode } from '../../../shared/model-proxy-mode.mjs';
12
+
13
+ export const PROVIDER_ID = 'command-code-oauth';
14
+ export const STUDIO_ORIGIN = 'https://commandcode.ai';
15
+ // The only origin the API key is ever sent to.
16
+ export const API_ORIGIN = 'https://api.commandcode.ai';
17
+ export const PROVIDER_BASE_URL = `${API_ORIGIN}/provider/v1`;
18
+ export const WHOAMI_URL = `${API_ORIGIN}/alpha/whoami`;
19
+ export const CREDITS_URL = `${API_ORIGIN}/alpha/billing/credits`;
20
+ export const LOGIN_TIMEOUT_MS = 120_000;
21
+ const REQUEST_TIMEOUT_MS = 15_000;
22
+ const MAX_API_KEY_LEN = 512;
23
+
24
+ export function getOwnTokenPath() {
25
+ const bound = boundProviderAuthPath(PROVIDER_ID);
26
+ if (bound) return resolve(bound);
27
+ const explicit = process.env.COMMAND_CODE_OAUTH_CREDENTIALS_PATH;
28
+ if (explicit) return resolve(explicit);
29
+ const dir = getPluginData();
30
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
31
+ return join(dir, 'command-code-oauth.json');
32
+ }
33
+
34
+ const text = (value) => (typeof value === 'string' ? value.trim() : '');
35
+
36
+ /** A key is one printable-ASCII token of bounded length; anything else is rejected before use. */
37
+ export function isWellFormedApiKey(value) {
38
+ return typeof value === 'string' && value.length <= MAX_API_KEY_LEN && /^[\x21-\x7e]+$/.test(value);
39
+ }
40
+
41
+ export function loadTokens() {
42
+ if (modelProxyClientMode()) return null;
43
+ const path = getOwnTokenPath();
44
+ if (!existsSync(path)) return null;
45
+ try {
46
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
47
+ const apiKey = text(raw?.apiKey);
48
+ if (!apiKey) return null;
49
+ return { apiKey, userId: text(raw.userId), userName: text(raw.userName), mtimeMs: fileMtimeMs(path) };
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ export function saveTokens({ apiKey, userId, userName }) {
56
+ const tokens = { apiKey, userId: text(userId), userName: text(userName) };
57
+ writeJsonAtomicSync(getOwnTokenPath(), tokens, { lock: true, fsyncDir: true, mode: 0o600, secret: true });
58
+ return tokens;
59
+ }
60
+
61
+ export function commandCodeReauthError(detail = '') {
62
+ return Object.assign(
63
+ new Error(
64
+ `[command-code-oauth] API key rejected (401) — open /providers in mixdog to sign in again${detail ? `: ${detail}` : ''}`
65
+ ),
66
+ { httpStatus: 401, status: 401, reauthRequired: true }
67
+ );
68
+ }
69
+
70
+ /**
71
+ * GET a Command Code API endpoint with the key. Refuses any URL outside
72
+ * API_ORIGIN and never follows redirects, so the key cannot leave that host.
73
+ */
74
+ export async function requestCommandCodeJson(url, apiKey, { fetchImpl = fetch, signal = null } = {}) {
75
+ const target = new URL(url);
76
+ if (target.origin !== API_ORIGIN)
77
+ throw new Error(`[command-code-oauth] refusing to send the API key to ${target.host}`);
78
+ const timeout = AbortSignal.timeout(REQUEST_TIMEOUT_MS);
79
+ const res = await fetchImpl(target.toString(), {
80
+ method: 'GET',
81
+ headers: { Accept: 'application/json', Authorization: `Bearer ${apiKey}` },
82
+ redirect: 'error',
83
+ signal: signal ? AbortSignal.any([signal, timeout]) : timeout,
84
+ });
85
+ const body = await res.text().catch(() => '');
86
+ if (!res.ok) {
87
+ const detail = scrubOAuthSecrets(body, [apiKey]).trim().slice(0, 300);
88
+ if (res.status === 401) throw commandCodeReauthError(detail);
89
+ throw Object.assign(
90
+ new Error(`[command-code-oauth] ${target.pathname} ${res.status}${detail ? `: ${detail}` : ''}`),
91
+ { status: res.status, httpStatus: res.status }
92
+ );
93
+ }
94
+ try {
95
+ return JSON.parse(body);
96
+ } catch {
97
+ throw new Error(`[command-code-oauth] ${target.pathname} returned invalid JSON`);
98
+ }
99
+ }
100
+
101
+ /** The account behind a key: whoami's user id is the stable account identity. */
102
+ export async function fetchCommandCodeIdentity(apiKey, options = {}) {
103
+ const body = await requestCommandCodeJson(WHOAMI_URL, apiKey, options);
104
+ const user = body?.user;
105
+ const userId = text(user?.id);
106
+ if (!userId) throw new Error('[command-code-oauth] whoami returned no user id');
107
+ return { userId, userName: text(user?.userName) };
108
+ }
109
+
110
+ export function hasCommandCodeOAuthCredentials() {
111
+ try {
112
+ return Boolean(loadTokens()?.apiKey);
113
+ } catch {
114
+ return false;
115
+ }
116
+ }
117
+
118
+ export function describeCommandCodeOAuthCredentials() {
119
+ try {
120
+ const tokens = loadTokens();
121
+ if (!tokens) {
122
+ return {
123
+ authenticated: false,
124
+ usable: false,
125
+ refreshable: false,
126
+ reauthRequired: false,
127
+ status: 'Not Set',
128
+ detail: 'Mixdog token store',
129
+ };
130
+ }
131
+ return {
132
+ authenticated: true,
133
+ usable: true,
134
+ refreshable: false,
135
+ reauthRequired: false,
136
+ status: 'Valid',
137
+ detail: 'Mixdog token store; API key',
138
+ expiresAt: 0,
139
+ ...accountIdentityFields({ id: tokens.userId }),
140
+ ...(tokens.userName ? { accountId: tokens.userName } : {}),
141
+ };
142
+ } catch (err) {
143
+ return {
144
+ authenticated: false,
145
+ usable: false,
146
+ refreshable: false,
147
+ reauthRequired: false,
148
+ status: 'Error',
149
+ detail: String(err?.message || err).slice(0, 200),
150
+ };
151
+ }
152
+ }
153
+
154
+ export function forgetCommandCodeOAuthCredentials() {
155
+ const path = getOwnTokenPath();
156
+ if (!existsSync(path)) return { removed: false };
157
+ unlinkSync(path);
158
+ return { removed: true };
159
+ }