mixdog 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -38
- package/package.json +10 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/r5-orchestrated-task.json +7 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +356 -0
- package/src/tui/app/model-options.mjs +334 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +489 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +677 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import stringWidth from 'string-width';
|
|
4
|
-
import {
|
|
5
|
-
shouldFoldPastedText,
|
|
6
|
-
formatPastedTextRef,
|
|
7
|
-
pastedTextReferenceIds,
|
|
8
|
-
expandPastedTextTokens,
|
|
9
|
-
} from './paste-attachments.mjs';
|
|
10
|
-
import { caretPosition } from './input-editing.mjs';
|
|
11
|
-
|
|
12
|
-
// --- Part 1: pasted-text token folding ----------------------------------
|
|
13
|
-
|
|
14
|
-
test('shouldFoldPastedText fires on >=3 lines or >200 chars', () => {
|
|
15
|
-
assert.equal(shouldFoldPastedText('a\nb\nc'), true); // 3 lines
|
|
16
|
-
assert.equal(shouldFoldPastedText('a\nb'), false); // 2 lines, short
|
|
17
|
-
assert.equal(shouldFoldPastedText('x'.repeat(201)), true); // > 200 chars
|
|
18
|
-
assert.equal(shouldFoldPastedText('x'.repeat(200)), false); // == 200, not over
|
|
19
|
-
assert.equal(shouldFoldPastedText(''), false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('token round-trips through format + expand', () => {
|
|
23
|
-
const original = 'line1\nline2\nline3';
|
|
24
|
-
const token = formatPastedTextRef(7, original);
|
|
25
|
-
assert.equal(token, '[Pasted text #7 +3 lines]');
|
|
26
|
-
const refs = pastedTextReferenceIds(`before ${token} after`);
|
|
27
|
-
assert.deepEqual([...refs], [7]);
|
|
28
|
-
const map = { 7: { id: 7, text: original } };
|
|
29
|
-
assert.equal(
|
|
30
|
-
expandPastedTextTokens(`before ${token} after`, map),
|
|
31
|
-
`before ${original} after`,
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('broken / partially-deleted tokens do not expand', () => {
|
|
36
|
-
const map = { 3: { id: 3, text: 'HIDDEN' } };
|
|
37
|
-
// Missing the "+N lines" tail — not a valid token, left verbatim.
|
|
38
|
-
assert.equal(expandPastedTextTokens('[Pasted text #3]', map), '[Pasted text #3]');
|
|
39
|
-
// Truncated bracket.
|
|
40
|
-
assert.equal(expandPastedTextTokens('[Pasted text #3 +2 lines', map), '[Pasted text #3 +2 lines');
|
|
41
|
-
// Unknown id stays as-is.
|
|
42
|
-
assert.equal(expandPastedTextTokens('[Pasted text #9 +2 lines]', map), '[Pasted text #9 +2 lines]');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// --- Part 2: caret row math vs ink wrap-ansi (hard, wordWrap:false) -------
|
|
46
|
-
|
|
47
|
-
// Faithful re-implementation of wrap-ansi's wrapWord loop for a single
|
|
48
|
-
// space-free line (the prompt caret math walks graphemes the same way). ink
|
|
49
|
-
// renders <Text wrap="hard"> which calls wrapAnsi(text, w, {hard,wordWrap:false}).
|
|
50
|
-
// For a run with no spaces the whole line goes through wrapWord, so the visual
|
|
51
|
-
// row count is what this produces.
|
|
52
|
-
function inkWrapWordRows(line, columns) {
|
|
53
|
-
const chars = Array.from(new Intl.Segmenter(undefined, { granularity: 'grapheme' }).segment(line), (s) => s.segment);
|
|
54
|
-
let rows = 1;
|
|
55
|
-
let visible = 0;
|
|
56
|
-
for (let i = 0; i < chars.length; i += 1) {
|
|
57
|
-
const cw = stringWidth(chars[i]);
|
|
58
|
-
if (visible + cw <= columns) {
|
|
59
|
-
// stays on current row
|
|
60
|
-
} else {
|
|
61
|
-
rows += 1;
|
|
62
|
-
visible = 0;
|
|
63
|
-
}
|
|
64
|
-
visible += cw;
|
|
65
|
-
if (visible === columns && i < chars.length - 1) {
|
|
66
|
-
rows += 1;
|
|
67
|
-
visible = 0;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return rows;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// caretPosition row for the END of the string == (visual row count - 1).
|
|
74
|
-
function caretRowsForWholeString(text, w) {
|
|
75
|
-
return caretPosition(text, text.length, w).row + 1;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
test('caret rows agree with ink wrap for wide-char lines at odd widths', () => {
|
|
79
|
-
// A width where a width-2 glyph cannot sit flush on the last column exposes
|
|
80
|
-
// the mismatch: caretPosition must NOT advance an extra row when the char is
|
|
81
|
-
// simply pushed to the next line (ink does the same — it never leaves a
|
|
82
|
-
// half-column then over-counts).
|
|
83
|
-
const cases = [
|
|
84
|
-
{ text: '가'.repeat(30), w: 41 },
|
|
85
|
-
{ text: '가'.repeat(30), w: 40 },
|
|
86
|
-
{ text: '가나다'.repeat(10) + 'ascii', w: 13 },
|
|
87
|
-
{ text: '한글'.repeat(8), w: 7 },
|
|
88
|
-
{ text: '가'.repeat(5), w: 2 },
|
|
89
|
-
];
|
|
90
|
-
for (const { text, w } of cases) {
|
|
91
|
-
assert.equal(
|
|
92
|
-
caretRowsForWholeString(text, w),
|
|
93
|
-
inkWrapWordRows(text, w),
|
|
94
|
-
`width ${w} on ${JSON.stringify(text.slice(0, 6))}…`,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Cursor exactly at a soft-wrap boundary MID-text: ink renders the following
|
|
100
|
-
// glyph on the next row, so the caret must report {row N+1, col 0}, never
|
|
101
|
-
// {row N, col == w} (which would sit outside the box).
|
|
102
|
-
test('caret at mid-text wrap boundary rolls to next row (col 0)', () => {
|
|
103
|
-
const value = '가나다라마'; // 5 wide glyphs, width 2 each
|
|
104
|
-
const w = 4; // 2 glyphs per row
|
|
105
|
-
// offset 2 = after '나' (each glyph is ONE UTF-16 unit) = end of visual
|
|
106
|
-
// row 0; content ('다라마') follows, so the caret rolls to the next row.
|
|
107
|
-
const at = caretPosition(value, 2, w);
|
|
108
|
-
assert.deepEqual(at, { row: 1, col: 0 }, 'boundary caret must sit on next row');
|
|
109
|
-
// offset 4 = after '라' = end of visual row 1 (rows 0-1 are full).
|
|
110
|
-
assert.deepEqual(caretPosition(value, 4, w), { row: 2, col: 0 }, 'second boundary rolls too');
|
|
111
|
-
// Cursor at the very end of a FLUSH-ending text ('가나다라' fills rows 0-1
|
|
112
|
-
// exactly) with a trailing cell (PromptInput appends one) also rolls.
|
|
113
|
-
const flush = '가나다라';
|
|
114
|
-
const end = caretPosition(flush, flush.length, w, true);
|
|
115
|
-
assert.deepEqual(end, { row: 2, col: 0 }, 'flush end with trailing cell rolls to next row');
|
|
116
|
-
// Non-flush end ('마' leaves col 2) must NOT roll even with a trailing cell.
|
|
117
|
-
const nonFlush = caretPosition(value, value.length, w, true);
|
|
118
|
-
assert.deepEqual(nonFlush, { row: 2, col: 2 }, 'non-flush end stays on its row');
|
|
119
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import test from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { tmpdir } from 'node:os';
|
|
6
|
-
import {
|
|
7
|
-
appendPromptHistory,
|
|
8
|
-
buildMergedPromptHistory,
|
|
9
|
-
loadPromptHistory,
|
|
10
|
-
pushSessionPromptHistory,
|
|
11
|
-
promptHistoryKey,
|
|
12
|
-
} from './prompt-history-store.mjs';
|
|
13
|
-
|
|
14
|
-
test('promptHistoryKey normalizes whitespace', () => {
|
|
15
|
-
assert.equal(promptHistoryKey(' hello world '), 'hello world');
|
|
16
|
-
assert.equal(promptHistoryKey(' '), '');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test('buildMergedPromptHistory prefers session order and dedupes', () => {
|
|
20
|
-
const merged = buildMergedPromptHistory(
|
|
21
|
-
['newest', 'older'],
|
|
22
|
-
['stale', 'newest', 'from-disk'],
|
|
23
|
-
50,
|
|
24
|
-
);
|
|
25
|
-
assert.deepEqual(merged, ['newest', 'older', 'from-disk', 'stale']);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('appendPromptHistory persists per cwd across reload', () => {
|
|
29
|
-
const dataDir = mkdtempSync(join(tmpdir(), 'mixdog-prompt-history-'));
|
|
30
|
-
const previous = process.env.MIXDOG_DATA_DIR;
|
|
31
|
-
process.env.MIXDOG_DATA_DIR = dataDir;
|
|
32
|
-
try {
|
|
33
|
-
const cwd = join(dataDir, 'project-a');
|
|
34
|
-
assert.deepEqual(appendPromptHistory(cwd, 'first prompt'), ['first prompt']);
|
|
35
|
-
assert.deepEqual(appendPromptHistory(cwd, 'second prompt'), ['first prompt', 'second prompt']);
|
|
36
|
-
assert.deepEqual(appendPromptHistory(cwd, 'first prompt'), ['second prompt', 'first prompt']);
|
|
37
|
-
const loaded = loadPromptHistory(cwd);
|
|
38
|
-
assert.deepEqual(loaded, ['second prompt', 'first prompt']);
|
|
39
|
-
} finally {
|
|
40
|
-
if (previous === undefined) delete process.env.MIXDOG_DATA_DIR;
|
|
41
|
-
else process.env.MIXDOG_DATA_DIR = previous;
|
|
42
|
-
rmSync(dataDir, { recursive: true, force: true });
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test('pushSessionPromptHistory is cwd-session newest-first with dedupe', () => {
|
|
47
|
-
let session = pushSessionPromptHistory([], 'alpha');
|
|
48
|
-
session = pushSessionPromptHistory(session, 'beta');
|
|
49
|
-
session = pushSessionPromptHistory(session, 'alpha');
|
|
50
|
-
assert.deepEqual(session, ['alpha', 'beta']);
|
|
51
|
-
});
|
|
52
|
-
|
|
@@ -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
|
-
|