mixdog 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -38
- package/package.json +10 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/r5-orchestrated-task.json +7 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +356 -0
- package/src/tui/app/model-options.mjs +334 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +489 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +677 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -1,292 +1,71 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (args.input && typeof args.input === 'object') return args.input;
|
|
70
|
-
return args;
|
|
71
|
-
}
|
|
72
|
-
return { value: args };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function displayToolPath(path) {
|
|
76
|
-
const text = String(path ?? '');
|
|
77
|
-
return text.replace(/\\/g, '/').split('/').filter(Boolean).at(-1) || text;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function compactParts(parts) {
|
|
81
|
-
return parts.filter((part) => part != null && String(part).trim()).map((part) => String(part).trim()).join(STATUS_SEPARATOR);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function compactSlash(left, right) {
|
|
85
|
-
const a = String(left ?? '').trim();
|
|
86
|
-
const b = String(right ?? '').trim();
|
|
87
|
-
return a && b ? `${a}/${b}` : a || b;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function mcpToolTarget(name, max = DEFAULT_SUMMARY_MAX) {
|
|
91
|
-
const mcp = parseMcpToolName(name);
|
|
92
|
-
if (!mcp) return '';
|
|
93
|
-
return truncateToolText(compactSlash(mcp.server, mcp.tool), max);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function quoted(value, max) {
|
|
97
|
-
const text = truncateToolText(value || '', max);
|
|
98
|
-
return text ? `"${text}"` : '';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function firstText(...values) {
|
|
102
|
-
for (const value of values) {
|
|
103
|
-
if (value != null && String(value).trim()) return String(value).trim();
|
|
104
|
-
}
|
|
105
|
-
return '';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function splitToolSearchSelection(value) {
|
|
109
|
-
if (Array.isArray(value)) return value.map((item) => String(item || '').trim()).filter(Boolean);
|
|
110
|
-
return String(value || '')
|
|
111
|
-
.split(/[,\s]+/)
|
|
112
|
-
.map((item) => item.trim())
|
|
113
|
-
.filter(Boolean);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function toolSearchTargetKind(value) {
|
|
117
|
-
const lower = String(value || '').trim().toLowerCase();
|
|
118
|
-
if (!lower) return '';
|
|
119
|
-
if (lower.startsWith('mcp__') || lower.includes('_mcp_') || lower.includes('mcp')) return 'MCP';
|
|
120
|
-
if (lower === 'skill' || lower.startsWith('skill:') || lower.startsWith('skill_') || lower.startsWith('skills_') || lower.includes('skill')) return 'Skills';
|
|
121
|
-
return 'Tools';
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function orderedToolSearchKinds(kinds) {
|
|
125
|
-
const set = new Set((kinds || []).filter(Boolean));
|
|
126
|
-
const order = ['Tools', 'MCP', 'Skills'];
|
|
127
|
-
const out = order.filter((kind) => set.has(kind));
|
|
128
|
-
return out.length ? out : ['Tools'];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function inferToolSearchKinds(args = {}) {
|
|
132
|
-
const selected = splitToolSearchSelection(args.select);
|
|
133
|
-
if (selected.length) return orderedToolSearchKinds(selected.map(toolSearchTargetKind));
|
|
134
|
-
|
|
135
|
-
const query = firstText(args.query, args.q, args.text).toLowerCase();
|
|
136
|
-
const kinds = [];
|
|
137
|
-
if (/\bmcp\b|mcp__|mcp[-_\s]?server/.test(query)) kinds.push('MCP');
|
|
138
|
-
if (/\bskills?\b|skill_|use[-_\s]?skill/.test(query)) kinds.push('Skills');
|
|
139
|
-
return orderedToolSearchKinds(kinds);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function toolSearchDisplayLabel(args = {}) {
|
|
143
|
-
return `Load ${inferToolSearchKinds(args).join('/')}`;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function displayToolSearchTarget(value) {
|
|
147
|
-
const text = String(value || '').trim();
|
|
148
|
-
if (/^skill:/i.test(text)) return text.slice('skill:'.length);
|
|
149
|
-
const mcp = /^mcp__(.*?)__(.+)$/.exec(text);
|
|
150
|
-
if (mcp) return `${mcp[1]}.${mcp[2]}`;
|
|
151
|
-
return stripToolPrefix(text);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function titleizeToolName(name) {
|
|
155
|
-
return stripToolPrefix(name)
|
|
156
|
-
.split(/[_\s-]+/)
|
|
157
|
-
.filter(Boolean)
|
|
158
|
-
.map((part) => {
|
|
159
|
-
const lower = part.toLowerCase();
|
|
160
|
-
if (lower === 'ui') return 'UI';
|
|
161
|
-
if (lower === 'mcp') return 'MCP';
|
|
162
|
-
if (lower === 'id') return 'ID';
|
|
163
|
-
return `${lower.slice(0, 1).toUpperCase()}${lower.slice(1)}`;
|
|
164
|
-
})
|
|
165
|
-
.join(' ') || 'Tool';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const AGENT_DISPLAY_NAMES = new Map([
|
|
169
|
-
['explore', 'Explore'],
|
|
170
|
-
['maintainer', 'Maintainer'],
|
|
171
|
-
['worker', 'Worker'],
|
|
172
|
-
['heavy-worker', 'Heavy Worker'],
|
|
173
|
-
['reviewer', 'Reviewer'],
|
|
174
|
-
['debugger', 'Debugger'],
|
|
175
|
-
]);
|
|
176
|
-
|
|
177
|
-
function titleizeDisplayName(value) {
|
|
178
|
-
const text = String(value || '').trim();
|
|
179
|
-
if (!text) return '';
|
|
180
|
-
return text
|
|
181
|
-
.replace(/[_-]+/g, ' ')
|
|
182
|
-
.split(/\s+/)
|
|
183
|
-
.filter(Boolean)
|
|
184
|
-
.map((part) => {
|
|
185
|
-
const lower = part.toLowerCase();
|
|
186
|
-
if (lower === 'ui') return 'UI';
|
|
187
|
-
if (lower === 'mcp') return 'MCP';
|
|
188
|
-
if (lower === 'id') return 'ID';
|
|
189
|
-
return `${lower.slice(0, 1).toUpperCase()}${lower.slice(1)}`;
|
|
190
|
-
})
|
|
191
|
-
.join(' ');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function displayAgentName(value) {
|
|
195
|
-
const text = String(value || '').trim();
|
|
196
|
-
if (!text) return '';
|
|
197
|
-
const key = text.toLowerCase().replace(/[\s_]+/g, '-');
|
|
198
|
-
return AGENT_DISPLAY_NAMES.get(key) || titleizeDisplayName(text);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function displayModelName(model, provider, displayHint) {
|
|
202
|
-
const text = String(model ?? '').trim();
|
|
203
|
-
const modelId = text
|
|
204
|
-
? (text.includes('/') ? (text.split('/').filter(Boolean).at(-1) || text) : text)
|
|
205
|
-
: '';
|
|
206
|
-
const shown = sharedDisplayModelName(modelId, provider, displayHint);
|
|
207
|
-
return shown || modelId;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function bridgeAgentModelSummary(args) {
|
|
211
|
-
const provider = firstText(args.provider, args.providerId, args.provider_id);
|
|
212
|
-
const modelId = firstText(args.model);
|
|
213
|
-
const displayHint = firstText(args.modelDisplay, args.model_display, args.displayModel);
|
|
214
|
-
return compactParts([
|
|
215
|
-
displayAgentName(firstText(args.agent, args.name, args.subagent_type)),
|
|
216
|
-
displayModelName(modelId, provider, displayHint),
|
|
217
|
-
]);
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function summarizeLineWindow(a) {
|
|
221
|
-
const offset = a.offset ?? a.start_line ?? a.startLine ?? a.line;
|
|
222
|
-
const limit = a.limit ?? a.line_count ?? a.lineCount ?? a.lines;
|
|
223
|
-
if (offset == null && limit == null) return '';
|
|
224
|
-
const start = Number(offset);
|
|
225
|
-
const count = Number(limit);
|
|
226
|
-
if (Number.isFinite(start) && Number.isFinite(count) && count > 0) {
|
|
227
|
-
return `lines ${start}-${Math.max(start, start + count - 1)}`;
|
|
228
|
-
}
|
|
229
|
-
if (Number.isFinite(start)) return `from line ${start}`;
|
|
230
|
-
if (Number.isFinite(count)) return `${count} lines`;
|
|
231
|
-
return '';
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
function summarizePatch(patch, basePath) {
|
|
235
|
-
const text = String(patch ?? '');
|
|
236
|
-
const files = [];
|
|
237
|
-
for (const line of text.split('\n')) {
|
|
238
|
-
const match = /^\*\*\*\s+(?:Update|Add|Delete) File:\s+(.+)\s*$/.exec(line);
|
|
239
|
-
if (match) files.push(displayToolPath(match[1]));
|
|
240
|
-
}
|
|
241
|
-
if (files.length === 1) return files[0];
|
|
242
|
-
if (files.length > 1) return `${files.length} files`;
|
|
243
|
-
if (basePath) return displayToolPath(basePath);
|
|
244
|
-
return text ? 'patch' : '';
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
function collectionCount(...values) {
|
|
248
|
-
for (const value of values) {
|
|
249
|
-
if (Array.isArray(value)) {
|
|
250
|
-
const count = value.filter((item) => item != null && String(item).trim()).length;
|
|
251
|
-
return count || value.length || 0;
|
|
252
|
-
}
|
|
253
|
-
if (value && typeof value === 'object') return 1;
|
|
254
|
-
if (value != null && String(value).trim()) return 1;
|
|
255
|
-
}
|
|
256
|
-
return 0;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function formatCountedUnit(count, singular, pluralText = `${singular}s`) {
|
|
260
|
-
const n = Math.max(0, Number(count || 0));
|
|
261
|
-
return n > 0 ? `${n} ${pluralize(n, singular, pluralText)}` : '';
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function patchFileCount(args = {}) {
|
|
265
|
-
const patchText = String(args.patch ?? '');
|
|
266
|
-
if (patchText) {
|
|
267
|
-
const files = new Set();
|
|
268
|
-
for (const line of patchText.split('\n')) {
|
|
269
|
-
const match = /^\*\*\*\s+(?:Update|Add|Delete) File:\s+(.+)\s*$/.exec(line);
|
|
270
|
-
if (match) files.add(match[1].trim());
|
|
271
|
-
}
|
|
272
|
-
if (files.size > 0) return files.size;
|
|
273
|
-
}
|
|
274
|
-
return collectionCount(args.path, args.file, args.file_path, args.base_path);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function codeGraphLabel(args) {
|
|
278
|
-
const mode = String(args.mode || args.action || '').toLowerCase();
|
|
279
|
-
if (mode === 'prewarm' || mode === 'index' || mode === 'build' || mode === 'refresh') return 'Setup';
|
|
280
|
-
if (mode === 'search' || mode === 'find_symbol' || mode === 'references' || mode === 'callers' || mode === 'callees') return 'Search';
|
|
281
|
-
return 'Read';
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function codeGraphSummary(args, max) {
|
|
285
|
-
return compactParts([
|
|
286
|
-
args.mode || args.action || '',
|
|
287
|
-
truncateToolText(firstText(args.symbol, Array.isArray(args.symbols) ? args.symbols.join(', ') : '', args.file, args.path, args.query), max),
|
|
288
|
-
]);
|
|
289
|
-
}
|
|
1
|
+
// ── Facade: tool-surface primitives + result summaries are re-exported from
|
|
2
|
+
// their cohesive modules so every existing importer resolves unchanged. This
|
|
3
|
+
// file keeps the display-name / arg-summary and aggregate-card clusters.
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_SUMMARY_MAX,
|
|
6
|
+
AGENT_SURFACE_BRIEF_MAX,
|
|
7
|
+
STATUS_SEPARATOR,
|
|
8
|
+
stripToolPrefix,
|
|
9
|
+
parseMcpToolName,
|
|
10
|
+
isMcpToolName,
|
|
11
|
+
normalizeToolName,
|
|
12
|
+
truncateToolText,
|
|
13
|
+
truncateSingleLine,
|
|
14
|
+
truncateCommand,
|
|
15
|
+
parseToolArgs,
|
|
16
|
+
displayToolPath,
|
|
17
|
+
compactParts,
|
|
18
|
+
compactSlash,
|
|
19
|
+
mcpToolTarget,
|
|
20
|
+
quoted,
|
|
21
|
+
firstText,
|
|
22
|
+
splitToolSearchSelection,
|
|
23
|
+
toolSearchDisplayLabel,
|
|
24
|
+
displayToolSearchTarget,
|
|
25
|
+
titleizeToolName,
|
|
26
|
+
displayAgentName,
|
|
27
|
+
displayModelName,
|
|
28
|
+
bridgeAgentModelSummary,
|
|
29
|
+
summarizeLineWindow,
|
|
30
|
+
summarizePatch,
|
|
31
|
+
collectionCount,
|
|
32
|
+
formatCountedUnit,
|
|
33
|
+
patchFileCount,
|
|
34
|
+
codeGraphLabel,
|
|
35
|
+
codeGraphSummary,
|
|
36
|
+
pluralize,
|
|
37
|
+
titleWord,
|
|
38
|
+
titleStatus,
|
|
39
|
+
} from './tool-primitives.mjs';
|
|
40
|
+
import {
|
|
41
|
+
parseLineDelta,
|
|
42
|
+
formatLineDelta,
|
|
43
|
+
parseUpdateSummary,
|
|
44
|
+
extractErrorCause,
|
|
45
|
+
summarizeToolResult,
|
|
46
|
+
isExplorerSurface,
|
|
47
|
+
summarizeAgentSurfaceBrief,
|
|
48
|
+
isMemorySurface,
|
|
49
|
+
} from './tool-result-summary.mjs';
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
AGENT_SURFACE_BRIEF_MAX,
|
|
53
|
+
DEFAULT_SUMMARY_MAX,
|
|
54
|
+
STATUS_SEPARATOR,
|
|
55
|
+
stripToolPrefix,
|
|
56
|
+
parseMcpToolName,
|
|
57
|
+
isMcpToolName,
|
|
58
|
+
normalizeToolName,
|
|
59
|
+
truncateToolText,
|
|
60
|
+
parseToolArgs,
|
|
61
|
+
displayToolPath,
|
|
62
|
+
displayModelName,
|
|
63
|
+
extractErrorCause,
|
|
64
|
+
summarizeToolResult,
|
|
65
|
+
isExplorerSurface,
|
|
66
|
+
summarizeAgentSurfaceBrief,
|
|
67
|
+
isMemorySurface,
|
|
68
|
+
};
|
|
290
69
|
|
|
291
70
|
export function displayToolName(name, args = {}) {
|
|
292
71
|
if (isMcpToolName(name)) return 'MCP';
|
|
@@ -315,8 +94,6 @@ export function displayToolName(name, args = {}) {
|
|
|
315
94
|
case 'list':
|
|
316
95
|
case 'ls':
|
|
317
96
|
return 'Search';
|
|
318
|
-
case 'trigger_schedule':
|
|
319
|
-
return 'Schedule';
|
|
320
97
|
case 'tool_search':
|
|
321
98
|
return toolSearchDisplayLabel(parseToolArgs(args));
|
|
322
99
|
case 'search':
|
|
@@ -329,15 +106,7 @@ export function displayToolName(name, args = {}) {
|
|
|
329
106
|
return 'Explore';
|
|
330
107
|
case 'web_fetch':
|
|
331
108
|
case 'fetch':
|
|
332
|
-
case 'download_attachment':
|
|
333
109
|
return 'Fetch';
|
|
334
|
-
case 'diagnostics':
|
|
335
|
-
case 'open_config':
|
|
336
|
-
case 'provider_status':
|
|
337
|
-
case 'channel_status':
|
|
338
|
-
case 'schedule_status':
|
|
339
|
-
case 'schedule_control':
|
|
340
|
-
case 'reload_config':
|
|
341
110
|
case 'list_mcp_resources':
|
|
342
111
|
case 'list_mcp_resource_templates':
|
|
343
112
|
case 'cwd':
|
|
@@ -366,10 +135,6 @@ export function displayToolName(name, args = {}) {
|
|
|
366
135
|
case 'code_graph':
|
|
367
136
|
return codeGraphLabel(parseToolArgs(args));
|
|
368
137
|
case 'reply':
|
|
369
|
-
case 'react':
|
|
370
|
-
case 'edit_message':
|
|
371
|
-
case 'activate_channel_bridge':
|
|
372
|
-
case 'inject_command':
|
|
373
138
|
return 'Channel';
|
|
374
139
|
default:
|
|
375
140
|
return titleizeToolName(name);
|
|
@@ -472,27 +237,11 @@ export function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}
|
|
|
472
237
|
return formatCountedUnit(collectionCount(a.url, a.uri), 'URL', 'URLs');
|
|
473
238
|
}
|
|
474
239
|
return truncateToolText(a.url || a.uri || '', max);
|
|
475
|
-
case 'download_attachment':
|
|
476
|
-
return displayToolPath(a.filename || a.name || a.url || '');
|
|
477
240
|
case 'read_mcp_resource':
|
|
478
241
|
return truncateToolText(a.uri || '', max);
|
|
479
242
|
case 'list_mcp_resources':
|
|
480
243
|
case 'list_mcp_resource_templates':
|
|
481
244
|
return a.server ? `server "${truncateToolText(a.server, max)}"` : 'all servers';
|
|
482
|
-
case 'diagnostics':
|
|
483
|
-
return truncateToolText(a.scope || a.mode || 'runtime', max);
|
|
484
|
-
case 'open_config':
|
|
485
|
-
return truncateToolText(a.section || a.tab || 'config', max);
|
|
486
|
-
case 'provider_status':
|
|
487
|
-
return truncateToolText(a.provider || 'providers', max);
|
|
488
|
-
case 'channel_status':
|
|
489
|
-
return truncateToolText(a.channel || a.name || 'channels', max);
|
|
490
|
-
case 'schedule_status':
|
|
491
|
-
case 'schedule_control':
|
|
492
|
-
case 'trigger_schedule':
|
|
493
|
-
return compactParts([a.action || a.type || 'schedule', a.name || a.id || '']);
|
|
494
|
-
case 'reload_config':
|
|
495
|
-
return truncateToolText(a.scope || 'config', max);
|
|
496
245
|
case 'cwd':
|
|
497
246
|
return truncateToolText(firstText(a.path, a.cwd, a.dir), max);
|
|
498
247
|
case 'memory':
|
|
@@ -524,13 +273,7 @@ export function summarizeToolArgs(name, args, { max = DEFAULT_SUMMARY_MAX } = {}
|
|
|
524
273
|
case 'code_graph':
|
|
525
274
|
return codeGraphSummary(a, max);
|
|
526
275
|
case 'reply':
|
|
527
|
-
case 'react':
|
|
528
|
-
case 'edit_message':
|
|
529
276
|
return truncateToolText(a.channel || a.channelId || a.messageId || a.emoji || '', max);
|
|
530
|
-
case 'activate_channel_bridge':
|
|
531
|
-
return a.active === false ? 'deactivate' : 'activate';
|
|
532
|
-
case 'inject_command':
|
|
533
|
-
return truncateToolText(firstText(a.command, a.text, a.name), max);
|
|
534
277
|
case 'skill':
|
|
535
278
|
case 'skill_execute':
|
|
536
279
|
case 'skill_view':
|
|
@@ -566,537 +309,6 @@ export function formatToolSurface(name, args, opts = {}) {
|
|
|
566
309
|
};
|
|
567
310
|
}
|
|
568
311
|
|
|
569
|
-
function pluralize(count, singular, pluralText = `${singular}s`) {
|
|
570
|
-
return count === 1 ? singular : pluralText;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function titleWord(value) {
|
|
574
|
-
const text = String(value || '');
|
|
575
|
-
return text ? `${text.charAt(0).toUpperCase()}${text.slice(1).toLowerCase()}` : '';
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function titleStatus(value) {
|
|
579
|
-
const text = String(value || '').trim();
|
|
580
|
-
if (!text) return '';
|
|
581
|
-
if (/^ok$/i.test(text)) return 'Ok';
|
|
582
|
-
if (/^(done|success|completed)$/i.test(text)) return 'Finished';
|
|
583
|
-
if (/^(error|failed|fail|killed|timeout)$/i.test(text)) return 'Failed';
|
|
584
|
-
if (/^(cancelled|canceled|cancel)$/i.test(text)) return 'Cancelled';
|
|
585
|
-
return titleWord(text);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
function countNonEmptyLines(text) {
|
|
589
|
-
return String(text ?? '')
|
|
590
|
-
.split('\n')
|
|
591
|
-
.filter((line) => line.trim()).length;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// Zero-result recognizer (audit HIGH): result text that SAYS "nothing found"
|
|
595
|
-
// must summarize as an explicit zero, not be line-counted into "1 match".
|
|
596
|
-
// Matches the shapes emitted by grep/glob/find/list/recall backends:
|
|
597
|
-
// "(no matches)", "no matches found", "(no results)", "No results",
|
|
598
|
-
// "(no fuzzy match for \"...\")", "0 matches", "(empty)", "(no entries)".
|
|
599
|
-
function looksLikeZeroResultText(text) {
|
|
600
|
-
const trimmed = String(text ?? '').trim();
|
|
601
|
-
if (!trimmed) return false;
|
|
602
|
-
// Only trust short, single-line-ish payloads — a real listing that merely
|
|
603
|
-
// CONTAINS the words "no matches" somewhere must not be zeroed.
|
|
604
|
-
if (trimmed.length > 200 || trimmed.includes('\n')) return false;
|
|
605
|
-
return /^\(?\s*(?:no|0)\s+(?:fuzzy\s+)?(?:match(?:es)?|results?|files?|entries|candidates?|hits?)\b/i.test(trimmed)
|
|
606
|
-
|| /^\(?\s*(?:empty|none)\s*\)?$/i.test(trimmed)
|
|
607
|
-
|| /^no\s+\S+\s+(?:found|matched)\b/i.test(trimmed);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function splitPathAndDelta(value, explicitDelta = '') {
|
|
611
|
-
let path = String(value ?? '').trim();
|
|
612
|
-
let delta = String(explicitDelta ?? '').trim();
|
|
613
|
-
if (!delta) {
|
|
614
|
-
const sep = path.lastIndexOf(' — ');
|
|
615
|
-
if (sep !== -1) {
|
|
616
|
-
delta = path.slice(sep + 3).trim();
|
|
617
|
-
path = path.slice(0, sep).trim();
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
return { path, delta };
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
function parseLineDelta(delta) {
|
|
624
|
-
const totals = { added: 0, removed: 0, seen: false };
|
|
625
|
-
for (const match of String(delta ?? '').matchAll(/([+-])\s*(\d+)\s*(?:line|lines)?/gi)) {
|
|
626
|
-
const n = Number(match[2]) || 0;
|
|
627
|
-
totals.seen = true;
|
|
628
|
-
if (match[1] === '+') totals.added += n;
|
|
629
|
-
else totals.removed += n;
|
|
630
|
-
}
|
|
631
|
-
return totals;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function formatLineDelta(totals) {
|
|
635
|
-
if (!totals?.seen) return '';
|
|
636
|
-
const added = Number(totals.added) || 0;
|
|
637
|
-
const removed = Number(totals.removed) || 0;
|
|
638
|
-
if (added === 0 && removed === 0) return '';
|
|
639
|
-
const parts = [];
|
|
640
|
-
if (added > 0) parts.push(`+${added} ${pluralize(added, 'line')}`);
|
|
641
|
-
if (removed > 0) parts.push(`-${removed} ${pluralize(removed, 'line')}`);
|
|
642
|
-
return parts.join(STATUS_SEPARATOR);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
function parseUpdateSummary(text) {
|
|
646
|
-
const match = /^(Updated|Created|Deleted)\s+(.+?)(?:\s+·\s+|$)/i.exec(String(text || '').trim());
|
|
647
|
-
if (!match) return null;
|
|
648
|
-
const action = titleWord(match[1]);
|
|
649
|
-
const target = match[2].trim();
|
|
650
|
-
const fileCountMatch = /^(\d+)\s+Files?$/i.exec(target);
|
|
651
|
-
const totals = parseLineDelta(text);
|
|
652
|
-
return {
|
|
653
|
-
action,
|
|
654
|
-
file: fileCountMatch ? '' : target,
|
|
655
|
-
fileCount: fileCountMatch ? Number(fileCountMatch[1]) || 0 : 0,
|
|
656
|
-
added: totals.added,
|
|
657
|
-
removed: totals.removed,
|
|
658
|
-
seen: totals.seen,
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
/** Heuristic: does the text look like a line-oriented listing rather than prose? */
|
|
663
|
-
function looksLineOriented(text) {
|
|
664
|
-
const lines = String(text ?? '').split('\n').filter((line) => line.trim());
|
|
665
|
-
if (lines.length === 0) return false;
|
|
666
|
-
// Prose tends to be a few long sentences; listings are many shorter rows.
|
|
667
|
-
const longLines = lines.filter((line) => line.trim().length > 200).length;
|
|
668
|
-
return longLines === 0;
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function summarizeUpdateResult(text, args) {
|
|
672
|
-
// A dry_run patch validates without writing, so the collapsed detail must not
|
|
673
|
-
// claim a real mutation ("Updated/Created/Deleted foo.js"). Map every action
|
|
674
|
-
// to "Checked" wording, matching the dry-run header (Checking/Checked).
|
|
675
|
-
const isDryRun = parseToolArgs(args)?.dry_run === true;
|
|
676
|
-
const changed = [];
|
|
677
|
-
for (const line of String(text ?? '').split('\n')) {
|
|
678
|
-
const ok = /^\s*OK\s+(modify|add|delete|create)\s+(.+?)\s*$/i.exec(line);
|
|
679
|
-
if (ok) {
|
|
680
|
-
const { path, delta } = splitPathAndDelta(ok[2]);
|
|
681
|
-
changed.push({ action: ok[1].toLowerCase(), path, delta });
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
const edited = /^\s*(Edited|Created|Updated|Wrote):\s+(.+?)(?:\s+\(([^)]+)\))?\s*$/i.exec(line);
|
|
685
|
-
if (edited) {
|
|
686
|
-
const { path, delta } = splitPathAndDelta(edited[2], edited[3] || '');
|
|
687
|
-
changed.push({ action: edited[1].toLowerCase(), path, delta });
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
if (changed.length === 1) {
|
|
691
|
-
const item = changed[0];
|
|
692
|
-
const action = isDryRun
|
|
693
|
-
? 'Checked'
|
|
694
|
-
: item.action === 'delete' ? 'Deleted' : item.action === 'add' || item.action === 'create' || item.action === 'created' ? 'Created' : 'Updated';
|
|
695
|
-
return compactParts([`${action} ${displayToolPath(item.path)}`, formatLineDelta(parseLineDelta(item.delta))]);
|
|
696
|
-
}
|
|
697
|
-
if (changed.length > 1) {
|
|
698
|
-
const totals = changed.reduce((acc, item) => {
|
|
699
|
-
const delta = parseLineDelta(item.delta);
|
|
700
|
-
acc.added += delta.added;
|
|
701
|
-
acc.removed += delta.removed;
|
|
702
|
-
acc.seen = acc.seen || delta.seen;
|
|
703
|
-
return acc;
|
|
704
|
-
}, { added: 0, removed: 0, seen: false });
|
|
705
|
-
return compactParts([`${isDryRun ? 'Checked' : 'Updated'} ${changed.length} Files`, formatLineDelta(totals)]);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
const parsedArgs = parseToolArgs(args);
|
|
709
|
-
const target = parsedArgs.path ?? parsedArgs.file ?? parsedArgs.file_path ?? '';
|
|
710
|
-
if (target) return `${isDryRun ? 'Checked' : 'Updated'} ${displayToolPath(target)}`;
|
|
711
|
-
return null;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
function textBetweenTag(text, tag) {
|
|
715
|
-
const re = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i');
|
|
716
|
-
const match = re.exec(String(text ?? ''));
|
|
717
|
-
return match ? match[1].trim() : '';
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
// Strip the most common inline-markdown markers so a one-line card summary
|
|
721
|
-
// reads as plain prose ("**not clean**" → "not clean", "`x`" → "x"). Block
|
|
722
|
-
// markers (#, >, -, 1.) at line start are dropped too. Whitespace is collapsed
|
|
723
|
-
// by the caller's truncateSingleLine.
|
|
724
|
-
function stripInlineMarkdown(value) {
|
|
725
|
-
return String(value ?? '')
|
|
726
|
-
.replace(/^\s{0,3}(?:#{1,6}\s+|>\s+|[-*+]\s+|\d+\.\s+)/, '')
|
|
727
|
-
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
728
|
-
.replace(/__([^_]+)__/g, '$1')
|
|
729
|
-
.replace(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g, '$1')
|
|
730
|
-
.replace(/`([^`]+)`/g, '$1')
|
|
731
|
-
.trim();
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function firstAgentResultLine(text) {
|
|
735
|
-
const finalAnswer = textBetweenTag(text, 'final-answer') || textBetweenTag(text, 'result');
|
|
736
|
-
const raw = finalAnswer || text;
|
|
737
|
-
for (const line of String(raw ?? '').split('\n')) {
|
|
738
|
-
const trimmed = line.trim();
|
|
739
|
-
if (!trimmed) continue;
|
|
740
|
-
if (/^agent result\b/i.test(trimmed)) continue;
|
|
741
|
-
if (/^<\/?(?:final-answer|task-notification|task-id|tool-use-id|output-file|result|status|summary|usage|total_tokens|tool_uses|duration_ms|worktree|worktreePath|worktreeBranch)[^>]*>$/i.test(trimmed)) continue;
|
|
742
|
-
if (/^(?:agent task|status|type|target|role|agent|preset|model|effort|fast|limits|session|task-id|task_id|notification|queueDepth):\s*/i.test(trimmed)) continue;
|
|
743
|
-
if (/^\[[a-z-]+:\s*[^\]]*\]$/i.test(trimmed)) continue;
|
|
744
|
-
return truncateSingleLine(trimmed, AGENT_SURFACE_BRIEF_MAX);
|
|
745
|
-
}
|
|
746
|
-
return '';
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
function summarizeGenericResult(text) {
|
|
750
|
-
const trimmed = String(text ?? '').trim();
|
|
751
|
-
if (!trimmed) return null;
|
|
752
|
-
if (/^(?:undefined|null)$/i.test(trimmed)) return null;
|
|
753
|
-
|
|
754
|
-
if (/^[\[{]/.test(trimmed)) {
|
|
755
|
-
try {
|
|
756
|
-
const parsed = JSON.parse(trimmed);
|
|
757
|
-
if (Array.isArray(parsed)) return `${parsed.length} ${pluralize(parsed.length, 'item')}`;
|
|
758
|
-
if (parsed && typeof parsed === 'object') {
|
|
759
|
-
const status = firstText(parsed.status, parsed.state, parsed.result, parsed.message);
|
|
760
|
-
if (status) return truncateSingleLine(titleStatus(status), AGENT_SURFACE_BRIEF_MAX);
|
|
761
|
-
if (parsed.cwd) return truncateSingleLine(parsed.cwd, AGENT_SURFACE_BRIEF_MAX);
|
|
762
|
-
if (typeof parsed.ok === 'boolean') return parsed.ok ? 'Ok' : 'Failed';
|
|
763
|
-
for (const key of ['items', 'results', 'resources', 'templates', 'providers', 'schedules', 'channels', 'tools']) {
|
|
764
|
-
if (Array.isArray(parsed[key])) return `${parsed[key].length} ${pluralize(parsed[key].length, (key.slice(0, -1) || 'item').toLowerCase())}`;
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
} catch {
|
|
768
|
-
return null;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
const line = firstAgentResultLine(text) || trimmed.split('\n').map((item) => item.trim()).find(Boolean) || '';
|
|
773
|
-
if (!line || line === '{' || line === '[') return null;
|
|
774
|
-
if (/^(ok|done|success|saved|sent|updated|reloaded|connected|enabled|disabled|active|inactive)$/i.test(line)) {
|
|
775
|
-
return titleStatus(line);
|
|
776
|
-
}
|
|
777
|
-
return truncateSingleLine(line, AGENT_SURFACE_BRIEF_MAX);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
// Error-cause extractor (audit HIGH): when a tool result is an error, surface
|
|
781
|
-
// the actual cause line instead of a bare "Failed"/raw first line. Handles
|
|
782
|
-
// JSON error envelopes ({error|message|cause|detail}) and plain-text bodies
|
|
783
|
-
// (first line that carries error-ish signal, else first non-empty line).
|
|
784
|
-
// Exported for ToolExecution's collapsed detail row.
|
|
785
|
-
export function extractErrorCause(resultText) {
|
|
786
|
-
const text = String(resultText ?? '').trim();
|
|
787
|
-
if (!text) return '';
|
|
788
|
-
// JSON envelope: prefer explicit error-ish keys.
|
|
789
|
-
if (text.startsWith('{') || text.startsWith('[')) {
|
|
790
|
-
try {
|
|
791
|
-
const parsed = JSON.parse(text);
|
|
792
|
-
const obj = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
793
|
-
if (obj && typeof obj === 'object') {
|
|
794
|
-
const cause = firstText(
|
|
795
|
-
typeof obj.error === 'string' ? obj.error : obj.error?.message,
|
|
796
|
-
obj.message, obj.cause, obj.detail, obj.reason, obj.status,
|
|
797
|
-
);
|
|
798
|
-
if (cause) return truncateSingleLine(String(cause), AGENT_SURFACE_BRIEF_MAX);
|
|
799
|
-
}
|
|
800
|
-
} catch { /* fall through to text scan */ }
|
|
801
|
-
}
|
|
802
|
-
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
803
|
-
// Prefer the first line that looks like an error statement.
|
|
804
|
-
const errorish = lines.find((l) => /\b(error|failed|failure|denied|refused|timed?\s*out|timeout|not\s+found|missing|invalid|cannot|can't|exception|exit\s+(?:code\s+)?[1-9])\b/i.test(l));
|
|
805
|
-
const picked = errorish || lines[0] || '';
|
|
806
|
-
return truncateSingleLine(stripInlineMarkdown(picked), AGENT_SURFACE_BRIEF_MAX);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
/**
|
|
810
|
-
* Derive a short semantic one-liner for a completed tool call using only the
|
|
811
|
-
* tool name, parsed args, and the raw result text. Returns null when nothing
|
|
812
|
-
* reliable can be derived, so the caller falls back to the raw result block.
|
|
813
|
-
*/
|
|
814
|
-
export function summarizeToolResult(name, args, resultText, isError = false) {
|
|
815
|
-
if (isError) {
|
|
816
|
-
// Audit HIGH: errors used to disable semantic summaries entirely, leaving
|
|
817
|
-
// the UI with a raw first line or a bare "Failed". Surface the extracted
|
|
818
|
-
// cause so the collapsed card answers "why" without ctrl+o.
|
|
819
|
-
const cause = extractErrorCause(resultText);
|
|
820
|
-
return cause || null;
|
|
821
|
-
}
|
|
822
|
-
const text = String(resultText ?? '');
|
|
823
|
-
const trimmed = text.trim();
|
|
824
|
-
if (/^(?:undefined|null)$/i.test(trimmed)) return null;
|
|
825
|
-
if (isMcpToolName(name)) return trimmed ? firstAgentResultLine(text) || null : null;
|
|
826
|
-
const normalized = normalizeToolName(name);
|
|
827
|
-
|
|
828
|
-
switch (normalized) {
|
|
829
|
-
case 'read':
|
|
830
|
-
case 'view_image':
|
|
831
|
-
case 'read_mcp_resource': {
|
|
832
|
-
if (/^\[image:/i.test(trimmed)) return 'Image';
|
|
833
|
-
if (!trimmed) return null;
|
|
834
|
-
const n = text.split('\n').length;
|
|
835
|
-
return `${n} ${pluralize(n, 'line')}`;
|
|
836
|
-
}
|
|
837
|
-
case 'apply_patch': {
|
|
838
|
-
const updateSummary = summarizeUpdateResult(text, args);
|
|
839
|
-
if (updateSummary) return updateSummary;
|
|
840
|
-
// Prefer explicit additions/removals hints if the result text states them.
|
|
841
|
-
const add = /(\d+)\s+addition/i.exec(text);
|
|
842
|
-
const rem = /(\d+)\s+removal/i.exec(text);
|
|
843
|
-
if (add || rem) {
|
|
844
|
-
const a = add ? Number(add[1]) : 0;
|
|
845
|
-
const r = rem ? Number(rem[1]) : 0;
|
|
846
|
-
return `+${a} -${r}`;
|
|
847
|
-
}
|
|
848
|
-
// Else count unified-diff style +/- lines (ignore +++/--- file headers).
|
|
849
|
-
let a = 0;
|
|
850
|
-
let r = 0;
|
|
851
|
-
for (const line of text.split('\n')) {
|
|
852
|
-
if (/^\+\+\+|^---/.test(line)) continue;
|
|
853
|
-
if (/^\+/.test(line)) a += 1;
|
|
854
|
-
else if (/^-/.test(line)) r += 1;
|
|
855
|
-
}
|
|
856
|
-
if (a > 0 || r > 0) return `+${a} -${r}`;
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
case 'grep': {
|
|
860
|
-
if (!trimmed || !looksLineOriented(text)) return null;
|
|
861
|
-
if (looksLikeZeroResultText(text)) return '0 matches';
|
|
862
|
-
const n = countNonEmptyLines(text);
|
|
863
|
-
if (n === 0) return null;
|
|
864
|
-
return `${n} ${pluralize(n, 'match', 'matches')}`;
|
|
865
|
-
}
|
|
866
|
-
case 'glob': {
|
|
867
|
-
if (!trimmed || !looksLineOriented(text)) return null;
|
|
868
|
-
if (looksLikeZeroResultText(text)) return '0 files';
|
|
869
|
-
const n = countNonEmptyLines(text);
|
|
870
|
-
if (n === 0) return null;
|
|
871
|
-
return `${n} ${pluralize(n, 'file')}`;
|
|
872
|
-
}
|
|
873
|
-
case 'find': {
|
|
874
|
-
if (!trimmed || !looksLineOriented(text)) return null;
|
|
875
|
-
if (looksLikeZeroResultText(text)) return '0 candidates';
|
|
876
|
-
const n = countNonEmptyLines(text);
|
|
877
|
-
if (n === 0) return null;
|
|
878
|
-
return `${n} ${pluralize(n, 'candidate')}`;
|
|
879
|
-
}
|
|
880
|
-
case 'list':
|
|
881
|
-
case 'ls': {
|
|
882
|
-
if (!trimmed || !looksLineOriented(text)) return null;
|
|
883
|
-
if (looksLikeZeroResultText(text)) return '0 entries';
|
|
884
|
-
const n = countNonEmptyLines(text);
|
|
885
|
-
if (n === 0) return null;
|
|
886
|
-
return `${n} ${pluralize(n, 'entry', 'entries')}`;
|
|
887
|
-
}
|
|
888
|
-
case 'shell':
|
|
889
|
-
case 'bash':
|
|
890
|
-
case 'bash_session':
|
|
891
|
-
case 'shell_command':
|
|
892
|
-
case 'job_wait': {
|
|
893
|
-
if (!trimmed) return '(No Output)';
|
|
894
|
-
const job = /^\[(?:task_id|job):\s*([^\]]+)\]/mi.exec(text);
|
|
895
|
-
const status = /^\[status:\s*([^\]]+)\]/mi.exec(text);
|
|
896
|
-
const exit = /^\[exit:\s*([^\]]+)\]/mi.exec(text);
|
|
897
|
-
if (job || status || exit) {
|
|
898
|
-
return compactParts([
|
|
899
|
-
job ? job[1] : '',
|
|
900
|
-
status ? titleStatus(status[1]) : '',
|
|
901
|
-
exit ? `Exit ${exit[1]}` : '',
|
|
902
|
-
]);
|
|
903
|
-
}
|
|
904
|
-
const firstLine = trimmed.split('\n').map((line) => line.trim()).find(Boolean) || trimmed;
|
|
905
|
-
return truncateSingleLine(firstLine, AGENT_SURFACE_BRIEF_MAX);
|
|
906
|
-
}
|
|
907
|
-
case 'code_graph': {
|
|
908
|
-
const match = /(\d+)\s+(references|definitions|symbols|callers|callees|results|matches)/i.exec(text);
|
|
909
|
-
if (match) return `${match[1]} ${String(match[2]).toLowerCase()}`;
|
|
910
|
-
if (looksLikeZeroResultText(text)) return 'No results';
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
case 'web_fetch':
|
|
914
|
-
case 'fetch': {
|
|
915
|
-
// Audit HIGH: channel `fetch` (Discord message fetch — args carry
|
|
916
|
-
// channel/messageId/limit, never url/uri) was summarized as a WEB fetch,
|
|
917
|
-
// so its result missed both the status/size probes and fell to raw JSON.
|
|
918
|
-
// Route it to the generic JSON/text summarizer instead.
|
|
919
|
-
if (normalized === 'fetch') {
|
|
920
|
-
const a = parseToolArgs(args);
|
|
921
|
-
const isChannelFetch = !firstText(a.url, a.uri)
|
|
922
|
-
&& Boolean(firstText(a.channel, a.channelId, a.chatId, a.messageId) || a.limit != null);
|
|
923
|
-
if (isChannelFetch) {
|
|
924
|
-
const n = countNonEmptyLines(text);
|
|
925
|
-
if (trimmed && looksLineOriented(text) && n > 0) return `${n} ${pluralize(n, 'message')}`;
|
|
926
|
-
return summarizeGenericResult(text);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
// Status: require a status-like context (HTTP NNN, "Status: NNN",
|
|
930
|
-
// or "NNN OK"/"NNN Not Found") rather than any bare 3-digit number.
|
|
931
|
-
const status = /(?:HTTP[\s/]*\d?\.?\d?\s*|status[:\s]+)([1-5]\d{2})\b/i.exec(text)
|
|
932
|
-
|| /\b([1-5]\d{2})\s+(?:OK|Not\s+Found|Forbidden|Moved|Found|Created|No\s+Content|Bad\s+Request|Unauthorized|Internal)/.exec(text);
|
|
933
|
-
const size = /\b(\d+(?:\.\d+)?\s?(?:[KMGT]?B|bytes))\b/i.exec(text);
|
|
934
|
-
if (size && status) return `${size[1]} · HTTP ${status[1]}`;
|
|
935
|
-
if (size) return size[1];
|
|
936
|
-
if (status) return `HTTP ${status[1]}`;
|
|
937
|
-
return null;
|
|
938
|
-
}
|
|
939
|
-
case 'download_attachment':
|
|
940
|
-
return summarizeGenericResult(text);
|
|
941
|
-
case 'search': {
|
|
942
|
-
const match = /(\d+)\s+results?/i.exec(text);
|
|
943
|
-
if (match) {
|
|
944
|
-
const n = Number(match[1]);
|
|
945
|
-
return `${n} ${pluralize(n, 'result')}`;
|
|
946
|
-
}
|
|
947
|
-
return null;
|
|
948
|
-
}
|
|
949
|
-
case 'search_query':
|
|
950
|
-
case 'image_query':
|
|
951
|
-
case 'web_search':
|
|
952
|
-
case 'web_search_call': {
|
|
953
|
-
const match = /(\d+)\s+results?/i.exec(text);
|
|
954
|
-
if (match) {
|
|
955
|
-
const n = Number(match[1]);
|
|
956
|
-
return `${n} ${pluralize(n, 'result')}`;
|
|
957
|
-
}
|
|
958
|
-
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
959
|
-
}
|
|
960
|
-
case 'explore': {
|
|
961
|
-
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
962
|
-
}
|
|
963
|
-
case 'recall':
|
|
964
|
-
case 'search_memories':
|
|
965
|
-
case 'memory': {
|
|
966
|
-
if (!trimmed || trimmed === '(no results)' || looksLikeZeroResultText(text)) return 'No Results';
|
|
967
|
-
let n = 0;
|
|
968
|
-
for (const line of text.split('\n')) {
|
|
969
|
-
if (/#\d+\s*$/.test(line)) n += 1;
|
|
970
|
-
}
|
|
971
|
-
if (n > 0) return `${n} ${pluralize(n, 'Memory', 'Memories')}`;
|
|
972
|
-
return summarizeGenericResult(text);
|
|
973
|
-
}
|
|
974
|
-
case 'remember':
|
|
975
|
-
case 'save_memory':
|
|
976
|
-
case 'update_memory':
|
|
977
|
-
case 'reply':
|
|
978
|
-
case 'react':
|
|
979
|
-
case 'edit_message':
|
|
980
|
-
case 'activate_channel_bridge':
|
|
981
|
-
case 'inject_command':
|
|
982
|
-
case 'request_user_input':
|
|
983
|
-
case 'update_plan':
|
|
984
|
-
case 'diagnostics':
|
|
985
|
-
case 'open_config':
|
|
986
|
-
case 'provider_status':
|
|
987
|
-
case 'channel_status':
|
|
988
|
-
case 'schedule_status':
|
|
989
|
-
case 'schedule_control':
|
|
990
|
-
case 'trigger_schedule':
|
|
991
|
-
case 'reload_config':
|
|
992
|
-
case 'cwd':
|
|
993
|
-
case 'list_mcp_resources':
|
|
994
|
-
case 'list_mcp_resource_templates':
|
|
995
|
-
return summarizeGenericResult(text);
|
|
996
|
-
case 'skill':
|
|
997
|
-
case 'skill_execute':
|
|
998
|
-
case 'skill_view':
|
|
999
|
-
case 'skills_list':
|
|
1000
|
-
case 'use_skill': {
|
|
1001
|
-
const parsedArgs = parseToolArgs(args);
|
|
1002
|
-
const target = firstText(parsedArgs.name, parsedArgs.skill, parsedArgs.skill_name);
|
|
1003
|
-
if (normalized === 'skills_list') {
|
|
1004
|
-
const count = /(\d+)\s+skills?/i.exec(text);
|
|
1005
|
-
if (count) return `${Number(count[1]) || count[1]} ${pluralize(Number(count[1]) || 0, 'skill')}`;
|
|
1006
|
-
const lines = countNonEmptyLines(text);
|
|
1007
|
-
return lines > 0 ? `${lines} ${pluralize(lines, 'skill')}` : null;
|
|
1008
|
-
}
|
|
1009
|
-
if (target) {
|
|
1010
|
-
const verb = normalized === 'skill' || normalized === 'skill_view' ? 'Loaded' : 'Used';
|
|
1011
|
-
return `${verb} ${truncateToolText(target, 80)}`;
|
|
1012
|
-
}
|
|
1013
|
-
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
1014
|
-
}
|
|
1015
|
-
case 'agent':
|
|
1016
|
-
case 'task': {
|
|
1017
|
-
// Status-check (list/status) envelopes start with "agents: N" / "tasks: M"
|
|
1018
|
-
// (or "(no agents or tasks)"). Collapse them to a tight count summary
|
|
1019
|
-
// instead of leaking the raw "agents: 3 …" worker dump into the card.
|
|
1020
|
-
if (/^\(no agents or tasks\)$/im.test(text)) return 'No agents or tasks';
|
|
1021
|
-
const agentsCount = /^agents:\s*(\d+)/im.exec(text);
|
|
1022
|
-
const tasksCount = /^tasks:\s*(\d+)/im.exec(text);
|
|
1023
|
-
if (agentsCount || tasksCount) {
|
|
1024
|
-
const a = agentsCount ? Number(agentsCount[1]) : 0;
|
|
1025
|
-
const t = tasksCount ? Number(tasksCount[1]) : 0;
|
|
1026
|
-
const parts = [];
|
|
1027
|
-
if (agentsCount) parts.push(`${a} ${pluralize(a, 'agent')}`);
|
|
1028
|
-
if (tasksCount && t > 0) parts.push(`${t} ${pluralize(t, 'task')}`);
|
|
1029
|
-
return compactParts(parts) || 'No agents or tasks';
|
|
1030
|
-
}
|
|
1031
|
-
const answerLine = firstAgentResultLine(text);
|
|
1032
|
-
// Agent/task result cards show only a one-liner; full report via ctrl+o.
|
|
1033
|
-
if (answerLine) return truncateSingleLine(stripInlineMarkdown(answerLine), AGENT_SURFACE_BRIEF_MAX);
|
|
1034
|
-
const task = /^agent task:\s*(\S+)/mi.exec(text);
|
|
1035
|
-
const status = /^status:\s*([^\s(]+)/mi.exec(text);
|
|
1036
|
-
const agent = /^agent:\s*(.+)$/mi.exec(text);
|
|
1037
|
-
const preset = /^preset:\s*(.+)$/mi.exec(text);
|
|
1038
|
-
const model = /^model:\s*(.+)$/mi.exec(text);
|
|
1039
|
-
const limits = /^limits:\s*(.+)$/mi.exec(text);
|
|
1040
|
-
const agentModel = compactParts([
|
|
1041
|
-
displayAgentName(agent ? agent[1] : ''),
|
|
1042
|
-
displayModelName(model ? model[1] : ''),
|
|
1043
|
-
]);
|
|
1044
|
-
if (agentModel) return agentModel;
|
|
1045
|
-
const parts = [
|
|
1046
|
-
task ? task[1] : '',
|
|
1047
|
-
agent ? agent[1] : '',
|
|
1048
|
-
preset ? preset[1] : '',
|
|
1049
|
-
model ? model[1] : '',
|
|
1050
|
-
status ? titleStatus(status[1]) : '',
|
|
1051
|
-
limits ? limits[1] : '',
|
|
1052
|
-
].filter(Boolean);
|
|
1053
|
-
if (parts.length) return compactParts(parts);
|
|
1054
|
-
if (task) return status ? `${task[1]} ${titleStatus(status[1])}` : task[1];
|
|
1055
|
-
return null;
|
|
1056
|
-
}
|
|
1057
|
-
default:
|
|
1058
|
-
return null;
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
export function isExplorerSurface(label) {
|
|
1063
|
-
return label === 'Read' || label === 'Search';
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
function truncateAgentSurfaceBrief(value, max = AGENT_SURFACE_BRIEF_MAX) {
|
|
1067
|
-
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
1068
|
-
if (!text) return '';
|
|
1069
|
-
if (text.length <= max) return text;
|
|
1070
|
-
return `${text.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
// Tight one-liner for an agent/task card: prefer the inbound prompt/message for
|
|
1074
|
-
// spawn/send, or a stripped first line of the result when a response landed.
|
|
1075
|
-
export function summarizeAgentSurfaceBrief(name, args, resultText, { isError = false, isResponse = false } = {}) {
|
|
1076
|
-
const a = parseToolArgs(args);
|
|
1077
|
-
const action = String(a?.type || a?.action || '').toLowerCase();
|
|
1078
|
-
const text = String(resultText ?? '').trim();
|
|
1079
|
-
if (isResponse && text) {
|
|
1080
|
-
const fromResult = summarizeToolResult(name, args, text, isError);
|
|
1081
|
-
if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
|
|
1082
|
-
const line = firstAgentResultLine(text);
|
|
1083
|
-
if (line) return truncateAgentSurfaceBrief(stripInlineMarkdown(line));
|
|
1084
|
-
}
|
|
1085
|
-
const outbound = firstText(a?.prompt, a?.message);
|
|
1086
|
-
if (outbound && (action === 'spawn' || action === 'send' || !action)) {
|
|
1087
|
-
return truncateAgentSurfaceBrief(outbound);
|
|
1088
|
-
}
|
|
1089
|
-
if ((action === 'spawn' || action === 'send') && text && !isResponse) {
|
|
1090
|
-
const fromResult = summarizeToolResult(name, args, text, isError);
|
|
1091
|
-
if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
|
|
1092
|
-
}
|
|
1093
|
-
return '';
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
export function isMemorySurface(label) {
|
|
1097
|
-
return label === 'Memory';
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
312
|
// ── Aggregate tool-card classification & formatting ──────────────
|
|
1101
313
|
|
|
1102
314
|
export const CATEGORY_ORDER = [
|
|
@@ -1121,7 +333,6 @@ const TOOL_CATEGORY = new Map([
|
|
|
1121
333
|
['web_search_call', 'Web Research'],
|
|
1122
334
|
['web_fetch', 'Web Research'],
|
|
1123
335
|
['fetch', 'Web Research'],
|
|
1124
|
-
['download_attachment', 'Web Research'],
|
|
1125
336
|
['recall', 'Memory'],
|
|
1126
337
|
['recall_memory', 'Memory'],
|
|
1127
338
|
['search_memories', 'Memory'],
|
|
@@ -1139,23 +350,11 @@ const TOOL_CATEGORY = new Map([
|
|
|
1139
350
|
['task', 'Task'],
|
|
1140
351
|
['agent', 'Agent'],
|
|
1141
352
|
['reply', 'Channel'],
|
|
1142
|
-
['react', 'Channel'],
|
|
1143
|
-
['edit_message', 'Channel'],
|
|
1144
|
-
['activate_channel_bridge', 'Channel'],
|
|
1145
|
-
['inject_command', 'Channel'],
|
|
1146
|
-
['diagnostics', 'Setup'],
|
|
1147
|
-
['open_config', 'Setup'],
|
|
1148
|
-
['provider_status', 'Setup'],
|
|
1149
|
-
['channel_status', 'Setup'],
|
|
1150
|
-
['schedule_status', 'Setup'],
|
|
1151
|
-
['schedule_control', 'Setup'],
|
|
1152
|
-
['reload_config', 'Setup'],
|
|
1153
353
|
['list_mcp_resources', 'Setup'],
|
|
1154
354
|
['list_mcp_resource_templates', 'Setup'],
|
|
1155
355
|
['cwd', 'Setup'],
|
|
1156
356
|
['request_user_input', 'Setup'],
|
|
1157
357
|
['update_plan', 'Setup'],
|
|
1158
|
-
['trigger_schedule', 'Schedule'],
|
|
1159
358
|
['skill', 'Skill'],
|
|
1160
359
|
['skill_execute', 'Skill'],
|
|
1161
360
|
['skill_view', 'Skill'],
|
|
@@ -1285,8 +484,6 @@ export function toolWorkUnit(name, args = {}, category = '') {
|
|
|
1285
484
|
case 'web_fetch':
|
|
1286
485
|
case 'fetch':
|
|
1287
486
|
return unitDescriptor('Web Research', { count: queryCount(a, 'url', 'urls', 'uri', 'uris') || 1, active: 'Fetching', done: 'Fetched', noun: 'URL', pluralNoun: 'URLs' });
|
|
1288
|
-
case 'download_attachment':
|
|
1289
|
-
return unitDescriptor('Web Research', { count: queryCount(a, 'url', 'urls', 'filename', 'name') || 1, active: 'Downloading', done: 'Downloaded', noun: 'attachment' });
|
|
1290
487
|
case 'recall':
|
|
1291
488
|
case 'recall_memory':
|
|
1292
489
|
case 'search_memories':
|
|
@@ -1317,16 +514,6 @@ export function toolWorkUnit(name, args = {}, category = '') {
|
|
|
1317
514
|
return unitDescriptor('Skill', { count: queryCount(a, 'name', 'skill', 'skill_name', 'query', 'q') || 1, noun: 'skill' });
|
|
1318
515
|
case 'reply':
|
|
1319
516
|
return unitDescriptor('Channel', { count: queryCount(a, 'messages', 'messageId', 'text') || 1, noun: 'message' });
|
|
1320
|
-
case 'react':
|
|
1321
|
-
return unitDescriptor('Channel', { count: queryCount(a, 'emoji', 'messageId') || 1, active: 'Reacting', done: 'Reacted', noun: 'reaction' });
|
|
1322
|
-
case 'edit_message':
|
|
1323
|
-
return unitDescriptor('Channel', { count: queryCount(a, 'messageId', 'text') || 1, active: 'Editing', done: 'Edited', noun: 'message' });
|
|
1324
|
-
case 'activate_channel_bridge':
|
|
1325
|
-
return unitDescriptor('Channel', { active: 'Toggling', done: 'Toggled', noun: 'channel bridge' });
|
|
1326
|
-
case 'inject_command':
|
|
1327
|
-
return unitDescriptor('Channel', { count: queryCount(a, 'command', 'text', 'name') || 1, active: 'Injecting', done: 'Injected', noun: 'command' });
|
|
1328
|
-
case 'trigger_schedule':
|
|
1329
|
-
return unitDescriptor('Schedule', { count: queryCount(a, 'name', 'id') || 1, noun: 'schedule' });
|
|
1330
517
|
case 'code_graph': {
|
|
1331
518
|
const mode = String(a.mode || a.action || '').toLowerCase();
|
|
1332
519
|
const searching = mode === 'search' || mode === 'find_symbol' || mode === 'references' || mode === 'callers' || mode === 'callees';
|
|
@@ -1341,20 +528,6 @@ export function toolWorkUnit(name, args = {}, category = '') {
|
|
|
1341
528
|
return unitDescriptor('Setup', { active: 'Asking', done: 'Asked', noun: 'user' });
|
|
1342
529
|
case 'update_plan':
|
|
1343
530
|
return unitDescriptor('Setup', { active: 'Updating', done: 'Updated', noun: 'plan' });
|
|
1344
|
-
case 'diagnostics':
|
|
1345
|
-
return unitDescriptor('Setup', { active: 'Checking', done: 'Checked', noun: 'diagnostic' });
|
|
1346
|
-
case 'open_config':
|
|
1347
|
-
return unitDescriptor('Setup', { active: 'Opening', done: 'Opened', noun: 'config' });
|
|
1348
|
-
case 'provider_status':
|
|
1349
|
-
return unitDescriptor('Setup', { active: 'Checking', done: 'Checked', noun: 'provider' });
|
|
1350
|
-
case 'channel_status':
|
|
1351
|
-
return unitDescriptor('Setup', { active: 'Checking', done: 'Checked', noun: 'channel' });
|
|
1352
|
-
case 'schedule_status':
|
|
1353
|
-
return unitDescriptor('Setup', { active: 'Checking', done: 'Checked', noun: 'schedule' });
|
|
1354
|
-
case 'schedule_control':
|
|
1355
|
-
return unitDescriptor('Setup', { active: 'Updating', done: 'Updated', noun: 'schedule' });
|
|
1356
|
-
case 'reload_config':
|
|
1357
|
-
return unitDescriptor('Setup', { active: 'Reloading', done: 'Reloaded', noun: 'config' });
|
|
1358
531
|
case 'list_mcp_resources':
|
|
1359
532
|
return unitDescriptor('Setup', { active: 'Listing', done: 'Listed', noun: 'MCP resource' });
|
|
1360
533
|
case 'list_mcp_resource_templates':
|
|
@@ -1504,9 +677,18 @@ export function formatAggregateDetail(summaries) {
|
|
|
1504
677
|
match = /^(?:Found\s+)?(\d+)\s+([a-z]+)$/i.exec(text);
|
|
1505
678
|
if (match) {
|
|
1506
679
|
const nounRaw = match[2].toLowerCase();
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
680
|
+
// Normalize to a canonical singular so singular/plural variants of the
|
|
681
|
+
// SAME noun merge into one metric. Previously "48 matches" keyed as
|
|
682
|
+
// found_matches while "1 match" keyed as found_matchs (naive +s), so the
|
|
683
|
+
// detail row showed "48 matches, 1 match" instead of "49 matches".
|
|
684
|
+
const singular = nounRaw.endsWith('ies') ? `${nounRaw.slice(0, -3)}y`
|
|
685
|
+
: /(?:ch|sh|x|z|s)es$/.test(nounRaw) ? nounRaw.slice(0, -2)
|
|
686
|
+
: nounRaw.endsWith('s') ? nounRaw.slice(0, -1)
|
|
687
|
+
: nounRaw;
|
|
688
|
+
const plural = singular.endsWith('y') ? `${singular.slice(0, -1)}ies`
|
|
689
|
+
: /(?:ch|sh|x|z|s)$/.test(singular) ? `${singular}es`
|
|
690
|
+
: `${singular}s`;
|
|
691
|
+
const key = `found_${singular}`;
|
|
1510
692
|
const metric = addMetric(key, { count: 0, singular, plural, render: (m) => `${m.count} ${pluralize(m.count, m.singular, m.plural)}` });
|
|
1511
693
|
metric.count += Number(match[1]);
|
|
1512
694
|
continue;
|