mixdog 0.9.3 → 0.9.5
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/README.md +112 -38
- package/package.json +10 -3
- 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/r5-orchestrated-task.json +7 -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-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -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 +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- 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/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- 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/providers/anthropic-effort.mjs +62 -20
- 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 +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- 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 +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- 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 +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -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/provider-catalog-cache.mjs +80 -0
- 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-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -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/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- 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 +20 -1
- 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 +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- 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 -92
- 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 +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- 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 +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- 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 +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -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/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- 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 +313 -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 +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -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/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -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 +24 -842
- 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-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -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 +5 -13
- 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/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- 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 +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -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 +11 -9
- 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 +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- 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 +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- 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 +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -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 +356 -0
- package/src/tui/app/model-options.mjs +334 -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 +489 -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 +677 -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 +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- 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/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -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.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- 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 -501
- 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 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- 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/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- 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/input-editing.selection.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/paste-fix.test.mjs +0 -119
- 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
|
@@ -144,10 +144,33 @@ export function createTranscriptWriter({ mixdogHome, sessionId, cwd, pid } = {})
|
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
+
// Idempotently create the transcript file as an empty 0-byte file if it
|
|
148
|
+
// does not already exist. Called on every remote turn (before the first
|
|
149
|
+
// append) so the channel worker's fs.watch can attach to the path right
|
|
150
|
+
// away instead of waiting on schedulePendingTranscriptRearm(). Never
|
|
151
|
+
// truncates an existing transcript: guarded by existsSync + the 'wx'
|
|
152
|
+
// (exclusive-create) flag, and any EEXIST/other error is swallowed via
|
|
153
|
+
// logOnce like the other writers.
|
|
154
|
+
function ensureTranscriptFile() {
|
|
155
|
+
try {
|
|
156
|
+
ensureProjectDir();
|
|
157
|
+
if (existsSync(transcriptPath)) return;
|
|
158
|
+
try {
|
|
159
|
+
writeFileSync(transcriptPath, '', { flag: 'wx' });
|
|
160
|
+
} catch (err) {
|
|
161
|
+
if (err && err.code === 'EEXIST') return;
|
|
162
|
+
logOnce(err);
|
|
163
|
+
}
|
|
164
|
+
} catch (err) {
|
|
165
|
+
logOnce(err);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
147
169
|
return {
|
|
148
170
|
transcriptPath,
|
|
149
171
|
sessionRecordPath,
|
|
150
172
|
writeSessionRecord,
|
|
173
|
+
ensureTranscriptFile,
|
|
151
174
|
refresh,
|
|
152
175
|
appendAssistant,
|
|
153
176
|
appendToolUse,
|
|
@@ -154,12 +154,15 @@ export async function checkLatestVersion({ force = false, dataDir } = {}) {
|
|
|
154
154
|
}
|
|
155
155
|
// Registry unreachable: fall back to a stale cache (if any) rather than
|
|
156
156
|
// reporting "no update info" every time the network hiccups.
|
|
157
|
-
|
|
157
|
+
// Re-read the cache here: a force:true call skips the read above, but a
|
|
158
|
+
// stale answer still beats none when the network is down.
|
|
159
|
+
const staleCache = cache || readCache(dataDir);
|
|
160
|
+
if (staleCache && staleCache.latestVersion) {
|
|
158
161
|
return {
|
|
159
162
|
currentVersion,
|
|
160
|
-
latestVersion:
|
|
161
|
-
updateAvailable: isNewerVersion(
|
|
162
|
-
lastCheckedAt:
|
|
163
|
+
latestVersion: staleCache.latestVersion,
|
|
164
|
+
updateAvailable: isNewerVersion(staleCache.latestVersion, currentVersion),
|
|
165
|
+
lastCheckedAt: staleCache.lastCheckedAt || now,
|
|
163
166
|
fromCache: true,
|
|
164
167
|
};
|
|
165
168
|
}
|
|
@@ -112,6 +112,79 @@ export function ensureProviderEnabled(config, provider) {
|
|
|
112
112
|
|
|
113
113
|
const AUTO_CLEAR_DEFAULT_IDLE_MS = 60 * 60 * 1000;
|
|
114
114
|
|
|
115
|
+
// Provider-aware auto-clear idle-sweep defaults. Rationale mirrors the
|
|
116
|
+
// provider cache TTLs documented in agent-runtime/cache-strategy.mjs
|
|
117
|
+
// (explicit breakpoint / managed-cache windows): Anthropic follows the BP4
|
|
118
|
+
// messages-tail TTL (5m default cache_control window; BP1~3 stay on the 1h
|
|
119
|
+
// extended TTL and are prefix-shared, so reaping at 5m only forfeits an
|
|
120
|
+
// already-cold tail — see resolveLeadMessagesTtl). Gemini, xAI and Mistral
|
|
121
|
+
// caches run ~1h, Groq's implicit cache persists ~2h, OpenAI/DeepSeek
|
|
122
|
+
// key-prefix/implicit caches persist far longer (~24h), and OpenAI OAuth's
|
|
123
|
+
// in-memory server cache is short-lived (~5-10min), so we round up slightly
|
|
124
|
+
// to 10m to avoid sweeping a still-warm cache. Unknown/unrecognized
|
|
125
|
+
// providers fall back to the 'default' 1h entry.
|
|
126
|
+
export const AUTO_CLEAR_PROVIDER_IDLE_MS = Object.freeze({
|
|
127
|
+
'anthropic': 5 * 60 * 1000,
|
|
128
|
+
'anthropic-oauth': 5 * 60 * 1000,
|
|
129
|
+
'gemini': 60 * 60 * 1000,
|
|
130
|
+
'groq': 2 * 60 * 60 * 1000,
|
|
131
|
+
'openai': 24 * 60 * 60 * 1000,
|
|
132
|
+
'deepseek': 24 * 60 * 60 * 1000,
|
|
133
|
+
'xai': 60 * 60 * 1000,
|
|
134
|
+
'openai-oauth': 10 * 60 * 1000,
|
|
135
|
+
'mistral': 60 * 60 * 1000,
|
|
136
|
+
'default': AUTO_CLEAR_DEFAULT_IDLE_MS,
|
|
137
|
+
});
|
|
138
|
+
export const AUTO_CLEAR_PROVIDER_IDS = Object.freeze(Object.keys(AUTO_CLEAR_PROVIDER_IDLE_MS));
|
|
139
|
+
export const AUTO_CLEAR_UNKNOWN_PROVIDER_IDLE_MS = AUTO_CLEAR_PROVIDER_IDLE_MS.default;
|
|
140
|
+
|
|
141
|
+
export function normalizeAutoClearProviderIdleMs(value = {}) {
|
|
142
|
+
const raw = value && typeof value === 'object' ? value : {};
|
|
143
|
+
const out = {};
|
|
144
|
+
for (const [key, ms] of Object.entries(raw)) {
|
|
145
|
+
const provider = clean(key).toLowerCase();
|
|
146
|
+
const idleMs = Number(ms);
|
|
147
|
+
if (!provider || !Number.isFinite(idleMs) || idleMs <= 0) continue;
|
|
148
|
+
out[provider] = Math.max(60_000, Math.round(idleMs));
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Resolve the auto-clear idle-sweep threshold for a given provider id.
|
|
154
|
+
// Unknown/missing providers fall back to the 'default' 1h entry (above).
|
|
155
|
+
export function autoClearIdleMsForProvider(provider, providerIdleMs = null) {
|
|
156
|
+
const overrides = normalizeAutoClearProviderIdleMs(providerIdleMs);
|
|
157
|
+
const key = clean(provider).toLowerCase();
|
|
158
|
+
if (overrides[key]) return overrides[key];
|
|
159
|
+
if (key && AUTO_CLEAR_PROVIDER_IDLE_MS[key]) return AUTO_CLEAR_PROVIDER_IDLE_MS[key];
|
|
160
|
+
if (overrides.default) return overrides.default;
|
|
161
|
+
return AUTO_CLEAR_PROVIDER_IDLE_MS[key] ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function autoClearProviderDefaults(providerIdleMs = null) {
|
|
165
|
+
const overrides = normalizeAutoClearProviderIdleMs(providerIdleMs);
|
|
166
|
+
return AUTO_CLEAR_PROVIDER_IDS.map((provider) => {
|
|
167
|
+
const builtInMs = AUTO_CLEAR_PROVIDER_IDLE_MS[provider] ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
|
|
168
|
+
const idleMs = overrides[provider] ?? builtInMs;
|
|
169
|
+
return {
|
|
170
|
+
provider,
|
|
171
|
+
idleMs,
|
|
172
|
+
builtInMs,
|
|
173
|
+
custom: overrides[provider] != null,
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Resolve the effective auto-clear idle window for a config + provider:
|
|
179
|
+
// an explicit user-set idleMs (config.autoClear.idleMs) always wins; else
|
|
180
|
+
// fall back to the provider's default; else the global 1h default.
|
|
181
|
+
export function resolveAutoClearIdleMs(config, provider) {
|
|
182
|
+
const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
|
|
183
|
+
const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
|
|
184
|
+
if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
|
|
185
|
+
return autoClearIdleMsForProvider(provider, raw.providerIdleMs ?? raw.providerDefaults ?? raw.providers);
|
|
186
|
+
}
|
|
187
|
+
|
|
115
188
|
export function normalizeSystemShellConfig(value = {}) {
|
|
116
189
|
const raw = value && typeof value === 'object' ? value : {};
|
|
117
190
|
const command = clean(raw.command ?? raw.path ?? raw.executable ?? raw.shell);
|
|
@@ -138,9 +211,16 @@ export function normalizeSystemShellCommand(value) {
|
|
|
138
211
|
export function normalizeAutoClearConfig(value = {}) {
|
|
139
212
|
const raw = value && typeof value === 'object' ? value : {};
|
|
140
213
|
const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
|
|
214
|
+
const hasExplicitIdle = Number.isFinite(idleMs) && idleMs > 0;
|
|
215
|
+
// idleMs: null means "no explicit override" — callers resolve the
|
|
216
|
+
// effective window via resolveAutoClearIdleMs/provider default. `custom`
|
|
217
|
+
// tells UI whether the stored idleMs is a user override (true) or the
|
|
218
|
+
// provider-default mode (false, idleMs null).
|
|
141
219
|
return {
|
|
142
220
|
enabled: raw.enabled !== false,
|
|
143
|
-
idleMs:
|
|
221
|
+
idleMs: hasExplicitIdle ? Math.max(60_000, Math.round(idleMs)) : null,
|
|
222
|
+
custom: hasExplicitIdle,
|
|
223
|
+
providerIdleMs: normalizeAutoClearProviderIdleMs(raw.providerIdleMs ?? raw.providerDefaults ?? raw.providers),
|
|
144
224
|
};
|
|
145
225
|
}
|
|
146
226
|
|
|
@@ -155,7 +235,11 @@ export function normalizeCompactTypeSetting(value, fallback = 'semantic') {
|
|
|
155
235
|
export function normalizeCompactionConfig(value = {}, { memoryEnabled = true } = {}) {
|
|
156
236
|
const raw = value && typeof value === 'object' ? value : {};
|
|
157
237
|
let compactType = normalizeCompactTypeSetting(raw.compactType ?? raw.compact_type ?? raw.type, 'semantic');
|
|
158
|
-
|
|
238
|
+
// Memory is now always-on, so recall-fasttrack no longer downgrades to
|
|
239
|
+
// semantic. `memoryEnabled` is retained as a param for API compatibility but
|
|
240
|
+
// is intentionally ignored (fasttrack drains run on-demand regardless of the
|
|
241
|
+
// recap/background-cycle toggle).
|
|
242
|
+
void memoryEnabled;
|
|
159
243
|
return {
|
|
160
244
|
...raw,
|
|
161
245
|
auto: raw.auto !== false && raw.enabled !== false,
|
|
@@ -170,6 +254,39 @@ export function moduleEnabled(configLike, name, fallback = true) {
|
|
|
170
254
|
return fallback !== false;
|
|
171
255
|
}
|
|
172
256
|
|
|
257
|
+
// Recap toggle: gates ONLY the background memory cycles (1/2/3). The memory
|
|
258
|
+
// module itself (transcript watcher/ingest, on-demand recall/fasttrack drains)
|
|
259
|
+
// is always-on. Default enabled. Reads the dedicated `recap` section; if a
|
|
260
|
+
// legacy `modules.memory === false` flag is present it is honored as recap off
|
|
261
|
+
// (migration folds it into `recap.enabled` on config load).
|
|
262
|
+
export function recapEnabled(configLike, fallback = true) {
|
|
263
|
+
const entry = configLike?.recap;
|
|
264
|
+
if (entry && typeof entry === 'object' && entry.enabled === false) return false;
|
|
265
|
+
// Legacy fallback: pre-migration configs may still carry modules.memory=false.
|
|
266
|
+
if (configLike?.modules?.memory === false) return false;
|
|
267
|
+
if (moduleEnabled(configLike, 'memory', true) === false) return false;
|
|
268
|
+
return fallback !== false;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function setRecapEnabledInConfig(configLike, enabled) {
|
|
272
|
+
const next = { ...(configLike || {}) };
|
|
273
|
+
next.recap = { ...(next.recap || {}), enabled: enabled !== false };
|
|
274
|
+
// Drop the legacy flag if it was carrying the disabled state.
|
|
275
|
+
if (next.modules && next.modules.memory === false) {
|
|
276
|
+
const modules = { ...next.modules };
|
|
277
|
+
delete modules.memory;
|
|
278
|
+
next.modules = modules;
|
|
279
|
+
} else if (next.modules && next.modules.memory && typeof next.modules.memory === 'object') {
|
|
280
|
+
const modules = { ...next.modules };
|
|
281
|
+
const memoryMod = { ...modules.memory };
|
|
282
|
+
delete memoryMod.enabled;
|
|
283
|
+
if (Object.keys(memoryMod).length === 0) delete modules.memory;
|
|
284
|
+
else modules.memory = memoryMod;
|
|
285
|
+
next.modules = modules;
|
|
286
|
+
}
|
|
287
|
+
return next;
|
|
288
|
+
}
|
|
289
|
+
|
|
173
290
|
export function setModuleEnabledInConfig(configLike, name, enabled) {
|
|
174
291
|
const next = { ...(configLike || {}) };
|
|
175
292
|
next.modules = { ...(next.modules || {}) };
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// config-lifecycle.mjs — config reload/save/adopt family + output-style status
|
|
2
|
+
// cache, extracted from mixdog-session-runtime.mjs. Dependency-injected factory:
|
|
3
|
+
// closes over config/searchRoute mutable state via getter/setter injection
|
|
4
|
+
// (getConfig/setConfig/getSearchRoute/setSearchRoute) and shared helpers, so the
|
|
5
|
+
// facade keeps ownership of the mutable locals while the debounce/adopt logic
|
|
6
|
+
// lives here.
|
|
7
|
+
//
|
|
8
|
+
// Debounce rationale (unchanged from the original inline implementation):
|
|
9
|
+
// persisting mixdog-config.json is heavy (cross-process lock, atomic
|
|
10
|
+
// temp+rename, win32 icacls owner-only ACL). Adopt in-memory IMMEDIATELY so
|
|
11
|
+
// same-tick readers see fresh state, and DEBOUNCE the disk write so a burst of
|
|
12
|
+
// toggles collapses into one persist. Three independent debounce channels:
|
|
13
|
+
// - config save (cfgMod.saveConfig, agent-section serialize)
|
|
14
|
+
// - backend save (channel-admin setBackend, file-locked RMW)
|
|
15
|
+
// - outputStyle (sharedCfgMod.updateConfig whole-root RMW — cfgMod.saveConfig
|
|
16
|
+
// only serializes agent-section fields, so a top-level
|
|
17
|
+
// outputStyle would never reach disk via that path)
|
|
18
|
+
|
|
19
|
+
export const CONFIG_SAVE_DEBOUNCE_MS = 150;
|
|
20
|
+
|
|
21
|
+
export function createConfigLifecycle({
|
|
22
|
+
// config mutable-state injection
|
|
23
|
+
getConfig,
|
|
24
|
+
setConfig,
|
|
25
|
+
getSearchRoute,
|
|
26
|
+
setSearchRoute,
|
|
27
|
+
getConfigHasSecrets,
|
|
28
|
+
setConfigHasSecrets,
|
|
29
|
+
getRoute,
|
|
30
|
+
// shared modules / helpers
|
|
31
|
+
cfgMod,
|
|
32
|
+
sharedCfgMod,
|
|
33
|
+
setBackend,
|
|
34
|
+
setConfiguredShell,
|
|
35
|
+
normalizeSystemShellConfig,
|
|
36
|
+
normalizeSearchRouteConfig,
|
|
37
|
+
outputStyleStatus,
|
|
38
|
+
LAZY_SECRET_PROVIDERS,
|
|
39
|
+
clean,
|
|
40
|
+
resolve,
|
|
41
|
+
performanceNow = () => performance.now(),
|
|
42
|
+
STANDALONE_DATA_DIR,
|
|
43
|
+
}) {
|
|
44
|
+
// --- output-style status cache (short TTL, keyed on plugin data dir) --------
|
|
45
|
+
let outputStyleStatusCache = null;
|
|
46
|
+
let outputStyleStatusCacheAt = 0;
|
|
47
|
+
let outputStyleStatusCacheDir = '';
|
|
48
|
+
|
|
49
|
+
const getOutputStyleStatusCached = ({ fresh = false } = {}) => {
|
|
50
|
+
const dataDir = cfgMod.getPluginData?.() || STANDALONE_DATA_DIR;
|
|
51
|
+
const cacheDir = resolve(dataDir);
|
|
52
|
+
const now = performanceNow();
|
|
53
|
+
if (
|
|
54
|
+
!fresh
|
|
55
|
+
&& outputStyleStatusCache
|
|
56
|
+
&& outputStyleStatusCacheDir === cacheDir
|
|
57
|
+
&& now - outputStyleStatusCacheAt < 2500
|
|
58
|
+
) {
|
|
59
|
+
return outputStyleStatusCache;
|
|
60
|
+
}
|
|
61
|
+
outputStyleStatusCache = outputStyleStatus(dataDir);
|
|
62
|
+
outputStyleStatusCacheAt = now;
|
|
63
|
+
outputStyleStatusCacheDir = cacheDir;
|
|
64
|
+
return outputStyleStatusCache;
|
|
65
|
+
};
|
|
66
|
+
const invalidateOutputStyleStatusCache = () => {
|
|
67
|
+
outputStyleStatusCache = null;
|
|
68
|
+
outputStyleStatusCacheAt = 0;
|
|
69
|
+
outputStyleStatusCacheDir = '';
|
|
70
|
+
};
|
|
71
|
+
// In-memory seed of the status cache after an outputStyle select (avoids a
|
|
72
|
+
// second forced-fresh filesystem scan during the debounce window).
|
|
73
|
+
const seedOutputStyleStatusCache = (status) => {
|
|
74
|
+
outputStyleStatusCache = status;
|
|
75
|
+
outputStyleStatusCacheAt = performanceNow();
|
|
76
|
+
outputStyleStatusCacheDir = resolve(cfgMod.getPluginData?.() || STANDALONE_DATA_DIR);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// --- config adopt -----------------------------------------------------------
|
|
80
|
+
function adoptConfig(nextConfig, { hasSecrets = getConfigHasSecrets() } = {}) {
|
|
81
|
+
setConfig(nextConfig);
|
|
82
|
+
setConfigHasSecrets(hasSecrets === true);
|
|
83
|
+
const config = getConfig();
|
|
84
|
+
setConfiguredShell(normalizeSystemShellConfig(config.shell).command);
|
|
85
|
+
setSearchRoute(
|
|
86
|
+
normalizeSearchRouteConfig(config.searchRoute)
|
|
87
|
+
|| normalizeSearchRouteConfig(getSearchRoute()),
|
|
88
|
+
);
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// --- debounced config save --------------------------------------------------
|
|
93
|
+
let pendingConfigToSave = null;
|
|
94
|
+
let configSaveTimer = null;
|
|
95
|
+
|
|
96
|
+
function flushConfigSave() {
|
|
97
|
+
if (configSaveTimer) {
|
|
98
|
+
clearTimeout(configSaveTimer);
|
|
99
|
+
configSaveTimer = null;
|
|
100
|
+
}
|
|
101
|
+
if (pendingConfigToSave === null) return;
|
|
102
|
+
const snapshot = pendingConfigToSave;
|
|
103
|
+
pendingConfigToSave = null;
|
|
104
|
+
try {
|
|
105
|
+
cfgMod.saveConfig(snapshot);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
process.stderr.write(`[config] debounced saveConfig failed: ${err?.message || err}\n`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function saveConfigAndAdopt(nextConfig, { hasSecrets = getConfigHasSecrets() } = {}) {
|
|
112
|
+
// In-memory adopt is synchronous and first so callers that read back the
|
|
113
|
+
// value immediately (e.g. setProfile -> getProfile) see the new state.
|
|
114
|
+
const adopted = adoptConfig(nextConfig, { hasSecrets });
|
|
115
|
+
// Persist the adopted object; coalesce rapid successive changes into one
|
|
116
|
+
// disk write after CONFIG_SAVE_DEBOUNCE_MS of quiet.
|
|
117
|
+
pendingConfigToSave = getConfig();
|
|
118
|
+
if (configSaveTimer) clearTimeout(configSaveTimer);
|
|
119
|
+
configSaveTimer = setTimeout(flushConfigSave, CONFIG_SAVE_DEBOUNCE_MS);
|
|
120
|
+
configSaveTimer.unref?.();
|
|
121
|
+
return adopted;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// --- debounced backend switch ----------------------------------------------
|
|
125
|
+
let pendingBackendName = null;
|
|
126
|
+
let backendSaveTimer = null;
|
|
127
|
+
|
|
128
|
+
function flushBackendSave() {
|
|
129
|
+
if (backendSaveTimer) {
|
|
130
|
+
clearTimeout(backendSaveTimer);
|
|
131
|
+
backendSaveTimer = null;
|
|
132
|
+
}
|
|
133
|
+
if (pendingBackendName === null) return;
|
|
134
|
+
const name = pendingBackendName;
|
|
135
|
+
pendingBackendName = null;
|
|
136
|
+
try {
|
|
137
|
+
setBackend(name);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
process.stderr.write(`[channels] debounced setBackend failed: ${err?.message || err}\n`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function scheduleBackendSave(name) {
|
|
144
|
+
pendingBackendName = name;
|
|
145
|
+
if (backendSaveTimer) clearTimeout(backendSaveTimer);
|
|
146
|
+
backendSaveTimer = setTimeout(flushBackendSave, CONFIG_SAVE_DEBOUNCE_MS);
|
|
147
|
+
backendSaveTimer.unref?.();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- debounced top-level outputStyle persist -------------------------------
|
|
151
|
+
let pendingOutputStyleId = null;
|
|
152
|
+
let outputStyleSaveTimer = null;
|
|
153
|
+
|
|
154
|
+
function flushOutputStyleSave() {
|
|
155
|
+
if (outputStyleSaveTimer) {
|
|
156
|
+
clearTimeout(outputStyleSaveTimer);
|
|
157
|
+
outputStyleSaveTimer = null;
|
|
158
|
+
}
|
|
159
|
+
if (pendingOutputStyleId === null) return;
|
|
160
|
+
const styleId = pendingOutputStyleId;
|
|
161
|
+
pendingOutputStyleId = null;
|
|
162
|
+
try {
|
|
163
|
+
sharedCfgMod.updateConfig((root) => {
|
|
164
|
+
const next = { ...(root || {}), outputStyle: styleId };
|
|
165
|
+
if (next.agent && typeof next.agent === 'object' && !Array.isArray(next.agent)) {
|
|
166
|
+
const agent = { ...next.agent };
|
|
167
|
+
delete agent.outputStyle;
|
|
168
|
+
next.agent = agent;
|
|
169
|
+
}
|
|
170
|
+
return next;
|
|
171
|
+
});
|
|
172
|
+
} catch (err) {
|
|
173
|
+
process.stderr.write(`[config] debounced outputStyle save failed: ${err?.message || err}\n`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function scheduleOutputStyleSave(styleId) {
|
|
178
|
+
pendingOutputStyleId = styleId;
|
|
179
|
+
if (outputStyleSaveTimer) clearTimeout(outputStyleSaveTimer);
|
|
180
|
+
outputStyleSaveTimer = setTimeout(flushOutputStyleSave, CONFIG_SAVE_DEBOUNCE_MS);
|
|
181
|
+
outputStyleSaveTimer.unref?.();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// --- reload / ensure --------------------------------------------------------
|
|
185
|
+
function reloadFullConfig() {
|
|
186
|
+
// A pending debounced write holds the only copy of the latest change.
|
|
187
|
+
// Flush it before re-reading from disk so loadConfig() observes (and the
|
|
188
|
+
// subsequent adopt preserves) that change instead of reverting to a stale
|
|
189
|
+
// on-disk snapshot.
|
|
190
|
+
flushConfigSave();
|
|
191
|
+
return adoptConfig(cfgMod.loadConfig(), { hasSecrets: true });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function ensureFullConfig() {
|
|
195
|
+
if (getConfigHasSecrets()) return getConfig();
|
|
196
|
+
return reloadFullConfig();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function displayConfig() {
|
|
200
|
+
return getConfig();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function ensureConfigForRouteProvider() {
|
|
204
|
+
const config = getConfig();
|
|
205
|
+
const providerId = clean(getRoute().provider);
|
|
206
|
+
const providerCfg = config?.providers?.[providerId];
|
|
207
|
+
if (getConfigHasSecrets() || LAZY_SECRET_PROVIDERS.has(providerId) || providerCfg?.apiKey) {
|
|
208
|
+
return config;
|
|
209
|
+
}
|
|
210
|
+
return ensureFullConfig();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
// output-style cache
|
|
215
|
+
getOutputStyleStatusCached,
|
|
216
|
+
invalidateOutputStyleStatusCache,
|
|
217
|
+
seedOutputStyleStatusCache,
|
|
218
|
+
// adopt / save
|
|
219
|
+
adoptConfig,
|
|
220
|
+
saveConfigAndAdopt,
|
|
221
|
+
flushConfigSave,
|
|
222
|
+
flushBackendSave,
|
|
223
|
+
scheduleBackendSave,
|
|
224
|
+
flushOutputStyleSave,
|
|
225
|
+
scheduleOutputStyleSave,
|
|
226
|
+
// reload / ensure
|
|
227
|
+
reloadFullConfig,
|
|
228
|
+
ensureFullConfig,
|
|
229
|
+
displayConfig,
|
|
230
|
+
ensureConfigForRouteProvider,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -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
|
+
}
|