mixdog 0.9.3 → 0.9.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -38
- package/package.json +10 -3
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/r5-orchestrated-task.json +7 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +62 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/provider-catalog-cache.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +20 -1
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -92
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +313 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +24 -842
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +5 -13
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +11 -9
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +356 -0
- package/src/tui/app/model-options.mjs +334 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +489 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +677 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -501
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/input-editing.selection.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/paste-fix.test.mjs +0 -119
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
package/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
|
@@ -16,6 +16,7 @@ import { installProcessSignalCleanup } from '../runtime/shared/process-shutdown.
|
|
|
16
16
|
import { touchUiHeartbeat } from '../runtime/channels/lib/runtime-paths.mjs';
|
|
17
17
|
import { emitTerminalBackground, loadThemeSettingFromConfig, theme } from './theme.mjs';
|
|
18
18
|
import { POP_KITTY, DISABLE_MODIFY_OTHER_KEYS } from './keyboard-protocol.mjs';
|
|
19
|
+
import { displayWidth } from './display-width.mjs';
|
|
19
20
|
|
|
20
21
|
const TERMINAL_MODE_RESET = '\x1b[?1006l\x1b[?1005l\x1b[?1015l\x1b[?1003l\x1b[?1002l\x1b[?1000l\x1b[?2004l\x1b[?25h';
|
|
21
22
|
const TERMINAL_OSC_RESET_BG = '\x1b]111\x07';
|
|
@@ -141,6 +142,59 @@ function resolveTuiStderrLogPath() {
|
|
|
141
142
|
|| join(process.env.MIXDOG_RUNTIME_ROOT || join(tmpdir(), 'mixdog'), 'mixdog-tui.stderr.log');
|
|
142
143
|
}
|
|
143
144
|
|
|
145
|
+
/** `rgb(r,g,b)` → truecolor SGR prefix ('' when unparsable). */
|
|
146
|
+
function ansiFg(rgb) {
|
|
147
|
+
const m = /rgb\((\d+),\s*(\d+),\s*(\d+)\)/.exec(String(rgb || ''));
|
|
148
|
+
return m ? `\x1b[38;2;${m[1]};${m[2]};${m[3]}m` : '';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Paint a static boot splash into the (already-entered) alternate screen so
|
|
153
|
+
* the seconds between alt-screen entry and the first ink frame are not a blank
|
|
154
|
+
* void. The logo is drawn at the SAME rows/columns as App.jsx's welcome banner
|
|
155
|
+
* (marginTop 3 + 5 logo rows + blank + subtitle), so ink's first real frame
|
|
156
|
+
* overprints it in place with no visible jump. Do NOT pre-paint the bottom
|
|
157
|
+
* picker/prompt frame here: the first ink frame may hide or move those rows
|
|
158
|
+
* while the initial project picker settles, and stale raw borders look like a
|
|
159
|
+
* broken frame. Returns { stop } — call it right before ink mounts.
|
|
160
|
+
*/
|
|
161
|
+
function paintBootSplash() {
|
|
162
|
+
try {
|
|
163
|
+
const cols = Math.max(1, Number(process.stdout.columns) || 80);
|
|
164
|
+
const rows = Math.max(1, Number(process.stdout.rows) || 24);
|
|
165
|
+
const windowsLikeTerminal = process.platform === 'win32' || Boolean(process.env.WT_SESSION);
|
|
166
|
+
const frameCols = Math.max(1, cols - (windowsLikeTerminal ? 1 : 0));
|
|
167
|
+
const center = (s) => `${' '.repeat(Math.max(0, Math.floor((frameCols - displayWidth(s)) / 2)))}${s}`;
|
|
168
|
+
const logo = [
|
|
169
|
+
'███╗ ███╗██╗██╗ ██╗██████╗ ██████╗ ██████╗ ',
|
|
170
|
+
'████╗ ████║██║╚██╗██╔╝██╔══██╗██╔═══██╗██╔════╝ ',
|
|
171
|
+
'██╔████╔██║██║ ╚███╔╝ ██║ ██║██║ ██║██║ ███╗',
|
|
172
|
+
'██║╚██╔╝██║██║ ██╔██╗ ██║ ██║██║ ██║██║ ██║',
|
|
173
|
+
'██║ ╚═╝ ██║██║██╔╝ ██╗██████╔╝╚██████╔╝╚██████╔╝',
|
|
174
|
+
];
|
|
175
|
+
const textFg = ansiFg(theme.text);
|
|
176
|
+
const logoFg = ansiFg(theme.logo ?? theme.claude) || textFg;
|
|
177
|
+
const subtleFg = ansiFg(theme.inactive);
|
|
178
|
+
const bold = '\x1b[1m';
|
|
179
|
+
const reset = '\x1b[0m';
|
|
180
|
+
let out = '\x1b[4;1H'; // row 4 == App banner's marginTop={3}
|
|
181
|
+
for (let i = 0; i < logo.length; i++) {
|
|
182
|
+
const fg = i < 2 ? textFg : logoFg;
|
|
183
|
+
out += `${bold}${fg}${center(logo[i])}${reset}\r\n`;
|
|
184
|
+
}
|
|
185
|
+
out += '\r\n';
|
|
186
|
+
out += `${subtleFg}${center(`mixdog coding agent · ${process.cwd()}`)}${reset}`;
|
|
187
|
+
|
|
188
|
+
// Park the cursor at home so ink's first frame paints top-down over the
|
|
189
|
+
// splash instead of starting at the bottom row and scrolling the screen.
|
|
190
|
+
out += '\x1b[H';
|
|
191
|
+
process.stdout.write(out);
|
|
192
|
+
|
|
193
|
+
return { stop: () => {} };
|
|
194
|
+
} catch { /* cosmetic only — never block boot */ }
|
|
195
|
+
return { stop: () => {} };
|
|
196
|
+
}
|
|
197
|
+
|
|
144
198
|
/** Drain stdin so queued key/mouse bytes do not leak into the shell after exit. */
|
|
145
199
|
function drainStdin(stdin = process.stdin) {
|
|
146
200
|
if (!stdin.isTTY) return;
|
|
@@ -277,17 +331,27 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
|
|
|
277
331
|
try { await loadThemeSettingFromConfig(); } catch { /* default theme stays */ }
|
|
278
332
|
emitTerminalBackground(theme.background);
|
|
279
333
|
|
|
334
|
+
// Static splash + loading spinner while createEngineSession() warms up
|
|
335
|
+
// (config, providers, sessions). Without this the user stares at an empty
|
|
336
|
+
// alt screen for the whole boot, then the UI "pops in" — the splash makes
|
|
337
|
+
// the first ink frame an in-place repaint instead.
|
|
338
|
+
const splash = paintBootSplash() || { stop: () => {} };
|
|
339
|
+
|
|
280
340
|
let store;
|
|
281
341
|
try {
|
|
282
342
|
store = await createEngineSession({ provider, model, toolMode, remote });
|
|
283
343
|
bootProfile('store:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
284
344
|
} catch (error) {
|
|
345
|
+
splash.stop();
|
|
285
346
|
restoreTerminal();
|
|
286
347
|
try { process.off('exit', restoreTerminal); } catch { /* ignore */ }
|
|
287
348
|
restoreStderr();
|
|
288
349
|
process.stderr.write(`mixdog: ${error?.message || error}\n`);
|
|
289
350
|
return 1;
|
|
290
351
|
}
|
|
352
|
+
// Stop the spinner BEFORE ink mounts so no stray splash write can land
|
|
353
|
+
// between (or after) ink's first frames.
|
|
354
|
+
splash.stop();
|
|
291
355
|
|
|
292
356
|
// Keep mouse handling app-owned by default: native terminal selections are
|
|
293
357
|
// cleared by the fullscreen redraws that happen while a turn streams. Users
|
|
@@ -367,7 +431,7 @@ export async function runTui({ provider, model, toolMode, remote, forceOnboardin
|
|
|
367
431
|
// does not exit abruptly. Explicit exits go through /exit or /quit so teardown
|
|
368
432
|
// still restores the cursor, mouse mode, and alternate screen cleanly.
|
|
369
433
|
try {
|
|
370
|
-
const instance = render(<App store={store} forceOnboarding={forceOnboarding === true} />, { exitOnCtrlC: false, maxFps:
|
|
434
|
+
const instance = render(<App store={store} forceOnboarding={forceOnboarding === true} />, { exitOnCtrlC: false, maxFps: 60, onRender: makeRenderProfiler() });
|
|
371
435
|
bootProfile('render:mounted', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
372
436
|
const { waitUntilExit } = instance;
|
|
373
437
|
// [mixdog fork] Hand the ink renderer's drag-selection setter to the store so
|
|
@@ -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
|
+
}
|