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
package/src/tui/figures.mjs
CHANGED
|
@@ -13,6 +13,11 @@ export const BULLET_OPERATOR = '\u00b7'; // · - inline separator
|
|
|
13
13
|
export const UP_ARROW = '\u2191'; // ↑ - history/scroll up
|
|
14
14
|
export const DOWN_ARROW = '\u2193'; // ↓ - history/scroll down
|
|
15
15
|
|
|
16
|
+
// Agent surface markers — dispatch (call out) vs response (result back).
|
|
17
|
+
// Basic Arrows block renders reliably in Cascadia/Consolas (same block as ↑↓).
|
|
18
|
+
export const RIGHT_ARROW = '\u2192'; // → - agent call/dispatch marker
|
|
19
|
+
export const LEFT_ARROW = '\u2190'; // ← - agent response marker
|
|
20
|
+
|
|
16
21
|
// Tool-result tree gutter. \u2514 (└, BOX DRAWINGS LIGHT UP AND RIGHT) renders
|
|
17
22
|
// in virtually every monospace font; the previous \u23bf (bracket extension)
|
|
18
23
|
// had no glyph in common terminal fonts (Cascadia/Consolas) and fell back to a
|
package/src/tui/index.jsx
CHANGED
|
@@ -141,6 +141,101 @@ function resolveTuiStderrLogPath() {
|
|
|
141
141
|
|| join(process.env.MIXDOG_RUNTIME_ROOT || join(tmpdir(), 'mixdog'), 'mixdog-tui.stderr.log');
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/** `rgb(r,g,b)` → truecolor SGR prefix ('' when unparsable). */
|
|
145
|
+
function ansiFg(rgb) {
|
|
146
|
+
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ''));
|
|
147
|
+
return m ? `\x1b[38;2;${m[1]};${m[2]};${m[3]}m` : '';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Paint a static boot splash into the (already-entered) alternate screen so
|
|
152
|
+
* the seconds between alt-screen entry and the first ink frame are not a blank
|
|
153
|
+
* void. The logo is drawn at the SAME rows/columns as App.jsx's welcome banner
|
|
154
|
+
* (marginTop 3 + 5 logo rows + blank + subtitle), so ink's first real frame
|
|
155
|
+
* overprints it in place with no visible jump. The bottom panel silhouette
|
|
156
|
+
* carries an animated "Loading projects…" spinner so the boot reads as an
|
|
157
|
+
* explicit loading state instead of an empty box that later fills with the
|
|
158
|
+
* list. Returns { stop } — call it right before ink mounts.
|
|
159
|
+
*/
|
|
160
|
+
function paintBootSplash() {
|
|
161
|
+
try {
|
|
162
|
+
const cols = Math.max(1, Number(process.stdout.columns) || 80);
|
|
163
|
+
const rows = Math.max(1, Number(process.stdout.rows) || 24);
|
|
164
|
+
const center = (s) => `${' '.repeat(Math.max(0, Math.floor((cols - s.length) / 2)))}${s}`;
|
|
165
|
+
const logo = [
|
|
166
|
+
'███╗ ███╗██╗██╗ ██╗██████╗ ██████╗ ██████╗ ',
|
|
167
|
+
'████╗ ████║██║╚██╗██╔╝██╔══██╗██╔═══██╗██╔════╝ ',
|
|
168
|
+
'██╔████╔██║██║ ╚███╔╝ ██║ ██║██║ ██║██║ ███╗',
|
|
169
|
+
'██║╚██╔╝██║██║ ██╔██╗ ██║ ██║██║ ██║██║ ██║',
|
|
170
|
+
'██║ ╚═╝ ██║██║██╔╝ ██╗██████╔╝╚██████╔╝╚██████╔╝',
|
|
171
|
+
];
|
|
172
|
+
const textFg = ansiFg(theme.text);
|
|
173
|
+
const logoFg = ansiFg(theme.logo ?? theme.claude) || textFg;
|
|
174
|
+
const subtleFg = ansiFg(theme.inactive);
|
|
175
|
+
const bold = '\x1b[1m';
|
|
176
|
+
const reset = '\x1b[0m';
|
|
177
|
+
let out = '\x1b[4;1H'; // row 4 == App banner's marginTop={3}
|
|
178
|
+
for (let i = 0; i < logo.length; i++) {
|
|
179
|
+
const fg = i < 2 ? textFg : logoFg;
|
|
180
|
+
out += `${bold}${fg}${center(logo[i])}${reset}\r\n`;
|
|
181
|
+
}
|
|
182
|
+
out += '\r\n';
|
|
183
|
+
out += `${subtleFg}${center(`mixdog coding agent · ${process.cwd()}`)}${reset}`;
|
|
184
|
+
// Bottom placeholder: the first REAL screen is the project picker — a tall
|
|
185
|
+
// floating panel sitting directly above the 3-row statusline — not the
|
|
186
|
+
// 3-row prompt box. Painting a short prompt silhouette here made the panel
|
|
187
|
+
// look like it "unfolded" from 3 rows to full height on the first ink
|
|
188
|
+
// frame. Mirror App.jsx's startup math instead:
|
|
189
|
+
// floatingPanelRows = min(17, rows - 15), bottom row = rows - 3.
|
|
190
|
+
const floating = Math.min(17, rows - 15);
|
|
191
|
+
let spinnerRow = 0;
|
|
192
|
+
let spinnerFg = '';
|
|
193
|
+
if (floating >= 3) {
|
|
194
|
+
const borderFg = ansiFg(theme.promptBorder) || subtleFg;
|
|
195
|
+
// Leave the last COLUMN untouched: writing the bottom-right cell makes
|
|
196
|
+
// Windows Terminal/conhost scroll the alt screen one row (same reason
|
|
197
|
+
// App.jsx reserves rightSafetyColumns), which made the whole splash jump
|
|
198
|
+
// when the box was painted at full width on the final row.
|
|
199
|
+
const boxW = Math.max(4, cols - 1);
|
|
200
|
+
const innerW = boxW - 2;
|
|
201
|
+
const topRow = rows - 3 - floating + 1;
|
|
202
|
+
out += `\x1b[${topRow};1H${borderFg}╭${'─'.repeat(innerW)}╮`;
|
|
203
|
+
for (let i = 1; i < floating - 1; i++) {
|
|
204
|
+
out += `\x1b[${topRow + i};1H│${' '.repeat(innerW)}│`;
|
|
205
|
+
}
|
|
206
|
+
out += `\x1b[${rows - 3};1H╰${'─'.repeat(innerW)}╯${reset}`;
|
|
207
|
+
// "Loading projects…" line inside the panel silhouette. Picker.jsx
|
|
208
|
+
// renders border, then (paddingX=1, no paddingY) title/help row as its
|
|
209
|
+
// very first interior row — not vertically centered. Match that rhythm
|
|
210
|
+
// (topRow + 1) instead of centering, so ink's first frame doesn't jump
|
|
211
|
+
// the text down/up when it overprints this splash.
|
|
212
|
+
spinnerRow = topRow + 1;
|
|
213
|
+
spinnerFg = subtleFg;
|
|
214
|
+
out += `\x1b[${spinnerRow};3H${subtleFg} Loading projects…${reset}`;
|
|
215
|
+
}
|
|
216
|
+
// Park the cursor at home so ink's first frame paints top-down over the
|
|
217
|
+
// splash instead of starting at the bottom row and scrolling the screen.
|
|
218
|
+
out += '\x1b[H';
|
|
219
|
+
process.stdout.write(out);
|
|
220
|
+
// Animate only the single spinner glyph cell — never reflow anything else,
|
|
221
|
+
// so stopping mid-frame can't leave torn output for ink to overwrite.
|
|
222
|
+
if (spinnerRow > 0) {
|
|
223
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
224
|
+
let tick = 0;
|
|
225
|
+
const timer = setInterval(() => {
|
|
226
|
+
try {
|
|
227
|
+
process.stdout.write(`\x1b[${spinnerRow};3H${spinnerFg}${frames[tick % frames.length]}${reset}\x1b[H`);
|
|
228
|
+
tick += 1;
|
|
229
|
+
} catch { /* ignore */ }
|
|
230
|
+
}, 80);
|
|
231
|
+
timer.unref?.();
|
|
232
|
+
return { stop: () => clearInterval(timer) };
|
|
233
|
+
}
|
|
234
|
+
return { stop: () => {} };
|
|
235
|
+
} catch { /* cosmetic only — never block boot */ }
|
|
236
|
+
return { stop: () => {} };
|
|
237
|
+
}
|
|
238
|
+
|
|
144
239
|
/** Drain stdin so queued key/mouse bytes do not leak into the shell after exit. */
|
|
145
240
|
function drainStdin(stdin = process.stdin) {
|
|
146
241
|
if (!stdin.isTTY) return;
|
|
@@ -277,17 +372,27 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
|
|
|
277
372
|
try { await loadThemeSettingFromConfig(); } catch { /* default theme stays */ }
|
|
278
373
|
emitTerminalBackground(theme.background);
|
|
279
374
|
|
|
375
|
+
// Static splash + loading spinner while createEngineSession() warms up
|
|
376
|
+
// (config, providers, sessions). Without this the user stares at an empty
|
|
377
|
+
// alt screen for the whole boot, then the UI "pops in" — the splash makes
|
|
378
|
+
// the first ink frame an in-place repaint instead.
|
|
379
|
+
const splash = paintBootSplash() || { stop: () => {} };
|
|
380
|
+
|
|
280
381
|
let store;
|
|
281
382
|
try {
|
|
282
383
|
store = await createEngineSession({ provider, model, toolMode, remote });
|
|
283
384
|
bootProfile('store:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
284
385
|
} catch (error) {
|
|
386
|
+
splash.stop();
|
|
285
387
|
restoreTerminal();
|
|
286
388
|
try { process.off('exit', restoreTerminal); } catch { /* ignore */ }
|
|
287
389
|
restoreStderr();
|
|
288
390
|
process.stderr.write(`mixdog: ${error?.message || error}\n`);
|
|
289
391
|
return 1;
|
|
290
392
|
}
|
|
393
|
+
// Stop the spinner BEFORE ink mounts so no stray splash write can land
|
|
394
|
+
// between (or after) ink's first frames.
|
|
395
|
+
splash.stop();
|
|
291
396
|
|
|
292
397
|
// Keep mouse handling app-owned by default: native terminal selections are
|
|
293
398
|
// cleared by the fullscreen redraws that happen while a turn streams. Users
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { displayWidth } from './display-width.mjs';
|
|
2
2
|
|
|
3
3
|
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
4
4
|
|
|
@@ -174,7 +174,7 @@ export function caretPosition(text, offset, width, hasTrailingContent = undefine
|
|
|
174
174
|
continue;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
const segmentWidth =
|
|
177
|
+
const segmentWidth = displayWidth(segment);
|
|
178
178
|
if (segmentWidth === 0) continue;
|
|
179
179
|
|
|
180
180
|
// Mirror ink's wrap-ansi wrapWord (wrap="hard", wordWrap:false): a glyph
|
|
@@ -722,6 +722,9 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
722
722
|
return `${OSC8_OPEN(href)}${ex.link(href)}${OSC8_CLOSE}`;
|
|
723
723
|
}
|
|
724
724
|
case 'list':
|
|
725
|
+
// Honor loose lists (blank line between items in the source markdown):
|
|
726
|
+
// items are joined with one extra EOL so multi-line items keep the
|
|
727
|
+
// author's breathing room. Tight lists render adjacent as before.
|
|
725
728
|
return token.items
|
|
726
729
|
.map((item, index) =>
|
|
727
730
|
formatToken(
|
|
@@ -733,7 +736,7 @@ export function formatToken(token, listBaseIndent = 0, orderedListNumber = null,
|
|
|
733
736
|
depth,
|
|
734
737
|
),
|
|
735
738
|
)
|
|
736
|
-
.join('');
|
|
739
|
+
.join(token.loose && depth === 0 ? EOL : '');
|
|
737
740
|
case 'list_item':
|
|
738
741
|
return formatListItem(token, listBaseIndent, orderedListNumber, parent, depth, width);
|
|
739
742
|
case 'paragraph':
|
|
@@ -42,11 +42,16 @@ export function isResetStatsState(stats) {
|
|
|
42
42
|
/**
|
|
43
43
|
* True when the footer should snap local / invalidate cached async output.
|
|
44
44
|
* Keep in sync with `shouldSnapLocalStatusline` in StatusLine.jsx.
|
|
45
|
+
* `agentRevision` is intentionally NOT checked here: agent worker/job churn
|
|
46
|
+
* must only kick off the async full-render refresh (the render effect already
|
|
47
|
+
* depends on `agentRevision`), never snap the footer back to the usage-less
|
|
48
|
+
* `localBootStatusLine` before/after the first full render — that was the
|
|
49
|
+
* source of the boot-entry flicker. `agentRevision` still participates in
|
|
50
|
+
* `statuslineFooterCacheKey` below so the async cache key churns correctly.
|
|
45
51
|
*/
|
|
46
52
|
export function statuslineFooterIdentityChanged(args, lastArgs) {
|
|
47
53
|
if (!args) return false;
|
|
48
54
|
if (!lastArgs) return true;
|
|
49
|
-
if (args.agentRevision !== lastArgs.agentRevision) return true;
|
|
50
55
|
if (args.sessionId !== lastArgs.sessionId) return true;
|
|
51
56
|
if (args.provider !== lastArgs.provider || args.model !== lastArgs.model) return true;
|
|
52
57
|
if (args.effort !== lastArgs.effort || args.fast !== lastArgs.fast) return true;
|
|
@@ -60,8 +65,11 @@ export function statuslineFooterIdentityChanged(args, lastArgs) {
|
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* Cache key for the last async full statusline (identity-changing props only).
|
|
63
|
-
*
|
|
64
|
-
*
|
|
68
|
+
* Includes `agentRevision` as a churn key (so a stale async result is not kept
|
|
69
|
+
* forever), but `agentRevision` is deliberately excluded from
|
|
70
|
+
* `statuslineFooterIdentityChanged` above — it must not itself force a local
|
|
71
|
+
* snap-back. Stats reset is not keyed here; `statuslineFooterIdentityChanged`
|
|
72
|
+
* clears cache instead.
|
|
65
73
|
*/
|
|
66
74
|
export function statuslineFooterCacheKey({
|
|
67
75
|
agentRevision = '',
|
package/src/tui/theme.mjs
CHANGED
|
@@ -168,10 +168,16 @@ import {
|
|
|
168
168
|
BLACK_CIRCLE,
|
|
169
169
|
RESULT_GUTTER_GLYPH,
|
|
170
170
|
RESULT_GUTTER_CONT_GLYPH,
|
|
171
|
+
RIGHT_ARROW,
|
|
172
|
+
LEFT_ARROW,
|
|
171
173
|
} from './figures.mjs';
|
|
172
174
|
|
|
173
175
|
/** Turn marker — BLACK_CIRCLE (`⏺` on macOS; `●` elsewhere). */
|
|
174
176
|
export const TURN_MARKER = BLACK_CIRCLE;
|
|
177
|
+
/** Agent call marker — `←` (request going out to a sub-agent). */
|
|
178
|
+
export const AGENT_CALL_MARKER = LEFT_ARROW;
|
|
179
|
+
/** Agent response marker — `→` (result coming back in to the transcript). */
|
|
180
|
+
export const AGENT_RESPONSE_MARKER = RIGHT_ARROW;
|
|
175
181
|
/** Result-tree gutter — `└` (U+2514) padded to a 2-col hanging indent. */
|
|
176
182
|
export const RESULT_GUTTER = ` ${RESULT_GUTTER_GLYPH} `;
|
|
177
183
|
/**
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/ui/statusline-agents.mjs — agent worker/job normalization for the L2 line.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from statusline.mjs: normalize/merge REPL agent workers &
|
|
5
|
+
* bridge jobs into a statusline payload, classify running vs hidden-maintenance
|
|
6
|
+
* workers, and enumerate active hidden agents from live session runtimes. No
|
|
7
|
+
* behavior change.
|
|
8
|
+
*/
|
|
9
|
+
import { forEachSessionRuntime } from '../runtime/agent/orchestrator/session/manager.mjs';
|
|
10
|
+
import { listHiddenAgentNames } from '../runtime/agent/orchestrator/internal-agents.mjs';
|
|
11
|
+
import { num, GRN, R, B } from './statusline-format.mjs';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
|
|
14
|
+
let _hiddenStatuslineAgents = null;
|
|
15
|
+
|
|
16
|
+
export function summarizeWorkerTags(workers, limit = 3) {
|
|
17
|
+
const cleanLabels = [...new Set((Array.isArray(workers) ? workers : [])
|
|
18
|
+
.map((worker) => String(worker?.tag || '').trim())
|
|
19
|
+
.filter(Boolean))];
|
|
20
|
+
if (cleanLabels.length <= limit) return cleanLabels.join(', ');
|
|
21
|
+
return `${cleanLabels.slice(0, limit).join(', ')}, +${cleanLabels.length - limit}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function normalizeAgentWorkerForStatusline(worker = {}) {
|
|
25
|
+
const tag = String(worker.tag || worker.agent || worker.name || '').trim();
|
|
26
|
+
if (!tag) return null;
|
|
27
|
+
const statusText = String(worker.stage || worker.status || '').toLowerCase();
|
|
28
|
+
const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
|
|
29
|
+
return {
|
|
30
|
+
tag,
|
|
31
|
+
status,
|
|
32
|
+
startedAtMs: timeMs(worker.startedAt || worker.startTime || worker.createdAt),
|
|
33
|
+
agent: worker.agent || null,
|
|
34
|
+
stage: worker.stage || worker.status || null,
|
|
35
|
+
sessionId: worker.sessionId || null,
|
|
36
|
+
provider: worker.provider || null,
|
|
37
|
+
model: worker.model || null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizeAgentJobForStatusline(job = {}) {
|
|
42
|
+
const statusText = String(job.status || job.stage || '').toLowerCase();
|
|
43
|
+
if (!statusText) return null;
|
|
44
|
+
const taskId = String(job.task_id || job.taskId || '').trim();
|
|
45
|
+
const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
|
|
46
|
+
if (!tag && !taskId) return null;
|
|
47
|
+
const startedAtMs = timeMs(job.startedAt);
|
|
48
|
+
const finishedAtMs = timeMs(job.finishedAt);
|
|
49
|
+
if (isTerminalBridgeStatus(statusText) && finishedAtMs > 0) {
|
|
50
|
+
return {
|
|
51
|
+
tag,
|
|
52
|
+
taskId,
|
|
53
|
+
status: 'finished',
|
|
54
|
+
finalStatus: statusText,
|
|
55
|
+
startedAtMs,
|
|
56
|
+
finishedAtMs,
|
|
57
|
+
agent: job.agent || null,
|
|
58
|
+
stage: job.stage || job.workerStatus || job.status || null,
|
|
59
|
+
sessionId: job.sessionId || null,
|
|
60
|
+
provider: job.provider || null,
|
|
61
|
+
model: job.model || null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (!/running/.test(statusText)) return null;
|
|
65
|
+
return {
|
|
66
|
+
tag,
|
|
67
|
+
taskId,
|
|
68
|
+
status: 'running',
|
|
69
|
+
startedAtMs,
|
|
70
|
+
agent: job.agent || null,
|
|
71
|
+
stage: job.stage || job.workerStatus || job.status || null,
|
|
72
|
+
sessionId: job.sessionId || null,
|
|
73
|
+
provider: job.provider || null,
|
|
74
|
+
model: job.model || null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
|
|
79
|
+
const byTag = new Map();
|
|
80
|
+
const finishedJobs = [];
|
|
81
|
+
for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
|
|
82
|
+
const row = normalizeAgentWorkerForStatusline(worker);
|
|
83
|
+
if (row) byTag.set(row.tag, row);
|
|
84
|
+
}
|
|
85
|
+
for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
|
|
86
|
+
const row = normalizeAgentJobForStatusline(job);
|
|
87
|
+
if (!row) continue;
|
|
88
|
+
if (row.status === 'finished') {
|
|
89
|
+
finishedJobs.push(row);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const prev = byTag.get(row.tag);
|
|
93
|
+
byTag.set(row.tag, { ...(prev || {}), ...row, status: 'running' });
|
|
94
|
+
}
|
|
95
|
+
const workers = [...byTag.values()];
|
|
96
|
+
return {
|
|
97
|
+
workers,
|
|
98
|
+
finishedJobs: finishedJobs.sort((a, b) => (b.finishedAtMs || 0) - (a.finishedAtMs || 0)),
|
|
99
|
+
sessions: {
|
|
100
|
+
roles: workers.filter((w) => w.status !== 'idle').map((w) => w.tag),
|
|
101
|
+
workers,
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function classifyAgentWorkers(workers = []) {
|
|
107
|
+
const maintenance = [];
|
|
108
|
+
const runningWorkers = [];
|
|
109
|
+
const seenMaintenance = new Set();
|
|
110
|
+
const seenRunning = new Set();
|
|
111
|
+
for (const w of Array.isArray(workers) ? workers : []) {
|
|
112
|
+
const tag = String(w?.tag || '').trim();
|
|
113
|
+
if (!tag) continue;
|
|
114
|
+
const maint = hiddenWorkerLabel(w);
|
|
115
|
+
if (maint) {
|
|
116
|
+
if (w.status !== 'idle' && !seenMaintenance.has(maint)) {
|
|
117
|
+
seenMaintenance.add(maint);
|
|
118
|
+
maintenance.push(`${GRN}↻${R} ${B}${maint}${R}`);
|
|
119
|
+
}
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (w.status !== 'idle' && !seenRunning.has(tag)) {
|
|
123
|
+
seenRunning.add(tag);
|
|
124
|
+
runningWorkers.push(w);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return { maintenance, runningWorkers };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hiddenWorkerLabel(worker = {}) {
|
|
131
|
+
const agent = String(worker?.agent || '').trim();
|
|
132
|
+
const tag = String(worker?.tag || '').trim();
|
|
133
|
+
return maintenanceLabel(agent) || (!agent ? maintenanceLabel(tag) : '');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hiddenStatuslineAgents() {
|
|
137
|
+
if (_hiddenStatuslineAgents) return _hiddenStatuslineAgents;
|
|
138
|
+
const agents = new Set(DEFAULT_HIDDEN_STATUSLINE_AGENTS);
|
|
139
|
+
try {
|
|
140
|
+
for (const agent of listHiddenAgentNames()) {
|
|
141
|
+
const clean = String(agent || '').trim();
|
|
142
|
+
if (clean) agents.add(clean);
|
|
143
|
+
}
|
|
144
|
+
} catch {}
|
|
145
|
+
_hiddenStatuslineAgents = agents;
|
|
146
|
+
return agents;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function isActiveHiddenStatus(statusText) {
|
|
150
|
+
return /^(connecting|requesting|streaming|tool_running|running)$/i.test(String(statusText || '').trim());
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
|
|
154
|
+
const agents = hiddenStatuslineAgents();
|
|
155
|
+
const ownerPid = positiveInt(clientHostPid);
|
|
156
|
+
const ownerSessionId = String(sessionId || '').trim();
|
|
157
|
+
const rows = [];
|
|
158
|
+
try {
|
|
159
|
+
for (const [runtimeSessionId, entry] of forEachSessionRuntime() || []) {
|
|
160
|
+
if (!entry || entry.closed === true) continue;
|
|
161
|
+
const session = entry.session || null;
|
|
162
|
+
if (!session || session.closed === true) continue;
|
|
163
|
+
const agent = String(session?.agent || '').trim();
|
|
164
|
+
if (!agent || !agents.has(agent)) continue;
|
|
165
|
+
const id = session?.id || runtimeSessionId || null;
|
|
166
|
+
if (ownerSessionId && id === ownerSessionId) continue;
|
|
167
|
+
const sessionOwnerId = String(session?.ownerSessionId || '').trim();
|
|
168
|
+
if (sessionOwnerId && ownerSessionId && sessionOwnerId !== ownerSessionId) continue;
|
|
169
|
+
const pid = positiveInt(session?.clientHostPid);
|
|
170
|
+
if (ownerPid && pid && pid !== ownerPid) continue;
|
|
171
|
+
const stage = String(entry.stage || session?.stage || session?.status || '').trim().toLowerCase();
|
|
172
|
+
const status = String(session?.status || stage || '').trim().toLowerCase();
|
|
173
|
+
if (!isActiveHiddenStatus(stage || status)) continue;
|
|
174
|
+
rows.push({
|
|
175
|
+
tag: String(session?.agentTag || `${agent}:${id || rows.length}`).trim(),
|
|
176
|
+
agent,
|
|
177
|
+
status: 'running',
|
|
178
|
+
stage: stage || status || 'running',
|
|
179
|
+
sessionId: id,
|
|
180
|
+
provider: session?.provider || null,
|
|
181
|
+
model: session?.model || null,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
} catch {}
|
|
185
|
+
return rows;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function isTerminalBridgeStatus(statusText) {
|
|
189
|
+
return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || '').toLowerCase());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function timeMs(value) {
|
|
193
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
|
|
194
|
+
const n = Date.parse(String(value || ''));
|
|
195
|
+
return Number.isFinite(n) ? n : 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function maintenanceLabel(tag) {
|
|
199
|
+
switch (tag) {
|
|
200
|
+
case 'cycle1-agent': return 'cycle1';
|
|
201
|
+
case 'cycle2-agent': return 'cycle2';
|
|
202
|
+
case 'cycle3-agent': return 'cycle3';
|
|
203
|
+
case 'scheduler-task': return 'scheduler';
|
|
204
|
+
case 'webhook-handler': return 'webhook';
|
|
205
|
+
case 'explorer': return 'explorer';
|
|
206
|
+
default: return '';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function positiveInt(value) {
|
|
211
|
+
const n = Number(value);
|
|
212
|
+
return Number.isInteger(n) && n > 0 ? n : 0;
|
|
213
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/ui/statusline-format.mjs — pure formatting primitives for the footer.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from statusline.mjs: ANSI SGR constants, the context%
|
|
5
|
+
* bar/segment formatters, small numeric helpers, and the TUI-independent
|
|
6
|
+
* formatElapsed() replica. No behavior change — statusline.mjs re-imports these.
|
|
7
|
+
*/
|
|
8
|
+
import { colorEnabled, rgb } from './ansi.mjs';
|
|
9
|
+
import { displayModelName, shortenModelName } from './model-display.mjs';
|
|
10
|
+
import { getModelMetadataSync } from '../runtime/agent/orchestrator/providers/model-catalog.mjs';
|
|
11
|
+
|
|
12
|
+
// Token window used to compute a fallback context% from our own session usage.
|
|
13
|
+
// The live gateway (when up) overrides this with the real route's window. This
|
|
14
|
+
// is only the last resort for unknown local models.
|
|
15
|
+
export const FALLBACK_CONTEXT_WINDOW = 200000;
|
|
16
|
+
export const statusText = rgb(198, 198, 198);
|
|
17
|
+
export const statusSubtle = rgb(136, 136, 136);
|
|
18
|
+
export const statusAccent = rgb(215, 119, 87);
|
|
19
|
+
|
|
20
|
+
function sgr(code) {
|
|
21
|
+
return colorEnabled() ? `\x1b[${code}m` : '';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const R = sgr('0');
|
|
25
|
+
export const B = sgr('1');
|
|
26
|
+
export const D = sgr('38;2;136;136;136');
|
|
27
|
+
export const GRN = sgr('38;2;0;170;75');
|
|
28
|
+
export const YLW = sgr('38;2;255;193;7');
|
|
29
|
+
export const RED = sgr('38;2;220;70;88');
|
|
30
|
+
export const CYN = sgr('38;2;136;136;136');
|
|
31
|
+
export const GREY = sgr('38;2;136;136;136');
|
|
32
|
+
|
|
33
|
+
export function terminalColumns() {
|
|
34
|
+
const cols = Number(process.stdout?.columns);
|
|
35
|
+
return Number.isFinite(cols) && cols > 0 ? Math.floor(cols) : 120;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function modelContextWindow(provider, model, explicitContextWindow = 0) {
|
|
39
|
+
const explicit = num(explicitContextWindow);
|
|
40
|
+
if (explicit > 0) return explicit;
|
|
41
|
+
const metaWindow = num(getModelMetadataSync(model, provider)?.contextWindow);
|
|
42
|
+
if (metaWindow > 0) return metaWindow;
|
|
43
|
+
return FALLBACK_CONTEXT_WINDOW;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function formatModelSegment({ provider, model, effort, fast, cols }) {
|
|
47
|
+
const raw = String(model || '').trim();
|
|
48
|
+
const meta = getModelMetadataSync(raw, provider) || {};
|
|
49
|
+
const displayHint = String(meta.displayName || meta.display || meta.name || '').trim();
|
|
50
|
+
const modelName = shortenModelName(displayModelName(raw, provider, displayHint), cols);
|
|
51
|
+
const bits = [`${B}${modelName}${R}`];
|
|
52
|
+
if (effort) bits.push(`${B}${String(effort).toUpperCase()}${R}`);
|
|
53
|
+
if (fast === true) bits.push(`${B}FAST${R}`);
|
|
54
|
+
return bits.join(` ${D}·${R} `);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Display label for context % (clamped to 100); raw pct still drives bar/color thresholds. */
|
|
58
|
+
export function contextPctDisplayLabel(ctxPct) {
|
|
59
|
+
const pct = Number(ctxPct);
|
|
60
|
+
if (!Number.isFinite(pct) || pct <= 0) return '0';
|
|
61
|
+
if (pct > 0 && pct < 1) return String(Math.round(pct * 10) / 10);
|
|
62
|
+
return String(Math.floor(Math.min(100, pct)));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function formatContextSegment(ctxPct, cols) {
|
|
66
|
+
const raw = Number(ctxPct);
|
|
67
|
+
const pct = Number.isFinite(raw) ? Math.max(0, raw) : 0;
|
|
68
|
+
const barPct = clampPct(pct);
|
|
69
|
+
const fill = pct >= 90 ? RED : pct >= 70 ? YLW : GRN;
|
|
70
|
+
const label = contextPctDisplayLabel(pct);
|
|
71
|
+
// Keep a full-width bar wherever there is room for one. Below 80 cols the bar
|
|
72
|
+
// is dropped (label-only) so the footer never overflows a narrow terminal;
|
|
73
|
+
// at 80+ it stays a fixed 14 cells instead of shrinking to 8, which read as
|
|
74
|
+
// too small/cramped on mid-width terminals.
|
|
75
|
+
const cells = cols >= 80 ? 14 : 0;
|
|
76
|
+
if (!cells) return `${fill}${label}%${R}`;
|
|
77
|
+
const bar = makeBar(barPct, cells);
|
|
78
|
+
const filled = bar.replace(/░/g, '');
|
|
79
|
+
const empty = bar.replace(/▓/g, '');
|
|
80
|
+
return `${fill}${filled}${R}${D}${empty}${R} ${label}%`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function makeBar(pct, cells) {
|
|
84
|
+
let filled = Math.floor((Number(pct) || 0) * cells / 100);
|
|
85
|
+
if (filled < 0) filled = 0;
|
|
86
|
+
if (filled > cells) filled = cells;
|
|
87
|
+
if (pct >= 1 && filled === 0) filled = 1;
|
|
88
|
+
return '▓'.repeat(filled) + '░'.repeat(cells - filled);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function colourPct(p) {
|
|
92
|
+
if (p >= 90) return `${RED}${p}%${R}`;
|
|
93
|
+
if (p >= 70) return `${YLW}${p}%${R}`;
|
|
94
|
+
return `${GRN}${p}%${R}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function epochMsToHHMM(ms) {
|
|
98
|
+
const d = new Date(Number(ms));
|
|
99
|
+
if (!Number.isFinite(d.getTime())) return '';
|
|
100
|
+
return d.toLocaleTimeString('sv-SE', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function num(v) {
|
|
104
|
+
const n = Number(v);
|
|
105
|
+
return Number.isFinite(n) ? n : 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function clampPct(v) {
|
|
109
|
+
const n = Number(v);
|
|
110
|
+
if (!Number.isFinite(n)) return 0;
|
|
111
|
+
return Math.max(0, Math.min(100, n));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Human-friendly token count: 1234 -> 1.2k. */
|
|
115
|
+
export function fmt(n) {
|
|
116
|
+
const v = Number(n) || 0;
|
|
117
|
+
if (v < 1000) return String(Math.round(v));
|
|
118
|
+
if (v < 1_000_000) {
|
|
119
|
+
const k = Math.round(v / 1000);
|
|
120
|
+
return k >= 1000 ? '1M' : `${k}k`;
|
|
121
|
+
}
|
|
122
|
+
return (v / 1_000_000).toFixed(1) + 'M';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Byte-identical replica of src/tui/time-format.mjs formatDuration() with
|
|
126
|
+
// DEFAULT options (no mostSignificantOnly / hideTrailingZeros), wrapped to drop
|
|
127
|
+
// sub-1s like formatElapsed there. Output shape: '' (<1s), `Xs`, `Xm Ys`,
|
|
128
|
+
// `Xh Ym Zs`, `Xd Yh Zm`. statusline.mjs is a standalone UI module that should
|
|
129
|
+
// not depend on the React/ink TUI tree, so the algorithm is replicated rather
|
|
130
|
+
// than imported. Used for ALL L2 elapsed (Agents/Explore/Search/Shell).
|
|
131
|
+
export function formatElapsed(ms) {
|
|
132
|
+
if (!Number.isFinite(Number(ms))) return '';
|
|
133
|
+
const value = Math.max(0, Number(ms) || 0);
|
|
134
|
+
if (value < 60_000) {
|
|
135
|
+
if (value < 1_000) return '';
|
|
136
|
+
return `${Math.floor(value / 1000)}s`;
|
|
137
|
+
}
|
|
138
|
+
const days = Math.floor(value / 86_400_000);
|
|
139
|
+
const hours = Math.floor((value % 86_400_000) / 3_600_000);
|
|
140
|
+
const minutes = Math.floor((value % 3_600_000) / 60_000);
|
|
141
|
+
const seconds = Math.floor((value % 60_000) / 1000);
|
|
142
|
+
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
|
|
143
|
+
if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
|
|
144
|
+
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
145
|
+
return `${seconds}s`;
|
|
146
|
+
}
|