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
|
@@ -11,9 +11,7 @@
|
|
|
11
11
|
import React, { useEffect, useState } from 'react';
|
|
12
12
|
import { Box, Text } from 'ink';
|
|
13
13
|
import stringWidth from 'string-width';
|
|
14
|
-
import
|
|
15
|
-
import { displayWidth } from '../display-width.mjs';
|
|
16
|
-
import { theme, TURN_MARKER, RESULT_GUTTER, RESULT_GUTTER_CONT } from '../theme.mjs';
|
|
14
|
+
import { theme, TURN_MARKER, AGENT_CALL_MARKER, AGENT_RESPONSE_MARKER } from '../theme.mjs';
|
|
17
15
|
import { formatElapsed } from '../time-format.mjs';
|
|
18
16
|
import { BULLET_OPERATOR } from '../figures.mjs';
|
|
19
17
|
import {
|
|
@@ -22,23 +20,52 @@ import {
|
|
|
22
20
|
summarizeToolResult as surfaceSummarizeToolResult,
|
|
23
21
|
formatAggregateHeader,
|
|
24
22
|
formatToolActionHeader,
|
|
25
|
-
displayModelName,
|
|
26
23
|
summarizeAgentSurfaceBrief,
|
|
27
24
|
AGENT_SURFACE_BRIEF_MAX,
|
|
28
25
|
} from '../../runtime/shared/tool-surface.mjs';
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
26
|
+
import { isBackgroundErrorOnlyBody, backgroundTaskFailureStatusLabel } from '../../runtime/shared/err-text.mjs';
|
|
27
|
+
import {
|
|
28
|
+
MIN_RESULT_LINE_CHARS,
|
|
29
|
+
RESULT_LINE_HARD_MAX,
|
|
30
|
+
SUMMARY_MAX_CHARS,
|
|
31
|
+
HEADER_FAILURE_STATUS_MAX,
|
|
32
|
+
safeInlineText,
|
|
33
|
+
normalizeCountMap,
|
|
34
|
+
truncateToWidth,
|
|
35
|
+
shellResultElapsed,
|
|
36
|
+
normalizeTerminalStatus,
|
|
37
|
+
resultTerminalStatus,
|
|
38
|
+
stripLeadingStatusMarkerLines,
|
|
39
|
+
stripLeadingStatusMarkerFromText,
|
|
40
|
+
} from './tool-execution/text-format.mjs';
|
|
41
|
+
import {
|
|
42
|
+
SKILL_SURFACE_NAMES,
|
|
43
|
+
isShellTool,
|
|
44
|
+
shellDisplayStatus,
|
|
45
|
+
shellHeader,
|
|
46
|
+
isAgentTool,
|
|
47
|
+
isBackgroundTaskTool,
|
|
48
|
+
agentResponseTitle,
|
|
49
|
+
agentActionTitle,
|
|
50
|
+
agentActionSummary,
|
|
51
|
+
hasAgentResponseResult,
|
|
52
|
+
resolveBackgroundTaskMeta,
|
|
53
|
+
backgroundTaskElapsed,
|
|
54
|
+
prefixElapsed,
|
|
55
|
+
mergeTerminalDetail,
|
|
56
|
+
shouldPrefixSyncElapsed,
|
|
57
|
+
backgroundTaskResultTitle,
|
|
58
|
+
backgroundTaskActionTitle,
|
|
59
|
+
backgroundTaskFailureDetail,
|
|
60
|
+
backgroundTaskDetail,
|
|
61
|
+
isBackgroundTaskResponseArgs,
|
|
62
|
+
genericCompletedDetail,
|
|
63
|
+
toolSearchLoadedSummary,
|
|
64
|
+
agentTerminalDetail,
|
|
65
|
+
clampFailureCount,
|
|
66
|
+
toolStatusColor,
|
|
67
|
+
} from './tool-execution/surface-detail.mjs';
|
|
68
|
+
import { ResultBody } from './tool-execution/ResultBody.jsx';
|
|
42
69
|
|
|
43
70
|
export function displayToolName(name, args) {
|
|
44
71
|
return surfaceDisplayToolName(name, args);
|
|
@@ -47,193 +74,6 @@ export function displayToolName(name, args) {
|
|
|
47
74
|
const TOOL_BLINK_MS = 500;
|
|
48
75
|
const TOOL_BLINK_LIMIT_MS = 3000;
|
|
49
76
|
const TOOL_PENDING_SHOW_DELAY_MS = 1000;
|
|
50
|
-
// Read `theme.subtle` at use-time (not captured here) so a live `/theme`
|
|
51
|
-
// switch re-tones the tool hints. `theme` is mutated in-place on switch.
|
|
52
|
-
// Collapsed tool headers/details are laid out as single terminal rows. Never let
|
|
53
|
-
// raw C0/control bytes (CR, tabs, cursor escapes, etc.) reach those rows: a
|
|
54
|
-
// terminal can apply them after Ink has already clipped/measured the row, which
|
|
55
|
-
// makes a scrolled tool card appear to write through the prompt/statusline.
|
|
56
|
-
const INLINE_CONTROL_RE = /[\u0000-\u001F\u007F]/g;
|
|
57
|
-
|
|
58
|
-
function safeInlineText(value) {
|
|
59
|
-
return stripAnsi(String(value ?? ''))
|
|
60
|
-
.replace(/\r\n?/g, '\n')
|
|
61
|
-
.replace(/\t/g, ' ')
|
|
62
|
-
.replace(/\n+/g, ' ')
|
|
63
|
-
.replace(INLINE_CONTROL_RE, ' ')
|
|
64
|
-
.replace(/\s+/g, ' ')
|
|
65
|
-
.trim();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function normalizeCount(value) {
|
|
69
|
-
const n = Number(value || 0);
|
|
70
|
-
return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : 0;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function normalizeCountMap(value = {}) {
|
|
74
|
-
const out = {};
|
|
75
|
-
for (const [key, raw] of Object.entries(value || {})) {
|
|
76
|
-
if (raw && typeof raw === 'object') {
|
|
77
|
-
out[key] = { ...raw, count: normalizeCount(raw.count) };
|
|
78
|
-
} else {
|
|
79
|
-
out[key] = normalizeCount(raw);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return out;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function deltaColor(token) {
|
|
86
|
-
return String(token || '').startsWith('+') ? theme.success : theme.error;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function deltaTextParts(text) {
|
|
90
|
-
const value = String(text ?? '');
|
|
91
|
-
const parts = [];
|
|
92
|
-
const re = /(^|[\s([,{·])([+-]\s*\d+)(?=\s+Lines?\b)/gi;
|
|
93
|
-
let last = 0;
|
|
94
|
-
let match;
|
|
95
|
-
while ((match = re.exec(value))) {
|
|
96
|
-
const prefix = match[1] || '';
|
|
97
|
-
const token = (match[2] || '').replace(/\s+/g, '');
|
|
98
|
-
const tokenStart = match.index + prefix.length;
|
|
99
|
-
if (match.index > last) parts.push({ text: value.slice(last, match.index) });
|
|
100
|
-
if (prefix) parts.push({ text: prefix });
|
|
101
|
-
if (token) parts.push({ text: token, color: deltaColor(token) });
|
|
102
|
-
last = tokenStart + (match[2] || '').length;
|
|
103
|
-
}
|
|
104
|
-
if (last < value.length) parts.push({ text: value.slice(last) });
|
|
105
|
-
return parts;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function renderDeltaText(text) {
|
|
109
|
-
return deltaTextParts(text).map((part, index) => (
|
|
110
|
-
part.color ? <Text key={index} color={part.color}>{part.text}</Text> : part.text
|
|
111
|
-
));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Shared multi-line result body: `└` on the first row, `│` continuation rail on
|
|
115
|
-
// every following row, body text in one flex column so wrapping stays aligned
|
|
116
|
-
// under the head gutter.
|
|
117
|
-
//
|
|
118
|
-
// Two render paths:
|
|
119
|
-
// - COLLAPSED (raw=false): a single fitted summary line, diff(+/-) colored via
|
|
120
|
-
// renderDeltaText.
|
|
121
|
-
// - EXPANDED (raw=true): formatExpandedResult then wrapExpandedResultLines so
|
|
122
|
-
// each physical row fits the body width before render (rail rows stay 1:1;
|
|
123
|
-
// ink does not re-wrap). Physical row mount cap: MIXDOG_TUI_TOOL_OUTPUT_MAX_RENDER_LINES
|
|
124
|
-
// (default 600; 0 disables). Shell/script bodies keep the newest tail when capped.
|
|
125
|
-
function ResultBody({ lines, rawText, pathArg = '', isShell = false, columns, color, raw }) {
|
|
126
|
-
const renderLines = raw
|
|
127
|
-
? wrapExpandedResultLines(
|
|
128
|
-
formatExpandedResult(rawText, { pathArg, isShell }),
|
|
129
|
-
columns,
|
|
130
|
-
{ isShell },
|
|
131
|
-
)
|
|
132
|
-
: (lines || []);
|
|
133
|
-
if (!renderLines || renderLines.length === 0) return null;
|
|
134
|
-
return (
|
|
135
|
-
<Box flexDirection="row">
|
|
136
|
-
<Box flexShrink={0} flexDirection="column">
|
|
137
|
-
{renderLines.map((_, i) => (
|
|
138
|
-
<Text key={i} color={theme.subtle}>{i === 0 ? RESULT_GUTTER : RESULT_GUTTER_CONT}</Text>
|
|
139
|
-
))}
|
|
140
|
-
</Box>
|
|
141
|
-
<Box flexDirection="column" flexShrink={1} flexGrow={1}>
|
|
142
|
-
{renderLines.map((line, i) => (
|
|
143
|
-
<Text key={i} color={raw ? undefined : color} wrap="truncate">
|
|
144
|
-
{raw
|
|
145
|
-
? (line || ' ')
|
|
146
|
-
: renderDeltaText(fitResultLine(line || ' ', columns))}
|
|
147
|
-
</Text>
|
|
148
|
-
))}
|
|
149
|
-
</Box>
|
|
150
|
-
</Box>
|
|
151
|
-
);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function plural(count, singular, pluralText = `${singular}s`) {
|
|
155
|
-
return count === 1 ? singular : pluralText;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function isShellTool(normalizedName, label = '') {
|
|
159
|
-
const n = String(normalizedName || '').toLowerCase();
|
|
160
|
-
const l = String(label || '').toLowerCase();
|
|
161
|
-
return n === 'shell' || n === 'bash' || n === 'bash_session' || n === 'shell_command' || n === 'job_wait' || l === 'run';
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function shellResultStatus(value) {
|
|
165
|
-
const match = String(value || '').match(/(?:^|\b)status:\s*(running|pending|queued|completed|failed|cancelled|canceled)\b/im);
|
|
166
|
-
return match ? String(match[1] || '').toLowerCase() : '';
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function normalizeTerminalStatus(value) {
|
|
170
|
-
const raw = String(value || '').trim().toLowerCase();
|
|
171
|
-
if (!raw) return '';
|
|
172
|
-
if (/^(running|pending|queued|in_progress|in-progress)$/.test(raw)) return 'running';
|
|
173
|
-
if (/^(completed|complete|done|success|succeeded|ok)$/.test(raw)) return 'completed';
|
|
174
|
-
if (/^(failed|fail|error|errored|timeout|timed_out|killed)$/.test(raw)) return 'failed';
|
|
175
|
-
if (/^(cancelled|canceled|cancel)$/.test(raw)) return 'cancelled';
|
|
176
|
-
return '';
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function displayTerminalStatus(value) {
|
|
180
|
-
const status = normalizeTerminalStatus(value);
|
|
181
|
-
if (status === 'running') return 'Running';
|
|
182
|
-
if (status === 'completed') return 'Finished';
|
|
183
|
-
if (status === 'failed') return 'Failed';
|
|
184
|
-
if (status === 'cancelled') return 'Cancelled';
|
|
185
|
-
return '';
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function resultTerminalStatus(value) {
|
|
189
|
-
const text = String(value || '');
|
|
190
|
-
const tagged = text.match(/<status[^>]*>([\s\S]*?)<\/status>/i)?.[1]?.trim();
|
|
191
|
-
if (tagged) return normalizeTerminalStatus(tagged);
|
|
192
|
-
const bracketed = text.match(/^\[status:\s*([^\]]*)\]/mi)?.[1]?.trim();
|
|
193
|
-
if (bracketed) return normalizeTerminalStatus(bracketed);
|
|
194
|
-
// Loose inline `status: x` / `state: x` matches are a last-resort fallback —
|
|
195
|
-
// prefer the engine-controlled `<status>` tag or `[status: …]` marker above.
|
|
196
|
-
// A loose match can false-positive on prose that happens to start with
|
|
197
|
-
// "status:" (rare, but shellResultStatus below already owns real shell
|
|
198
|
-
// output parsing; this fallback stays narrow and unchanged in behavior).
|
|
199
|
-
const inline = text.match(/^(?:status|state):\s*([^\s·,;]+)/mi)?.[1]?.trim();
|
|
200
|
-
return normalizeTerminalStatus(inline);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const LEADING_STATUS_MARKER_LINE_RE = /^\[status:\s*[^\]]*\]\s*$/i;
|
|
204
|
-
|
|
205
|
-
function stripLeadingStatusMarkerLines(lines) {
|
|
206
|
-
const out = Array.isArray(lines) ? lines.slice() : [];
|
|
207
|
-
if (out.length > 0 && LEADING_STATUS_MARKER_LINE_RE.test(String(out[0] ?? '').trim())) out.shift();
|
|
208
|
-
return out;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function stripLeadingStatusMarkerFromText(text) {
|
|
212
|
-
return stripLeadingStatusMarkerLines(String(text || '').split('\n')).join('\n');
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function shellDisplayStatus({ pending = false, failedCount = 0, isError = false, result = '' } = {}) {
|
|
216
|
-
const status = shellResultStatus(result);
|
|
217
|
-
if (pending || /^(running|pending|queued)$/.test(status)) return 'running';
|
|
218
|
-
if (/^cancel/.test(status)) return 'cancelled';
|
|
219
|
-
if (/^(failed|error|killed|timeout)$/.test(status) || isError || failedCount > 0) return 'failed';
|
|
220
|
-
return 'completed';
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function shellHeader(status, count = 1) {
|
|
224
|
-
const n = Math.max(1, Number(count) || 1);
|
|
225
|
-
const object = `${n} ${plural(n, 'command')}`;
|
|
226
|
-
if (status === 'running') return `Running ${object}`;
|
|
227
|
-
return `Ran ${object}`;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function shellResultElapsed(value) {
|
|
231
|
-
const match = String(value || '').match(/^\[elapsed:\s*(\d+)\s*ms\]/mi);
|
|
232
|
-
if (!match) return '';
|
|
233
|
-
const elapsedMs = Number(match[1]);
|
|
234
|
-
return Number.isFinite(elapsedMs) && elapsedMs >= 1000 ? formatElapsed(elapsedMs) : '';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
77
|
function statusCopy(name, label, count, doneCount, pending, isError, args = {}) {
|
|
238
78
|
// No stableVerbWidth padding: it padded the done verb to the active ("-ing")
|
|
239
79
|
// width, which Ink trims at the line END (vendor output trimEnd) so it never
|
|
@@ -243,395 +83,6 @@ function statusCopy(name, label, count, doneCount, pending, isError, args = {})
|
|
|
243
83
|
// dropping the pad just normalizes the spacing.
|
|
244
84
|
return formatToolActionHeader(name, args, { pending, count });
|
|
245
85
|
}
|
|
246
|
-
|
|
247
|
-
function fitResultLine(line, columns) {
|
|
248
|
-
const max = Math.min(RESULT_LINE_HARD_MAX, Math.max(MIN_RESULT_LINE_CHARS, Number(columns || 80) - 7));
|
|
249
|
-
const text = safeInlineText(line);
|
|
250
|
-
return displayWidth(text) > max ? truncateToWidth(text, max) : text;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/** Trim text from the end (by display width) so it fits maxWidth, appending '…'. */
|
|
254
|
-
function truncateToWidth(text, maxWidth) {
|
|
255
|
-
const str = safeInlineText(text);
|
|
256
|
-
if (maxWidth < 1) return '';
|
|
257
|
-
if (displayWidth(str) <= maxWidth) return str;
|
|
258
|
-
const chars = Array.from(str);
|
|
259
|
-
let out = '';
|
|
260
|
-
for (const ch of chars) {
|
|
261
|
-
if (displayWidth(out + ch + '…') > maxWidth) break;
|
|
262
|
-
out += ch;
|
|
263
|
-
}
|
|
264
|
-
return `${out}…`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function isAgentTool(normalizedName) {
|
|
268
|
-
return normalizedName === 'agent';
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const SKILL_SURFACE_NAMES = new Set([
|
|
272
|
-
'skill', 'skill_execute', 'skill_view', 'skills_list', 'use_skill',
|
|
273
|
-
]);
|
|
274
|
-
|
|
275
|
-
function isBackgroundTaskTool(normalizedName) {
|
|
276
|
-
return new Set(['explore', 'search', 'shell', 'bash', 'bash_session', 'shell_command', 'task']).has(String(normalizedName || '').toLowerCase());
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const AGENT_DISPLAY_NAMES = new Map([
|
|
280
|
-
['explore', 'Explore'],
|
|
281
|
-
['maintainer', 'Maintainer'],
|
|
282
|
-
['worker', 'Worker'],
|
|
283
|
-
['heavy-worker', 'Heavy Worker'],
|
|
284
|
-
['reviewer', 'Reviewer'],
|
|
285
|
-
['debugger', 'Debugger'],
|
|
286
|
-
]);
|
|
287
|
-
|
|
288
|
-
function titleizeAgentName(value) {
|
|
289
|
-
const text = String(value || '').trim();
|
|
290
|
-
if (!text) return '';
|
|
291
|
-
const key = text.toLowerCase().replace(/[\s_]+/g, '-');
|
|
292
|
-
if (AGENT_DISPLAY_NAMES.has(key)) return AGENT_DISPLAY_NAMES.get(key);
|
|
293
|
-
return text
|
|
294
|
-
.replace(/[_-]+/g, ' ')
|
|
295
|
-
.split(/\s+/)
|
|
296
|
-
.filter(Boolean)
|
|
297
|
-
.map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1).toLowerCase()}`)
|
|
298
|
-
.join(' ');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function agentModelLabel(args) {
|
|
302
|
-
const a = args && typeof args === 'object' ? args : {};
|
|
303
|
-
const provider = String(a.provider || a.providerId || a.provider_id || '').trim();
|
|
304
|
-
const model = String(a.model || '').trim();
|
|
305
|
-
const displayHint = String(a.modelDisplay || a.model_display || a.displayModel || '').trim();
|
|
306
|
-
return displayModelName(model, provider, displayHint);
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function agentTagLabel(args) {
|
|
310
|
-
// The real spawn tag (engine fills parsedArgs.tag from the envelope target).
|
|
311
|
-
// Never fall back to task_id — only the human-meaningful spawn tag belongs in
|
|
312
|
-
// the header parentheses.
|
|
313
|
-
return String(args?.tag || '').trim();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function withModelAndTag(label, args) {
|
|
317
|
-
const model = agentModelLabel(args);
|
|
318
|
-
const tag = agentTagLabel(args);
|
|
319
|
-
const inner = [model, tag].filter(Boolean).join(', ');
|
|
320
|
-
return inner ? `${label} (${inner})` : label;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Append an agent name to a base action word without leaving a trailing space
|
|
324
|
-
// when the agent is unknown (no generic "Agent" fallback).
|
|
325
|
-
function joinActionAgent(action, agent) {
|
|
326
|
-
return agent ? `${action} ${agent}` : action;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function agentResponseTitle(args) {
|
|
330
|
-
const name = titleizeAgentName(args?.agent || args?.subagent_type || args?.name || '');
|
|
331
|
-
// The agent + model identify the responder; the response summary itself
|
|
332
|
-
// is hidden in the collapsed card (ctrl+o expand still shows the full body).
|
|
333
|
-
// No generic "Agent" fallback — render just "Response" when the agent is empty.
|
|
334
|
-
return withModelAndTag(joinActionAgent('Response', name), args);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
function agentActionTitle(args) {
|
|
338
|
-
const name = titleizeAgentName(args?.agent || args?.subagent_type || args?.name || '');
|
|
339
|
-
// Runtime treats an omitted type/action as "spawn" (see agent-tool.mjs default),
|
|
340
|
-
// so mirror that contract here instead of falling through to the generic
|
|
341
|
-
// "Called agent" status copy.
|
|
342
|
-
const action = String(args?.type || args?.action || 'spawn').toLowerCase();
|
|
343
|
-
// Fixed action verbs regardless of running/completed status. No generic
|
|
344
|
-
// "Agent" fallback for the agent: when the agent is unknown render the action
|
|
345
|
-
// word alone ("Spawn") instead of "Spawn Agent".
|
|
346
|
-
if (action === 'spawn') return withModelAndTag(joinActionAgent('Spawn', name), args);
|
|
347
|
-
if (action === 'send') return withModelAndTag(joinActionAgent('Send', name), args);
|
|
348
|
-
if (action === 'list') return 'Agent status';
|
|
349
|
-
if (action === 'cancel') return withModelAndTag(joinActionAgent('Cancel', name), args);
|
|
350
|
-
if (action === 'close') return withModelAndTag(joinActionAgent('Close', name), args);
|
|
351
|
-
if (action === 'cleanup') return withModelAndTag(joinActionAgent('Cleanup', name), args);
|
|
352
|
-
if (action === 'read' || action === 'status') return withModelAndTag(joinActionAgent('Status', name), args);
|
|
353
|
-
return '';
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function agentActionSummary(args, summary) {
|
|
357
|
-
const text = String(summary || '').trim();
|
|
358
|
-
if (!text) return '';
|
|
359
|
-
const name = titleizeAgentName(args?.agent || args?.subagent_type || args?.name || '');
|
|
360
|
-
if (name && text === name) return '';
|
|
361
|
-
let rest = name && text.startsWith(`${name} · `) ? text.slice(name.length + 3).trim() : text;
|
|
362
|
-
// The agent/model/tag surface summary ("Heavy Worker · Opus 4.8") is now folded
|
|
363
|
-
// into the header label itself ("Spawn Heavy Worker (Opus 4.8, tag)"), so drop
|
|
364
|
-
// the model and tag tokens from the parenthesized summary to avoid showing
|
|
365
|
-
// them twice.
|
|
366
|
-
const model = agentModelLabel(args);
|
|
367
|
-
if (model && rest === model) return '';
|
|
368
|
-
const tag = agentTagLabel(args);
|
|
369
|
-
if (tag && rest === tag) return '';
|
|
370
|
-
return rest;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function hasAgentResponseResult(value) {
|
|
374
|
-
const text = String(value || '').trim();
|
|
375
|
-
if (!text) return false;
|
|
376
|
-
if (/^(?:undefined|null)$/i.test(text)) return false;
|
|
377
|
-
if (/^status:\s*(?:running|pending|queued|completed|failed|cancelled|canceled)(?:\s*·\s*task_id:\s*\S+)?$/i.test(text)) return false;
|
|
378
|
-
const isBridgeEnvelope = /^(?:agent task:|background task\b|agent message queued\b|agent close:)/i.test(text)
|
|
379
|
-
|| /^(?:agents|tasks):\s*\d/i.test(text)
|
|
380
|
-
|| /^\(no agents or tasks\)$/i.test(text)
|
|
381
|
-
|| (/^task_id:\s*\S+/mi.test(text) && /^(?:surface|operation|status):\s*/mi.test(text));
|
|
382
|
-
if (!isBridgeEnvelope) return true;
|
|
383
|
-
let sawBlank = false;
|
|
384
|
-
for (const line of text.split('\n')) {
|
|
385
|
-
const trimmed = line.trim();
|
|
386
|
-
if (!trimmed) {
|
|
387
|
-
sawBlank = true;
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
if (/^agent result\b/i.test(trimmed)) continue;
|
|
391
|
-
if (/^(?:undefined|null)$/i.test(trimmed)) continue;
|
|
392
|
-
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;
|
|
393
|
-
if (!sawBlank && /^(?:agent task|background task|agent message queued\b|agent close:|task_id|surface|operation|label|status|type|target|agent|preset|model|effort|fast|limits|started|finished|error|notification|queueDepth):?\s*/i.test(trimmed)) continue;
|
|
394
|
-
if (!sawBlank && /^(?:agents|tasks):\s*/i.test(trimmed)) continue;
|
|
395
|
-
if (/^\(no agents or tasks\)$/i.test(trimmed)) continue;
|
|
396
|
-
if (!sawBlank && /^-\s+\S+/i.test(trimmed)) continue;
|
|
397
|
-
return true;
|
|
398
|
-
}
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
function parseBackgroundTaskResult(value) {
|
|
403
|
-
const text = String(value || '').trim();
|
|
404
|
-
if (!text) return null;
|
|
405
|
-
const allLines = text.split('\n');
|
|
406
|
-
const start = allLines.findIndex((line) => line.trim() === 'background task');
|
|
407
|
-
if (start < 0) return null;
|
|
408
|
-
const rest = allLines.slice(start + 1);
|
|
409
|
-
const blank = rest.findIndex((line) => !line.trim());
|
|
410
|
-
const headLines = blank >= 0 ? rest.slice(0, blank) : rest;
|
|
411
|
-
const body = blank >= 0 ? rest.slice(blank + 1).join('\n').trim() : '';
|
|
412
|
-
const fields = {};
|
|
413
|
-
for (const line of headLines) {
|
|
414
|
-
const match = /^([a-zA-Z][\w-]*):\s*(.*)$/.exec(line.trim());
|
|
415
|
-
if (match) fields[match[1].toLowerCase()] = match[2].trim();
|
|
416
|
-
}
|
|
417
|
-
const status = String(fields.status || '').toLowerCase();
|
|
418
|
-
const error = fields.error || '';
|
|
419
|
-
return {
|
|
420
|
-
taskId: fields.task_id || fields.taskid || '',
|
|
421
|
-
surface: fields.surface || '',
|
|
422
|
-
operation: fields.operation || '',
|
|
423
|
-
label: fields.label || '',
|
|
424
|
-
status,
|
|
425
|
-
startedAt: fields.started || fields.startedat || '',
|
|
426
|
-
finishedAt: fields.finished || fields.finishedat || '',
|
|
427
|
-
body,
|
|
428
|
-
error,
|
|
429
|
-
hasResponse: Boolean(body) && !isBackgroundErrorOnlyBody(body, error) && !/^(running|pending|queued)$/i.test(status),
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
function backgroundTaskMetaFromArgs(args = {}) {
|
|
434
|
-
const taskId = String(args.task_id || args.taskId || '').trim();
|
|
435
|
-
if (!taskId) return null;
|
|
436
|
-
return {
|
|
437
|
-
taskId,
|
|
438
|
-
surface: args.surface || '',
|
|
439
|
-
operation: args.operation || '',
|
|
440
|
-
label: args.label || '',
|
|
441
|
-
status: String(args.status || '').toLowerCase(),
|
|
442
|
-
startedAt: args.startedAt || args.started || '',
|
|
443
|
-
finishedAt: args.finishedAt || args.finished || '',
|
|
444
|
-
error: args.error || '',
|
|
445
|
-
body: '',
|
|
446
|
-
hasResponse: false,
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
function resolveBackgroundTaskMeta(parsedArgs = {}, resultText = '') {
|
|
451
|
-
const parsed = parseBackgroundTaskResult(resultText);
|
|
452
|
-
if (parsed) {
|
|
453
|
-
if (!parsed.error && parsedArgs?.error) parsed.error = parsedArgs.error;
|
|
454
|
-
if (!parsed.status && parsedArgs?.status) parsed.status = String(parsedArgs.status).toLowerCase();
|
|
455
|
-
if (!parsed.surface && parsedArgs?.surface) parsed.surface = parsedArgs.surface;
|
|
456
|
-
return parsed;
|
|
457
|
-
}
|
|
458
|
-
return backgroundTaskMetaFromArgs(parsedArgs);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function backgroundTaskElapsed(meta = {}, fallback = '') {
|
|
462
|
-
const startedMs = Date.parse(meta.startedAt || '');
|
|
463
|
-
const finishedMs = Date.parse(meta.finishedAt || '');
|
|
464
|
-
if (Number.isFinite(startedMs) && Number.isFinite(finishedMs) && finishedMs >= startedMs) {
|
|
465
|
-
const elapsedMs = finishedMs - startedMs;
|
|
466
|
-
return elapsedMs >= 1000 ? formatElapsed(elapsedMs) : '';
|
|
467
|
-
}
|
|
468
|
-
return fallback || '';
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function prefixElapsed(detail, elapsed = '') {
|
|
472
|
-
const text = String(detail || '').trim();
|
|
473
|
-
const time = String(elapsed || '').trim();
|
|
474
|
-
if (!time) return text;
|
|
475
|
-
// Unified convention: the elapsed time ALWAYS goes at the END, ` · ` separated.
|
|
476
|
-
// Guard against a double-append when the text already ends with the same time.
|
|
477
|
-
if (text && text.endsWith(`· ${time}`)) return text;
|
|
478
|
-
return text ? `${text} · ${time}` : time;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
function mergeTerminalDetail(status, detail = '') {
|
|
482
|
-
const label = displayTerminalStatus(status);
|
|
483
|
-
const text = String(detail || '').trim();
|
|
484
|
-
if (!label) return text;
|
|
485
|
-
if (label === 'Finished' && text) return text;
|
|
486
|
-
if (!text) return label;
|
|
487
|
-
if (text.toLowerCase().startsWith(label.toLowerCase())) return text;
|
|
488
|
-
return `${label} · ${text}`;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
function shouldPrefixSyncElapsed(normalizedName, label) {
|
|
492
|
-
const n = String(normalizedName || '').toLowerCase();
|
|
493
|
-
const l = String(label || '').toLowerCase();
|
|
494
|
-
return n === 'explore' || l === 'explore' || n === 'search' || l === 'search' || l === 'web search';
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
function backgroundTaskDisplayName(normalizedName, meta = {}) {
|
|
498
|
-
const surface = String(meta.surface || normalizedName || '').toLowerCase();
|
|
499
|
-
if (surface === 'explore') return 'Explore';
|
|
500
|
-
if (surface === 'search') return 'Search';
|
|
501
|
-
if (surface === 'shell' || surface === 'bash' || surface === 'bash_session' || surface === 'shell_command' || surface === 'task') return 'Shell';
|
|
502
|
-
return titleizeAgentName(surface || normalizedName || 'Task');
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function backgroundTaskResultTitle(normalizedName, meta = {}) {
|
|
506
|
-
const display = backgroundTaskDisplayName(normalizedName, meta);
|
|
507
|
-
if (display === 'Shell') return 'Shell output';
|
|
508
|
-
if (display === 'Search') return 'Search results';
|
|
509
|
-
return `${display} response`;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function backgroundTaskActionTitle(normalizedName, meta = {}) {
|
|
513
|
-
const display = backgroundTaskDisplayName(normalizedName, meta);
|
|
514
|
-
if (/^(running|pending|queued)$/i.test(meta.status || '')) return `Started ${display}`;
|
|
515
|
-
if (meta.hasResponse) return backgroundTaskResultTitle(normalizedName, meta);
|
|
516
|
-
return `${display} status`;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
function backgroundTaskFailureDetail(meta = {}, parsedArgs = {}) {
|
|
520
|
-
const status = meta.status || parsedArgs?.status;
|
|
521
|
-
const error = meta.error || parsedArgs?.error;
|
|
522
|
-
if (!error) return '';
|
|
523
|
-
const surface = meta.surface || parsedArgs?.surface || '';
|
|
524
|
-
return backgroundTaskFailureStatusLabel(status, error, { surface });
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function backgroundTaskDetail(meta = {}, elapsed = '', parsedArgs = {}) {
|
|
528
|
-
const parts = [];
|
|
529
|
-
const status = displayTerminalStatus(meta.status);
|
|
530
|
-
if (status) parts.push(status);
|
|
531
|
-
if (meta.taskId) parts.push(`task_id: ${meta.taskId}`);
|
|
532
|
-
const firstBodyLine = String(meta.body || '').split('\n').map((line) => line.trim()).find(Boolean) || '';
|
|
533
|
-
if (firstBodyLine && /^(running|pending|queued)$/i.test(meta.status || '')) parts.push(firstBodyLine);
|
|
534
|
-
return prefixElapsed(parts.join(' · '), elapsed);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function isBackgroundTaskResponseArgs(normalizedName, args = {}) {
|
|
538
|
-
if (!isBackgroundTaskTool(normalizedName)) return false;
|
|
539
|
-
const type = String(args?.type || args?.action || '').toLowerCase();
|
|
540
|
-
const status = String(args?.status || '').toLowerCase();
|
|
541
|
-
return type === 'result' || type === 'completion' || (/^(completed|cancelled|canceled)$/i.test(status) && Boolean(args?.task_id));
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
function isOutputDetailTool(normalizedName, label) {
|
|
545
|
-
const n = String(normalizedName || '').toLowerCase();
|
|
546
|
-
const l = String(label || '').toLowerCase();
|
|
547
|
-
return new Set([
|
|
548
|
-
'shell', 'bash', 'bash_session', 'shell_command', 'job_wait',
|
|
549
|
-
'read', 'view_image', 'read_mcp_resource',
|
|
550
|
-
'grep', 'glob', 'search', 'search_query', 'image_query', 'web_search', 'web_search_call', 'explore', 'web_fetch', 'fetch', 'download_attachment',
|
|
551
|
-
'list', 'ls', 'code_graph',
|
|
552
|
-
'recall', 'recall_memory', 'search_memories', 'remember', 'save_memory', 'update_memory',
|
|
553
|
-
]).has(n) || l === 'read' || l === 'search' || l === 'web search' || l === 'run';
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
function genericCompletedDetail({ normalizedName, label, hasResult, firstResultLine, isError }) {
|
|
557
|
-
const n = String(normalizedName || '').toLowerCase();
|
|
558
|
-
const l = String(label || '').toLowerCase();
|
|
559
|
-
if (isError) return hasResult ? firstResultLine : 'Failed';
|
|
560
|
-
if (n === 'shell' || n === 'bash' || n === 'bash_session' || n === 'shell_command' || n === 'job_wait') {
|
|
561
|
-
return '';
|
|
562
|
-
}
|
|
563
|
-
if (isOutputDetailTool(n, l)) {
|
|
564
|
-
return hasResult ? firstResultLine : '';
|
|
565
|
-
}
|
|
566
|
-
return '';
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
function toolSearchLoadedSummary(resultText) {
|
|
570
|
-
let parsed;
|
|
571
|
-
try {
|
|
572
|
-
parsed = JSON.parse(String(resultText || ''));
|
|
573
|
-
} catch {
|
|
574
|
-
const text = String(resultText || '');
|
|
575
|
-
const match = /^Loaded deferred tools:\s*(.+)$/m.exec(text);
|
|
576
|
-
if (!match) return '';
|
|
577
|
-
return [...new Set(match[1].split(',').map((name) => name.trim()).filter(Boolean))].join(', ');
|
|
578
|
-
}
|
|
579
|
-
const tools = parsed?.selected?.tools;
|
|
580
|
-
if (!tools || typeof tools !== 'object') return '';
|
|
581
|
-
const names = [
|
|
582
|
-
...(Array.isArray(tools.added) ? tools.added : []),
|
|
583
|
-
...(Array.isArray(tools.already) ? tools.already : []),
|
|
584
|
-
]
|
|
585
|
-
.map((name) => String(name || '').trim())
|
|
586
|
-
.filter(Boolean);
|
|
587
|
-
return [...new Set(names)].join(', ');
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
function agentTerminalDetail(status, isError, elapsed, error = '') {
|
|
591
|
-
const failureDetail = isError && error
|
|
592
|
-
? backgroundTaskFailureStatusLabel(status, error, { surface: 'agent' })
|
|
593
|
-
: '';
|
|
594
|
-
if (failureDetail) return failureDetail;
|
|
595
|
-
const s = String(status || '').toLowerCase();
|
|
596
|
-
const word = /cancel/.test(s)
|
|
597
|
-
? 'Cancelled'
|
|
598
|
-
: /error|fail|killed|timeout/.test(s) || isError
|
|
599
|
-
? 'Failed'
|
|
600
|
-
: /done|success|complete|closed/.test(s)
|
|
601
|
-
? 'Finished'
|
|
602
|
-
: '';
|
|
603
|
-
// Unified ` · <time>` convention (previously "Finished after 12s").
|
|
604
|
-
return word ? `${word}${elapsed ? ` · ${elapsed}` : ''}` : '';
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
function clampFailureCount(errorCount, groupCount, isError) {
|
|
608
|
-
const explicit = Number(errorCount);
|
|
609
|
-
if (Number.isFinite(explicit)) return Math.max(0, Math.min(groupCount, Math.floor(explicit)));
|
|
610
|
-
return isError ? groupCount : 0;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
// Single source of truth for the tool-card dot (●) color. Both the aggregate
|
|
614
|
-
// and normal (single-tool) render paths must call this with a resolved
|
|
615
|
-
// `terminalStatus` — do not recompute color inline elsewhere.
|
|
616
|
-
// running/pending -> theme.text (white; blink handled by caller)
|
|
617
|
-
// success -> theme.success
|
|
618
|
-
// partial failure -> mixdogOrange || warning (some, not all, of the group failed)
|
|
619
|
-
// all failed -> theme.error
|
|
620
|
-
// cancelled -> theme.warning
|
|
621
|
-
// Note: callers resolve terminalStatus to 'failed' whenever failedCount > 0
|
|
622
|
-
// (they cannot tell partial from total failure), so a 'failed' status must
|
|
623
|
-
// still fall through to the failedCount check below rather than short-circuit
|
|
624
|
-
// to error — otherwise a mixed-success batch (e.g. 1 of 3 failed) renders as
|
|
625
|
-
// full-failure red instead of partial orange.
|
|
626
|
-
function toolStatusColor({ pending, groupCount, failedCount, terminalStatus = '' }) {
|
|
627
|
-
if (pending) return theme.text;
|
|
628
|
-
const status = normalizeTerminalStatus(terminalStatus);
|
|
629
|
-
if (status === 'cancelled') return theme.warning;
|
|
630
|
-
if (failedCount <= 0) return status === 'failed' ? theme.error : theme.success;
|
|
631
|
-
if (groupCount > 1 && failedCount < groupCount) return theme.mixdogOrange || theme.warning;
|
|
632
|
-
return theme.error;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
86
|
export function ToolExecution({ name, args, result, rawResult, isError, errorCount, expanded, columns = 80, attached = false, count = 1, completedCount = 0, startedAt = 0, completedAt = 0, aggregate = false, categories = {}, headerFinalized = true, deferredDisplayReady = false }) {
|
|
636
87
|
const rowWidth = Math.max(1, Number(columns || 80));
|
|
637
88
|
const [blinkOn, setBlinkOn] = useState(true);
|
|
@@ -988,7 +439,14 @@ export function ToolExecution({ name, args, result, rawResult, isError, errorCou
|
|
|
988
439
|
}
|
|
989
440
|
const finalStatusColor = toolStatusColor({ pending, groupCount, failedCount, terminalStatus });
|
|
990
441
|
const dotColor = finalStatusColor;
|
|
991
|
-
|
|
442
|
+
// Agent surface cards use directional markers: `←` for requests going OUT
|
|
443
|
+
// (spawn/send/etc.) and `→` for the response coming back IN. Background
|
|
444
|
+
// task cards (shell async / explore / search / task) and every other tool
|
|
445
|
+
// keep the BLACK_CIRCLE turn marker. Blink behavior is shared.
|
|
446
|
+
const markerGlyph = isAgentResponse
|
|
447
|
+
? AGENT_RESPONSE_MARKER
|
|
448
|
+
: (isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER);
|
|
449
|
+
const dotText = pending && !blinkExpired && !blinkOn ? ' ' : markerGlyph;
|
|
992
450
|
let labelText;
|
|
993
451
|
if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
|
|
994
452
|
else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
|