mixdog 0.9.3 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -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/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +23 -63
- 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 +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- 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 +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -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 +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/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 +182 -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 +251 -397
- 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/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 +32 -41
- 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 +12 -292
- package/src/runtime/channels/index.mjs +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/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/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 +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- 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/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 +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -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/launcher-control.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 +84 -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 +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +319 -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/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 +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- 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 +945 -8094
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +23 -101
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +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 +19652 -18630
- 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 +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- 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 +67 -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 +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/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
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_SURFACE_BRIEF_MAX,
|
|
3
|
+
STATUS_SEPARATOR,
|
|
4
|
+
compactParts,
|
|
5
|
+
displayToolPath,
|
|
6
|
+
firstText,
|
|
7
|
+
isMcpToolName,
|
|
8
|
+
normalizeToolName,
|
|
9
|
+
parseToolArgs,
|
|
10
|
+
pluralize,
|
|
11
|
+
titleStatus,
|
|
12
|
+
titleWord,
|
|
13
|
+
truncateSingleLine,
|
|
14
|
+
truncateToolText,
|
|
15
|
+
displayAgentName,
|
|
16
|
+
displayModelName,
|
|
17
|
+
} from './tool-primitives.mjs';
|
|
18
|
+
|
|
19
|
+
function countNonEmptyLines(text) {
|
|
20
|
+
return String(text ?? '')
|
|
21
|
+
.split('\n')
|
|
22
|
+
.filter((line) => line.trim()).length;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Zero-result recognizer (audit HIGH): result text that SAYS "nothing found"
|
|
26
|
+
// must summarize as an explicit zero, not be line-counted into "1 match".
|
|
27
|
+
// Matches the shapes emitted by grep/glob/find/list/recall backends:
|
|
28
|
+
// "(no matches)", "no matches found", "(no results)", "No results",
|
|
29
|
+
// "(no fuzzy match for \"...\")", "0 matches", "(empty)", "(no entries)".
|
|
30
|
+
function looksLikeZeroResultText(text) {
|
|
31
|
+
const trimmed = String(text ?? '').trim();
|
|
32
|
+
if (!trimmed) return false;
|
|
33
|
+
// Only trust short, single-line-ish payloads — a real listing that merely
|
|
34
|
+
// CONTAINS the words "no matches" somewhere must not be zeroed.
|
|
35
|
+
if (trimmed.length > 200 || trimmed.includes('\n')) return false;
|
|
36
|
+
return /^\(?\s*(?:no|0)\s+(?:fuzzy\s+)?(?:match(?:es)?|results?|files?|entries|candidates?|hits?)\b/i.test(trimmed)
|
|
37
|
+
|| /^\(?\s*(?:empty|none)\s*\)?$/i.test(trimmed)
|
|
38
|
+
|| /^no\s+\S+\s+(?:found|matched)\b/i.test(trimmed);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function splitPathAndDelta(value, explicitDelta = '') {
|
|
42
|
+
let path = String(value ?? '').trim();
|
|
43
|
+
let delta = String(explicitDelta ?? '').trim();
|
|
44
|
+
if (!delta) {
|
|
45
|
+
const sep = path.lastIndexOf(' — ');
|
|
46
|
+
if (sep !== -1) {
|
|
47
|
+
delta = path.slice(sep + 3).trim();
|
|
48
|
+
path = path.slice(0, sep).trim();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { path, delta };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function parseLineDelta(delta) {
|
|
55
|
+
const totals = { added: 0, removed: 0, seen: false };
|
|
56
|
+
for (const match of String(delta ?? '').matchAll(/([+-])\s*(\d+)\s*(?:line|lines)?/gi)) {
|
|
57
|
+
const n = Number(match[2]) || 0;
|
|
58
|
+
totals.seen = true;
|
|
59
|
+
if (match[1] === '+') totals.added += n;
|
|
60
|
+
else totals.removed += n;
|
|
61
|
+
}
|
|
62
|
+
return totals;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatLineDelta(totals) {
|
|
66
|
+
if (!totals?.seen) return '';
|
|
67
|
+
const added = Number(totals.added) || 0;
|
|
68
|
+
const removed = Number(totals.removed) || 0;
|
|
69
|
+
if (added === 0 && removed === 0) return '';
|
|
70
|
+
const parts = [];
|
|
71
|
+
if (added > 0) parts.push(`+${added} ${pluralize(added, 'line')}`);
|
|
72
|
+
if (removed > 0) parts.push(`-${removed} ${pluralize(removed, 'line')}`);
|
|
73
|
+
return parts.join(STATUS_SEPARATOR);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function parseUpdateSummary(text) {
|
|
77
|
+
const match = /^(Updated|Created|Deleted)\s+(.+?)(?:\s+·\s+|$)/i.exec(String(text || '').trim());
|
|
78
|
+
if (!match) return null;
|
|
79
|
+
const action = titleWord(match[1]);
|
|
80
|
+
const target = match[2].trim();
|
|
81
|
+
const fileCountMatch = /^(\d+)\s+Files?$/i.exec(target);
|
|
82
|
+
const totals = parseLineDelta(text);
|
|
83
|
+
return {
|
|
84
|
+
action,
|
|
85
|
+
file: fileCountMatch ? '' : target,
|
|
86
|
+
fileCount: fileCountMatch ? Number(fileCountMatch[1]) || 0 : 0,
|
|
87
|
+
added: totals.added,
|
|
88
|
+
removed: totals.removed,
|
|
89
|
+
seen: totals.seen,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Heuristic: does the text look like a line-oriented listing rather than prose? */
|
|
94
|
+
function looksLineOriented(text) {
|
|
95
|
+
const lines = String(text ?? '').split('\n').filter((line) => line.trim());
|
|
96
|
+
if (lines.length === 0) return false;
|
|
97
|
+
// Prose tends to be a few long sentences; listings are many shorter rows.
|
|
98
|
+
const longLines = lines.filter((line) => line.trim().length > 200).length;
|
|
99
|
+
return longLines === 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function summarizeUpdateResult(text, args) {
|
|
103
|
+
// A dry_run patch validates without writing, so the collapsed detail must not
|
|
104
|
+
// claim a real mutation ("Updated/Created/Deleted foo.js"). Map every action
|
|
105
|
+
// to "Checked" wording, matching the dry-run header (Checking/Checked).
|
|
106
|
+
const isDryRun = parseToolArgs(args)?.dry_run === true;
|
|
107
|
+
const changed = [];
|
|
108
|
+
for (const line of String(text ?? '').split('\n')) {
|
|
109
|
+
const ok = /^\s*OK\s+(modify|add|delete|create)\s+(.+?)\s*$/i.exec(line);
|
|
110
|
+
if (ok) {
|
|
111
|
+
const { path, delta } = splitPathAndDelta(ok[2]);
|
|
112
|
+
changed.push({ action: ok[1].toLowerCase(), path, delta });
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const edited = /^\s*(Edited|Created|Updated|Wrote):\s+(.+?)(?:\s+\(([^)]+)\))?\s*$/i.exec(line);
|
|
116
|
+
if (edited) {
|
|
117
|
+
const { path, delta } = splitPathAndDelta(edited[2], edited[3] || '');
|
|
118
|
+
changed.push({ action: edited[1].toLowerCase(), path, delta });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (changed.length === 1) {
|
|
122
|
+
const item = changed[0];
|
|
123
|
+
const action = isDryRun
|
|
124
|
+
? 'Checked'
|
|
125
|
+
: item.action === 'delete' ? 'Deleted' : item.action === 'add' || item.action === 'create' || item.action === 'created' ? 'Created' : 'Updated';
|
|
126
|
+
return compactParts([`${action} ${displayToolPath(item.path)}`, formatLineDelta(parseLineDelta(item.delta))]);
|
|
127
|
+
}
|
|
128
|
+
if (changed.length > 1) {
|
|
129
|
+
const totals = changed.reduce((acc, item) => {
|
|
130
|
+
const delta = parseLineDelta(item.delta);
|
|
131
|
+
acc.added += delta.added;
|
|
132
|
+
acc.removed += delta.removed;
|
|
133
|
+
acc.seen = acc.seen || delta.seen;
|
|
134
|
+
return acc;
|
|
135
|
+
}, { added: 0, removed: 0, seen: false });
|
|
136
|
+
return compactParts([`${isDryRun ? 'Checked' : 'Updated'} ${changed.length} Files`, formatLineDelta(totals)]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const parsedArgs = parseToolArgs(args);
|
|
140
|
+
const target = parsedArgs.path ?? parsedArgs.file ?? parsedArgs.file_path ?? '';
|
|
141
|
+
if (target) return `${isDryRun ? 'Checked' : 'Updated'} ${displayToolPath(target)}`;
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function textBetweenTag(text, tag) {
|
|
146
|
+
const re = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, 'i');
|
|
147
|
+
const match = re.exec(String(text ?? ''));
|
|
148
|
+
return match ? match[1].trim() : '';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Strip the most common inline-markdown markers so a one-line card summary
|
|
152
|
+
// reads as plain prose ("**not clean**" → "not clean", "`x`" → "x"). Block
|
|
153
|
+
// markers (#, >, -, 1.) at line start are dropped too. Whitespace is collapsed
|
|
154
|
+
// by the caller's truncateSingleLine.
|
|
155
|
+
function stripInlineMarkdown(value) {
|
|
156
|
+
return String(value ?? '')
|
|
157
|
+
.replace(/^\s{0,3}(?:#{1,6}\s+|>\s+|[-*+]\s+|\d+\.\s+)/, '')
|
|
158
|
+
.replace(/\*\*([^*]+)\*\*/g, '$1')
|
|
159
|
+
.replace(/__([^_]+)__/g, '$1')
|
|
160
|
+
.replace(/(?<!\*)\*(?!\*)([^*]+)\*(?!\*)/g, '$1')
|
|
161
|
+
.replace(/`([^`]+)`/g, '$1')
|
|
162
|
+
.trim();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function firstAgentResultLine(text) {
|
|
166
|
+
const finalAnswer = textBetweenTag(text, 'final-answer') || textBetweenTag(text, 'result');
|
|
167
|
+
const raw = finalAnswer || text;
|
|
168
|
+
for (const line of String(raw ?? '').split('\n')) {
|
|
169
|
+
const trimmed = line.trim();
|
|
170
|
+
if (!trimmed) continue;
|
|
171
|
+
if (/^agent result\b/i.test(trimmed)) continue;
|
|
172
|
+
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;
|
|
173
|
+
if (/^(?:agent task|status|type|target|role|agent|preset|model|effort|fast|limits|session|task-id|task_id|notification|queueDepth|worker|worker_stage|last_progress|silent_for|watchdog|queued_followups|diagnostic|started|finished|elapsed|reused):\s*/i.test(trimmed)) continue;
|
|
174
|
+
if (/^\[[a-z-]+:\s*[^\]]*\]$/i.test(trimmed)) continue;
|
|
175
|
+
return truncateSingleLine(trimmed, AGENT_SURFACE_BRIEF_MAX);
|
|
176
|
+
}
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function summarizeGenericResult(text) {
|
|
181
|
+
const trimmed = String(text ?? '').trim();
|
|
182
|
+
if (!trimmed) return null;
|
|
183
|
+
if (/^(?:undefined|null)$/i.test(trimmed)) return null;
|
|
184
|
+
|
|
185
|
+
if (/^[\[{]/.test(trimmed)) {
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(trimmed);
|
|
188
|
+
if (Array.isArray(parsed)) return `${parsed.length} ${pluralize(parsed.length, 'item')}`;
|
|
189
|
+
if (parsed && typeof parsed === 'object') {
|
|
190
|
+
const status = firstText(parsed.status, parsed.state, parsed.result, parsed.message);
|
|
191
|
+
if (status) return truncateSingleLine(titleStatus(status), AGENT_SURFACE_BRIEF_MAX);
|
|
192
|
+
if (parsed.cwd) return truncateSingleLine(parsed.cwd, AGENT_SURFACE_BRIEF_MAX);
|
|
193
|
+
if (typeof parsed.ok === 'boolean') return parsed.ok ? 'Ok' : 'Failed';
|
|
194
|
+
for (const key of ['items', 'results', 'resources', 'templates', 'providers', 'schedules', 'channels', 'tools']) {
|
|
195
|
+
if (Array.isArray(parsed[key])) return `${parsed[key].length} ${pluralize(parsed[key].length, (key.slice(0, -1) || 'item').toLowerCase())}`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const line = firstAgentResultLine(text) || trimmed.split('\n').map((item) => item.trim()).find(Boolean) || '';
|
|
204
|
+
if (!line || line === '{' || line === '[') return null;
|
|
205
|
+
if (/^(ok|done|success|saved|sent|updated|reloaded|connected|enabled|disabled|active|inactive)$/i.test(line)) {
|
|
206
|
+
return titleStatus(line);
|
|
207
|
+
}
|
|
208
|
+
return truncateSingleLine(line, AGENT_SURFACE_BRIEF_MAX);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Error-cause extractor (audit HIGH): when a tool result is an error, surface
|
|
212
|
+
// the actual cause line instead of a bare "Failed"/raw first line. Handles
|
|
213
|
+
// JSON error envelopes ({error|message|cause|detail}) and plain-text bodies
|
|
214
|
+
// (first line that carries error-ish signal, else first non-empty line).
|
|
215
|
+
// Exported for ToolExecution's collapsed detail row.
|
|
216
|
+
export function extractErrorCause(resultText) {
|
|
217
|
+
const text = String(resultText ?? '').trim();
|
|
218
|
+
if (!text) return '';
|
|
219
|
+
// JSON envelope: prefer explicit error-ish keys.
|
|
220
|
+
if (text.startsWith('{') || text.startsWith('[')) {
|
|
221
|
+
try {
|
|
222
|
+
const parsed = JSON.parse(text);
|
|
223
|
+
const obj = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
224
|
+
if (obj && typeof obj === 'object') {
|
|
225
|
+
const cause = firstText(
|
|
226
|
+
typeof obj.error === 'string' ? obj.error : obj.error?.message,
|
|
227
|
+
obj.message, obj.cause, obj.detail, obj.reason, obj.status,
|
|
228
|
+
);
|
|
229
|
+
if (cause) return truncateSingleLine(String(cause), AGENT_SURFACE_BRIEF_MAX);
|
|
230
|
+
}
|
|
231
|
+
} catch { /* fall through to text scan */ }
|
|
232
|
+
}
|
|
233
|
+
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
234
|
+
// Prefer the first line that looks like an error statement.
|
|
235
|
+
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));
|
|
236
|
+
const picked = errorish || lines[0] || '';
|
|
237
|
+
return truncateSingleLine(stripInlineMarkdown(picked), AGENT_SURFACE_BRIEF_MAX);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Derive a short semantic one-liner for a completed tool call using only the
|
|
242
|
+
* tool name, parsed args, and the raw result text. Returns null when nothing
|
|
243
|
+
* reliable can be derived, so the caller falls back to the raw result block.
|
|
244
|
+
*/
|
|
245
|
+
export function summarizeToolResult(name, args, resultText, isError = false) {
|
|
246
|
+
if (isError) {
|
|
247
|
+
// Audit HIGH: errors used to disable semantic summaries entirely, leaving
|
|
248
|
+
// the UI with a raw first line or a bare "Failed". Surface the extracted
|
|
249
|
+
// cause so the collapsed card answers "why" without ctrl+o.
|
|
250
|
+
const cause = extractErrorCause(resultText);
|
|
251
|
+
return cause || null;
|
|
252
|
+
}
|
|
253
|
+
const text = String(resultText ?? '');
|
|
254
|
+
const trimmed = text.trim();
|
|
255
|
+
if (/^(?:undefined|null)$/i.test(trimmed)) return null;
|
|
256
|
+
if (isMcpToolName(name)) return trimmed ? firstAgentResultLine(text) || null : null;
|
|
257
|
+
const normalized = normalizeToolName(name);
|
|
258
|
+
|
|
259
|
+
switch (normalized) {
|
|
260
|
+
case 'read':
|
|
261
|
+
case 'view_image':
|
|
262
|
+
case 'read_mcp_resource': {
|
|
263
|
+
if (/^\[image:/i.test(trimmed)) return 'Image';
|
|
264
|
+
if (!trimmed) return null;
|
|
265
|
+
const n = text.split('\n').length;
|
|
266
|
+
return `${n} ${pluralize(n, 'line')}`;
|
|
267
|
+
}
|
|
268
|
+
case 'apply_patch': {
|
|
269
|
+
const updateSummary = summarizeUpdateResult(text, args);
|
|
270
|
+
if (updateSummary) return updateSummary;
|
|
271
|
+
// Prefer explicit additions/removals hints if the result text states them.
|
|
272
|
+
const add = /(\d+)\s+addition/i.exec(text);
|
|
273
|
+
const rem = /(\d+)\s+removal/i.exec(text);
|
|
274
|
+
if (add || rem) {
|
|
275
|
+
const a = add ? Number(add[1]) : 0;
|
|
276
|
+
const r = rem ? Number(rem[1]) : 0;
|
|
277
|
+
return `+${a} -${r}`;
|
|
278
|
+
}
|
|
279
|
+
// Else count unified-diff style +/- lines (ignore +++/--- file headers).
|
|
280
|
+
let a = 0;
|
|
281
|
+
let r = 0;
|
|
282
|
+
for (const line of text.split('\n')) {
|
|
283
|
+
if (/^\+\+\+|^---/.test(line)) continue;
|
|
284
|
+
if (/^\+/.test(line)) a += 1;
|
|
285
|
+
else if (/^-/.test(line)) r += 1;
|
|
286
|
+
}
|
|
287
|
+
if (a > 0 || r > 0) return `+${a} -${r}`;
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
case 'grep': {
|
|
291
|
+
if (!trimmed || !looksLineOriented(text)) return null;
|
|
292
|
+
if (looksLikeZeroResultText(text)) return '0 matches';
|
|
293
|
+
const n = countNonEmptyLines(text);
|
|
294
|
+
if (n === 0) return null;
|
|
295
|
+
return `${n} ${pluralize(n, 'match', 'matches')}`;
|
|
296
|
+
}
|
|
297
|
+
case 'glob': {
|
|
298
|
+
if (!trimmed || !looksLineOriented(text)) return null;
|
|
299
|
+
if (looksLikeZeroResultText(text)) return '0 files';
|
|
300
|
+
const n = countNonEmptyLines(text);
|
|
301
|
+
if (n === 0) return null;
|
|
302
|
+
return `${n} ${pluralize(n, 'file')}`;
|
|
303
|
+
}
|
|
304
|
+
case 'find': {
|
|
305
|
+
if (!trimmed || !looksLineOriented(text)) return null;
|
|
306
|
+
if (looksLikeZeroResultText(text)) return '0 candidates';
|
|
307
|
+
const n = countNonEmptyLines(text);
|
|
308
|
+
if (n === 0) return null;
|
|
309
|
+
return `${n} ${pluralize(n, 'candidate')}`;
|
|
310
|
+
}
|
|
311
|
+
case 'list':
|
|
312
|
+
case 'ls': {
|
|
313
|
+
if (!trimmed || !looksLineOriented(text)) return null;
|
|
314
|
+
if (looksLikeZeroResultText(text)) return '0 entries';
|
|
315
|
+
const n = countNonEmptyLines(text);
|
|
316
|
+
if (n === 0) return null;
|
|
317
|
+
return `${n} ${pluralize(n, 'entry', 'entries')}`;
|
|
318
|
+
}
|
|
319
|
+
case 'shell':
|
|
320
|
+
case 'bash':
|
|
321
|
+
case 'bash_session':
|
|
322
|
+
case 'shell_command':
|
|
323
|
+
case 'job_wait': {
|
|
324
|
+
if (!trimmed) return '(No Output)';
|
|
325
|
+
const job = /^\[(?:task_id|job):\s*([^\]]+)\]/mi.exec(text);
|
|
326
|
+
const status = /^\[status:\s*([^\]]+)\]/mi.exec(text);
|
|
327
|
+
const exit = /^\[exit:\s*([^\]]+)\]/mi.exec(text);
|
|
328
|
+
if (job || status || exit) {
|
|
329
|
+
return compactParts([
|
|
330
|
+
job ? job[1] : '',
|
|
331
|
+
status ? titleStatus(status[1]) : '',
|
|
332
|
+
exit ? `Exit ${exit[1]}` : '',
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
const firstLine = trimmed.split('\n').map((line) => line.trim()).find(Boolean) || trimmed;
|
|
336
|
+
return truncateSingleLine(firstLine, AGENT_SURFACE_BRIEF_MAX);
|
|
337
|
+
}
|
|
338
|
+
case 'code_graph': {
|
|
339
|
+
const match = /(\d+)\s+(references|definitions|symbols|callers|callees|results|matches)/i.exec(text);
|
|
340
|
+
if (match) return `${match[1]} ${String(match[2]).toLowerCase()}`;
|
|
341
|
+
if (looksLikeZeroResultText(text)) return 'No results';
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
case 'web_fetch':
|
|
345
|
+
case 'fetch': {
|
|
346
|
+
// Audit HIGH: channel `fetch` (Discord message fetch — args carry
|
|
347
|
+
// channel/messageId/limit, never url/uri) was summarized as a WEB fetch,
|
|
348
|
+
// so its result missed both the status/size probes and fell to raw JSON.
|
|
349
|
+
// Route it to the generic JSON/text summarizer instead.
|
|
350
|
+
if (normalized === 'fetch') {
|
|
351
|
+
const a = parseToolArgs(args);
|
|
352
|
+
const isChannelFetch = !firstText(a.url, a.uri)
|
|
353
|
+
&& Boolean(firstText(a.channel, a.channelId, a.chatId, a.messageId) || a.limit != null);
|
|
354
|
+
if (isChannelFetch) {
|
|
355
|
+
const n = countNonEmptyLines(text);
|
|
356
|
+
if (trimmed && looksLineOriented(text) && n > 0) return `${n} ${pluralize(n, 'message')}`;
|
|
357
|
+
return summarizeGenericResult(text);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
// Status: require a status-like context (HTTP NNN, "Status: NNN",
|
|
361
|
+
// or "NNN OK"/"NNN Not Found") rather than any bare 3-digit number.
|
|
362
|
+
const status = /(?:HTTP[\s/]*\d?\.?\d?\s*|status[:\s]+)([1-5]\d{2})\b/i.exec(text)
|
|
363
|
+
|| /\b([1-5]\d{2})\s+(?:OK|Not\s+Found|Forbidden|Moved|Found|Created|No\s+Content|Bad\s+Request|Unauthorized|Internal)/.exec(text);
|
|
364
|
+
const size = /\b(\d+(?:\.\d+)?\s?(?:[KMGT]?B|bytes))\b/i.exec(text);
|
|
365
|
+
if (size && status) return `${size[1]} · HTTP ${status[1]}`;
|
|
366
|
+
if (size) return size[1];
|
|
367
|
+
if (status) return `HTTP ${status[1]}`;
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
case 'search': {
|
|
371
|
+
const match = /(\d+)\s+results?/i.exec(text);
|
|
372
|
+
if (match) {
|
|
373
|
+
const n = Number(match[1]);
|
|
374
|
+
return `${n} ${pluralize(n, 'result')}`;
|
|
375
|
+
}
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
case 'search_query':
|
|
379
|
+
case 'image_query':
|
|
380
|
+
case 'web_search':
|
|
381
|
+
case 'web_search_call': {
|
|
382
|
+
const match = /(\d+)\s+results?/i.exec(text);
|
|
383
|
+
if (match) {
|
|
384
|
+
const n = Number(match[1]);
|
|
385
|
+
return `${n} ${pluralize(n, 'result')}`;
|
|
386
|
+
}
|
|
387
|
+
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
388
|
+
}
|
|
389
|
+
case 'explore': {
|
|
390
|
+
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
391
|
+
}
|
|
392
|
+
case 'recall':
|
|
393
|
+
case 'search_memories':
|
|
394
|
+
case 'memory': {
|
|
395
|
+
if (!trimmed || trimmed === '(no results)' || looksLikeZeroResultText(text)) return 'No Results';
|
|
396
|
+
let n = 0;
|
|
397
|
+
for (const line of text.split('\n')) {
|
|
398
|
+
if (/#\d+\s*$/.test(line)) n += 1;
|
|
399
|
+
}
|
|
400
|
+
if (n > 0) return `${n} ${pluralize(n, 'Memory', 'Memories')}`;
|
|
401
|
+
return summarizeGenericResult(text);
|
|
402
|
+
}
|
|
403
|
+
case 'remember':
|
|
404
|
+
case 'save_memory':
|
|
405
|
+
case 'update_memory':
|
|
406
|
+
case 'reply':
|
|
407
|
+
case 'request_user_input':
|
|
408
|
+
case 'update_plan':
|
|
409
|
+
case 'cwd':
|
|
410
|
+
case 'list_mcp_resources':
|
|
411
|
+
case 'list_mcp_resource_templates':
|
|
412
|
+
return summarizeGenericResult(text);
|
|
413
|
+
case 'skill':
|
|
414
|
+
case 'skill_execute':
|
|
415
|
+
case 'skill_view':
|
|
416
|
+
case 'skills_list':
|
|
417
|
+
case 'use_skill': {
|
|
418
|
+
const parsedArgs = parseToolArgs(args);
|
|
419
|
+
const target = firstText(parsedArgs.name, parsedArgs.skill, parsedArgs.skill_name);
|
|
420
|
+
if (normalized === 'skills_list') {
|
|
421
|
+
const count = /(\d+)\s+skills?/i.exec(text);
|
|
422
|
+
if (count) return `${Number(count[1]) || count[1]} ${pluralize(Number(count[1]) || 0, 'skill')}`;
|
|
423
|
+
const lines = countNonEmptyLines(text);
|
|
424
|
+
return lines > 0 ? `${lines} ${pluralize(lines, 'skill')}` : null;
|
|
425
|
+
}
|
|
426
|
+
if (target) {
|
|
427
|
+
const verb = normalized === 'skill' || normalized === 'skill_view' ? 'Loaded' : 'Used';
|
|
428
|
+
return `${verb} ${truncateToolText(target, 80)}`;
|
|
429
|
+
}
|
|
430
|
+
return trimmed ? firstAgentResultLine(text) || null : null;
|
|
431
|
+
}
|
|
432
|
+
case 'agent':
|
|
433
|
+
case 'task': {
|
|
434
|
+
// Status-check (list/status) envelopes start with "agents: N" / "tasks: M"
|
|
435
|
+
// (or "(no agents or tasks)"). Collapse them to a tight count summary
|
|
436
|
+
// instead of leaking the raw "agents: 3 …" worker dump into the card.
|
|
437
|
+
if (/^\(no agents or tasks\)$/im.test(text)) return 'No agents or tasks';
|
|
438
|
+
const agentsCount = /^agents:\s*(\d+)/im.exec(text);
|
|
439
|
+
const tasksCount = /^tasks:\s*(\d+)/im.exec(text);
|
|
440
|
+
if (agentsCount || tasksCount) {
|
|
441
|
+
const a = agentsCount ? Number(agentsCount[1]) : 0;
|
|
442
|
+
const t = tasksCount ? Number(tasksCount[1]) : 0;
|
|
443
|
+
const parts = [];
|
|
444
|
+
if (agentsCount) parts.push(`${a} ${pluralize(a, 'agent')}`);
|
|
445
|
+
if (tasksCount && t > 0) parts.push(`${t} ${pluralize(t, 'task')}`);
|
|
446
|
+
return compactParts(parts) || 'No agents or tasks';
|
|
447
|
+
}
|
|
448
|
+
const answerLine = firstAgentResultLine(text);
|
|
449
|
+
// Agent/task result cards show only a one-liner; full report via ctrl+o.
|
|
450
|
+
if (answerLine) return truncateSingleLine(stripInlineMarkdown(answerLine), AGENT_SURFACE_BRIEF_MAX);
|
|
451
|
+
const task = /^agent task:\s*(\S+)/mi.exec(text);
|
|
452
|
+
const status = /^status:\s*([^\s(]+)/mi.exec(text);
|
|
453
|
+
const agent = /^agent:\s*(.+)$/mi.exec(text);
|
|
454
|
+
const preset = /^preset:\s*(.+)$/mi.exec(text);
|
|
455
|
+
const model = /^model:\s*(.+)$/mi.exec(text);
|
|
456
|
+
const limits = /^limits:\s*(.+)$/mi.exec(text);
|
|
457
|
+
const agentModel = compactParts([
|
|
458
|
+
displayAgentName(agent ? agent[1] : ''),
|
|
459
|
+
displayModelName(model ? model[1] : ''),
|
|
460
|
+
]);
|
|
461
|
+
if (agentModel) return agentModel;
|
|
462
|
+
const parts = [
|
|
463
|
+
task ? task[1] : '',
|
|
464
|
+
agent ? agent[1] : '',
|
|
465
|
+
preset ? preset[1] : '',
|
|
466
|
+
model ? model[1] : '',
|
|
467
|
+
status ? titleStatus(status[1]) : '',
|
|
468
|
+
limits ? limits[1] : '',
|
|
469
|
+
].filter(Boolean);
|
|
470
|
+
if (parts.length) return compactParts(parts);
|
|
471
|
+
if (task) return status ? `${task[1]} ${titleStatus(status[1])}` : task[1];
|
|
472
|
+
return null;
|
|
473
|
+
}
|
|
474
|
+
default:
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export function isExplorerSurface(label) {
|
|
480
|
+
return label === 'Read' || label === 'Search';
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function truncateAgentSurfaceBrief(value, max = AGENT_SURFACE_BRIEF_MAX) {
|
|
484
|
+
const text = String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
485
|
+
if (!text) return '';
|
|
486
|
+
if (text.length <= max) return text;
|
|
487
|
+
return `${text.slice(0, Math.max(1, max - 1))}\u2026`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Tight one-liner for an agent/task card: prefer the inbound prompt/message for
|
|
491
|
+
// spawn/send, or a stripped first line of the result when a response landed.
|
|
492
|
+
export function summarizeAgentSurfaceBrief(name, args, resultText, { isError = false, isResponse = false } = {}) {
|
|
493
|
+
const a = parseToolArgs(args);
|
|
494
|
+
const action = String(a?.type || a?.action || '').toLowerCase();
|
|
495
|
+
const text = String(resultText ?? '').trim();
|
|
496
|
+
if (isResponse && text) {
|
|
497
|
+
const fromResult = summarizeToolResult(name, args, text, isError);
|
|
498
|
+
if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
|
|
499
|
+
const line = firstAgentResultLine(text);
|
|
500
|
+
if (line) return truncateAgentSurfaceBrief(stripInlineMarkdown(line));
|
|
501
|
+
}
|
|
502
|
+
const outbound = firstText(a?.prompt, a?.message);
|
|
503
|
+
if (outbound && (action === 'spawn' || action === 'send' || !action)) {
|
|
504
|
+
return truncateAgentSurfaceBrief(outbound);
|
|
505
|
+
}
|
|
506
|
+
if ((action === 'spawn' || action === 'send') && text && !isResponse) {
|
|
507
|
+
const fromResult = summarizeToolResult(name, args, text, isError);
|
|
508
|
+
if (fromResult) return truncateAgentSurfaceBrief(stripInlineMarkdown(fromResult));
|
|
509
|
+
}
|
|
510
|
+
return '';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export function isMemorySurface(label) {
|
|
514
|
+
return label === 'Memory';
|
|
515
|
+
}
|