mixdog 0.9.2 → 0.9.4
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.
- package/package.json +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/telegram-format.mjs +19 -22
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +6 -14
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +259 -80
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +56 -588
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -462
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// cwd-plugins.mjs — cwd resolution/apply + plugins-status + core-memory context,
|
|
2
|
+
// extracted from mixdog-session-runtime.mjs. Dependency-injected factory that
|
|
3
|
+
// closes over the facade's mutable cwd/config/session state via getter/setter
|
|
4
|
+
// injection (getCurrentCwd/setCurrentCwd/getConfig/getSession/...) plus the MCP
|
|
5
|
+
// glue + prewarm callbacks. The facade keeps ownership of the mutable locals;
|
|
6
|
+
// this module owns the pure logic that was previously inline.
|
|
7
|
+
|
|
8
|
+
export function createCwdPlugins({
|
|
9
|
+
// mutable-state injection
|
|
10
|
+
getCurrentCwd,
|
|
11
|
+
setCurrentCwd,
|
|
12
|
+
getConfig,
|
|
13
|
+
getSession,
|
|
14
|
+
getRoute,
|
|
15
|
+
getLastProjectMcpKey,
|
|
16
|
+
setLastProjectMcpKey,
|
|
17
|
+
isCodeGraphPrewarmLazy,
|
|
18
|
+
isCodeGraphFirstTurnPrewarmDone,
|
|
19
|
+
getCodeGraphPrewarmDelayMs,
|
|
20
|
+
setSessionNeedsCwdRefresh,
|
|
21
|
+
// callbacks / deps
|
|
22
|
+
connectConfiguredMcp,
|
|
23
|
+
invalidatePreSessionToolSurface,
|
|
24
|
+
scheduleCodeGraphPrewarm,
|
|
25
|
+
hooks,
|
|
26
|
+
hookCommonPayload,
|
|
27
|
+
bootProfile,
|
|
28
|
+
getMemoryModule,
|
|
29
|
+
// channel-admin / registry helpers
|
|
30
|
+
listRegisteredPlugins,
|
|
31
|
+
pluginAdminStatus,
|
|
32
|
+
pluginManifest,
|
|
33
|
+
pluginMcpServerName,
|
|
34
|
+
mcpScriptForPlugin,
|
|
35
|
+
countSkillFiles,
|
|
36
|
+
readProjectMcpServers,
|
|
37
|
+
writeLastSessionCwd,
|
|
38
|
+
// shared utils
|
|
39
|
+
clean,
|
|
40
|
+
resolve,
|
|
41
|
+
statSync,
|
|
42
|
+
existsSync,
|
|
43
|
+
cfgMod,
|
|
44
|
+
STANDALONE_DATA_DIR,
|
|
45
|
+
}) {
|
|
46
|
+
function resolveCwdPath(value) {
|
|
47
|
+
const raw = clean(value);
|
|
48
|
+
if (!raw) throw new Error('cwd: path is required for action=set');
|
|
49
|
+
const next = resolve(getCurrentCwd() || process.cwd(), raw);
|
|
50
|
+
const stat = statSync(next);
|
|
51
|
+
if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${next}`);
|
|
52
|
+
return next;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function applyResolvedCwd(nextCwd, { markRefresh = true } = {}) {
|
|
56
|
+
const resolved = resolve(nextCwd);
|
|
57
|
+
const stat = statSync(resolved);
|
|
58
|
+
if (!stat.isDirectory()) throw new Error(`cwd: not a directory: ${resolved}`);
|
|
59
|
+
const changed = resolve(getCurrentCwd()) !== resolved;
|
|
60
|
+
setCurrentCwd(resolved);
|
|
61
|
+
const currentCwd = resolved;
|
|
62
|
+
process.env.MIXDOG_SESSION_CWD = currentCwd;
|
|
63
|
+
writeLastSessionCwd(currentCwd);
|
|
64
|
+
const session = getSession();
|
|
65
|
+
if (session) session.cwd = currentCwd;
|
|
66
|
+
// cwd changes NEVER recreate the session: a mid-conversation cwd switch must
|
|
67
|
+
// preserve the full message history (and the BP1–BP3 prompt cache). We only
|
|
68
|
+
// retarget the live session's cwd in place; tool execution already reads the
|
|
69
|
+
// current cwd per turn. `cwd` is intentionally absent from the prompt
|
|
70
|
+
// context (see composeSystemPrompt), so there is nothing prompt-side to
|
|
71
|
+
// refresh either. `markRefresh`/`changed` are kept only for signature
|
|
72
|
+
// compatibility with existing callers.
|
|
73
|
+
void markRefresh;
|
|
74
|
+
// Lazy mode: before the first turn (e.g. the initial project-selection
|
|
75
|
+
// cwd set), do NOT prewarm — that is exactly the post-first-frame freeze
|
|
76
|
+
// we are avoiding. Once a turn has run, an in-session cwd switch DOES
|
|
77
|
+
// prewarm the new dir, since a lookup there is now likely.
|
|
78
|
+
if (isCodeGraphPrewarmLazy() && !isCodeGraphFirstTurnPrewarmDone()) {
|
|
79
|
+
bootProfile('code-graph:prewarm-lazy', { reason: 'cwd-deferred-to-first-turn' });
|
|
80
|
+
} else {
|
|
81
|
+
const delay = getCodeGraphPrewarmDelayMs();
|
|
82
|
+
scheduleCodeGraphPrewarm(changed ? 0 : delay, changed ? 'cwd-change' : 'cwd');
|
|
83
|
+
}
|
|
84
|
+
// Project-local `.mcp.json` follows the cwd: when the effective project MCP
|
|
85
|
+
// set changes, reconnect in the background (never await — this stays sync,
|
|
86
|
+
// and the session is preserved). Guarded so a no-op cwd change does not
|
|
87
|
+
// churn connections.
|
|
88
|
+
if (changed) {
|
|
89
|
+
try {
|
|
90
|
+
const nextKey = resolved + '\u0000' + JSON.stringify(readProjectMcpServers(resolved));
|
|
91
|
+
if (nextKey !== getLastProjectMcpKey()) {
|
|
92
|
+
setLastProjectMcpKey(nextKey);
|
|
93
|
+
void connectConfiguredMcp({ reset: true })
|
|
94
|
+
.then(() => invalidatePreSessionToolSurface())
|
|
95
|
+
.catch(() => {});
|
|
96
|
+
}
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
// CwdChanged: bridge an effective cwd switch to the standard hook bus.
|
|
100
|
+
// No matcher event — payload is minimal { cwd }. Fire-and-forget.
|
|
101
|
+
if (changed) {
|
|
102
|
+
try { void hooks.dispatch('CwdChanged', hookCommonPayload({ cwd: currentCwd })); } catch {}
|
|
103
|
+
}
|
|
104
|
+
return currentCwd;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function refreshSessionForCwdIfNeeded(reason = 'cwd-change') {
|
|
108
|
+
// No-op: cwd changes are applied in place by applyResolvedCwd and never
|
|
109
|
+
// tear down the session. Retained as a stable hook for ask()'s pre-turn
|
|
110
|
+
// call so the surrounding turn flow is unchanged.
|
|
111
|
+
void reason;
|
|
112
|
+
setSessionNeedsCwdRefresh(false);
|
|
113
|
+
return getSession();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function pluginsStatus() {
|
|
117
|
+
const config = getConfig();
|
|
118
|
+
const dataDir = cfgMod.getPluginData?.();
|
|
119
|
+
const configuredMcp = config?.mcpServers && typeof config.mcpServers === 'object'
|
|
120
|
+
? config.mcpServers
|
|
121
|
+
: {};
|
|
122
|
+
const plugins = [];
|
|
123
|
+
const addRegisteredPlugin = (entry) => {
|
|
124
|
+
const root = clean(entry.root);
|
|
125
|
+
if (!root || !existsSync(root)) return;
|
|
126
|
+
const manifest = pluginManifest(root);
|
|
127
|
+
const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
|
|
128
|
+
const plugin = {
|
|
129
|
+
id: clean(entry.id) || name,
|
|
130
|
+
name,
|
|
131
|
+
title: clean(manifest.title) || clean(manifest.displayName) || clean(entry.title) || name,
|
|
132
|
+
version: clean(manifest.version) || clean(entry.version) || null,
|
|
133
|
+
description: clean(manifest.description) || clean(entry.description),
|
|
134
|
+
marketplace: null,
|
|
135
|
+
source: clean(entry.sourceType) === 'local' ? 'local' : 'registry',
|
|
136
|
+
sourceUrl: clean(entry.source),
|
|
137
|
+
sourceType: clean(entry.sourceType) || 'git',
|
|
138
|
+
managed: entry.managed !== false,
|
|
139
|
+
root,
|
|
140
|
+
installedAt: entry.installedAt || null,
|
|
141
|
+
updatedAt: entry.updatedAt || null,
|
|
142
|
+
skillCount: countSkillFiles(root),
|
|
143
|
+
mcpScript: mcpScriptForPlugin(root),
|
|
144
|
+
};
|
|
145
|
+
plugin.mcpServerName = pluginMcpServerName(plugin);
|
|
146
|
+
plugin.mcpEnabled = Object.prototype.hasOwnProperty.call(configuredMcp, plugin.mcpServerName)
|
|
147
|
+
|| Object.keys(configuredMcp).some((k) => k.startsWith(`${plugin.mcpServerName}--`));
|
|
148
|
+
plugins.push(plugin);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (const entry of listRegisteredPlugins({ dataDir })) addRegisteredPlugin(entry);
|
|
152
|
+
|
|
153
|
+
plugins.sort((a, b) => {
|
|
154
|
+
if (a.source !== b.source) return a.source.localeCompare(b.source);
|
|
155
|
+
return a.name.localeCompare(b.name);
|
|
156
|
+
});
|
|
157
|
+
const admin = pluginAdminStatus({ dataDir });
|
|
158
|
+
return {
|
|
159
|
+
count: plugins.length,
|
|
160
|
+
plugins,
|
|
161
|
+
roots: {
|
|
162
|
+
registry: admin.registryPath,
|
|
163
|
+
installed: admin.installRoot,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function formatCoreMemoryLines(payload = {}) {
|
|
169
|
+
const seen = new Set();
|
|
170
|
+
const lines = [];
|
|
171
|
+
for (const value of [
|
|
172
|
+
...(Array.isArray(payload.userLines) ? payload.userLines : []),
|
|
173
|
+
...(Array.isArray(payload.dbLines) ? payload.dbLines : []),
|
|
174
|
+
]) {
|
|
175
|
+
const text = clean(value).replace(/\s+/g, ' ');
|
|
176
|
+
if (!text) continue;
|
|
177
|
+
const key = text.toLowerCase();
|
|
178
|
+
if (seen.has(key)) continue;
|
|
179
|
+
seen.add(key);
|
|
180
|
+
lines.push(`- ${text}`);
|
|
181
|
+
if (lines.length >= 40) break;
|
|
182
|
+
}
|
|
183
|
+
const out = lines.join('\n');
|
|
184
|
+
const maxChars = 6000;
|
|
185
|
+
return out.length > maxChars ? `${out.slice(0, maxChars).replace(/\s+\S*$/, '')}\n- ...` : out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function loadCoreMemoryContext() {
|
|
189
|
+
// Boot should not pay for memory/PG startup unless explicitly requested.
|
|
190
|
+
// Recall and memory tools still initialize the memory service on first use.
|
|
191
|
+
if (process.env.MIXDOG_BOOT_CORE_MEMORY !== '1') {
|
|
192
|
+
bootProfile('core-memory:skipped');
|
|
193
|
+
return '';
|
|
194
|
+
}
|
|
195
|
+
const startedAt = performance.now();
|
|
196
|
+
let timer = null;
|
|
197
|
+
const timeout = new Promise((resolveTimeout) => {
|
|
198
|
+
timer = setTimeout(() => resolveTimeout(''), 2000);
|
|
199
|
+
timer.unref?.();
|
|
200
|
+
});
|
|
201
|
+
try {
|
|
202
|
+
return await Promise.race([
|
|
203
|
+
(async () => {
|
|
204
|
+
const memoryMod = await getMemoryModule();
|
|
205
|
+
if (typeof memoryMod?.buildSessionCoreMemoryPayload !== 'function') return '';
|
|
206
|
+
return formatCoreMemoryLines(await memoryMod.buildSessionCoreMemoryPayload(getCurrentCwd()));
|
|
207
|
+
})(),
|
|
208
|
+
timeout,
|
|
209
|
+
]);
|
|
210
|
+
} catch {
|
|
211
|
+
return '';
|
|
212
|
+
} finally {
|
|
213
|
+
if (timer) clearTimeout(timer);
|
|
214
|
+
bootProfile('core-memory:done', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
resolveCwdPath,
|
|
220
|
+
applyResolvedCwd,
|
|
221
|
+
refreshSessionForCwdIfNeeded,
|
|
222
|
+
pluginsStatus,
|
|
223
|
+
formatCoreMemoryLines,
|
|
224
|
+
loadCoreMemoryContext,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Reasoning-effort catalogs and coercion. Pure helpers.
|
|
2
|
+
import { clean } from './session-text.mjs';
|
|
3
|
+
|
|
4
|
+
export const TOOL_MODES = new Set(['full', 'readonly', 'lead']);
|
|
5
|
+
export const ALL_EFFORT_LEVELS = new Set(['none', 'low', 'medium', 'high', 'xhigh', 'max']);
|
|
6
|
+
export const EFFORT_LABELS = {
|
|
7
|
+
none: 'None',
|
|
8
|
+
low: 'Low',
|
|
9
|
+
medium: 'Medium',
|
|
10
|
+
high: 'High',
|
|
11
|
+
xhigh: 'Extra High',
|
|
12
|
+
max: 'Max',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const EFFORT_OPTIONS_BY_PROVIDER = {
|
|
16
|
+
openai: ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
17
|
+
'openai-oauth': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
18
|
+
anthropic: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
19
|
+
'anthropic-oauth': ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
20
|
+
xai: ['none', 'low', 'medium', 'high'],
|
|
21
|
+
'grok-oauth': ['none', 'low', 'medium', 'high'],
|
|
22
|
+
'opencode-go': ['high', 'max'],
|
|
23
|
+
};
|
|
24
|
+
export const EFFORT_BY_FAMILY = {
|
|
25
|
+
opus: ['low', 'medium', 'high', 'xhigh', 'max'],
|
|
26
|
+
sonnet: ['low', 'medium', 'high'],
|
|
27
|
+
haiku: [],
|
|
28
|
+
'gpt-5.5': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
29
|
+
'gpt-5.4': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
30
|
+
'gpt-5.2': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
31
|
+
'gpt-5': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
32
|
+
'gpt-mini': ['none', 'low', 'medium', 'high', 'xhigh'],
|
|
33
|
+
'gpt-nano': ['none', 'low', 'medium', 'high'],
|
|
34
|
+
'gpt-codex': ['none', 'low', 'medium', 'high'],
|
|
35
|
+
grok: ['none', 'low', 'medium', 'high'],
|
|
36
|
+
};
|
|
37
|
+
export const EFFORT_FALLBACKS = {
|
|
38
|
+
max: ['max', 'xhigh', 'high', 'medium', 'low'],
|
|
39
|
+
xhigh: ['xhigh', 'high', 'medium', 'low'],
|
|
40
|
+
high: ['high', 'medium', 'low'],
|
|
41
|
+
medium: ['medium', 'low'],
|
|
42
|
+
low: ['low'],
|
|
43
|
+
none: ['none'],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function normalizeToolMode(mode) {
|
|
47
|
+
const value = String(mode || '').trim().toLowerCase();
|
|
48
|
+
return TOOL_MODES.has(value) ? value : 'full';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function normalizeEffortInput(value) {
|
|
52
|
+
const v = clean(value).toLowerCase();
|
|
53
|
+
if (!v || v === 'auto') return null;
|
|
54
|
+
if (!ALL_EFFORT_LEVELS.has(v)) {
|
|
55
|
+
throw new Error(`effort must be one of auto, ${[...ALL_EFFORT_LEVELS].join(', ')}`);
|
|
56
|
+
}
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function effortOptionsFor(provider, model) {
|
|
61
|
+
const providerAllowed = EFFORT_OPTIONS_BY_PROVIDER[provider] || null;
|
|
62
|
+
const filterProvider = (values) => {
|
|
63
|
+
const unique = [...new Set((values || []).map(clean).filter(Boolean))];
|
|
64
|
+
return providerAllowed ? unique.filter((v) => providerAllowed.includes(v)) : unique;
|
|
65
|
+
};
|
|
66
|
+
const declared = Array.isArray(model?.reasoningLevels)
|
|
67
|
+
? model.reasoningLevels.map(clean).filter(Boolean)
|
|
68
|
+
: [];
|
|
69
|
+
const family = clean(model?.family).toLowerCase();
|
|
70
|
+
if (Array.isArray(model?.reasoningLevels)) {
|
|
71
|
+
if (declared.length) return filterProvider(declared);
|
|
72
|
+
if (Object.prototype.hasOwnProperty.call(EFFORT_BY_FAMILY, family)) {
|
|
73
|
+
return filterProvider(EFFORT_BY_FAMILY[family]);
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const reasoningOptionEffort = Array.isArray(model?.reasoningOptions)
|
|
78
|
+
? model.reasoningOptions.find((option) => clean(option?.type).toLowerCase() === 'effort')
|
|
79
|
+
: null;
|
|
80
|
+
const reasoningOptionValues = Array.isArray(reasoningOptionEffort?.values)
|
|
81
|
+
? reasoningOptionEffort.values.map(clean).filter(Boolean)
|
|
82
|
+
: [];
|
|
83
|
+
if (reasoningOptionValues.length) return filterProvider(reasoningOptionValues);
|
|
84
|
+
if (Object.prototype.hasOwnProperty.call(EFFORT_BY_FAMILY, family)) {
|
|
85
|
+
return filterProvider(EFFORT_BY_FAMILY[family]);
|
|
86
|
+
}
|
|
87
|
+
return providerAllowed || [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function coerceEffortFor(provider, model, effort) {
|
|
91
|
+
if (!effort) return null;
|
|
92
|
+
const allowed = effortOptionsFor(provider, model);
|
|
93
|
+
if (!allowed || allowed.length === 0) return null;
|
|
94
|
+
if (allowed.includes(effort)) return effort;
|
|
95
|
+
for (const candidate of EFFORT_FALLBACKS[effort] || []) {
|
|
96
|
+
if (allowed.includes(candidate)) return candidate;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function normalizeSavedEffort(value) {
|
|
102
|
+
try {
|
|
103
|
+
return normalizeEffortInput(value);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function effortItemsFor(provider, model, activeEffort) {
|
|
110
|
+
const allowed = effortOptionsFor(provider, model);
|
|
111
|
+
const items = [];
|
|
112
|
+
for (const value of allowed || []) {
|
|
113
|
+
items.push({
|
|
114
|
+
value,
|
|
115
|
+
label: EFFORT_LABELS[value] || value,
|
|
116
|
+
description: value === activeEffort ? 'current' : '',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return items;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function toolSpecForMode(mode) {
|
|
123
|
+
return mode === 'readonly' ? ['tools:readonly'] : 'full';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function deferredSurfaceModeForLead(mode) {
|
|
127
|
+
return mode === 'readonly' ? 'readonly' : 'lead';
|
|
128
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Small filesystem read helpers used by session-runtime submodules.
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
export function readJsonSafe(path) {
|
|
5
|
+
try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function readTextSafe(path) {
|
|
9
|
+
try { return readFileSync(path, 'utf8').trim(); } catch { return ''; }
|
|
10
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// MCP config/status/connect glue, extracted from mixdog-session-runtime.mjs.
|
|
2
|
+
// Dependency-injected factory: all live state (config, currentCwd, connect
|
|
3
|
+
// generation/in-flight/failures) is threaded through accessors + a caller-owned
|
|
4
|
+
// `state` object so the facade's teardown/reconnect paths still observe it.
|
|
5
|
+
// Method behavior is byte-for-byte identical; only grouping changes.
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { clean } from './session-text.mjs';
|
|
8
|
+
import { readProjectMcpServers } from './plugin-mcp.mjs';
|
|
9
|
+
|
|
10
|
+
export function createMcpGlue({
|
|
11
|
+
mcpClient,
|
|
12
|
+
getConfig,
|
|
13
|
+
getCurrentCwd,
|
|
14
|
+
state,
|
|
15
|
+
}) {
|
|
16
|
+
function mcpTransportLabel(cfg = {}) {
|
|
17
|
+
if (cfg.autoDetect) return `autoDetect:${cfg.autoDetect}`;
|
|
18
|
+
try {
|
|
19
|
+
return mcpClient.resolveMcpTransportKind(cfg);
|
|
20
|
+
} catch {
|
|
21
|
+
return 'unknown';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Merge mixdog-config `agent.mcpServers` with project-local `.mcp.json`.
|
|
26
|
+
// On name collision the project-local `.mcp.json` entry WINS (Claude Code
|
|
27
|
+
// precedence: project > user config). `sources[name]` records each server's
|
|
28
|
+
// origin ('config' | 'project') for status reporting.
|
|
29
|
+
function resolveEffectiveMcpServers() {
|
|
30
|
+
const config = getConfig();
|
|
31
|
+
const configured = config?.mcpServers && typeof config.mcpServers === 'object'
|
|
32
|
+
? config.mcpServers
|
|
33
|
+
: {};
|
|
34
|
+
const project = readProjectMcpServers(getCurrentCwd());
|
|
35
|
+
const servers = { ...configured, ...project };
|
|
36
|
+
const sources = {};
|
|
37
|
+
for (const name of Object.keys(configured)) sources[name] = 'config';
|
|
38
|
+
for (const name of Object.keys(project)) sources[name] = 'project';
|
|
39
|
+
return { servers, sources };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function mcpStatus() {
|
|
43
|
+
const { servers: configured, sources } = resolveEffectiveMcpServers();
|
|
44
|
+
const connected = new Map((mcpClient.getMcpServerStatus?.() || []).map((row) => [row.name, row]));
|
|
45
|
+
const failures = new Map((state.mcpFailures || []).map((row) => [row.name, row]));
|
|
46
|
+
const servers = [];
|
|
47
|
+
for (const [name, cfg] of Object.entries(configured)) {
|
|
48
|
+
const live = connected.get(name);
|
|
49
|
+
const fail = failures.get(name);
|
|
50
|
+
servers.push({
|
|
51
|
+
name,
|
|
52
|
+
configured: true,
|
|
53
|
+
enabled: cfg?.enabled !== false,
|
|
54
|
+
connected: Boolean(live),
|
|
55
|
+
status: cfg?.enabled === false ? 'disabled' : live ? 'connected' : fail ? 'failed' : 'disconnected',
|
|
56
|
+
transport: mcpTransportLabel(cfg),
|
|
57
|
+
toolCount: live?.toolCount || 0,
|
|
58
|
+
tools: live?.tools || [],
|
|
59
|
+
error: fail?.msg || null,
|
|
60
|
+
source: sources[name] || 'config',
|
|
61
|
+
});
|
|
62
|
+
connected.delete(name);
|
|
63
|
+
}
|
|
64
|
+
for (const live of connected.values()) {
|
|
65
|
+
servers.push({ ...live, configured: false, status: 'connected' });
|
|
66
|
+
}
|
|
67
|
+
servers.sort((a, b) => String(a.name).localeCompare(String(b.name)));
|
|
68
|
+
return {
|
|
69
|
+
servers,
|
|
70
|
+
configuredCount: Object.keys(configured).length,
|
|
71
|
+
connectedCount: servers.filter((row) => row.connected).length,
|
|
72
|
+
failedCount: servers.filter((row) => row.status === 'failed').length,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function connectConfiguredMcp({ reset = false } = {}) {
|
|
77
|
+
// Serialize reconnects: boot connect, cwd-change reset, and rapid cwd
|
|
78
|
+
// switches must never interleave their disconnect/connect phases, or an
|
|
79
|
+
// older run finishing after a newer reset could re-add stale servers into
|
|
80
|
+
// the shared client registry. Approach: a generation token + a single
|
|
81
|
+
// in-flight promise. Each call bumps the generation, waits for any prior
|
|
82
|
+
// run to finish, then bails if a newer call has superseded it — leaving the
|
|
83
|
+
// latest requested effective-server-set in the registry.
|
|
84
|
+
const gen = ++state.mcpConnectGeneration;
|
|
85
|
+
if (state.mcpConnectInFlight) {
|
|
86
|
+
try { await state.mcpConnectInFlight; } catch { /* prior run's failures already captured */ }
|
|
87
|
+
}
|
|
88
|
+
if (gen !== state.mcpConnectGeneration) return mcpStatus();
|
|
89
|
+
const run = (async () => {
|
|
90
|
+
if (reset) await mcpClient.disconnectAll?.();
|
|
91
|
+
state.mcpFailures = [];
|
|
92
|
+
const { servers } = resolveEffectiveMcpServers();
|
|
93
|
+
if (Object.keys(servers).length === 0) return;
|
|
94
|
+
try {
|
|
95
|
+
await mcpClient.connectMcpServers(servers);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
state.mcpFailures = Array.isArray(error?.failures)
|
|
98
|
+
? error.failures
|
|
99
|
+
: [{ name: 'mcp', msg: error?.message || String(error) }];
|
|
100
|
+
}
|
|
101
|
+
})();
|
|
102
|
+
state.mcpConnectInFlight = run;
|
|
103
|
+
try {
|
|
104
|
+
await run;
|
|
105
|
+
} finally {
|
|
106
|
+
if (state.mcpConnectInFlight === run) state.mcpConnectInFlight = null;
|
|
107
|
+
}
|
|
108
|
+
return mcpStatus();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function normalizeMcpServerInput(input = {}) {
|
|
112
|
+
const currentCwd = getCurrentCwd();
|
|
113
|
+
const name = clean(input.name).toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
114
|
+
if (!name) throw new Error('MCP server name is required');
|
|
115
|
+
const coerceStringRecord = (value) => {
|
|
116
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
|
|
117
|
+
const out = {};
|
|
118
|
+
for (const [key, val] of Object.entries(value)) {
|
|
119
|
+
if (val === undefined || val === null) continue;
|
|
120
|
+
out[String(key)] = String(val);
|
|
121
|
+
}
|
|
122
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
123
|
+
};
|
|
124
|
+
const withOptionalHeaders = (config) => {
|
|
125
|
+
const headers = coerceStringRecord(input.headers);
|
|
126
|
+
if (headers) config.headers = headers;
|
|
127
|
+
return config;
|
|
128
|
+
};
|
|
129
|
+
const url = clean(input.url);
|
|
130
|
+
const type = clean(input.type).toLowerCase();
|
|
131
|
+
if (url) {
|
|
132
|
+
if (type === 'sse') {
|
|
133
|
+
if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
|
|
134
|
+
return { name, config: withOptionalHeaders({ type: 'sse', url }) };
|
|
135
|
+
}
|
|
136
|
+
if (type === 'ws') {
|
|
137
|
+
if (!/^(?:wss?|https?):\/\//i.test(url)) {
|
|
138
|
+
throw new Error('MCP WebSocket URL must start with ws://, wss://, http://, or https://');
|
|
139
|
+
}
|
|
140
|
+
return { name, config: withOptionalHeaders({ type: 'ws', url }) };
|
|
141
|
+
}
|
|
142
|
+
if (type === 'http' || type === 'streamable-http') {
|
|
143
|
+
if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
|
|
144
|
+
return { name, config: withOptionalHeaders({ type: 'http', url }) };
|
|
145
|
+
}
|
|
146
|
+
if (/^wss?:\/\//i.test(url)) {
|
|
147
|
+
return { name, config: withOptionalHeaders({ type: 'ws', url }) };
|
|
148
|
+
}
|
|
149
|
+
if (!/^https?:\/\//i.test(url)) throw new Error('MCP URL must start with http:// or https://');
|
|
150
|
+
return { name, config: withOptionalHeaders({ type: 'http', url }) };
|
|
151
|
+
}
|
|
152
|
+
const command = clean(input.command);
|
|
153
|
+
if (!command) throw new Error('MCP server command or URL is required');
|
|
154
|
+
const args = Array.isArray(input.args)
|
|
155
|
+
? input.args.map((v) => String(v)).filter(Boolean)
|
|
156
|
+
: clean(input.args).split(/\s+/).filter(Boolean);
|
|
157
|
+
const requestedCwd = clean(input.cwd);
|
|
158
|
+
const cwdForServer = requestedCwd ? resolve(currentCwd, requestedCwd) : currentCwd;
|
|
159
|
+
const root = resolve(currentCwd);
|
|
160
|
+
const resolvedCwd = resolve(cwdForServer);
|
|
161
|
+
if (resolvedCwd !== root && !resolvedCwd.startsWith(`${root}\\`) && !resolvedCwd.startsWith(`${root}/`)) {
|
|
162
|
+
throw new Error('MCP server cwd must stay under the current project');
|
|
163
|
+
}
|
|
164
|
+
const config = { type: 'stdio', command, args, cwd: resolvedCwd };
|
|
165
|
+
const env = coerceStringRecord(input.env);
|
|
166
|
+
if (env) config.env = env;
|
|
167
|
+
return { name, config };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
mcpTransportLabel,
|
|
172
|
+
resolveEffectiveMcpServers,
|
|
173
|
+
mcpStatus,
|
|
174
|
+
connectConfiguredMcp,
|
|
175
|
+
normalizeMcpServerInput,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// Provider/model capability probes: fast-tier + hosted web-search support, and
|
|
2
|
+
// model-settings persistence. Pure except saveModelSettings (takes cfgMod).
|
|
3
|
+
import { clean, hasOwn } from './session-text.mjs';
|
|
4
|
+
|
|
5
|
+
const FAST_CAPABLE_PROVIDERS = new Set(['anthropic', 'anthropic-oauth', 'openai', 'openai-oauth']);
|
|
6
|
+
export const LAZY_SECRET_PROVIDERS = new Set(['openai-oauth', 'anthropic-oauth', 'grok-oauth', 'ollama', 'lmstudio']);
|
|
7
|
+
|
|
8
|
+
export function routeFastKey(provider, model) {
|
|
9
|
+
const p = clean(provider);
|
|
10
|
+
const m = clean(model);
|
|
11
|
+
return p && m ? `${p}/${m}` : '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function openAiModelMetaSupportsFast(model) {
|
|
15
|
+
const tiers = Array.isArray(model?.serviceTiers) ? model.serviceTiers : [];
|
|
16
|
+
const speedTiers = Array.isArray(model?.additionalSpeedTiers) ? model.additionalSpeedTiers : [];
|
|
17
|
+
if (tiers.length || speedTiers.length || model?.defaultServiceTier) {
|
|
18
|
+
return tiers.some((tier) => tier?.id === 'priority')
|
|
19
|
+
|| speedTiers.includes('priority')
|
|
20
|
+
|| model?.defaultServiceTier === 'priority';
|
|
21
|
+
}
|
|
22
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
23
|
+
if (id.includes('mini') || id.includes('nano') || id.includes('codex')) return false;
|
|
24
|
+
return /^gpt-5(\.|-|$)/.test(id);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function openAiDirectModelSupportsFast(model) {
|
|
28
|
+
const id = clean(model?.id || model);
|
|
29
|
+
return /^gpt-5\.5(?:-\d{4}|$)/.test(id)
|
|
30
|
+
|| /^gpt-5\.4(?:-\d{4}|$)/.test(id)
|
|
31
|
+
|| /^gpt-5\.4-mini(?:-\d{4}|$)/.test(id);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function openAiModelSupportsHostedWebSearch(model) {
|
|
35
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
36
|
+
if (!id) return false;
|
|
37
|
+
if (model?.supportsWebSearch === true) return true;
|
|
38
|
+
const tools = [
|
|
39
|
+
...(Array.isArray(model?.supportedTools) ? model.supportedTools : []),
|
|
40
|
+
...(Array.isArray(model?.tools) ? model.tools : []),
|
|
41
|
+
...(Array.isArray(model?.capabilities?.tools) ? model.capabilities.tools : []),
|
|
42
|
+
].map((tool) => clean(tool?.type || tool?.name || tool).toLowerCase());
|
|
43
|
+
if (tools.some((tool) => tool === 'web_search' || tool === 'web_search_preview')) return true;
|
|
44
|
+
if (/codex|image|audio|tts|stt|embedding|rerank|moderation|search-preview/.test(id)) return false;
|
|
45
|
+
return /^gpt-(5(?:\.|$|-)|4\.1(?:-|$)|4o(?:-|$)|4\.5(?:-|$))/.test(id)
|
|
46
|
+
|| /^o[34](?:-|$)/.test(id);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function grokModelSupportsHostedWebSearch(model) {
|
|
50
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
51
|
+
if (!id || /imagine|image|video|composer/.test(id)) return false;
|
|
52
|
+
if (id === 'grok-build') return false;
|
|
53
|
+
return /^grok-/.test(id);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function geminiModelSupportsHostedWebSearch(model) {
|
|
57
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
58
|
+
if (!id || /embedding|aqa|imagen|veo|tts|image|computer-use|customtools/.test(id)) return false;
|
|
59
|
+
return /^gemini-(3(?:\.|-|$)|2\.5-|2\.0-flash)/.test(id);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function anthropicModelSupportsHostedWebSearch(model) {
|
|
63
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
64
|
+
if (!id) return false;
|
|
65
|
+
const match = id.match(/^claude-(opus|sonnet|haiku)-(\d+)(?:[-.](\d+))?/);
|
|
66
|
+
if (!match) return false;
|
|
67
|
+
const major = Number(match[2]) || 0;
|
|
68
|
+
const minor = Number(match[3]) || 0;
|
|
69
|
+
return major > 4 || (major === 4 && minor >= 0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function anthropicModelMetaSupportsFast(model) {
|
|
73
|
+
const id = clean(model?.id || model).toLowerCase();
|
|
74
|
+
return /^claude-(opus|sonnet)/.test(id);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function fastCapableFor(provider, model) {
|
|
78
|
+
const p = clean(provider);
|
|
79
|
+
if (!FAST_CAPABLE_PROVIDERS.has(p)) return false;
|
|
80
|
+
if (p === 'openai') return openAiDirectModelSupportsFast(model);
|
|
81
|
+
if (p === 'openai-oauth') return openAiModelMetaSupportsFast(model);
|
|
82
|
+
if (p === 'anthropic' || p === 'anthropic-oauth') return anthropicModelMetaSupportsFast(model);
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// searchCapableFor needs the search-route normalizers, which live in
|
|
87
|
+
// search-routes.mjs and themselves are pure. Wire them in via a factory to keep
|
|
88
|
+
// this module free of a circular import at load time.
|
|
89
|
+
export function makeSearchCapableFor(normalizeSearchProviderId, isSearchCapableProvider) {
|
|
90
|
+
return function searchCapableFor(provider, model) {
|
|
91
|
+
const p = normalizeSearchProviderId(provider);
|
|
92
|
+
if (!isSearchCapableProvider(p)) return false;
|
|
93
|
+
if (p === 'openai' || p === 'openai-oauth') return openAiModelSupportsHostedWebSearch(model);
|
|
94
|
+
if (p === 'grok-oauth' || p === 'xai') return grokModelSupportsHostedWebSearch(model);
|
|
95
|
+
if (p === 'gemini') return geminiModelSupportsHostedWebSearch(model);
|
|
96
|
+
if (p === 'anthropic' || p === 'anthropic-oauth') return anthropicModelSupportsHostedWebSearch(model);
|
|
97
|
+
return model?.supportsWebSearch === true;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function fastPreferenceFor(config, provider, model) {
|
|
102
|
+
const key = routeFastKey(provider, model);
|
|
103
|
+
if (!key) return false;
|
|
104
|
+
const saved = config?.modelSettings?.[key];
|
|
105
|
+
if (saved && typeof saved === 'object' && hasOwn(saved, 'fast')) return saved.fast === true;
|
|
106
|
+
return config?.fastModels?.[key] === true;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function saveModelSettings(cfgMod, route, { fastCapable = true, baseConfig = null } = {}) {
|
|
110
|
+
const key = routeFastKey(route?.provider, route?.model);
|
|
111
|
+
if (!key) return baseConfig || cfgMod.loadConfig();
|
|
112
|
+
const nextConfig = baseConfig || cfgMod.loadConfig();
|
|
113
|
+
const modelSettings = { ...(nextConfig.modelSettings || {}) };
|
|
114
|
+
const nextSetting = { ...(modelSettings[key] || {}) };
|
|
115
|
+
if (hasOwn(route, 'effort') && route.effort) nextSetting.effort = route.effort;
|
|
116
|
+
else delete nextSetting.effort;
|
|
117
|
+
if (fastCapable) nextSetting.fast = route.fast === true;
|
|
118
|
+
else nextSetting.fast = false;
|
|
119
|
+
modelSettings[key] = nextSetting;
|
|
120
|
+
|
|
121
|
+
// Legacy compatibility: keep fastModels true entries for old readers, but
|
|
122
|
+
// let modelSettings.fast=false override them in new readers.
|
|
123
|
+
const fastModels = { ...(nextConfig.fastModels || {}) };
|
|
124
|
+
if (nextSetting.fast === true) fastModels[key] = true;
|
|
125
|
+
else delete fastModels[key];
|
|
126
|
+
|
|
127
|
+
const savedConfig = { ...nextConfig, modelSettings, fastModels };
|
|
128
|
+
cfgMod.saveConfig(savedConfig);
|
|
129
|
+
return savedConfig;
|
|
130
|
+
}
|