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
package/scripts/recall-bench.mjs
CHANGED
|
@@ -68,27 +68,67 @@ function textOfResult(result) {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function countEntryLines(text) {
|
|
71
|
-
|
|
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;
|
|
71
|
+
return resultLines(text).length;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
function topN(text, n = 3, maxLen = 140) {
|
|
77
|
-
|
|
78
|
-
if (!t || t === '(no results)') return [];
|
|
79
|
-
return t.split('\n')
|
|
80
|
-
.filter((line) => line.trim() && !line.startsWith('[recall truncated') && !line.startsWith('note:'))
|
|
75
|
+
return resultLines(text)
|
|
81
76
|
.slice(0, n)
|
|
82
77
|
.map((line) => (line.length > maxLen ? `${line.slice(0, maxLen - 1)}…` : line));
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
// Full ordered list of result lines (no slice), used for topNContains rank
|
|
81
|
+
// scoring. Same filtering rule as countEntryLines/topN.
|
|
82
|
+
function resultLines(text) {
|
|
83
|
+
const t = String(text || '').trim();
|
|
84
|
+
if (!t || t === '(no results)' || t === '(no valid ids)') return [];
|
|
85
|
+
return t.split('\n').filter((line) => {
|
|
86
|
+
const trimmed = line.trim();
|
|
87
|
+
return trimmed
|
|
88
|
+
&& !trimmed.startsWith('[recall truncated')
|
|
89
|
+
&& !trimmed.startsWith('note:')
|
|
90
|
+
&& !/^\[[^\]]+\]$/.test(trimmed);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Score expect.topNContains: for each expected substring, find the 1-indexed
|
|
95
|
+
// rank of the first result line containing it (case-insensitive), over the
|
|
96
|
+
// FULL result list (not just the topN cutoff) so we can tell "missed
|
|
97
|
+
// entirely" from "present but ranked below cutoff". hit@N/MRR are then
|
|
98
|
+
// computed against the topN cutoff.
|
|
99
|
+
function scoreTopNContains(lines, substrings, n) {
|
|
100
|
+
const lower = lines.map((l) => l.toLowerCase());
|
|
101
|
+
const perSubstring = substrings.map((needle) => {
|
|
102
|
+
const hay = String(needle || '').toLowerCase();
|
|
103
|
+
let rank = null;
|
|
104
|
+
for (let i = 0; i < lower.length; i++) {
|
|
105
|
+
if (hay && lower[i].includes(hay)) { rank = i + 1; break; }
|
|
106
|
+
}
|
|
107
|
+
const hit = rank !== null && rank <= n;
|
|
108
|
+
return { needle, rank, hit, rr: hit ? 1 / rank : 0 };
|
|
109
|
+
});
|
|
110
|
+
const total = perSubstring.length || 1;
|
|
111
|
+
const hitAtN = perSubstring.reduce((s, p) => s + (p.hit ? 1 : 0), 0) / total;
|
|
112
|
+
const mrr = perSubstring.reduce((s, p) => s + p.rr, 0) / total;
|
|
113
|
+
return { perSubstring, hitAtN, mrr, n };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function evaluateCase(kase, { count, ms, isError }, quality) {
|
|
86
117
|
const warnings = [];
|
|
87
118
|
if (isError) warnings.push('error result');
|
|
88
119
|
if (ms > WARN_LATENCY_MS) warnings.push(`latency ${ms}ms > ${WARN_LATENCY_MS}ms`);
|
|
89
120
|
if ((kase.expect === 'browse' || kase.expect === 'idlookup') && count === 0) {
|
|
90
121
|
warnings.push('0 results for a browse/id-lookup case (expected data present)');
|
|
91
122
|
}
|
|
123
|
+
if (quality) {
|
|
124
|
+
for (const p of quality.perSubstring) {
|
|
125
|
+
if (!p.hit) {
|
|
126
|
+
warnings.push(p.rank === null
|
|
127
|
+
? `topNContains miss: "${p.needle}" not found in results`
|
|
128
|
+
: `topNContains miss: "${p.needle}" found at rank ${p.rank} > topN ${quality.n}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
92
132
|
const status = warnings.length ? 'WARN' : 'PASS';
|
|
93
133
|
return { status, warnings };
|
|
94
134
|
}
|
|
@@ -109,7 +149,14 @@ async function runCase(memoryModule, kase) {
|
|
|
109
149
|
const ms = Date.now() - started;
|
|
110
150
|
const text = textOfResult(result);
|
|
111
151
|
const count = countEntryLines(text);
|
|
112
|
-
|
|
152
|
+
// expect stays a plain string for legacy cases ('results'/'browse'/'idlookup').
|
|
153
|
+
// New quality cases use an object: { kind?, topNContains: [...], topN? }.
|
|
154
|
+
const expectObj = kase.expect && typeof kase.expect === 'object' ? kase.expect : null;
|
|
155
|
+
const expectKind = expectObj ? expectObj.kind : kase.expect;
|
|
156
|
+
const topNContains = expectObj && Array.isArray(expectObj.topNContains) ? expectObj.topNContains : null;
|
|
157
|
+
const cutoffN = expectObj && Number.isInteger(expectObj.topN) ? expectObj.topN : 5;
|
|
158
|
+
const quality = topNContains ? scoreTopNContains(resultLines(text), topNContains, cutoffN) : null;
|
|
159
|
+
const evalResult = evaluateCase({ ...kase, expect: expectKind }, { count, ms, isError }, quality);
|
|
113
160
|
return {
|
|
114
161
|
id: kase.id,
|
|
115
162
|
label: kase.label,
|
|
@@ -119,6 +166,7 @@ async function runCase(memoryModule, kase) {
|
|
|
119
166
|
isError,
|
|
120
167
|
errMsg,
|
|
121
168
|
top3: topN(text, 3),
|
|
169
|
+
quality,
|
|
122
170
|
status: evalResult.status,
|
|
123
171
|
warnings: evalResult.warnings,
|
|
124
172
|
};
|
|
@@ -128,6 +176,12 @@ function printCase(row) {
|
|
|
128
176
|
process.stdout.write(`\n[${row.status}] ${row.id} — ${row.label}\n`);
|
|
129
177
|
process.stdout.write(` params: ${JSON.stringify(row.args)}\n`);
|
|
130
178
|
process.stdout.write(` results: ${row.count} latency: ${row.ms}ms${row.isError ? ` ERROR: ${row.errMsg}` : ''}\n`);
|
|
179
|
+
if (row.quality) {
|
|
180
|
+
process.stdout.write(` hit@${row.quality.n}: ${row.quality.hitAtN.toFixed(2)} MRR: ${row.quality.mrr.toFixed(2)}\n`);
|
|
181
|
+
for (const p of row.quality.perSubstring) {
|
|
182
|
+
process.stdout.write(` substr "${p.needle}" -> rank ${p.rank ?? 'none'}${p.hit ? '' : ' (miss)'}\n`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
131
185
|
if (row.top3.length) {
|
|
132
186
|
for (const line of row.top3) process.stdout.write(` - ${line}\n`);
|
|
133
187
|
} else {
|
|
@@ -138,18 +192,27 @@ function printCase(row) {
|
|
|
138
192
|
|
|
139
193
|
function printSummary(rows) {
|
|
140
194
|
process.stdout.write('\n=== recall-bench summary ===\n');
|
|
141
|
-
const widths = { id: 18, results: 8, ms: 8, status: 6 };
|
|
195
|
+
const widths = { id: 18, results: 8, ms: 8, status: 6, hit: 9, mrr: 7 };
|
|
142
196
|
process.stdout.write(
|
|
143
|
-
`${'case'.padEnd(widths.id)}${'results'.padEnd(widths.results)}${'ms'.padEnd(widths.ms)}${'status'.padEnd(widths.status)}notes\n`,
|
|
197
|
+
`${'case'.padEnd(widths.id)}${'results'.padEnd(widths.results)}${'ms'.padEnd(widths.ms)}${'status'.padEnd(widths.status)}${'hit@N'.padEnd(widths.hit)}${'MRR'.padEnd(widths.mrr)}notes\n`,
|
|
144
198
|
);
|
|
145
199
|
for (const r of rows) {
|
|
200
|
+
const hitStr = r.quality ? r.quality.hitAtN.toFixed(2) : '-';
|
|
201
|
+
const mrrStr = r.quality ? r.quality.mrr.toFixed(2) : '-';
|
|
146
202
|
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`,
|
|
203
|
+
`${String(r.id).padEnd(widths.id)}${String(r.count).padEnd(widths.results)}${String(r.ms).padEnd(widths.ms)}${String(r.status).padEnd(widths.status)}${hitStr.padEnd(widths.hit)}${mrrStr.padEnd(widths.mrr)}${r.warnings.join('; ')}\n`,
|
|
148
204
|
);
|
|
149
205
|
}
|
|
150
206
|
const warnCount = rows.filter((r) => r.status === 'WARN').length;
|
|
151
207
|
const totalMs = rows.reduce((s, r) => s + r.ms, 0);
|
|
152
|
-
|
|
208
|
+
const qualityRows = rows.filter((r) => r.quality);
|
|
209
|
+
let aggLine = '';
|
|
210
|
+
if (qualityRows.length) {
|
|
211
|
+
const aggHit = qualityRows.reduce((s, r) => s + r.quality.hitAtN, 0) / qualityRows.length;
|
|
212
|
+
const aggMrr = qualityRows.reduce((s, r) => s + r.quality.mrr, 0) / qualityRows.length;
|
|
213
|
+
aggLine = ` agg_hit@N=${aggHit.toFixed(3)} agg_MRR=${aggMrr.toFixed(3)} (${qualityRows.length} scored cases)`;
|
|
214
|
+
}
|
|
215
|
+
process.stdout.write(`\ncases=${rows.length} pass=${rows.length - warnCount} warn=${warnCount} total_latency=${totalMs}ms${aggLine}\n`);
|
|
153
216
|
}
|
|
154
217
|
|
|
155
218
|
async function main() {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{ "id": "q-kr-recall-phrase", "label": "Korean distinctive phrase: persistent memory", "args": { "query": "영속 메모리" }, "expect": { "kind": "results", "topNContains": ["영속 메모리"], "topN": 5 } },
|
|
3
|
+
{ "id": "q-en-recall-pipeline", "label": "English distinctive phrase: recall fast-track builder", "args": { "query": "buildRecallFastTrackQuery" }, "expect": { "kind": "results", "topNContains": ["buildRecallFastTrackQuery"], "topN": 5 } },
|
|
4
|
+
{ "id": "q-cycle1-drain", "label": "cycle1 ECONNRESET topic", "args": { "query": "cycle1 ECONNRESET" }, "expect": { "kind": "results", "topNContains": ["cycle1", "ECONNRESET"], "topN": 5 } },
|
|
5
|
+
{ "id": "q-recall-general", "label": "bare recall keyword", "args": { "query": "recall" }, "expect": { "kind": "results", "topNContains": ["recall"], "topN": 5 } },
|
|
6
|
+
{ "id": "q-uc4-topic-kr", "label": "UC4 topic query with Korean qualifier", "args": { "query": "recall period last 개선", "limit": 8 }, "expect": { "kind": "results", "topNContains": ["개선"], "topN": 5 } },
|
|
7
|
+
{ "id": "q-recall-improve-kr", "label": "cache improvement topic", "args": { "query": "캐시 개선" }, "expect": { "kind": "results", "topNContains": ["개선"], "topN": 5 } },
|
|
8
|
+
{ "id": "q-battle-balance", "label": "ProjectAA topic", "args": { "query": "ProjectAA" }, "expect": { "kind": "results", "topNContains": ["ProjectAA"], "topN": 5 } },
|
|
9
|
+
{ "id": "q-fanout-recall-battle", "label": "fan-out array query cache+ProjectAA", "args": { "query": ["캐시 개선", "ProjectAA"], "limit": 5 }, "expect": { "kind": "results", "topNContains": ["개선", "ProjectAA"], "topN": 10 } },
|
|
10
|
+
{ "id": "q-short-2tok-cycle", "label": "short 2-token cycle1 query", "args": { "query": "cycle1 drain", "limit": 8 }, "expect": { "kind": "results", "topNContains": ["cycle1"], "topN": 3 } },
|
|
11
|
+
{ "id": "q-recall-scope-project", "label": "project-scoped recall query", "args": { "query": "recall", "cwd": "C:\\Project\\mixdog" , "limit": 10 }, "expect": { "kind": "results", "topNContains": ["recall"], "topN": 5 } }
|
|
12
|
+
]
|
|
@@ -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
|
+
]
|
|
@@ -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}`);
|
package/scripts/tool-smoke.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { buildHeadlessSpawnArgs } from '../src/headless-role.mjs';
|
|
|
11
11
|
import { createStandaloneChannelWorker } from '../src/standalone/channel-worker.mjs';
|
|
12
12
|
import { OpenAIOAuthProvider, buildRequestBody, sendViaHttpSse } from '../src/runtime/agent/orchestrator/providers/openai-oauth.mjs';
|
|
13
13
|
import { _test as _anthropicOAuthTest } from '../src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs';
|
|
14
|
-
import { _logicalResponseItemMatch
|
|
14
|
+
import { _logicalResponseItemMatch } from '../src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs';
|
|
15
15
|
import { _mergePendingMessageEntries, applyAskTerminalUsageTotals, closeSession, createSession, drainPendingMessages, enqueuePendingMessage, resumeSession } from '../src/runtime/agent/orchestrator/session/manager.mjs';
|
|
16
16
|
import {
|
|
17
17
|
contentHasImage,
|
|
@@ -137,46 +137,6 @@ function assertOk(name, result, pattern = null) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
{
|
|
140
|
-
const fullPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
141
|
-
mode: 'full',
|
|
142
|
-
frameInputItems: 40,
|
|
143
|
-
deltaTokens: 9000,
|
|
144
|
-
hasPreviousResponseId: false,
|
|
145
|
-
});
|
|
146
|
-
assert(fullPolicy.policy === 'full_guard' && fullPolicy.limitPerMin === 12, `full cache lane should keep 12rpm guard: ${JSON.stringify(fullPolicy)}`);
|
|
147
|
-
|
|
148
|
-
const deltaPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
149
|
-
mode: 'delta',
|
|
150
|
-
frameInputItems: 1,
|
|
151
|
-
deltaTokens: 9000,
|
|
152
|
-
hasPreviousResponseId: true,
|
|
153
|
-
});
|
|
154
|
-
assert(deltaPolicy.policy === 'delta_relaxed' && deltaPolicy.limitPerMin === 60, `small delta should use relaxed cache lane rpm: ${JSON.stringify(deltaPolicy)}`);
|
|
155
|
-
|
|
156
|
-
const largeDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({}, {
|
|
157
|
-
mode: 'delta',
|
|
158
|
-
frameInputItems: 20,
|
|
159
|
-
deltaTokens: 9000,
|
|
160
|
-
hasPreviousResponseId: true,
|
|
161
|
-
});
|
|
162
|
-
assert(largeDeltaPolicy.policy === 'delta_guarded' && largeDeltaPolicy.limitPerMin === 12, `large delta should fall back to full guard: ${JSON.stringify(largeDeltaPolicy)}`);
|
|
163
|
-
|
|
164
|
-
const customDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({ openaiCacheLaneDeltaRateLimitPerMin: 90 }, {
|
|
165
|
-
mode: 'delta',
|
|
166
|
-
frameInputItems: 1,
|
|
167
|
-
deltaTokens: 9000,
|
|
168
|
-
hasPreviousResponseId: true,
|
|
169
|
-
});
|
|
170
|
-
assert(customDeltaPolicy.limitPerMin === 90, `custom delta rpm should be honored: ${JSON.stringify(customDeltaPolicy)}`);
|
|
171
|
-
|
|
172
|
-
const unlimitedDeltaPolicy = _resolveOpenAiPromptCacheRatePolicy({ openaiCacheLaneDeltaRateLimitPerMin: 0 }, {
|
|
173
|
-
mode: 'delta',
|
|
174
|
-
frameInputItems: 1,
|
|
175
|
-
deltaTokens: 9000,
|
|
176
|
-
hasPreviousResponseId: true,
|
|
177
|
-
});
|
|
178
|
-
assert(unlimitedDeltaPolicy.policy === 'delta_unlimited' && unlimitedDeltaPolicy.limitPerMin === 0, `delta rpm=0 should disable delta rate wait: ${JSON.stringify(unlimitedDeltaPolicy)}`);
|
|
179
|
-
|
|
180
140
|
const originalFunctionCall = {
|
|
181
141
|
type: 'function_call',
|
|
182
142
|
call_id: 'call_tool_1',
|
|
@@ -589,20 +549,28 @@ if (grepContextPolicyArgs['-C'] !== GREP_CONTEXT_MAX || Object.prototype.hasOwnP
|
|
|
589
549
|
throw new Error(`grep context policy must canonicalize and clamp explicit context: ${JSON.stringify(grepContextPolicyArgs)}`);
|
|
590
550
|
}
|
|
591
551
|
|
|
592
|
-
|
|
552
|
+
// Multiple absolute paths in one string are now auto-split into a path array
|
|
553
|
+
// (arg-guard splitMultipleAbsoluteWindowsPaths) instead of rejected.
|
|
554
|
+
const multiGrepPathArgs = {
|
|
593
555
|
pattern: 'providerStatus',
|
|
594
556
|
path: 'C:\\Project\\mixdog\\src\\tui C:\\Project\\mixdog\\src\\mixdog-session-runtime.mjs',
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
|
|
557
|
+
};
|
|
558
|
+
const multiGrepPathErr = validateBuiltinArgs('grep', multiGrepPathArgs);
|
|
559
|
+
if (multiGrepPathErr) {
|
|
560
|
+
throw new Error(`grep multi-path auto-split should pass validation: ${multiGrepPathErr}`);
|
|
561
|
+
}
|
|
562
|
+
if (!Array.isArray(multiGrepPathArgs.path) || multiGrepPathArgs.path.length !== 2) {
|
|
563
|
+
throw new Error(`grep multi-path auto-split should coerce to 2-element array: ${JSON.stringify(multiGrepPathArgs.path)}`);
|
|
598
564
|
}
|
|
599
565
|
|
|
600
|
-
|
|
566
|
+
// Lookaround/backrefs are no longer rejected at validation time: search-tool
|
|
567
|
+
// routes them to rg --pcre2 at runtime (arg-guard.mjs comment near globKeys).
|
|
568
|
+
const lookaroundGrepErr = validateBuiltinArgs('grep', {
|
|
601
569
|
pattern: 'C:\\\\Project(?!\\\\mixdog)',
|
|
602
570
|
path: root,
|
|
603
571
|
});
|
|
604
|
-
if (
|
|
605
|
-
throw new Error(`grep
|
|
572
|
+
if (lookaroundGrepErr) {
|
|
573
|
+
throw new Error(`grep lookaround pattern should pass validation (PCRE2 runtime routing): ${lookaroundGrepErr}`);
|
|
606
574
|
}
|
|
607
575
|
|
|
608
576
|
// Windows drive path + no explicit shell used to be rejected with a retry hint;
|
|
@@ -991,10 +959,7 @@ setInternalToolsProvider({
|
|
|
991
959
|
{ name: 'recall', description: 'Memory recall surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true } },
|
|
992
960
|
{ name: 'search', description: 'Web search surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true, openWorldHint: true } },
|
|
993
961
|
{ name: 'reply', description: 'Channel reply surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
994
|
-
{ name: 'edit_message', description: 'Channel edit surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
995
962
|
{ name: 'web_fetch', description: 'Web fetch surface.', inputSchema: { type: 'object', properties: {} }, annotations: { readOnlyHint: true, openWorldHint: true } },
|
|
996
|
-
{ name: 'reload_config', description: 'Config reload surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
997
|
-
{ name: 'inject_command', description: 'Command injection surface.', inputSchema: { type: 'object', properties: {} }, annotations: { destructiveHint: true } },
|
|
998
963
|
],
|
|
999
964
|
});
|
|
1000
965
|
{
|
|
@@ -1197,12 +1162,12 @@ setInternalToolsProvider({
|
|
|
1197
1162
|
if (readTools.includes('shell') || writeTools.includes('shell')) {
|
|
1198
1163
|
throw new Error(`read/read-write agent schemas must omit shell: read=${readTools.join(', ')} write=${writeTools.join(', ')}`);
|
|
1199
1164
|
}
|
|
1200
|
-
for (const name of ['shell', 'apply_patch', 'task'
|
|
1165
|
+
for (const name of ['shell', 'apply_patch', 'task']) {
|
|
1201
1166
|
if (readTools.includes(name)) {
|
|
1202
1167
|
throw new Error(`read agent schema must omit non-read tool ${name}: read=${readTools.join(', ')}`);
|
|
1203
1168
|
}
|
|
1204
1169
|
}
|
|
1205
|
-
for (const name of ['apply_patch', 'task'
|
|
1170
|
+
for (const name of ['apply_patch', 'task']) {
|
|
1206
1171
|
if (name === 'apply_patch' && !writeTools.includes(name)) {
|
|
1207
1172
|
throw new Error(`read-write agent schema must preserve apply_patch: write=${writeTools.join(', ')}`);
|
|
1208
1173
|
}
|
|
@@ -1210,12 +1175,7 @@ setInternalToolsProvider({
|
|
|
1210
1175
|
throw new Error(`read-write agent schema must omit non-edit tool ${name}: write=${writeTools.join(', ')}`);
|
|
1211
1176
|
}
|
|
1212
1177
|
}
|
|
1213
|
-
for (const name of ['
|
|
1214
|
-
if (writeTools.includes(name)) {
|
|
1215
|
-
throw new Error(`read-write agent schema must omit config/skill tool ${name}: write=${writeTools.join(', ')}`);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
for (const name of ['memory', 'recall', 'reply', 'edit_message', 'reload_config', 'inject_command']) {
|
|
1178
|
+
for (const name of ['memory', 'recall', 'reply']) {
|
|
1219
1179
|
if (readTools.includes(name) || writeTools.includes(name)) {
|
|
1220
1180
|
throw new Error(`read/read-write agent schema must not expose full-runtime internal tool ${name}: read=${readTools.join(', ')} write=${writeTools.join(', ')}`);
|
|
1221
1181
|
}
|
|
@@ -1293,7 +1253,7 @@ setInternalToolsProvider({
|
|
|
1293
1253
|
const hiddenAgents = JSON.parse(readFileSync(join(root, 'src', 'defaults', 'agents.json'), 'utf8')).agents || [];
|
|
1294
1254
|
const hiddenPreset = { id: 'hidden-smoke', name: 'hidden-smoke', type: 'agent', provider: 'openai-oauth', model: 'tool-smoke-model', tools: 'full' };
|
|
1295
1255
|
const hiddenRuntimeSpec = { scopeKey: 'hidden-role-smoke', lane: 'agent' };
|
|
1296
|
-
const hiddenBadTools = new Set(['shell', 'task', '
|
|
1256
|
+
const hiddenBadTools = new Set(['shell', 'task', 'Skill', 'memory', 'reply', 'recall']);
|
|
1297
1257
|
const expectedForHiddenAgent = (permission, schemaAllowedTools) => {
|
|
1298
1258
|
if (Array.isArray(schemaAllowedTools)) return schemaAllowedTools.slice();
|
|
1299
1259
|
if (permission === 'none') return [];
|
|
@@ -1517,13 +1477,13 @@ if (!/before text grep/i.test(codeGraphDescription)) {
|
|
|
1517
1477
|
if (!/repo-local/i.test(codeGraphDescription) || !/NOT web search|not web/i.test(codeGraphDescription)) {
|
|
1518
1478
|
throw new Error('code_graph description must mark itself repo-local (not web search)');
|
|
1519
1479
|
}
|
|
1520
|
-
if (!/repo-local|not web/i.test(codeGraphProps.mode?.description || '') || !/source file/i.test(codeGraphProps.
|
|
1480
|
+
if (!/repo-local|not web/i.test(codeGraphProps.mode?.description || '') || !/source file/i.test(codeGraphProps.files?.description || '')) {
|
|
1521
1481
|
throw new Error('code_graph schema must keep compact, repo-local field descriptions');
|
|
1522
1482
|
}
|
|
1523
1483
|
const recallTool = MEMORY_TOOL_DEFS.find((tool) => tool.name === 'recall');
|
|
1524
1484
|
const recallProps = recallTool?.inputSchema?.properties || {};
|
|
1525
|
-
if (!/
|
|
1526
|
-
throw new Error('recall schema must preserve prior-context guidance and id lookup shape');
|
|
1485
|
+
if (!/Use only to check prior conversation or past events/i.test(recallTool?.description || '') || !recallProps.id?.anyOf || !/Do not invent ids/i.test(recallProps.id?.description || '')) {
|
|
1486
|
+
throw new Error('recall schema must preserve scoped prior-context guidance and id lookup shape');
|
|
1527
1487
|
}
|
|
1528
1488
|
if (!/array for independent fan-out/i.test(recallProps.query?.description || '') || !/Project pool selector/i.test(recallProps.projectScope?.description || '')) {
|
|
1529
1489
|
throw new Error('recall schema must explain fan-out query and project scope filters');
|
|
@@ -1588,7 +1548,7 @@ if (!recallProps.sessionOnly || !/session only/i.test(recallProps.sessionOnly?.d
|
|
|
1588
1548
|
}
|
|
1589
1549
|
const memoryTool = MEMORY_TOOL_DEFS.find((tool) => tool.name === 'memory');
|
|
1590
1550
|
const memoryProps = memoryTool?.inputSchema?.properties || {};
|
|
1591
|
-
if (!/
|
|
1551
|
+
if (!/mutation/i.test(memoryTool?.description || '') || !/Exact confirmation phrase/i.test(memoryProps.confirm?.description || '')) {
|
|
1592
1552
|
throw new Error('memory schema must preserve mutation/destructive confirmation guidance');
|
|
1593
1553
|
}
|
|
1594
1554
|
const searchTool = SEARCH_TOOL_DEFS.find((tool) => tool.name === 'search');
|
|
@@ -1783,7 +1743,7 @@ if (grepTool?.inputSchema?.properties?.head_limit?.minimum !== 0 || !/keep small
|
|
|
1783
1743
|
if (grepTool?.inputSchema?.properties?.type) {
|
|
1784
1744
|
throw new Error('grep type schema must stay hidden; prefer glob for extension narrowing');
|
|
1785
1745
|
}
|
|
1786
|
-
if (!/code flow before text search/i.test(codeGraphProps.mode?.description || '') || !/one anchor is enough/i.test(codeGraphProps.
|
|
1746
|
+
if (!/code flow before text search/i.test(codeGraphProps.mode?.description || '') || !/one anchor is enough/i.test(codeGraphProps.symbols?.description || '') || !/one call/i.test(codeGraphProps.symbols?.description || '')) {
|
|
1787
1747
|
throw new Error('code_graph schema fields must steer away from repeated lookup loops');
|
|
1788
1748
|
}
|
|
1789
1749
|
|