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
|
@@ -946,17 +946,21 @@ test('anthropic effort: legacy sonnet-4-5 maps effort to thinking budget', () =>
|
|
|
946
946
|
assert.equal(modelSupportsEffort(model), false);
|
|
947
947
|
});
|
|
948
948
|
|
|
949
|
-
test('anthropic effort: xhigh on opus-4-8
|
|
949
|
+
test('anthropic effort: xhigh on opus-4-8 is a first-class level (not downgraded to max)', () => {
|
|
950
|
+
// Opus 4.8 supports xhigh (modelSupportsXhighEffort), so xhigh is kept
|
|
951
|
+
// verbatim as a first-class effort level — matching codex's ReasoningEffort
|
|
952
|
+
// enum which lists xhigh between high and max. Only models WITHOUT xhigh
|
|
953
|
+
// support clamp it down to high.
|
|
950
954
|
const model = 'claude-opus-4-8';
|
|
951
955
|
assert.equal(modelSupportsMaxEffort(model), true);
|
|
952
|
-
assert.equal(normalizeAnthropicEffortInput('xhigh', model), '
|
|
956
|
+
assert.equal(normalizeAnthropicEffortInput('xhigh', model), 'xhigh');
|
|
953
957
|
const body = _buildRequestBodyForCacheSmoke(
|
|
954
958
|
[{ role: 'user', content: 'hi' }],
|
|
955
959
|
model,
|
|
956
960
|
[],
|
|
957
961
|
{ effort: 'xhigh' },
|
|
958
962
|
);
|
|
959
|
-
assert.deepEqual(body.output_config, { effort: '
|
|
963
|
+
assert.deepEqual(body.output_config, { effort: 'xhigh' });
|
|
960
964
|
assert.equal(body.thinking, undefined);
|
|
961
965
|
});
|
|
962
966
|
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// recall-bench.mjs — case-based recall quality bench against the LIVE memory
|
|
3
|
+
// DB. READ-ONLY: every case goes through handleToolCall('search_memories')
|
|
4
|
+
// which only performs SELECTs (handleSearch path). No mutation actions
|
|
5
|
+
// ('memory' tool / cycle1 / cycle2 / prune / purge / etc.) are ever invoked
|
|
6
|
+
// here — do not add any.
|
|
7
|
+
//
|
|
8
|
+
// node scripts/recall-bench.mjs [--cases scripts/recall-bench-cases.json] [--json]
|
|
9
|
+
//
|
|
10
|
+
// Loads src/runtime/memory/index.mjs in-process (same module the daemon
|
|
11
|
+
// uses), calls init() once, runs each bench case through handleToolCall,
|
|
12
|
+
// prints params/result-count/top-3/latency/PASS-WARN per case, then a
|
|
13
|
+
// summary table, then calls stop().
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import { resolve, dirname } from 'node:path';
|
|
16
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
17
|
+
|
|
18
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const ROOT = resolve(__dir, '..');
|
|
20
|
+
const DEFAULT_CASES_PATH = resolve(__dir, 'recall-bench-cases.json');
|
|
21
|
+
|
|
22
|
+
function argValue(name, fallback = null) {
|
|
23
|
+
const idx = process.argv.indexOf(`--${name}`);
|
|
24
|
+
if (idx >= 0 && idx + 1 < process.argv.length) return process.argv[idx + 1];
|
|
25
|
+
const pref = `--${name}=`;
|
|
26
|
+
const hit = process.argv.find((a) => a.startsWith(pref));
|
|
27
|
+
return hit ? hit.slice(pref.length) : fallback;
|
|
28
|
+
}
|
|
29
|
+
function hasFlag(name) { return process.argv.includes(`--${name}`); }
|
|
30
|
+
|
|
31
|
+
const WARN_LATENCY_MS = 3000;
|
|
32
|
+
|
|
33
|
+
const DEFAULT_CASES = [
|
|
34
|
+
{ id: 'kw-ko', label: 'keyword query (ko)', args: { query: '메모리 재현' }, expect: 'results' },
|
|
35
|
+
{ id: 'kw-en', label: 'keyword query (en)', args: { query: 'memory recall pipeline' }, expect: 'results' },
|
|
36
|
+
{ id: 'short-1tok', label: 'short 1-token query', args: { query: 'recall' }, expect: 'results' },
|
|
37
|
+
{ id: 'short-2tok', label: 'short 2-token query', args: { query: 'cycle1 drain' }, expect: 'results' },
|
|
38
|
+
{ id: 'browse-last', label: 'query-less recent browse (period=last)', args: { period: 'last', limit: 10 }, expect: 'browse' },
|
|
39
|
+
{ id: 'period-24h', label: 'period window 24h', args: { period: '24h', limit: 10 }, expect: 'browse' },
|
|
40
|
+
{ id: 'period-7d', label: 'period window 7d', args: { period: '7d', limit: 10 }, expect: 'browse' },
|
|
41
|
+
{ id: 'category-filter', label: 'category filter (decision)', args: { period: '30d', category: 'decision', limit: 10 }, expect: 'browse' },
|
|
42
|
+
{ id: 'id-lookup', label: 'id lookup', args: { id: 1 }, expect: 'idlookup' },
|
|
43
|
+
{ id: 'scope-project', label: 'project-scoped query', args: { query: 'recall', cwd: ROOT, limit: 10 }, expect: 'results' },
|
|
44
|
+
{ id: 'scope-all', label: 'all-scope query', args: { query: 'recall', projectScope: 'all', limit: 10 }, expect: 'results' },
|
|
45
|
+
{ id: 'raw-on', label: 'includeRaw on', args: { query: 'recall', includeRaw: true, limit: 10 }, expect: 'results' },
|
|
46
|
+
{ id: 'raw-off', label: 'includeRaw off', args: { query: 'recall', includeRaw: false, limit: 10 }, expect: 'results' },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
function loadCases(path) {
|
|
50
|
+
if (path && existsSync(resolve(path))) {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(readFileSync(resolve(path), 'utf8'));
|
|
53
|
+
if (Array.isArray(parsed) && parsed.length) return parsed;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
process.stderr.write(`[recall-bench] failed to parse cases file ${path}: ${e.message}; using built-in cases\n`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return DEFAULT_CASES;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function textOfResult(result) {
|
|
62
|
+
if (result && typeof result === 'object' && Array.isArray(result.content)) {
|
|
63
|
+
return result.content.map((p) => (p?.type === 'text' ? p.text || '' : JSON.stringify(p))).join('\n');
|
|
64
|
+
}
|
|
65
|
+
if (result && typeof result === 'object' && typeof result.text === 'string') return result.text;
|
|
66
|
+
if (typeof result === 'string') return result;
|
|
67
|
+
return JSON.stringify(result ?? '');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function countEntryLines(text) {
|
|
71
|
+
const t = String(text || '').trim();
|
|
72
|
+
if (!t || t === '(no results)' || t === '(no valid ids)') return 0;
|
|
73
|
+
return t.split('\n').filter((line) => line.trim() && !line.startsWith('[recall truncated') && !line.startsWith('note:')).length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function topN(text, n = 3, maxLen = 140) {
|
|
77
|
+
const t = String(text || '').trim();
|
|
78
|
+
if (!t || t === '(no results)') return [];
|
|
79
|
+
return t.split('\n')
|
|
80
|
+
.filter((line) => line.trim() && !line.startsWith('[recall truncated') && !line.startsWith('note:'))
|
|
81
|
+
.slice(0, n)
|
|
82
|
+
.map((line) => (line.length > maxLen ? `${line.slice(0, maxLen - 1)}…` : line));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function evaluateCase(kase, { count, ms, isError }) {
|
|
86
|
+
const warnings = [];
|
|
87
|
+
if (isError) warnings.push('error result');
|
|
88
|
+
if (ms > WARN_LATENCY_MS) warnings.push(`latency ${ms}ms > ${WARN_LATENCY_MS}ms`);
|
|
89
|
+
if ((kase.expect === 'browse' || kase.expect === 'idlookup') && count === 0) {
|
|
90
|
+
warnings.push('0 results for a browse/id-lookup case (expected data present)');
|
|
91
|
+
}
|
|
92
|
+
const status = warnings.length ? 'WARN' : 'PASS';
|
|
93
|
+
return { status, warnings };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function runCase(memoryModule, kase) {
|
|
97
|
+
const started = Date.now();
|
|
98
|
+
let result;
|
|
99
|
+
let isError = false;
|
|
100
|
+
let errMsg = null;
|
|
101
|
+
try {
|
|
102
|
+
result = await memoryModule.handleToolCall('search_memories', kase.args || {});
|
|
103
|
+
isError = Boolean(result?.isError);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
isError = true;
|
|
106
|
+
errMsg = e?.message || String(e);
|
|
107
|
+
result = { text: `(error: ${errMsg})` };
|
|
108
|
+
}
|
|
109
|
+
const ms = Date.now() - started;
|
|
110
|
+
const text = textOfResult(result);
|
|
111
|
+
const count = countEntryLines(text);
|
|
112
|
+
const evalResult = evaluateCase(kase, { count, ms, isError });
|
|
113
|
+
return {
|
|
114
|
+
id: kase.id,
|
|
115
|
+
label: kase.label,
|
|
116
|
+
args: kase.args,
|
|
117
|
+
count,
|
|
118
|
+
ms,
|
|
119
|
+
isError,
|
|
120
|
+
errMsg,
|
|
121
|
+
top3: topN(text, 3),
|
|
122
|
+
status: evalResult.status,
|
|
123
|
+
warnings: evalResult.warnings,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function printCase(row) {
|
|
128
|
+
process.stdout.write(`\n[${row.status}] ${row.id} — ${row.label}\n`);
|
|
129
|
+
process.stdout.write(` params: ${JSON.stringify(row.args)}\n`);
|
|
130
|
+
process.stdout.write(` results: ${row.count} latency: ${row.ms}ms${row.isError ? ` ERROR: ${row.errMsg}` : ''}\n`);
|
|
131
|
+
if (row.top3.length) {
|
|
132
|
+
for (const line of row.top3) process.stdout.write(` - ${line}\n`);
|
|
133
|
+
} else {
|
|
134
|
+
process.stdout.write(' (no results)\n');
|
|
135
|
+
}
|
|
136
|
+
for (const w of row.warnings) process.stdout.write(` WARN: ${w}\n`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function printSummary(rows) {
|
|
140
|
+
process.stdout.write('\n=== recall-bench summary ===\n');
|
|
141
|
+
const widths = { id: 18, results: 8, ms: 8, status: 6 };
|
|
142
|
+
process.stdout.write(
|
|
143
|
+
`${'case'.padEnd(widths.id)}${'results'.padEnd(widths.results)}${'ms'.padEnd(widths.ms)}${'status'.padEnd(widths.status)}notes\n`,
|
|
144
|
+
);
|
|
145
|
+
for (const r of rows) {
|
|
146
|
+
process.stdout.write(
|
|
147
|
+
`${String(r.id).padEnd(widths.id)}${String(r.count).padEnd(widths.results)}${String(r.ms).padEnd(widths.ms)}${String(r.status).padEnd(widths.status)}${r.warnings.join('; ')}\n`,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
const warnCount = rows.filter((r) => r.status === 'WARN').length;
|
|
151
|
+
const totalMs = rows.reduce((s, r) => s + r.ms, 0);
|
|
152
|
+
process.stdout.write(`\ncases=${rows.length} pass=${rows.length - warnCount} warn=${warnCount} total_latency=${totalMs}ms\n`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function main() {
|
|
156
|
+
const casesPath = argValue('cases', DEFAULT_CASES_PATH);
|
|
157
|
+
const jsonMode = hasFlag('json');
|
|
158
|
+
const cases = loadCases(casesPath);
|
|
159
|
+
|
|
160
|
+
let memoryModule;
|
|
161
|
+
try {
|
|
162
|
+
memoryModule = await import(pathToFileURL(resolve(ROOT, 'src/runtime/memory/index.mjs')).href);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
process.stderr.write(`[recall-bench] failed to load memory module: ${e?.stack || e?.message || e}\n`);
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
try {
|
|
170
|
+
await memoryModule.init();
|
|
171
|
+
} catch (e) {
|
|
172
|
+
process.stdout.write(`recall-bench: DB unreachable / init failed — reporting clearly, no fabricated results.\n`);
|
|
173
|
+
process.stdout.write(`error: ${e?.stack || e?.message || e}\n`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const rows = [];
|
|
179
|
+
try {
|
|
180
|
+
for (const kase of cases) {
|
|
181
|
+
const row = await runCase(memoryModule, kase);
|
|
182
|
+
rows.push(row);
|
|
183
|
+
if (!jsonMode) printCase(row);
|
|
184
|
+
}
|
|
185
|
+
} finally {
|
|
186
|
+
try { await memoryModule.stop?.(); } catch {}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const allZero = rows.length > 0 && rows.every((r) => r.count === 0 && !r.isError);
|
|
190
|
+
if (allZero) {
|
|
191
|
+
process.stdout.write('\nNOTE: every case returned 0 results — DB is likely empty (or unreachable pool). Treat WARNs below as expected-empty, not a recall bug, until data is present.\n');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (jsonMode) {
|
|
195
|
+
process.stdout.write(JSON.stringify({ cases: rows }, null, 2) + '\n');
|
|
196
|
+
} else {
|
|
197
|
+
printSummary(rows);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const hardErrors = rows.filter((r) => r.isError);
|
|
201
|
+
if (hardErrors.length) process.exitCode = 1;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main().catch((e) => {
|
|
205
|
+
process.stderr.write(`${e?.stack || e?.message || e}\n`);
|
|
206
|
+
process.exitCode = 1;
|
|
207
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "id": "uc1-last", "label": "UC1 previous conversation (period=last)", "args": { "period": "last", "limit": 12 }, "expect": "browse" },
|
|
3
|
+
{ "id": "uc1-last-page2", "label": "UC1 last paging (offset)", "args": { "period": "last", "limit": 12, "offset": 12 }, "expect": "browse" },
|
|
4
|
+
{ "id": "uc2-recent-30m", "label": "UC2 recent 30m", "args": { "period": "30m", "limit": 10 }, "expect": "browse" },
|
|
5
|
+
{ "id": "uc2-recent-3h", "label": "UC2 recent 3h", "args": { "period": "3h", "limit": 10 }, "expect": "browse" },
|
|
6
|
+
{ "id": "uc2-recent-24h", "label": "UC2 recent 24h", "args": { "period": "24h", "limit": 10 }, "expect": "browse" },
|
|
7
|
+
{ "id": "uc3-tod-today", "label": "UC3 time-of-day today 12:00~14:00", "args": { "period": "12:00~14:00", "limit": 10 }, "expect": "browse" },
|
|
8
|
+
{ "id": "uc3-tod-dated", "label": "UC3 dated time window 2026-07-03 09:00~12:00", "args": { "period": "2026-07-03 09:00~12:00", "limit": 10 }, "expect": "browse" },
|
|
9
|
+
{ "id": "uc3-date", "label": "UC3 specific date 2026-07-02", "args": { "period": "2026-07-02", "limit": 10 }, "expect": "browse" },
|
|
10
|
+
{ "id": "uc3-range", "label": "UC3 date range 2026-07-01~2026-07-03", "args": { "period": "2026-07-01~2026-07-03", "limit": 10 }, "expect": "browse" },
|
|
11
|
+
{ "id": "uc4-topic", "label": "UC4 topic query", "args": { "query": "recall period last 개선", "limit": 8 }, "expect": "results" },
|
|
12
|
+
{ "id": "uc4-topic-time", "label": "UC4 topic + time window", "args": { "query": "recall", "period": "24h", "limit": 8 }, "expect": "results" },
|
|
13
|
+
{ "id": "uc4-topic-tod", "label": "UC4 topic + time-of-day", "args": { "query": "recall", "period": "12:00~15:00", "limit": 8 }, "expect": "results" },
|
|
14
|
+
{ "id": "uc4-fanout", "label": "UC4 fan-out array query", "args": { "query": ["recall 개선", "battle balance"], "limit": 5 }, "expect": "results" },
|
|
15
|
+
{ "id": "uc5-yesterday", "label": "UC5 calendar yesterday", "args": { "period": "yesterday", "limit": 10 }, "expect": "browse" },
|
|
16
|
+
{ "id": "uc5-thisweek", "label": "UC5 calendar this_week", "args": { "period": "this_week", "limit": 10 }, "expect": "browse" },
|
|
17
|
+
{ "id": "uc6-category", "label": "UC6 category decision 7d", "args": { "period": "7d", "category": "decision", "limit": 10 }, "expect": "browse" }
|
|
18
|
+
]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "id": "t1", "label": "topic ko-en mixed", "args": { "query": "recall period last 개선", "limit": 8 }, "expect": "results" },
|
|
3
|
+
{ "id": "t2", "label": "topic ko only", "args": { "query": "recall 개선", "limit": 8 }, "expect": "results" },
|
|
4
|
+
{ "id": "t3", "label": "topic en only", "args": { "query": "recall period last improvement", "limit": 8 }, "expect": "results" },
|
|
5
|
+
{ "id": "t4", "label": "category no-raw", "args": { "period": "7d", "category": "decision", "limit": 10, "includeRaw": false }, "expect": "browse" }
|
|
6
|
+
]
|
|
@@ -155,6 +155,10 @@ function grepSweepKey(row) {
|
|
|
155
155
|
|
|
156
156
|
const GREP_SWEEP_MIN_CALLS = 20;
|
|
157
157
|
const GREP_SWEEP_MIN_UNIQUE = 16;
|
|
158
|
+
const READONLY_TOOL_NAMES = new Set(['read', 'grep', 'glob', 'list', 'find', 'code_graph', 'recall', 'explore', 'search', 'web_fetch']);
|
|
159
|
+
const READONLY_STALL_MIN_RUN = 8;
|
|
160
|
+
const READONLY_ROLE_AGENTS = new Set(['reviewer', 'explore', 'explorer', 'debugger']);
|
|
161
|
+
const IDENTICAL_CALL_MIN_COUNT = 3;
|
|
158
162
|
|
|
159
163
|
function fmtMs(ms) {
|
|
160
164
|
const n = Number(ms);
|
|
@@ -313,6 +317,9 @@ function sessionMatches(id, query) {
|
|
|
313
317
|
}
|
|
314
318
|
|
|
315
319
|
function selectSessionIds(sessionMetas, query) {
|
|
320
|
+
if (String(query || '').trim().toLowerCase() === 'all') {
|
|
321
|
+
return sessionMetas.map((m) => m.session_id);
|
|
322
|
+
}
|
|
316
323
|
let selected;
|
|
317
324
|
if (!query || query === 'current') selected = chooseCurrentSession(sessionMetas);
|
|
318
325
|
else selected = sessionMetas.find((m) => sessionMatches(m.session_id, query));
|
|
@@ -331,16 +338,27 @@ function selectSessionIds(sessionMetas, query) {
|
|
|
331
338
|
function filterAgent(rows, agent) {
|
|
332
339
|
if (!agent) return rows;
|
|
333
340
|
const q = String(agent).toLowerCase();
|
|
334
|
-
|
|
341
|
+
const rowMatches = (r) => {
|
|
335
342
|
const values = [
|
|
336
|
-
field(r, 'agent'),
|
|
337
343
|
field(r, 'agent'),
|
|
338
344
|
field(r, 'sourceName'),
|
|
339
345
|
field(r, 'preset'),
|
|
340
346
|
field(r, 'preset_name'),
|
|
341
347
|
].filter(Boolean).map((v) => String(v).toLowerCase());
|
|
342
348
|
return values.some((v) => v.includes(q));
|
|
343
|
-
}
|
|
349
|
+
};
|
|
350
|
+
// Row-level agent fields only exist on some kinds (tool/steer); usage_raw and
|
|
351
|
+
// transport rows carry no agent. Filter at SESSION granularity: keep every
|
|
352
|
+
// row of any session where at least one row matches, so per-agent views keep
|
|
353
|
+
// their usage/cache/turn aggregates intact.
|
|
354
|
+
const matchedSessions = new Set();
|
|
355
|
+
for (const r of rows) {
|
|
356
|
+
if (rowMatches(r)) {
|
|
357
|
+
const sid = sessionId(r);
|
|
358
|
+
if (sid) matchedSessions.add(sid);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return rows.filter((r) => (sessionId(r) ? matchedSessions.has(sessionId(r)) : rowMatches(r)));
|
|
344
362
|
}
|
|
345
363
|
|
|
346
364
|
function buildRouteGroups(rows) {
|
|
@@ -384,6 +402,7 @@ function buildCacheDiagnostics(rows) {
|
|
|
384
402
|
const transport = rows.filter((r) => r.kind === 'transport');
|
|
385
403
|
const usage = rows.filter((r) => r.kind === 'usage_raw');
|
|
386
404
|
const breaks = rows.filter((r) => r.kind === 'cache_break');
|
|
405
|
+
const misses = rows.filter((r) => r.kind === 'cache_miss');
|
|
387
406
|
const keyCounts = new Map();
|
|
388
407
|
for (const r of transport) {
|
|
389
408
|
const key = field(r, 'cache_key_hash');
|
|
@@ -443,6 +462,20 @@ function buildCacheDiagnostics(rows) {
|
|
|
443
462
|
ts: r.ts,
|
|
444
463
|
};
|
|
445
464
|
}),
|
|
465
|
+
actual_cache_misses: misses.map((r) => ({
|
|
466
|
+
session_id: sessionId(r),
|
|
467
|
+
iteration: num(r, 'iteration'),
|
|
468
|
+
reason: field(r, 'reason') || field(r, 'payload')?.reason || 'unknown',
|
|
469
|
+
ws_mode: field(r, 'ws_mode'),
|
|
470
|
+
cache_key_hash: field(r, 'cache_key_hash'),
|
|
471
|
+
request_has_previous_response_id: field(r, 'request_has_previous_response_id'),
|
|
472
|
+
prompt_tokens: num(r, 'prompt_tokens') || num(field(r, 'payload'), 'prompt_tokens') || 0,
|
|
473
|
+
cached_tokens: num(r, 'cached_tokens') || num(field(r, 'payload'), 'cached_tokens') || 0,
|
|
474
|
+
uncached_tokens: num(r, 'uncached_tokens') || num(field(r, 'payload'), 'uncached_tokens') || 0,
|
|
475
|
+
previous_max_cached_tokens: num(r, 'previous_max_cached_tokens') || num(field(r, 'payload'), 'previous_max_cached_tokens') || 0,
|
|
476
|
+
cache_ratio: num(r, 'cache_ratio') ?? num(field(r, 'payload'), 'cache_ratio'),
|
|
477
|
+
ts: r.ts,
|
|
478
|
+
})),
|
|
446
479
|
service_tier_downgrades: downgrades,
|
|
447
480
|
low_cache_turns: usage.map((r) => {
|
|
448
481
|
const denom = cacheDenom(r);
|
|
@@ -452,10 +485,21 @@ function buildCacheDiagnostics(rows) {
|
|
|
452
485
|
};
|
|
453
486
|
}
|
|
454
487
|
|
|
488
|
+
function summarizePathArg(pathValue) {
|
|
489
|
+
if (Array.isArray(pathValue)) {
|
|
490
|
+
const parts = pathValue.map((p) => (typeof p === 'string' ? p : `${p?.path || '?'}${p?.offset != null ? `:${p.offset}` : ''}`));
|
|
491
|
+
return `batch[${parts.length}] ${parts.slice(0, 3).join(', ')}${parts.length > 3 ? ', …' : ''}`;
|
|
492
|
+
}
|
|
493
|
+
return String(pathValue || '');
|
|
494
|
+
}
|
|
495
|
+
|
|
455
496
|
function summarizeToolTarget(row) {
|
|
456
497
|
const args = toolArgs(row) || {};
|
|
457
498
|
const name = String(field(row, 'tool_name') || '');
|
|
458
|
-
if (name === 'read')
|
|
499
|
+
if (name === 'read') {
|
|
500
|
+
if (Array.isArray(args.path)) return summarizePathArg(args.path);
|
|
501
|
+
return `${args.path || '?'}:${args.line || args.offset || ''}`;
|
|
502
|
+
}
|
|
459
503
|
if (name === 'grep') return `${args.path || '?'} :: ${String(args.pattern || '').slice(0, 80)}`;
|
|
460
504
|
if (name === 'code_graph') return `${args.mode || '?'} ${args.file || ''} ${args.symbol || ''}`.trim();
|
|
461
505
|
if (name === 'find') return `${args.path || '?'} :: ${args.query || ''}`;
|
|
@@ -731,6 +775,62 @@ function buildToolDiagnostics(rows, failureRows = []) {
|
|
|
731
775
|
};
|
|
732
776
|
}).sort((a, b) => b.count - a.count || b.tool_ms - a.tool_ms).slice(0, 20);
|
|
733
777
|
|
|
778
|
+
const readonlyStalls = [];
|
|
779
|
+
for (const [sid, srows] of groupBy(tools, sessionId).entries()) {
|
|
780
|
+
for (const [agent, arows] of groupBy(srows, (r) => field(r, 'agent') || field(r, 'sourceName') || '').entries()) {
|
|
781
|
+
const sorted = [...arows].sort((a, b) => Number(a.ts || 0) - Number(b.ts || 0));
|
|
782
|
+
let run = [];
|
|
783
|
+
const flushRun = () => {
|
|
784
|
+
const turns = new Set(run.map((r) => num(r, 'iteration')).filter((n) => n != null)).size;
|
|
785
|
+
if (turns >= READONLY_STALL_MIN_RUN) {
|
|
786
|
+
const pathCounts = countBy(run, (r) => summarizePathArg((toolArgs(r) || {}).path)).filter(([p]) => p).slice(0, 3);
|
|
787
|
+
readonlyStalls.push({
|
|
788
|
+
session_id: sid,
|
|
789
|
+
agent: agent || null,
|
|
790
|
+
run_length: run.length,
|
|
791
|
+
run_turns: turns,
|
|
792
|
+
start_it: num(run[0], 'iteration'),
|
|
793
|
+
end_it: num(run[run.length - 1], 'iteration'),
|
|
794
|
+
top_paths: pathCounts.map(([path, count]) => ({ path, count })),
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
run = [];
|
|
798
|
+
};
|
|
799
|
+
for (const row of sorted) {
|
|
800
|
+
const name = String(field(row, 'tool_name') || '');
|
|
801
|
+
if (!READONLY_TOOL_NAMES.has(name)) { flushRun(); continue; }
|
|
802
|
+
// A backwards iteration jump means a new prompt/steer restarted the
|
|
803
|
+
// loop; that is a fresh run, not a continuation of the same stall.
|
|
804
|
+
const it = num(row, 'iteration');
|
|
805
|
+
const prevIt = run.length ? num(run[run.length - 1], 'iteration') : null;
|
|
806
|
+
if (it != null && prevIt != null && it < prevIt) flushRun();
|
|
807
|
+
run.push(row);
|
|
808
|
+
}
|
|
809
|
+
flushRun();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
readonlyStalls.sort((a, b) => (b.run_turns || 0) - (a.run_turns || 0) || b.run_length - a.run_length);
|
|
813
|
+
|
|
814
|
+
const identicalCallRepeats = [];
|
|
815
|
+
for (const [key, trows] of groupBy(
|
|
816
|
+
tools.filter((r) => toolResultKind(r) !== 'error'),
|
|
817
|
+
(r) => `${sessionId(r)}::${field(r, 'tool_name') || ''}::${toolArgsHash(r) || ''}`,
|
|
818
|
+
).entries()) {
|
|
819
|
+
if (!toolArgsHash(trows[0])) continue;
|
|
820
|
+
const iterations = [...new Set(trows.map((r) => num(r, 'iteration')).filter((n) => n != null))];
|
|
821
|
+
if (iterations.length >= IDENTICAL_CALL_MIN_COUNT) {
|
|
822
|
+
const sorted = [...trows].sort((a, b) => Number(a.ts || 0) - Number(b.ts || 0));
|
|
823
|
+
identicalCallRepeats.push({
|
|
824
|
+
session_id: sessionId(sorted[0]),
|
|
825
|
+
tool: field(sorted[0], 'tool_name'),
|
|
826
|
+
args_hash: toolArgsHash(sorted[0]),
|
|
827
|
+
count: iterations.length,
|
|
828
|
+
iterations: iterations.slice(0, 6),
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
identicalCallRepeats.sort((a, b) => b.count - a.count);
|
|
833
|
+
|
|
734
834
|
return {
|
|
735
835
|
total_tool_calls: tools.length,
|
|
736
836
|
total_tool_ms: sum(tools.map((r) => num(r, 'tool_ms'))),
|
|
@@ -748,6 +848,8 @@ function buildToolDiagnostics(rows, failureRows = []) {
|
|
|
748
848
|
single_tool_batches: singleToolBatches.length,
|
|
749
849
|
},
|
|
750
850
|
sequential_tool_clusters: sequentialToolClusters,
|
|
851
|
+
readonly_stalls: readonlyStalls.slice(0, 20),
|
|
852
|
+
identical_call_repeats: identicalCallRepeats.slice(0, 20),
|
|
751
853
|
};
|
|
752
854
|
}
|
|
753
855
|
|
|
@@ -1067,22 +1169,52 @@ function buildCompactDiagnostics(rows, selectedIds) {
|
|
|
1067
1169
|
|
|
1068
1170
|
function buildIssues(routeGroups, cache, tools) {
|
|
1069
1171
|
const issues = [];
|
|
1172
|
+
for (const stall of (tools.readonly_stalls || []).slice(0, 10)) {
|
|
1173
|
+
const readonlyRole = READONLY_ROLE_AGENTS.has(String(stall.agent || '').toLowerCase());
|
|
1174
|
+
const turns = stall.run_turns ?? stall.run_length;
|
|
1175
|
+
issues.push({
|
|
1176
|
+
severity: readonlyRole ? 'low' : (turns >= 15 ? 'high' : 'medium'),
|
|
1177
|
+
type: 'readonly_stall',
|
|
1178
|
+
message: `${stall.agent || shortId(stall.session_id)} readonly stall ${turns} turns/${stall.run_length} calls (it=${stall.start_it ?? '-'}..${stall.end_it ?? '-'})${readonlyRole ? ' [readonly role]' : ''} top paths: ${stall.top_paths.map((p) => `${p.path}×${p.count}`).join(', ') || '-'}`,
|
|
1179
|
+
session_id: stall.session_id,
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
for (const rep of (tools.identical_call_repeats || []).slice(0, 10)) {
|
|
1183
|
+
issues.push({
|
|
1184
|
+
severity: 'medium',
|
|
1185
|
+
type: 'identical_call_repeat',
|
|
1186
|
+
message: `identical ${rep.tool} call x${rep.count} (hash=${rep.args_hash}) at it=${rep.iterations.join(',')}`,
|
|
1187
|
+
session_id: rep.session_id,
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1070
1190
|
for (const g of routeGroups) {
|
|
1191
|
+
// One-shot pseudo-sessions (native web search stamps `<caller>:native-search:<ts>`
|
|
1192
|
+
// per send) have no loop, so full-WS/low-cache is their normal shape, not an issue.
|
|
1193
|
+
if (/:native-search:/.test(String(g.session_id || ''))) continue;
|
|
1071
1194
|
if (g.ws_full > 0 && g.ws_delta === 0 && g.transport_rows > 0) {
|
|
1072
1195
|
issues.push({ severity: 'high', type: 'cache', message: `${g.agent || shortId(g.session_id)} stayed full WS (${g.ws_full} full, 0 delta)`, session_id: g.session_id });
|
|
1073
1196
|
} else if (g.ws_full > 0) {
|
|
1074
1197
|
issues.push({ severity: 'medium', type: 'cache', message: `${g.agent || shortId(g.session_id)} had ${g.ws_full} full WS turn(s) before delta`, session_id: g.session_id });
|
|
1075
1198
|
}
|
|
1076
|
-
|
|
1199
|
+
// Sessions with <=2 turns (explorer-style ephemeral one-shots) have no
|
|
1200
|
+
// prior turn to cache against; low ratio is structural, not actionable.
|
|
1201
|
+
if (g.cache_ratio != null && g.prompt_tokens > 5000 && g.cache_ratio < 0.25 && (g.turns ?? 0) > 2) {
|
|
1077
1202
|
issues.push({ severity: 'high', type: 'cache', message: `${g.agent || shortId(g.session_id)} low cache ratio ${fmtPct(g.cache_ratio * 100)}`, session_id: g.session_id });
|
|
1078
1203
|
}
|
|
1079
1204
|
}
|
|
1080
1205
|
for (const b of cache.cache_breaks.slice(0, 10)) {
|
|
1081
1206
|
issues.push({ severity: b.reason === 'no_anchor' ? 'medium' : 'high', type: 'cache_break', message: `cache_break ${b.reason || 'unknown'} at it=${b.iteration ?? '-'}`, session_id: b.session_id });
|
|
1082
1207
|
}
|
|
1208
|
+
for (const m of (cache.actual_cache_misses || []).slice(0, 10)) {
|
|
1209
|
+
issues.push({ severity: 'high', type: 'cache_miss', message: `cache_miss ${m.reason || 'unknown'} at it=${m.iteration ?? '-'} uncached=${fmtTok(m.uncached_tokens)}`, session_id: m.session_id });
|
|
1210
|
+
}
|
|
1083
1211
|
if (cache.service_tier_downgrades.length) {
|
|
1084
1212
|
const first = cache.service_tier_downgrades[0];
|
|
1085
|
-
|
|
1213
|
+
// priority->default is the backend's normal answer when the account/plan
|
|
1214
|
+
// has no priority lane — expected, not actionable per session. Keep it
|
|
1215
|
+
// visible as info-level; only other downgrade shapes stay medium.
|
|
1216
|
+
const expected = cache.service_tier_downgrades.every((d) => d.requested === 'priority' && d.response === 'default');
|
|
1217
|
+
issues.push({ severity: expected ? 'low' : 'medium', type: 'service_tier', message: `${cache.service_tier_downgrades.length} service tier downgrade(s), e.g. ${first.requested}->${first.response}${expected ? ' (expected: no priority lane on this account)' : ''}`, session_id: first.session_id });
|
|
1086
1218
|
}
|
|
1087
1219
|
if (tools.failures.length) {
|
|
1088
1220
|
const byTool = new Map();
|
|
@@ -1146,6 +1278,10 @@ function buildWhySlowRankings(report) {
|
|
|
1146
1278
|
const summary = countBy(report.cache.cache_breaks, (b) => `${b.reason || 'unknown'}/${b.phase || 'unknown'}`).slice(0, 3).map(([k, v]) => `${k}×${v}`).join(', ');
|
|
1147
1279
|
ranks.push({ score: 40 + report.cache.cache_breaks.length, type: 'cache_breaks', message: `${report.cache.cache_breaks.length} cache break(s): ${summary}` });
|
|
1148
1280
|
}
|
|
1281
|
+
if (report.cache.actual_cache_misses?.length) {
|
|
1282
|
+
const top = [...report.cache.actual_cache_misses].sort((a, b) => (b.uncached_tokens || 0) - (a.uncached_tokens || 0))[0];
|
|
1283
|
+
ranks.push({ score: 60 + (top?.uncached_tokens || 0) / 1000, type: 'cache_miss', message: `${report.cache.actual_cache_misses.length} actual cache miss(es), top uncached=${fmtTok(top?.uncached_tokens || 0)} it=${top?.iteration ?? '-'}` });
|
|
1284
|
+
}
|
|
1149
1285
|
const topGrowth = report.tokens.growth_turns[0];
|
|
1150
1286
|
if (topGrowth) {
|
|
1151
1287
|
ranks.push({ score: Math.max(0, topGrowth.prompt_delta) / 1000, type: 'prompt_growth', message: `${topGrowth.agent || '-'} it=${topGrowth.turn_label} prompt Δ=${fmtTok(topGrowth.prompt_delta)} out=${fmtTok(topGrowth.output_tokens + topGrowth.thinking_tokens)} cache=${fmtPct((topGrowth.cache_ratio ?? 0) * 100)}` });
|
|
@@ -1173,6 +1309,10 @@ function buildExecutiveSummary(report) {
|
|
|
1173
1309
|
const summary = countBy(report.cache.cache_breaks, (b) => `${b.reason || 'unknown'}/${b.phase || 'unknown'}`).slice(0, 3).map(([k, v]) => `${k}×${v}`).join(', ');
|
|
1174
1310
|
lines.push(`캐시 깨짐=${report.cache.cache_breaks.length} (${summary})`);
|
|
1175
1311
|
}
|
|
1312
|
+
if (report.cache.actual_cache_misses?.length) {
|
|
1313
|
+
const top = [...report.cache.actual_cache_misses].sort((a, b) => (b.uncached_tokens || 0) - (a.uncached_tokens || 0))[0];
|
|
1314
|
+
lines.push(`실제 캐시 미스=${report.cache.actual_cache_misses.length} (top uncached=${fmtTok(top?.uncached_tokens || 0)})`);
|
|
1315
|
+
}
|
|
1176
1316
|
if (report.compact?.cache_breaks?.length) {
|
|
1177
1317
|
const summary = countBy(report.compact.cache_breaks, (b) => `${b.reason || 'unknown'}/${b.phase || 'unknown'}`).slice(0, 3).map(([k, v]) => `${k}×${v}`).join(', ');
|
|
1178
1318
|
lines.push(`컴팩트 호출=${report.compact.sessions.length} session, cache reset=${report.compact.cache_breaks.length} (${summary})`);
|
|
@@ -1344,6 +1484,12 @@ function renderText(report) {
|
|
|
1344
1484
|
lines.push(`- ${shortId(b.session_id)} it=${b.iteration ?? '-'} phase=${b.phase || '-'} reason=${b.reason || '-'} (${b.explanation || '-'}) ws=${b.ws_mode || '-'} prev=${b.request_has_previous_response_id} cache=${fmtPct((b.cache_ratio ?? 0) * 100)} prompt=${fmtTok(b.prompt_tokens)} out=${fmtTok(b.output_tokens)} body/frame=${b.body_input_items ?? '-'}/${b.frame_input_items ?? '-'}`);
|
|
1345
1485
|
}
|
|
1346
1486
|
}
|
|
1487
|
+
if (report.cache.actual_cache_misses?.length) {
|
|
1488
|
+
lines.push('actual cache misses:');
|
|
1489
|
+
for (const m of report.cache.actual_cache_misses.slice(0, 10)) {
|
|
1490
|
+
lines.push(`- ${shortId(m.session_id)} it=${m.iteration ?? '-'} reason=${m.reason || '-'} ws=${m.ws_mode || '-'} prev=${m.request_has_previous_response_id} cache=${fmtPct((m.cache_ratio ?? 0) * 100)} prompt=${fmtTok(m.prompt_tokens)} uncached=${fmtTok(m.uncached_tokens)} prevMax=${fmtTok(m.previous_max_cached_tokens)}`);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1347
1493
|
if (report.cache.service_tier_downgrades.length) {
|
|
1348
1494
|
lines.push('service tier downgrades:');
|
|
1349
1495
|
for (const d of report.cache.service_tier_downgrades.slice(0, 10)) lines.push(`- ${shortId(d.session_id)} it=${d.iteration ?? '-'} ${d.requested}->${d.response}`);
|