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
|
@@ -14,8 +14,14 @@
|
|
|
14
14
|
* `mixdog: transcript-writer: `) and suppress duplicates so a broken path
|
|
15
15
|
* cannot spam the terminal.
|
|
16
16
|
*/
|
|
17
|
-
import {
|
|
17
|
+
import { existsSync, mkdirSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import { dirname, join, resolve } from 'node:path';
|
|
19
|
+
import { appendBuffered, drainPathSync, hasInFlightWrite } from './buffered-appender.mjs';
|
|
20
|
+
|
|
21
|
+
// Rotate the JSONL transcript once it exceeds this size, keeping one prior
|
|
22
|
+
// generation (`<path>.1`). Checked on each append; the check itself is a
|
|
23
|
+
// cheap statSync so no extra timer/interval is needed.
|
|
24
|
+
const TRANSCRIPT_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
19
25
|
|
|
20
26
|
// Byte-identical to cwdToProjectSlug() in
|
|
21
27
|
// src/runtime/channels/lib/session-discovery.mjs. Inlined to avoid a
|
|
@@ -56,10 +62,31 @@ export function createTranscriptWriter({ mixdogHome, sessionId, cwd, pid } = {})
|
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
function rotateIfNeeded() {
|
|
66
|
+
try {
|
|
67
|
+
if (!existsSync(transcriptPath)) return;
|
|
68
|
+
const { size } = statSync(transcriptPath);
|
|
69
|
+
if (size < TRANSCRIPT_ROTATE_BYTES) return;
|
|
70
|
+
// An async appendFile may be in flight for this path; renaming out
|
|
71
|
+
// from under it races the write on Windows. Skip this round and
|
|
72
|
+
// retry rotation on the next append instead.
|
|
73
|
+
if (hasInFlightWrite(transcriptPath)) return;
|
|
74
|
+
// Force any still-buffered bytes onto disk before renaming, so the
|
|
75
|
+
// rotated-out file ends with everything queued for it and the fresh
|
|
76
|
+
// file post-rename doesn't inherit stale in-memory chunks.
|
|
77
|
+
drainPathSync(transcriptPath);
|
|
78
|
+
const rotatedPath = `${transcriptPath}.1`;
|
|
79
|
+
try { renameSync(transcriptPath, rotatedPath); } catch (err) { logOnce(err); }
|
|
80
|
+
} catch (err) {
|
|
81
|
+
logOnce(err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
59
85
|
function appendLine(entry) {
|
|
60
86
|
ensureProjectDir();
|
|
87
|
+
rotateIfNeeded();
|
|
61
88
|
try {
|
|
62
|
-
|
|
89
|
+
appendBuffered(transcriptPath, `${JSON.stringify(entry)}\n`);
|
|
63
90
|
} catch (err) {
|
|
64
91
|
logOnce(err);
|
|
65
92
|
}
|
|
@@ -117,10 +144,33 @@ export function createTranscriptWriter({ mixdogHome, sessionId, cwd, pid } = {})
|
|
|
117
144
|
});
|
|
118
145
|
}
|
|
119
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
|
+
|
|
120
169
|
return {
|
|
121
170
|
transcriptPath,
|
|
122
171
|
sessionRecordPath,
|
|
123
172
|
writeSessionRecord,
|
|
173
|
+
ensureTranscriptFile,
|
|
124
174
|
refresh,
|
|
125
175
|
appendAssistant,
|
|
126
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
|
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// Config normalization helpers: default provider, presets/routes, module
|
|
2
|
+
// enable flags, shell/auto-clear/compaction config, and duration formatting.
|
|
3
|
+
import { clean, hasOwn } from './session-text.mjs';
|
|
4
|
+
import { normalizeEffortInput, normalizeSavedEffort } from './effort.mjs';
|
|
5
|
+
import { routeFastKey, fastPreferenceFor } from './model-capabilities.mjs';
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_PROVIDER = 'anthropic-oauth';
|
|
8
|
+
export const DEFAULT_MODEL = '';
|
|
9
|
+
|
|
10
|
+
// Resolve the provider to use when a route carries no explicit provider.
|
|
11
|
+
// Priority: config.defaultProvider (when it names a known provider) > DEFAULT_PROVIDER.
|
|
12
|
+
export function makeResolveDefaultProvider(isKnownProvider) {
|
|
13
|
+
return function resolveDefaultProvider(config) {
|
|
14
|
+
const configured = clean(config?.defaultProvider);
|
|
15
|
+
if (configured && isKnownProvider(configured)) return configured;
|
|
16
|
+
return DEFAULT_PROVIDER;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function modelSettingsFor(config, provider, model) {
|
|
21
|
+
const key = routeFastKey(provider, model);
|
|
22
|
+
const value = key ? config?.modelSettings?.[key] : null;
|
|
23
|
+
return value && typeof value === 'object' ? value : {};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function findPreset(config, key) {
|
|
27
|
+
const wanted = clean(key).toLowerCase();
|
|
28
|
+
if (!wanted) return null;
|
|
29
|
+
const presets = Array.isArray(config?.presets) ? config.presets : [];
|
|
30
|
+
return presets.find((p) => {
|
|
31
|
+
const id = clean(p?.id).toLowerCase();
|
|
32
|
+
const name = clean(p?.name).toLowerCase();
|
|
33
|
+
return id === wanted || name === wanted;
|
|
34
|
+
}) || null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function makeResolveRoute(resolveDefaultProvider) {
|
|
38
|
+
return function resolveRoute(config, { provider, model, effort, fast } = {}) {
|
|
39
|
+
const explicitProvider = clean(provider);
|
|
40
|
+
const explicitModel = clean(model);
|
|
41
|
+
const hasExplicitEffort = effort !== undefined;
|
|
42
|
+
const explicitEffort = hasExplicitEffort ? normalizeEffortInput(effort) : undefined;
|
|
43
|
+
const hasExplicitFast = fast !== undefined;
|
|
44
|
+
const explicitFast = fast === true;
|
|
45
|
+
|
|
46
|
+
if (explicitModel && !explicitProvider) {
|
|
47
|
+
const preset = findPreset(config, explicitModel);
|
|
48
|
+
if (preset) {
|
|
49
|
+
const p = clean(preset.provider) || DEFAULT_PROVIDER;
|
|
50
|
+
const m = clean(preset.model) || DEFAULT_MODEL;
|
|
51
|
+
const saved = modelSettingsFor(config, p, m);
|
|
52
|
+
return {
|
|
53
|
+
provider: p,
|
|
54
|
+
model: m,
|
|
55
|
+
preset,
|
|
56
|
+
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
|
|
57
|
+
fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!explicitProvider && !explicitModel) {
|
|
63
|
+
const defaultKey = config?.default;
|
|
64
|
+
const preset = findPreset(config, defaultKey);
|
|
65
|
+
if (preset) {
|
|
66
|
+
const p = clean(preset.provider) || DEFAULT_PROVIDER;
|
|
67
|
+
const m = clean(preset.model) || DEFAULT_MODEL;
|
|
68
|
+
const saved = modelSettingsFor(config, p, m);
|
|
69
|
+
return {
|
|
70
|
+
provider: p,
|
|
71
|
+
model: m,
|
|
72
|
+
preset,
|
|
73
|
+
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort ?? preset.effort),
|
|
74
|
+
fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : (preset.fast === true || fastPreferenceFor(config, p, m))),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const p = explicitProvider || resolveDefaultProvider(config);
|
|
80
|
+
const m = explicitModel || DEFAULT_MODEL;
|
|
81
|
+
const saved = modelSettingsFor(config, p, m);
|
|
82
|
+
return {
|
|
83
|
+
provider: p,
|
|
84
|
+
model: m,
|
|
85
|
+
preset: null,
|
|
86
|
+
effort: hasExplicitEffort ? explicitEffort : normalizeSavedEffort(saved.effort),
|
|
87
|
+
fast: hasExplicitFast ? explicitFast : (hasOwn(saved, 'fast') ? saved.fast === true : fastPreferenceFor(config, p, m)),
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function isLikelyRawModelId(value) {
|
|
93
|
+
const model = clean(value);
|
|
94
|
+
if (!model || model.length > 160) return false;
|
|
95
|
+
if (/\s/.test(model)) return false;
|
|
96
|
+
return /^[A-Za-z0-9][A-Za-z0-9._:/@+-]*$/.test(model);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function validateRequestedModelSelector(config, requested = {}) {
|
|
100
|
+
const model = clean(requested.model);
|
|
101
|
+
if (!model) return;
|
|
102
|
+
if (findPreset(config, model)) return;
|
|
103
|
+
if (isLikelyRawModelId(model)) return;
|
|
104
|
+
throw new Error(`Invalid model selector "${model}". Use a preset or a model id; free-form text cannot be used as a model.`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function ensureProviderEnabled(config, provider) {
|
|
108
|
+
const providers = { ...(config?.providers || {}) };
|
|
109
|
+
providers[provider] = { ...(providers[provider] || {}), enabled: true };
|
|
110
|
+
return providers;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const AUTO_CLEAR_DEFAULT_IDLE_MS = 60 * 60 * 1000;
|
|
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, Gemini, xAI and
|
|
118
|
+
// Mistral caches run ~1h, Groq's implicit cache persists ~2h, OpenAI/
|
|
119
|
+
// DeepSeek key-prefix/implicit caches persist far longer (~24h), and OpenAI
|
|
120
|
+
// OAuth's in-memory server cache is short-lived (~5-10min), so we round up
|
|
121
|
+
// slightly to 10m to avoid sweeping a still-warm cache. Unknown/
|
|
122
|
+
// unrecognized providers fall back to the 'default' 1h entry.
|
|
123
|
+
export const AUTO_CLEAR_PROVIDER_IDLE_MS = Object.freeze({
|
|
124
|
+
'anthropic': 60 * 60 * 1000,
|
|
125
|
+
'anthropic-oauth': 60 * 60 * 1000,
|
|
126
|
+
'gemini': 60 * 60 * 1000,
|
|
127
|
+
'groq': 2 * 60 * 60 * 1000,
|
|
128
|
+
'openai': 24 * 60 * 60 * 1000,
|
|
129
|
+
'deepseek': 24 * 60 * 60 * 1000,
|
|
130
|
+
'xai': 60 * 60 * 1000,
|
|
131
|
+
'openai-oauth': 10 * 60 * 1000,
|
|
132
|
+
'mistral': 60 * 60 * 1000,
|
|
133
|
+
'default': AUTO_CLEAR_DEFAULT_IDLE_MS,
|
|
134
|
+
});
|
|
135
|
+
export const AUTO_CLEAR_UNKNOWN_PROVIDER_IDLE_MS = AUTO_CLEAR_PROVIDER_IDLE_MS.default;
|
|
136
|
+
|
|
137
|
+
// Resolve the auto-clear idle-sweep threshold for a given provider id.
|
|
138
|
+
// Unknown/missing providers fall back to the 'default' 1h entry (above).
|
|
139
|
+
export function autoClearIdleMsForProvider(provider) {
|
|
140
|
+
const key = clean(provider).toLowerCase();
|
|
141
|
+
return AUTO_CLEAR_PROVIDER_IDLE_MS[key] ?? AUTO_CLEAR_PROVIDER_IDLE_MS.default;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Resolve the effective auto-clear idle window for a config + provider:
|
|
145
|
+
// an explicit user-set idleMs (config.autoClear.idleMs) always wins; else
|
|
146
|
+
// fall back to the provider's default; else the global 1h default.
|
|
147
|
+
export function resolveAutoClearIdleMs(config, provider) {
|
|
148
|
+
const raw = config?.autoClear && typeof config.autoClear === 'object' ? config.autoClear : {};
|
|
149
|
+
const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
|
|
150
|
+
if (Number.isFinite(idleMs) && idleMs > 0) return Math.max(60_000, Math.round(idleMs));
|
|
151
|
+
return autoClearIdleMsForProvider(provider);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function normalizeSystemShellConfig(value = {}) {
|
|
155
|
+
const raw = value && typeof value === 'object' ? value : {};
|
|
156
|
+
const command = clean(raw.command ?? raw.path ?? raw.executable ?? raw.shell);
|
|
157
|
+
const envCommand = clean(process.env.MIXDOG_SHELL);
|
|
158
|
+
return {
|
|
159
|
+
command,
|
|
160
|
+
effective: command || envCommand || '',
|
|
161
|
+
source: command ? 'config' : (envCommand ? 'env' : 'auto'),
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function normalizeSystemShellCommand(value) {
|
|
166
|
+
const command = clean(value).replace(/^auto$/i, '').replace(/^['"](.+)['"]$/, '$1').trim();
|
|
167
|
+
if (!command) return '';
|
|
168
|
+
if (process.platform === 'win32') {
|
|
169
|
+
const stem = command.split(/[\\/]/).pop().toLowerCase().replace(/\.exe$/, '');
|
|
170
|
+
if (stem !== 'powershell' && stem !== 'pwsh') {
|
|
171
|
+
throw new Error('system shell command must be powershell.exe or pwsh on Windows');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return command;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function normalizeAutoClearConfig(value = {}) {
|
|
178
|
+
const raw = value && typeof value === 'object' ? value : {};
|
|
179
|
+
const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
|
|
180
|
+
const hasExplicitIdle = Number.isFinite(idleMs) && idleMs > 0;
|
|
181
|
+
// idleMs: null means "no explicit override" — callers resolve the
|
|
182
|
+
// effective window via resolveAutoClearIdleMs/provider default. `custom`
|
|
183
|
+
// tells UI whether the stored idleMs is a user override (true) or the
|
|
184
|
+
// provider-default mode (false, idleMs null).
|
|
185
|
+
return {
|
|
186
|
+
enabled: raw.enabled !== false,
|
|
187
|
+
idleMs: hasExplicitIdle ? Math.max(60_000, Math.round(idleMs)) : null,
|
|
188
|
+
custom: hasExplicitIdle,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function normalizeCompactTypeSetting(value, fallback = 'semantic') {
|
|
193
|
+
const raw = clean(value).toLowerCase().replace(/_/g, '-');
|
|
194
|
+
if (!raw) return fallback;
|
|
195
|
+
if (raw === '1' || raw === 'type1' || raw === 'type-1' || raw === 'semantic' || raw === 'summary' || raw === 'default') return 'semantic';
|
|
196
|
+
if (raw === '2' || raw === 'type2' || raw === 'type-2' || raw === 'recall' || raw === 'recall-fast' || raw === 'recall-fasttrack' || raw === 'recall-fast-track' || raw === 'fasttrack' || raw === 'fast-track') return 'recall-fasttrack';
|
|
197
|
+
return fallback;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function normalizeCompactionConfig(value = {}, { memoryEnabled = true } = {}) {
|
|
201
|
+
const raw = value && typeof value === 'object' ? value : {};
|
|
202
|
+
let compactType = normalizeCompactTypeSetting(raw.compactType ?? raw.compact_type ?? raw.type, 'semantic');
|
|
203
|
+
// Memory is now always-on, so recall-fasttrack no longer downgrades to
|
|
204
|
+
// semantic. `memoryEnabled` is retained as a param for API compatibility but
|
|
205
|
+
// is intentionally ignored (fasttrack drains run on-demand regardless of the
|
|
206
|
+
// recap/background-cycle toggle).
|
|
207
|
+
void memoryEnabled;
|
|
208
|
+
return {
|
|
209
|
+
...raw,
|
|
210
|
+
auto: raw.auto !== false && raw.enabled !== false,
|
|
211
|
+
type: compactType,
|
|
212
|
+
compactType,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function moduleEnabled(configLike, name, fallback = true) {
|
|
217
|
+
const entry = configLike?.modules?.[name];
|
|
218
|
+
if (entry && typeof entry === 'object' && entry.enabled === false) return false;
|
|
219
|
+
return fallback !== false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Recap toggle: gates ONLY the background memory cycles (1/2/3). The memory
|
|
223
|
+
// module itself (transcript watcher/ingest, on-demand recall/fasttrack drains)
|
|
224
|
+
// is always-on. Default enabled. Reads the dedicated `recap` section; if a
|
|
225
|
+
// legacy `modules.memory === false` flag is present it is honored as recap off
|
|
226
|
+
// (migration folds it into `recap.enabled` on config load).
|
|
227
|
+
export function recapEnabled(configLike, fallback = true) {
|
|
228
|
+
const entry = configLike?.recap;
|
|
229
|
+
if (entry && typeof entry === 'object' && entry.enabled === false) return false;
|
|
230
|
+
// Legacy fallback: pre-migration configs may still carry modules.memory=false.
|
|
231
|
+
if (configLike?.modules?.memory === false) return false;
|
|
232
|
+
if (moduleEnabled(configLike, 'memory', true) === false) return false;
|
|
233
|
+
return fallback !== false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function setRecapEnabledInConfig(configLike, enabled) {
|
|
237
|
+
const next = { ...(configLike || {}) };
|
|
238
|
+
next.recap = { ...(next.recap || {}), enabled: enabled !== false };
|
|
239
|
+
// Drop the legacy flag if it was carrying the disabled state.
|
|
240
|
+
if (next.modules && next.modules.memory === false) {
|
|
241
|
+
const modules = { ...next.modules };
|
|
242
|
+
delete modules.memory;
|
|
243
|
+
next.modules = modules;
|
|
244
|
+
} else if (next.modules && next.modules.memory && typeof next.modules.memory === 'object') {
|
|
245
|
+
const modules = { ...next.modules };
|
|
246
|
+
const memoryMod = { ...modules.memory };
|
|
247
|
+
delete memoryMod.enabled;
|
|
248
|
+
if (Object.keys(memoryMod).length === 0) delete modules.memory;
|
|
249
|
+
else modules.memory = memoryMod;
|
|
250
|
+
next.modules = modules;
|
|
251
|
+
}
|
|
252
|
+
return next;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export function setModuleEnabledInConfig(configLike, name, enabled) {
|
|
256
|
+
const next = { ...(configLike || {}) };
|
|
257
|
+
next.modules = { ...(next.modules || {}) };
|
|
258
|
+
next.modules[name] = {
|
|
259
|
+
...(next.modules[name] || {}),
|
|
260
|
+
enabled: enabled !== false,
|
|
261
|
+
};
|
|
262
|
+
return next;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function formatDurationMs(ms) {
|
|
266
|
+
const value = Math.max(0, Number(ms) || 0);
|
|
267
|
+
if (value % 3_600_000 === 0) return `${value / 3_600_000}h`;
|
|
268
|
+
if (value % 60_000 === 0) return `${value / 60_000}m`;
|
|
269
|
+
return `${Math.round(value / 1000)}s`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function parseDurationMs(input) {
|
|
273
|
+
const text = clean(input).toLowerCase();
|
|
274
|
+
if (!text) return null;
|
|
275
|
+
const match = /^(\d+(?:\.\d+)?)(ms|s|m|h)?$/.exec(text);
|
|
276
|
+
if (!match) return null;
|
|
277
|
+
const n = Number(match[1]);
|
|
278
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
279
|
+
const unit = match[2] || 'm';
|
|
280
|
+
const mult = unit === 'h' ? 3_600_000 : unit === 'm' ? 60_000 : unit === 's' ? 1000 : 1;
|
|
281
|
+
return Math.max(60_000, Math.round(n * mult));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// A resolved model meta carries catalog-derived fields (contextWindow, pricing,
|
|
285
|
+
// capabilities, …). The lookupModelMeta() fallback for an unknown id is the
|
|
286
|
+
// bare shape `{ id, provider }`, so "more than id/provider" reliably tells a
|
|
287
|
+
// real catalog hit apart from that placeholder.
|
|
288
|
+
export function modelMetaLooksResolved(meta) {
|
|
289
|
+
if (!meta || typeof meta !== 'object') return false;
|
|
290
|
+
return Object.keys(meta).some((key) => key !== 'id' && key !== 'provider');
|
|
291
|
+
}
|
|
@@ -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
|
+
}
|