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
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
4
|
-
remapCanonicalStatuslineTruecolor,
|
|
5
|
-
normalizeStatuslineAnsi,
|
|
6
|
-
statuslineFooterCacheKey,
|
|
7
|
-
statuslineFooterIdentityChanged,
|
|
8
|
-
isResetStatsState,
|
|
9
|
-
STATUSLINE_CANONICAL_TRUECOLOR,
|
|
10
|
-
} from './statusline-ansi-bridge.mjs';
|
|
11
|
-
import { theme, setThemeSetting } from './theme.mjs';
|
|
12
|
-
|
|
13
|
-
function sgr38(r, g, b) {
|
|
14
|
-
return `\x1b[38;2;${r};${g};${b}m`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function statusColorsFromTheme() {
|
|
18
|
-
const ansiRgb = (value, fallback) => {
|
|
19
|
-
const match = /^rgb\((\d+),(\d+),(\d+)\)$/.exec(String(value || '').replace(/\s+/g, ''));
|
|
20
|
-
if (!match) return fallback;
|
|
21
|
-
return sgr38(match[1], match[2], match[3]);
|
|
22
|
-
};
|
|
23
|
-
return {
|
|
24
|
-
STATUS: ansiRgb(theme.statusText, sgr38(198, 198, 198)),
|
|
25
|
-
SUBTLE: ansiRgb(theme.statusSubtle, sgr38(136, 136, 136)),
|
|
26
|
-
SUCCESS: ansiRgb(theme.success, sgr38(0, 170, 75)),
|
|
27
|
-
WARNING: ansiRgb(theme.warning, sgr38(255, 193, 7)),
|
|
28
|
-
ERROR: ansiRgb(theme.error, sgr38(220, 70, 88)),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
test('remaps canonical truecolor sequences from ui/statusline.mjs', () => {
|
|
33
|
-
const colors = statusColorsFromTheme();
|
|
34
|
-
const subtle = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle);
|
|
35
|
-
const success = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.success);
|
|
36
|
-
const raw = `${subtle}│\x1b[0m ${success}42%`;
|
|
37
|
-
const out = remapCanonicalStatuslineTruecolor(raw, colors);
|
|
38
|
-
assert.ok(!out.includes('38;2;136;136;136'), 'subtle canonical RGB should be replaced');
|
|
39
|
-
assert.ok(!out.includes('38;2;0;170;75'), 'success canonical RGB should be replaced');
|
|
40
|
-
assert.ok(out.includes(colors.SUBTLE));
|
|
41
|
-
assert.ok(out.includes(colors.SUCCESS));
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test('normalizeStatuslineAnsi maps bold and legacy SGR plus truecolor', () => {
|
|
45
|
-
const colors = statusColorsFromTheme();
|
|
46
|
-
const raw = `\x1b[1mmodel\x1b[0m \x1b[33m5H\x1b[0m ${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.error)}quota`;
|
|
47
|
-
const out = normalizeStatuslineAnsi(raw, colors);
|
|
48
|
-
assert.ok(out.includes(colors.STATUS));
|
|
49
|
-
assert.ok(out.includes(colors.WARNING));
|
|
50
|
-
assert.ok(out.includes(colors.ERROR));
|
|
51
|
-
assert.ok(!out.includes('\x1b[33m'));
|
|
52
|
-
assert.ok(!out.includes('38;2;220;70;88'));
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('normalizeStatuslineAnsi preserves leading diamond glyph', () => {
|
|
56
|
-
const colors = statusColorsFromTheme();
|
|
57
|
-
const subtle = sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle);
|
|
58
|
-
const raw = `${subtle}◆\x1b[0m \x1b[1mgpt-4o\x1b[0m`;
|
|
59
|
-
const out = normalizeStatuslineAnsi(raw, colors);
|
|
60
|
-
assert.ok(out.includes('◆'), 'visible ◆ must remain');
|
|
61
|
-
assert.ok(out.includes('gpt-4o'));
|
|
62
|
-
assert.ok(out.indexOf('◆') < out.indexOf('gpt-4o'));
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test('retimes bare values after reset without clobbering explicit segment colors', () => {
|
|
66
|
-
const colors = statusColorsFromTheme();
|
|
67
|
-
const subtle = colors.SUBTLE;
|
|
68
|
-
const success = colors.SUCCESS;
|
|
69
|
-
const warning = colors.WARNING;
|
|
70
|
-
const reset = '\x1b[0m';
|
|
71
|
-
const creditSeg = `${subtle}Credit${reset} $12.34`;
|
|
72
|
-
const outCredit = normalizeStatuslineAnsi(creditSeg, colors, { reset });
|
|
73
|
-
assert.ok(outCredit.includes('$12.34'));
|
|
74
|
-
const moneyIdx = outCredit.indexOf('$12.34');
|
|
75
|
-
assert.ok(moneyIdx > 0);
|
|
76
|
-
assert.ok(outCredit.slice(0, moneyIdx).includes(colors.STATUS), 'bare credit amount uses status text');
|
|
77
|
-
|
|
78
|
-
const usedLimit = `${subtle}5H${reset} $3.00/$10.00`;
|
|
79
|
-
const outUsed = normalizeStatuslineAnsi(usedLimit, colors, { reset });
|
|
80
|
-
assert.ok(outUsed.includes('$3.00/$10.00'));
|
|
81
|
-
assert.ok(outUsed.slice(0, outUsed.indexOf('$3.00')).includes(colors.STATUS));
|
|
82
|
-
|
|
83
|
-
const colored = `${subtle}5H${reset} ${warning}est $5.00${reset}`;
|
|
84
|
-
const outColored = normalizeStatuslineAnsi(colored, colors, { reset });
|
|
85
|
-
assert.ok(outColored.includes(warning), 'warning color on value must remain');
|
|
86
|
-
const estIdx = outColored.indexOf('est');
|
|
87
|
-
assert.ok(estIdx > 0);
|
|
88
|
-
assert.ok(outColored.slice(0, estIdx).includes(warning));
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('statuslineFooterCacheKey changes when session or route identity changes', () => {
|
|
92
|
-
const a = statuslineFooterCacheKey({ sessionId: 's1', provider: 'p', model: 'm1' });
|
|
93
|
-
const b = statuslineFooterCacheKey({ sessionId: 's2', provider: 'p', model: 'm1' });
|
|
94
|
-
const c = statuslineFooterCacheKey({ sessionId: 's1', provider: 'p', model: 'm2' });
|
|
95
|
-
assert.notEqual(a, b);
|
|
96
|
-
assert.notEqual(a, c);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test('statusline footer identity tracks context display boundary fields', () => {
|
|
100
|
-
const base = {
|
|
101
|
-
sessionId: 's1',
|
|
102
|
-
provider: 'p',
|
|
103
|
-
model: 'm',
|
|
104
|
-
contextWindow: 1_000_000,
|
|
105
|
-
displayContextWindow: 900_000,
|
|
106
|
-
rawContextWindow: 1_000_000,
|
|
107
|
-
compactBoundaryTokens: 900_000,
|
|
108
|
-
autoCompactTokenLimit: 810_000,
|
|
109
|
-
stats: { currentEstimatedContextTokens: 10_000 },
|
|
110
|
-
};
|
|
111
|
-
const changedDisplay = { ...base, displayContextWindow: 800_000 };
|
|
112
|
-
const changedBoundary = { ...base, compactBoundaryTokens: 800_000 };
|
|
113
|
-
const changedTrigger = { ...base, autoCompactTokenLimit: 720_000 };
|
|
114
|
-
|
|
115
|
-
assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedDisplay));
|
|
116
|
-
assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedBoundary));
|
|
117
|
-
assert.notEqual(statuslineFooterCacheKey(base), statuslineFooterCacheKey(changedTrigger));
|
|
118
|
-
assert.equal(statuslineFooterIdentityChanged(changedDisplay, base), true);
|
|
119
|
-
assert.equal(statuslineFooterIdentityChanged(changedBoundary, base), true);
|
|
120
|
-
assert.equal(statuslineFooterIdentityChanged(changedTrigger, base), true);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test('stats reset transition is footer identity change (cache must not reuse on /theme)', () => {
|
|
124
|
-
const activeStats = {
|
|
125
|
-
currentContextTokens: 1200,
|
|
126
|
-
inputTokens: 800,
|
|
127
|
-
turns: 2,
|
|
128
|
-
};
|
|
129
|
-
const resetStats = {
|
|
130
|
-
currentContextTokens: 0,
|
|
131
|
-
currentEstimatedContextTokens: 0,
|
|
132
|
-
inputTokens: 0,
|
|
133
|
-
latestInputTokens: 0,
|
|
134
|
-
promptTokens: 0,
|
|
135
|
-
turns: 0,
|
|
136
|
-
};
|
|
137
|
-
assert.equal(isResetStatsState(resetStats), true);
|
|
138
|
-
assert.equal(isResetStatsState(activeStats), false);
|
|
139
|
-
const sameRoute = { sessionId: 's1', provider: 'p', model: 'm', stats: resetStats };
|
|
140
|
-
const lastRoute = { sessionId: 's1', provider: 'p', model: 'm', stats: activeStats };
|
|
141
|
-
assert.equal(statuslineFooterCacheKey(sameRoute), statuslineFooterCacheKey(lastRoute));
|
|
142
|
-
assert.equal(statuslineFooterIdentityChanged(sameRoute, lastRoute), true);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('theme switch changes normalized output when raw line is canonical', () => {
|
|
146
|
-
setThemeSetting('basic', { persist: false });
|
|
147
|
-
const colorsA = statusColorsFromTheme();
|
|
148
|
-
const raw = `${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.success)}▓▓\x1b[0m ${sgr38(...STATUSLINE_CANONICAL_TRUECOLOR.subtle)}░░`;
|
|
149
|
-
const tonedA = normalizeStatuslineAnsi(raw, colorsA);
|
|
150
|
-
|
|
151
|
-
setThemeSetting('teal', { persist: false });
|
|
152
|
-
const colorsB = statusColorsFromTheme();
|
|
153
|
-
const tonedB = normalizeStatuslineAnsi(raw, colorsB);
|
|
154
|
-
|
|
155
|
-
assert.notEqual(tonedA, tonedB);
|
|
156
|
-
assert.ok(tonedB.includes(colorsB.SUCCESS));
|
|
157
|
-
assert.ok(tonedB.includes(colorsB.SUBTLE));
|
|
158
|
-
setThemeSetting('basic', { persist: false });
|
|
159
|
-
});
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
4
|
-
formatHookDenialDetail,
|
|
5
|
-
hasUsefulFailedToolResultBody,
|
|
6
|
-
isFullyFailedToolBatch,
|
|
7
|
-
isHookApprovalDenialToolItem,
|
|
8
|
-
isHookApprovalDenialToolResult,
|
|
9
|
-
shouldSuppressFullyFailedToolItem,
|
|
10
|
-
} from './transcript-tool-failures.mjs';
|
|
11
|
-
|
|
12
|
-
const hookDeniedItem = {
|
|
13
|
-
kind: 'tool',
|
|
14
|
-
name: 'shell',
|
|
15
|
-
args: { command: 'rm -rf /' },
|
|
16
|
-
result: 'Error: tool "shell" denied by hook: approval required but no approval UI is available (policy)',
|
|
17
|
-
isError: true,
|
|
18
|
-
count: 1,
|
|
19
|
-
completedCount: 1,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const noisyFailedItem = {
|
|
23
|
-
kind: 'tool',
|
|
24
|
-
name: 'grep',
|
|
25
|
-
args: { pattern: 'foo' },
|
|
26
|
-
result: 'no matches',
|
|
27
|
-
isError: true,
|
|
28
|
-
count: 1,
|
|
29
|
-
completedCount: 1,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const emptyFailedItem = {
|
|
33
|
-
kind: 'tool',
|
|
34
|
-
name: 'grep',
|
|
35
|
-
args: { pattern: 'foo' },
|
|
36
|
-
result: null,
|
|
37
|
-
rawResult: null,
|
|
38
|
-
isError: true,
|
|
39
|
-
count: 1,
|
|
40
|
-
completedCount: 1,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const failedTaskJsonStringArgs = {
|
|
44
|
-
kind: 'tool',
|
|
45
|
-
name: 'shell',
|
|
46
|
-
args: '{"task_id":"t1","status":"failed","error":"boom"}',
|
|
47
|
-
result: null,
|
|
48
|
-
rawResult: null,
|
|
49
|
-
isError: true,
|
|
50
|
-
count: 1,
|
|
51
|
-
completedCount: 1,
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const failedTaskInputWrapperArgs = {
|
|
55
|
-
kind: 'tool',
|
|
56
|
-
name: 'shell',
|
|
57
|
-
args: { input: { task_id: 't2', status: 'timeout' } },
|
|
58
|
-
result: null,
|
|
59
|
-
rawResult: null,
|
|
60
|
-
isError: true,
|
|
61
|
-
count: 1,
|
|
62
|
-
completedCount: 1,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
test('detects hook denial result text', () => {
|
|
66
|
-
assert.equal(
|
|
67
|
-
isHookApprovalDenialToolResult('Error: tool "read" denied by hook: blocked'),
|
|
68
|
-
true,
|
|
69
|
-
);
|
|
70
|
-
assert.equal(
|
|
71
|
-
isHookApprovalDenialToolResult('Error: tool "shell" denied by hook: approval required but no approval UI is available'),
|
|
72
|
-
true,
|
|
73
|
-
);
|
|
74
|
-
assert.equal(isHookApprovalDenialToolResult('grep: no matches'), false);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('fully-failed hook denials are not suppressed', () => {
|
|
78
|
-
assert.equal(isFullyFailedToolBatch(hookDeniedItem), true);
|
|
79
|
-
assert.equal(isHookApprovalDenialToolItem(hookDeniedItem), true);
|
|
80
|
-
assert.equal(shouldSuppressFullyFailedToolItem(hookDeniedItem), false);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test('fully-failed non-hook errors with visible body stay visible', () => {
|
|
84
|
-
assert.equal(isFullyFailedToolBatch(noisyFailedItem), true);
|
|
85
|
-
assert.equal(hasUsefulFailedToolResultBody(noisyFailedItem), true);
|
|
86
|
-
assert.equal(shouldSuppressFullyFailedToolItem(noisyFailedItem), false);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('fully-failed non-hook errors without useful body stay suppressed', () => {
|
|
90
|
-
assert.equal(isFullyFailedToolBatch(emptyFailedItem), true);
|
|
91
|
-
assert.equal(hasUsefulFailedToolResultBody(emptyFailedItem), false);
|
|
92
|
-
assert.equal(shouldSuppressFullyFailedToolItem(emptyFailedItem), true);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('failed background task with JSON-string args is not suppressed', () => {
|
|
96
|
-
assert.equal(isFullyFailedToolBatch(failedTaskJsonStringArgs), false);
|
|
97
|
-
assert.equal(shouldSuppressFullyFailedToolItem(failedTaskJsonStringArgs), false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test('failed background task with input-wrapped args is not suppressed', () => {
|
|
101
|
-
assert.equal(isFullyFailedToolBatch(failedTaskInputWrapperArgs), false);
|
|
102
|
-
assert.equal(shouldSuppressFullyFailedToolItem(failedTaskInputWrapperArgs), false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('formatHookDenialDetail strips tool prefix', () => {
|
|
106
|
-
assert.equal(
|
|
107
|
-
formatHookDenialDetail(hookDeniedItem.result),
|
|
108
|
-
'denied by hook: approval required but no approval UI is available (policy)',
|
|
109
|
-
);
|
|
110
|
-
});
|
|
111
|
-
|
package/src/ui/markdown.test.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { env } from 'node:process';
|
|
4
|
-
import { renderMarkdown } from './markdown.mjs';
|
|
5
|
-
import { refreshColorSupport, stripAnsi } from './ansi.mjs';
|
|
6
|
-
|
|
7
|
-
test.before(() => {
|
|
8
|
-
env.FORCE_COLOR = '1';
|
|
9
|
-
refreshColorSupport();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test('renderMarkdown never throws on garbage input', () => {
|
|
13
|
-
assert.doesNotThrow(() => renderMarkdown('**unclosed `fence\n> quote\n'));
|
|
14
|
-
assert.equal(renderMarkdown(null), '');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('diff fenced blocks color hunks and +/- lines', () => {
|
|
18
|
-
const src = [
|
|
19
|
-
'```diff',
|
|
20
|
-
'--- a/file.mjs',
|
|
21
|
-
'+++ b/file.mjs',
|
|
22
|
-
'@@ -1,2 +1,2 @@',
|
|
23
|
-
'-old',
|
|
24
|
-
'+new',
|
|
25
|
-
'```',
|
|
26
|
-
].join('\n');
|
|
27
|
-
const out = renderMarkdown(src);
|
|
28
|
-
assert.match(out, /\x1b\[38;2;204;157;44/);
|
|
29
|
-
assert.match(out, /\x1b\[38;2;0;170;75/);
|
|
30
|
-
assert.match(out, /\x1b\[38;2;220;70;88/);
|
|
31
|
-
assert.ok(stripAnsi(out).includes('diff'));
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('inline code and headings use Mixdog accent colors', () => {
|
|
35
|
-
const out = renderMarkdown('# Title\nUse `code` here.');
|
|
36
|
-
assert.match(out, /\x1b\[38;2;215;119;87/);
|
|
37
|
-
assert.match(out, /\x1b\[38;2;138;190;183/);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('longer fence opener does not close on shorter backtick line inside block', () => {
|
|
41
|
-
const src = ['````', '```', 'still inside', '````'].join('\n');
|
|
42
|
-
const plain = stripAnsi(renderMarkdown(src));
|
|
43
|
-
assert.ok(plain.includes('still inside'));
|
|
44
|
-
assert.ok(!plain.includes('```\nstill'));
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('mixed backtick-tilde closer does not end backtick fence', () => {
|
|
48
|
-
const src = ['```', '``~', 'still inside', '```'].join('\n');
|
|
49
|
-
const plain = stripAnsi(renderMarkdown(src));
|
|
50
|
-
assert.ok(plain.includes('still inside'));
|
|
51
|
-
assert.ok(plain.includes('``~'));
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('fenced code block body rows include right border', () => {
|
|
55
|
-
const out = stripAnsi(renderMarkdown('```js\nx\n```'));
|
|
56
|
-
const rows = out.split('\n').filter((line) => line.startsWith('│ '));
|
|
57
|
-
assert.ok(rows.length >= 1);
|
|
58
|
-
for (const row of rows) {
|
|
59
|
-
assert.match(row, /│\s*$/);
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('inline code inside link labels is preserved', () => {
|
|
64
|
-
const out = renderMarkdown('[see `foo`](https://example.com)');
|
|
65
|
-
const plain = stripAnsi(out);
|
|
66
|
-
assert.ok(plain.includes('foo'));
|
|
67
|
-
assert.ok(plain.includes('https://example.com'));
|
|
68
|
-
assert.match(out, /\x1b\[38;2;138;190;183/);
|
|
69
|
-
});
|
|
70
|
-
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { contextPctDisplayLabel } from './statusline.mjs';
|
|
4
|
-
|
|
5
|
-
test('contextPctDisplayLabel clamps displayed percent at 100', () => {
|
|
6
|
-
assert.equal(contextPctDisplayLabel(105), '100');
|
|
7
|
-
assert.equal(contextPctDisplayLabel(100.9), '100');
|
|
8
|
-
assert.equal(contextPctDisplayLabel(99.2), '99');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test('contextPctDisplayLabel keeps sub-1% decimal display', () => {
|
|
12
|
-
assert.equal(contextPctDisplayLabel(0.45), '0.5');
|
|
13
|
-
assert.equal(contextPctDisplayLabel(0.04), '0');
|
|
14
|
-
});
|
|
15
|
-
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
// mixdog statusline renderer — function form for in-daemon reuse.
|
|
2
|
-
//
|
|
3
|
-
// Mirrors `bin/statusline.mjs` exactly, but takes the CC stdin JSON as an
|
|
4
|
-
// argument and returns the rendered status text instead of reading fd 0 and
|
|
5
|
-
// writing to process.stdout. This allows the hook-pipe daemon to compute the
|
|
6
|
-
// statusline without paying the ~100ms bun cold-start on every refresh tick.
|
|
7
|
-
|
|
8
|
-
import fs from 'fs';
|
|
9
|
-
import os from 'os';
|
|
10
|
-
import http from 'node:http';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { formatGatewayLimitSegments, loadGatewayStatus } from './statusline-route.mjs';
|
|
13
|
-
import {
|
|
14
|
-
isClaudeNativeModelSelection,
|
|
15
|
-
isMixdogModelSelection,
|
|
16
|
-
writeClaudeCodeCurrentSnapshot,
|
|
17
|
-
} from '../src/gateway/claude-current.mjs';
|
|
18
|
-
import {
|
|
19
|
-
ANTHROPIC_OFFICIAL_BASE_URL,
|
|
20
|
-
syncAnthropicBaseUrl,
|
|
21
|
-
syncAutoCompactWindow,
|
|
22
|
-
} from '../scripts/lib/gateway-settings.mjs';
|
|
23
|
-
import {
|
|
24
|
-
readGatewaySessionRoute,
|
|
25
|
-
readLatestGatewayHostRoute,
|
|
26
|
-
} from '../src/gateway/session-routes.mjs';
|
|
27
|
-
|
|
28
|
-
function claudeConfigDir() {
|
|
29
|
-
return process.env.MIXDOG_CONFIG_DIR || path.join(os.homedir(), '.mixdog');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function pluginDataDir() {
|
|
33
|
-
return process.env.MIXDOG_DATA_DIR || path.join(process.env.MIXDOG_HOME || path.join(os.homedir(), '.mixdog'), 'data');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function writeFileIfChangedSync(file, content) {
|
|
37
|
-
try {
|
|
38
|
-
if (fs.existsSync(file) && fs.readFileSync(file, 'utf8') === content) return true;
|
|
39
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
40
|
-
fs.writeFileSync(file, content);
|
|
41
|
-
return true;
|
|
42
|
-
} catch {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function gatewayPort() {
|
|
48
|
-
const envPort = Number(process.env.MIXDOG_GATEWAY_PORT);
|
|
49
|
-
if (Number.isFinite(envPort) && envPort > 0) return Math.floor(envPort);
|
|
50
|
-
try {
|
|
51
|
-
const cfg = JSON.parse(fs.readFileSync(path.join(pluginDataDir(), 'mixdog-config.json'), 'utf8'));
|
|
52
|
-
const port = Number(cfg?.gateway?.port);
|
|
53
|
-
if (Number.isFinite(port) && port > 0) return Math.floor(port);
|
|
54
|
-
} catch {}
|
|
55
|
-
return 3468;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function readGatewayConfig() {
|
|
59
|
-
try {
|
|
60
|
-
return JSON.parse(fs.readFileSync(path.join(pluginDataDir(), 'mixdog-config.json'), 'utf8'));
|
|
61
|
-
} catch {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function gatewayOwnsMainBaseUrl() {
|
|
67
|
-
const cfg = readGatewayConfig();
|
|
68
|
-
if (cfg?.modules?.gateway?.enabled !== true) return false;
|
|
69
|
-
return cfg?.gateway?.mode === 'claude-current';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function settingsModelIsMixdog() {
|
|
73
|
-
try {
|
|
74
|
-
const settings = JSON.parse(fs.readFileSync(path.join(claudeConfigDir(), 'settings.json'), 'utf8'));
|
|
75
|
-
return isMixdogModelSelection(settings?.model);
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function routeUsesNonClaudeGateway(route) {
|
|
82
|
-
if (!route || typeof route !== 'object') return false;
|
|
83
|
-
const provider = String(route.defaultProvider || route.provider || '').trim();
|
|
84
|
-
const model = String(route.defaultModel || route.model || '').trim();
|
|
85
|
-
if (!provider || !model) return false;
|
|
86
|
-
return provider !== 'anthropic-oauth' && provider !== 'anthropic';
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function storedRouteNeedsGatewayStatus(sessionId, clientHostPid) {
|
|
90
|
-
try {
|
|
91
|
-
if (!(Number(clientHostPid) > 0)) return false;
|
|
92
|
-
const route = readGatewaySessionRoute(sessionId, { clientHostPid })
|
|
93
|
-
|| readLatestGatewayHostRoute(clientHostPid, { excludeSessionId: sessionId });
|
|
94
|
-
return routeUsesNonClaudeGateway(route);
|
|
95
|
-
} catch {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function shouldLoadGatewayStatus(currentRoute, sessionId, clientHostPid) {
|
|
101
|
-
if (process.env.MIXDOG_STATUSLINE_STANDALONE === '1') return false;
|
|
102
|
-
if (process.env.MIXDOG_STANDALONE === '1') return true;
|
|
103
|
-
if (!isClaudeNativeModelSelection(currentRoute)) return true;
|
|
104
|
-
// Native model selection, but the gateway still owns the main base URL in
|
|
105
|
-
// claude-current mode: every request flows through 3468, so the gateway's
|
|
106
|
-
// oauth usage cache (5H/7D) is the only quota source — CC's stdin rate_limits
|
|
107
|
-
// arrive empty on a gateway-proxied native route. Read the gateway status so
|
|
108
|
-
// the usage segment renders instead of going blank.
|
|
109
|
-
return gatewayOwnsMainBaseUrl()
|
|
110
|
-
|| storedRouteNeedsGatewayStatus(sessionId, clientHostPid)
|
|
111
|
-
|| settingsModelIsMixdog();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function traceStatusline(message) {
|
|
115
|
-
if (!process.env.MIXDOG_STATUSLINE_TRACE) return;
|
|
116
|
-
try {
|
|
117
|
-
fs.appendFileSync(path.join(pluginDataDir(), 'statusline-trace.log'), `${new Date().toISOString()} ${message}\n`);
|
|
118
|
-
} catch {}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function syncBaseUrlForCurrentModel(route) {
|
|
122
|
-
if (!route) return;
|
|
123
|
-
const target = isMixdogModelSelection(route) || gatewayOwnsMainBaseUrl() || settingsModelIsMixdog()
|
|
124
|
-
? `http://127.0.0.1:${gatewayPort()}`
|
|
125
|
-
: isClaudeNativeModelSelection(route)
|
|
126
|
-
? ANTHROPIC_OFFICIAL_BASE_URL
|
|
127
|
-
: '';
|
|
128
|
-
if (!target) return;
|
|
129
|
-
const result = syncAnthropicBaseUrl(target, { requireExisting: true });
|
|
130
|
-
if (!result.ok) traceStatusline(`[base-url-sync] ${result.error || 'failed'}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function syncBaseUrlForGatewayStatus(status) {
|
|
134
|
-
if (!status?.provider || !status?.model) return;
|
|
135
|
-
const result = syncAnthropicBaseUrl(`http://127.0.0.1:${gatewayPort()}`, { requireExisting: true });
|
|
136
|
-
if (!result.ok) traceStatusline(`[base-url-sync:gateway-status] ${result.error || 'failed'}`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function syncCompactWindowValue(win, label) {
|
|
140
|
-
if (!(Number.isFinite(Number(win)) && Number(win) > 0)) return;
|
|
141
|
-
const result = syncAutoCompactWindow(Math.floor(Number(win)));
|
|
142
|
-
if (!result.ok) traceStatusline(`[compact-window-sync:${label}] ${result.error || 'failed'}`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function compactWindowForRouteLike(route) {
|
|
146
|
-
if (!route || typeof route !== 'object') return null;
|
|
147
|
-
const num = (v) => {
|
|
148
|
-
const n = Number(v);
|
|
149
|
-
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
150
|
-
};
|
|
151
|
-
const explicit = num(route.autoCompactTokenLimit ?? route.auto_compact_token_limit);
|
|
152
|
-
const raw = num(route.rawContextWindow ?? route.raw_context_window ?? route.contextWindow ?? route.context_window);
|
|
153
|
-
const context = num(route.contextWindow ?? route.context_window);
|
|
154
|
-
const pct = num(route.effectiveContextWindowPercent ?? route.effective_context_window_percent) || 90;
|
|
155
|
-
const effective = context || (raw > 0 ? Math.max(1, Math.floor(raw * Math.min(100, pct) / 100)) : 0);
|
|
156
|
-
if (explicit > 0 && effective > 0) return Math.min(explicit, effective);
|
|
157
|
-
if (explicit > 0) return explicit;
|
|
158
|
-
return effective > 0 ? effective : null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function syncCompactWindowForCurrentModel(route) {
|
|
162
|
-
if (!isClaudeNativeModelSelection(route)) return;
|
|
163
|
-
try {
|
|
164
|
-
syncCompactWindowValue(compactWindowForRouteLike(route), 'current');
|
|
165
|
-
} catch (e) {
|
|
166
|
-
traceStatusline(`[compact-window-sync:current] ${e?.message || e}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function syncCompactWindowForGatewayStatus(status) {
|
|
171
|
-
if (!status?.provider || !status?.model) return;
|
|
172
|
-
syncCompactWindowValue(compactWindowForRouteLike(status), 'gateway-status');
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
function writeStatuslineLastSnapshot(json) {
|
|
176
|
-
if (!json) return;
|
|
177
|
-
try { JSON.parse(json); } catch { return; }
|
|
178
|
-
try {
|
|
179
|
-
const file = path.join(claudeConfigDir(), 'cc-statusline-last.json');
|
|
180
|
-
const tmp = `${file}.${process.pid}.tmp`;
|
|
181
|
-
if (fs.existsSync(file) && fs.readFileSync(file, 'utf8') === json) return;
|
|
182
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
183
|
-
fs.writeFileSync(tmp, json);
|
|
184
|
-
fs.renameSync(tmp, file);
|
|
185
|
-
} catch {}
|
|
186
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { formatGatewayLimitSegments } from './statusline-route.mjs';
|
|
4
|
-
|
|
5
|
-
test('formatGatewayLimitSegments hides OpenAI OAuth credit balance on L1', () => {
|
|
6
|
-
const segments = formatGatewayLimitSegments({
|
|
7
|
-
provider: 'openai-oauth',
|
|
8
|
-
providerKind: 'oauth',
|
|
9
|
-
source: 'openai-codex-wham',
|
|
10
|
-
quotaWindows: [
|
|
11
|
-
{ label: '5H', source: 'openai-codex-oauth', usedPct: 42 },
|
|
12
|
-
{ label: '7D', source: 'openai-codex-oauth', usedPct: 5 },
|
|
13
|
-
],
|
|
14
|
-
balance: {
|
|
15
|
-
source: 'openai-codex-credits',
|
|
16
|
-
remainingUsd: 12.34,
|
|
17
|
-
},
|
|
18
|
-
}, { COLS: 120 });
|
|
19
|
-
|
|
20
|
-
assert.deepEqual(segments, ['5H 42%', '7D 5%']);
|
|
21
|
-
assert.equal(segments.some((segment) => /Credit|\$12\.34/.test(segment)), false);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('formatGatewayLimitSegments falls back to percent for OpenAI OAuth credit windows', () => {
|
|
25
|
-
const segments = formatGatewayLimitSegments({
|
|
26
|
-
provider: 'openai-oauth',
|
|
27
|
-
providerKind: 'oauth',
|
|
28
|
-
quotaWindows: [
|
|
29
|
-
{
|
|
30
|
-
label: '5H',
|
|
31
|
-
source: 'openai-codex-oauth',
|
|
32
|
-
usedPct: 55,
|
|
33
|
-
remainingUsd: 4,
|
|
34
|
-
usedUsd: 6,
|
|
35
|
-
limitUsd: 10,
|
|
36
|
-
remainingCredits: 40,
|
|
37
|
-
usedCredits: 60,
|
|
38
|
-
limitCredits: 100,
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
}, { COLS: 120 });
|
|
42
|
-
|
|
43
|
-
assert.deepEqual(segments, ['5H 55%']);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('formatGatewayLimitSegments keeps non-OpenAI OAuth credit balance on L1', () => {
|
|
47
|
-
const segments = formatGatewayLimitSegments({
|
|
48
|
-
provider: 'anthropic-oauth',
|
|
49
|
-
providerKind: 'oauth',
|
|
50
|
-
source: 'anthropic-oauth',
|
|
51
|
-
quotaWindows: [
|
|
52
|
-
{ label: '5H', source: 'anthropic-oauth', usedPct: 20 },
|
|
53
|
-
],
|
|
54
|
-
balance: {
|
|
55
|
-
source: 'anthropic-oauth-extra',
|
|
56
|
-
remainingUsd: 7.5,
|
|
57
|
-
},
|
|
58
|
-
}, { COLS: 120 });
|
|
59
|
-
|
|
60
|
-
assert.ok(segments.includes('Credit $7.50'));
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('formatGatewayLimitSegments suppresses OpenAI OAuth credit via providerId even without a source hint', () => {
|
|
64
|
-
// Mirrors the merged quotaStatus shape (mergeQuotaStatus) where `source` may
|
|
65
|
-
// be absent and `balance.source` is generic: the providerId === openai-oauth
|
|
66
|
-
// branch alone must still suppress any Credit/$ leak on L1.
|
|
67
|
-
const segments = formatGatewayLimitSegments({
|
|
68
|
-
provider: 'openai-oauth',
|
|
69
|
-
providerKind: 'oauth',
|
|
70
|
-
quotaWindows: [
|
|
71
|
-
{ label: '5H', source: 'openai-codex-oauth', usedPct: 30 },
|
|
72
|
-
],
|
|
73
|
-
balance: {
|
|
74
|
-
remainingUsd: 99.99,
|
|
75
|
-
},
|
|
76
|
-
}, { COLS: 120 });
|
|
77
|
-
|
|
78
|
-
assert.deepEqual(segments, ['5H 30%']);
|
|
79
|
-
assert.equal(segments.some((segment) => /Credit|\$/.test(segment)), false);
|
|
80
|
-
});
|