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/ui/statusline.mjs
CHANGED
|
@@ -9,47 +9,28 @@
|
|
|
9
9
|
* reused as read-only data sources, but display identity always belongs to the
|
|
10
10
|
* CLI route passed here.
|
|
11
11
|
*/
|
|
12
|
-
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
13
|
-
import { homedir } from 'node:os';
|
|
14
|
-
import { join } from 'node:path';
|
|
15
|
-
import { bold, colorEnabled, rgb } from './ansi.mjs';
|
|
16
|
-
import { displayModelName, shortenModelName } from './model-display.mjs';
|
|
17
|
-
import { createSessionStats } from './session-stats.mjs';
|
|
18
|
-
import { forEachSessionRuntime } from '../runtime/agent/orchestrator/session/manager.mjs';
|
|
19
|
-
import { listHiddenAgentNames } from '../runtime/agent/orchestrator/internal-agents.mjs';
|
|
20
|
-
import { getModelMetadataSync } from '../runtime/agent/orchestrator/providers/model-catalog.mjs';
|
|
21
12
|
import { readCachedOAuthUsageSnapshot } from '../runtime/agent/orchestrator/providers/oauth-usage.mjs';
|
|
22
13
|
import { readCachedOpenCodeGoUsageSnapshot } from '../runtime/agent/orchestrator/providers/opencode-go-usage.mjs';
|
|
23
14
|
import { buildGatewayLimits } from '../runtime/agent/orchestrator/providers/statusline-route-meta.mjs';
|
|
24
15
|
import { compactBoundaryForStatus, formatGatewayLimitSegments, loadGatewayStatus } from '../vendor/statusline/bin/statusline-route.mjs';
|
|
16
|
+
import { createSessionStats } from './session-stats.mjs';
|
|
17
|
+
import {
|
|
18
|
+
FALLBACK_CONTEXT_WINDOW, statusSubtle,
|
|
19
|
+
R, B, D, GRN, YLW, RED,
|
|
20
|
+
terminalColumns, modelContextWindow, formatModelSegment,
|
|
21
|
+
formatContextSegment, colourPct, epochMsToHHMM,
|
|
22
|
+
num, formatElapsed,
|
|
23
|
+
} from './statusline-format.mjs';
|
|
24
|
+
import { shellJobsStatus, memoryCycleStatus } from './statusline-segments.mjs';
|
|
25
|
+
import {
|
|
26
|
+
summarizeWorkerTags, agentStatuslinePayload, classifyAgentWorkers, activeHiddenAgentWorkers,
|
|
27
|
+
} from './statusline-agents.mjs';
|
|
25
28
|
export { createSessionStats, applyUsageDelta } from './session-stats.mjs';
|
|
29
|
+
// Facade re-exports: keep these public symbols resolving from statusline.mjs.
|
|
30
|
+
export { contextPctDisplayLabel } from './statusline-format.mjs';
|
|
26
31
|
|
|
27
|
-
// Token window used to compute a fallback context% from our own session usage.
|
|
28
|
-
// The live gateway (when up) overrides this with the real route's window. This
|
|
29
|
-
// is only the last resort for unknown local models.
|
|
30
|
-
const FALLBACK_CONTEXT_WINDOW = 200000;
|
|
31
|
-
const statusText = rgb(198, 198, 198);
|
|
32
|
-
const statusSubtle = rgb(136, 136, 136);
|
|
33
|
-
const statusAccent = rgb(215, 119, 87);
|
|
34
|
-
const DEFAULT_MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
|
|
35
|
-
const DEFAULT_STANDALONE_DATA_DIR = join(DEFAULT_MIXDOG_HOME, 'data');
|
|
36
|
-
|
|
37
|
-
function sgr(code) {
|
|
38
|
-
return colorEnabled() ? `\x1b[${code}m` : '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const R = sgr('0');
|
|
42
|
-
const B = sgr('1');
|
|
43
|
-
const D = sgr('38;2;136;136;136');
|
|
44
|
-
const GRN = sgr('38;2;0;170;75');
|
|
45
|
-
const YLW = sgr('38;2;255;193;7');
|
|
46
|
-
const RED = sgr('38;2;220;70;88');
|
|
47
|
-
const CYN = sgr('38;2;136;136;136');
|
|
48
|
-
const GREY = sgr('38;2;136;136;136');
|
|
49
|
-
const SHELL_JOBS_SEGMENT_CACHE_MS = 1000;
|
|
50
32
|
const GATEWAY_QUOTA_STATUS_CACHE_MS = 500;
|
|
51
33
|
const WORKER_SPINNER_FRAMES = Object.freeze(['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']);
|
|
52
|
-
const WORKER_SPINNER_FRAME_MS = 160;
|
|
53
34
|
// L2 segment spinner: reuses the original WORKER_SPINNER_FRAMES dot glyphs (no
|
|
54
35
|
// separate glyph list) but spins them at a faster 120ms step than the worker
|
|
55
36
|
// spinner's 160ms. l2SpinnerFrame() indexes straight into WORKER_SPINNER_FRAMES.
|
|
@@ -61,11 +42,46 @@ const L2_SPINNER_FRAME_MS = 120;
|
|
|
61
42
|
// replaces them. Snapshots from a previous launch stay hidden during boot so the
|
|
62
43
|
// statusline starts empty until the current session captures usage once.
|
|
63
44
|
const STATUSLINE_PROCESS_STARTED_AT_MS = Date.now() - Math.floor((Number(process.uptime?.()) || 0) * 1000);
|
|
64
|
-
const DEFAULT_HIDDEN_STATUSLINE_AGENTS = Object.freeze(['explorer', 'cycle1-agent', 'cycle2-agent', 'cycle3-agent', 'scheduler-task', 'webhook-handler']);
|
|
65
|
-
let _shellJobsSegmentCache = { ownerPid: 0, at: 0, value: { count: 0, elapsedLabel: '' } };
|
|
66
45
|
let _gatewayQuotaStatusCache = { key: '', at: 0, value: null };
|
|
67
46
|
let _fallbackQuotaStatusCache = { key: '', at: 0, value: null };
|
|
68
|
-
|
|
47
|
+
// Holds the last non-empty rendered L1 quota/usage segments per provider+route
|
|
48
|
+
// key, but ONLY for providers that have actually armed the OAuth boot latch
|
|
49
|
+
// (_oauthUsageArmedProviders) — non-OAuth providers (which oauthUsageSegmentReady
|
|
50
|
+
// also returns true for, since they are never gated) must keep their exact
|
|
51
|
+
// prior empty/null behavior, unaffected by this hold. Once armed and at least
|
|
52
|
+
// one non-empty quota segment set has been rendered for a route, a transient
|
|
53
|
+
// resolution failure (cache miss / gateway hiccup) that would otherwise
|
|
54
|
+
// collapse quotaSegments to [] instead re-uses the last held segments, so the
|
|
55
|
+
// L1 segment does not blink out. Replaced only when a newer non-empty result
|
|
56
|
+
// lands for the SAME key. The key includes sessionId + clientHostPid because
|
|
57
|
+
// rendered segments can embed session-scoped routeSpend — without those, a new
|
|
58
|
+
// session sharing provider/model/effort/fast could reuse a stale prior
|
|
59
|
+
// session's spend. Capped at a small LRU size so long-running processes that
|
|
60
|
+
// cycle through many sessions/routes don't grow this map unbounded.
|
|
61
|
+
const _lastNonEmptyQuotaSegmentsByKey = new Map();
|
|
62
|
+
const LAST_NON_EMPTY_QUOTA_SEGMENTS_CACHE_MAX = 8;
|
|
63
|
+
|
|
64
|
+
function quotaSegmentsHoldKey({ provider, model, effort, fast, sessionId, clientHostPid } = {}) {
|
|
65
|
+
return [
|
|
66
|
+
String(provider || '').trim().toLowerCase(),
|
|
67
|
+
String(model || '').trim(),
|
|
68
|
+
String(effort || '').trim(),
|
|
69
|
+
fast === true ? 'fast' : '',
|
|
70
|
+
String(sessionId || ''),
|
|
71
|
+
String(clientHostPid || ''),
|
|
72
|
+
].join('\0');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function rememberNonEmptyQuotaSegments(key, segments) {
|
|
76
|
+
// Delete-then-set to bump this key to most-recently-used position (Map
|
|
77
|
+
// iterates in insertion order), then evict the oldest entry if over cap.
|
|
78
|
+
_lastNonEmptyQuotaSegmentsByKey.delete(key);
|
|
79
|
+
_lastNonEmptyQuotaSegmentsByKey.set(key, segments);
|
|
80
|
+
if (_lastNonEmptyQuotaSegmentsByKey.size > LAST_NON_EMPTY_QUOTA_SEGMENTS_CACHE_MAX) {
|
|
81
|
+
const oldestKey = _lastNonEmptyQuotaSegmentsByKey.keys().next().value;
|
|
82
|
+
_lastNonEmptyQuotaSegmentsByKey.delete(oldestKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
69
85
|
// Option A boot gate: the L1 usage/quota segment stays fully empty until THIS
|
|
70
86
|
// process has captured its FIRST confirmed (current-process) OAuth usage
|
|
71
87
|
// snapshot for THAT provider. The latch is monotonic PER PROVIDER — once a
|
|
@@ -85,19 +101,6 @@ function isConfirmedCurrentProcessSnapshot(snapshot) {
|
|
|
85
101
|
return cachedAt > 0 && cachedAt >= STATUSLINE_PROCESS_STARTED_AT_MS;
|
|
86
102
|
}
|
|
87
103
|
|
|
88
|
-
function summarizeWorkerTags(workers, limit = 3) {
|
|
89
|
-
const cleanLabels = [...new Set((Array.isArray(workers) ? workers : [])
|
|
90
|
-
.map((worker) => String(worker?.tag || '').trim())
|
|
91
|
-
.filter(Boolean))];
|
|
92
|
-
if (cleanLabels.length <= limit) return cleanLabels.join(', ');
|
|
93
|
-
return `${cleanLabels.slice(0, limit).join(', ')}, +${cleanLabels.length - limit}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function workerSpinnerFrame(now = Date.now()) {
|
|
97
|
-
const index = Math.floor(now / WORKER_SPINNER_FRAME_MS) % WORKER_SPINNER_FRAMES.length;
|
|
98
|
-
return WORKER_SPINNER_FRAMES[index] || WORKER_SPINNER_FRAMES[0];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
104
|
function l2SpinnerFrame(now = Date.now()) {
|
|
102
105
|
const index = Math.floor(now / L2_SPINNER_FRAME_MS) % WORKER_SPINNER_FRAMES.length;
|
|
103
106
|
return WORKER_SPINNER_FRAMES[index] || WORKER_SPINNER_FRAMES[0];
|
|
@@ -114,21 +117,6 @@ function l2SpinnerFrame(now = Date.now()) {
|
|
|
114
117
|
* model / context% / effort / 5H-7D are overridden by the live gateway via
|
|
115
118
|
* loadGatewayStatus() when it's running; these are the standalone fallbacks.
|
|
116
119
|
*/
|
|
117
|
-
function providerInputExcludesCache(provider) {
|
|
118
|
-
return String(provider || '').toLowerCase().includes('anthropic');
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function promptFootprintTokens(provider, stats) {
|
|
122
|
-
const s = stats || createSessionStats();
|
|
123
|
-
const explicit = num(s.promptTokens);
|
|
124
|
-
if (explicit > 0) return explicit;
|
|
125
|
-
const input = num(s.inputTokens);
|
|
126
|
-
if (providerInputExcludesCache(provider)) {
|
|
127
|
-
return input + num(s.cachedTokens) + num(s.cacheWriteTokens);
|
|
128
|
-
}
|
|
129
|
-
return input;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
120
|
function activeContextNumerator(provider, stats) {
|
|
133
121
|
const s = stats || createSessionStats();
|
|
134
122
|
const source = String(s.currentContextSource || '').toLowerCase();
|
|
@@ -144,18 +132,6 @@ function activeContextNumerator(provider, stats) {
|
|
|
144
132
|
return 0;
|
|
145
133
|
}
|
|
146
134
|
|
|
147
|
-
function currentContextTokens(provider, stats) {
|
|
148
|
-
return activeContextNumerator(provider, stats);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function modelContextWindow(provider, model, explicitContextWindow = 0) {
|
|
152
|
-
const explicit = num(explicitContextWindow);
|
|
153
|
-
if (explicit > 0) return explicit;
|
|
154
|
-
const metaWindow = num(getModelMetadataSync(model, provider)?.contextWindow);
|
|
155
|
-
if (metaWindow > 0) return metaWindow;
|
|
156
|
-
return FALLBACK_CONTEXT_WINDOW;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
135
|
function displayContextBoundary({
|
|
160
136
|
contextWindow = 0,
|
|
161
137
|
displayContextWindow = 0,
|
|
@@ -197,6 +173,16 @@ function resolveContextUsedPct({
|
|
|
197
173
|
autoCompactTokenLimit,
|
|
198
174
|
compact,
|
|
199
175
|
});
|
|
176
|
+
// Trigger-as-denominator: when a sub-boundary compaction trigger
|
|
177
|
+
// (boundary - buffer) is known, context % is measured against IT so the
|
|
178
|
+
// gauge reads 100% exactly when auto-compact fires instead of stalling at
|
|
179
|
+
// ~90% of the boundary window. The gateway's own pct is computed against
|
|
180
|
+
// the boundary denominator, so it is bypassed in that case.
|
|
181
|
+
const trigger = num(autoCompactTokenLimit);
|
|
182
|
+
const triggerDenominator = trigger > 0 && (!(boundary > 0) || trigger < boundary);
|
|
183
|
+
if (triggerDenominator && numerator > 0) {
|
|
184
|
+
return (numerator / trigger) * 100;
|
|
185
|
+
}
|
|
200
186
|
const gatewayRawPct = gatewayStatus?.contextUsedPct;
|
|
201
187
|
if (
|
|
202
188
|
gatewayStatus
|
|
@@ -210,88 +196,6 @@ function resolveContextUsedPct({
|
|
|
210
196
|
return 0;
|
|
211
197
|
}
|
|
212
198
|
|
|
213
|
-
function normalizeAgentWorkerForStatusline(worker = {}) {
|
|
214
|
-
const tag = String(worker.tag || worker.agent || worker.name || '').trim();
|
|
215
|
-
if (!tag) return null;
|
|
216
|
-
const statusText = String(worker.stage || worker.status || '').toLowerCase();
|
|
217
|
-
const status = isTerminalBridgeStatus(statusText) ? 'idle' : 'running';
|
|
218
|
-
return {
|
|
219
|
-
tag,
|
|
220
|
-
status,
|
|
221
|
-
startedAtMs: timeMs(worker.startedAt || worker.startTime || worker.createdAt),
|
|
222
|
-
agent: worker.agent || null,
|
|
223
|
-
stage: worker.stage || worker.status || null,
|
|
224
|
-
sessionId: worker.sessionId || null,
|
|
225
|
-
provider: worker.provider || null,
|
|
226
|
-
model: worker.model || null,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function normalizeAgentJobForStatusline(job = {}) {
|
|
231
|
-
const statusText = String(job.status || job.stage || '').toLowerCase();
|
|
232
|
-
if (!statusText) return null;
|
|
233
|
-
const taskId = String(job.task_id || job.taskId || '').trim();
|
|
234
|
-
const tag = String(job.tag || job.agent || job.type || taskId || '').trim();
|
|
235
|
-
if (!tag && !taskId) return null;
|
|
236
|
-
const startedAtMs = timeMs(job.startedAt);
|
|
237
|
-
const finishedAtMs = timeMs(job.finishedAt);
|
|
238
|
-
if (isTerminalBridgeStatus(statusText) && finishedAtMs > 0) {
|
|
239
|
-
return {
|
|
240
|
-
tag,
|
|
241
|
-
taskId,
|
|
242
|
-
status: 'finished',
|
|
243
|
-
finalStatus: statusText,
|
|
244
|
-
startedAtMs,
|
|
245
|
-
finishedAtMs,
|
|
246
|
-
agent: job.agent || null,
|
|
247
|
-
stage: job.stage || job.workerStatus || job.status || null,
|
|
248
|
-
sessionId: job.sessionId || null,
|
|
249
|
-
provider: job.provider || null,
|
|
250
|
-
model: job.model || null,
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
if (!/running/.test(statusText)) return null;
|
|
254
|
-
return {
|
|
255
|
-
tag,
|
|
256
|
-
taskId,
|
|
257
|
-
status: 'running',
|
|
258
|
-
startedAtMs,
|
|
259
|
-
agent: job.agent || null,
|
|
260
|
-
stage: job.stage || job.workerStatus || job.status || null,
|
|
261
|
-
sessionId: job.sessionId || null,
|
|
262
|
-
provider: job.provider || null,
|
|
263
|
-
model: job.model || null,
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function agentStatuslinePayload(agentWorkers = [], agentJobs = []) {
|
|
268
|
-
const byTag = new Map();
|
|
269
|
-
const finishedJobs = [];
|
|
270
|
-
for (const worker of Array.isArray(agentWorkers) ? agentWorkers : []) {
|
|
271
|
-
const row = normalizeAgentWorkerForStatusline(worker);
|
|
272
|
-
if (row) byTag.set(row.tag, row);
|
|
273
|
-
}
|
|
274
|
-
for (const job of Array.isArray(agentJobs) ? agentJobs : []) {
|
|
275
|
-
const row = normalizeAgentJobForStatusline(job);
|
|
276
|
-
if (!row) continue;
|
|
277
|
-
if (row.status === 'finished') {
|
|
278
|
-
finishedJobs.push(row);
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
const prev = byTag.get(row.tag);
|
|
282
|
-
byTag.set(row.tag, { ...(prev || {}), ...row, status: 'running' });
|
|
283
|
-
}
|
|
284
|
-
const workers = [...byTag.values()];
|
|
285
|
-
return {
|
|
286
|
-
workers,
|
|
287
|
-
finishedJobs: finishedJobs.sort((a, b) => (b.finishedAtMs || 0) - (a.finishedAtMs || 0)),
|
|
288
|
-
sessions: {
|
|
289
|
-
roles: workers.filter((w) => w.status !== 'idle').map((w) => w.tag),
|
|
290
|
-
workers,
|
|
291
|
-
},
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
199
|
/**
|
|
296
200
|
* Render the L1/L2 statusline footer from CLI state.
|
|
297
201
|
*
|
|
@@ -380,9 +284,23 @@ function renderNativeStatusline({
|
|
|
380
284
|
const quotaStatus = usageReady
|
|
381
285
|
? mergeQuotaStatus(gatewayStatus, fallbackQuotaStatus({ provider, model }))
|
|
382
286
|
: null;
|
|
383
|
-
|
|
287
|
+
let quotaSegments = quotaStatus
|
|
384
288
|
? formatGatewayLimitSegments(quotaStatus, { COLS: cols, D, R, GRN, YLW, RED, colourPct, epochMsToHHMM })
|
|
385
289
|
: [];
|
|
290
|
+
// Only apply the hold to providers that actually armed the OAuth boot latch.
|
|
291
|
+
// oauthUsageSegmentReady() also returns true for non-OAuth providers (they
|
|
292
|
+
// are never gated), so gate the hold itself on _oauthUsageArmedProviders to
|
|
293
|
+
// keep non-OAuth empty/null behavior byte-for-byte unchanged.
|
|
294
|
+
const normalizedHoldProvider = String(provider || '').trim().toLowerCase();
|
|
295
|
+
if (usageReady && _oauthUsageArmedProviders.has(normalizedHoldProvider)) {
|
|
296
|
+
const holdKey = quotaSegmentsHoldKey({ provider, model, effort, fast, sessionId, clientHostPid });
|
|
297
|
+
if (quotaSegments.length) {
|
|
298
|
+
rememberNonEmptyQuotaSegments(holdKey, quotaSegments);
|
|
299
|
+
} else {
|
|
300
|
+
const held = _lastNonEmptyQuotaSegmentsByKey.get(holdKey);
|
|
301
|
+
if (held && held.length) quotaSegments = held;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
386
304
|
for (const seg of quotaSegments) addL1(seg);
|
|
387
305
|
|
|
388
306
|
const agentPayload = agentStatuslinePayload([
|
|
@@ -440,15 +358,6 @@ function renderNativeStatusline({
|
|
|
440
358
|
return l2 ? `${l1}\n${l2}` : l1;
|
|
441
359
|
}
|
|
442
360
|
|
|
443
|
-
function terminalColumns() {
|
|
444
|
-
const cols = Number(process.stdout?.columns);
|
|
445
|
-
return Number.isFinite(cols) && cols > 0 ? Math.floor(cols) : 120;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function dataDir() {
|
|
449
|
-
return process.env.MIXDOG_DATA_DIR || DEFAULT_STANDALONE_DATA_DIR;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
361
|
function loadGatewayQuotaStatus({
|
|
453
362
|
provider, model, effort, fast, contextWindow, rawContextWindow, autoCompactTokenLimit = 0,
|
|
454
363
|
sessionId, activeContextTokens, clientHostPid,
|
|
@@ -615,317 +524,6 @@ function mergeQuotaStatus(primary, fallback) {
|
|
|
615
524
|
};
|
|
616
525
|
}
|
|
617
526
|
|
|
618
|
-
function formatModelSegment({ provider, model, effort, fast, cols }) {
|
|
619
|
-
const raw = String(model || '').trim();
|
|
620
|
-
const meta = getModelMetadataSync(raw, provider) || {};
|
|
621
|
-
const displayHint = String(meta.displayName || meta.display || meta.name || '').trim();
|
|
622
|
-
const modelName = shortenModelName(displayModelName(raw, provider, displayHint), cols);
|
|
623
|
-
const bits = [`${B}${modelName}${R}`];
|
|
624
|
-
if (effort) bits.push(`${B}${String(effort).toUpperCase()}${R}`);
|
|
625
|
-
if (fast === true) bits.push(`${B}FAST${R}`);
|
|
626
|
-
return bits.join(` ${D}·${R} `);
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/** Display label for context % (clamped to 100); raw pct still drives bar/color thresholds. */
|
|
630
|
-
export function contextPctDisplayLabel(ctxPct) {
|
|
631
|
-
const pct = Number(ctxPct);
|
|
632
|
-
if (!Number.isFinite(pct) || pct <= 0) return '0';
|
|
633
|
-
if (pct > 0 && pct < 1) return String(Math.round(pct * 10) / 10);
|
|
634
|
-
return String(Math.floor(Math.min(100, pct)));
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function formatContextSegment(ctxPct, cols) {
|
|
638
|
-
const raw = Number(ctxPct);
|
|
639
|
-
const pct = Number.isFinite(raw) ? Math.max(0, raw) : 0;
|
|
640
|
-
const barPct = clampPct(pct);
|
|
641
|
-
const fill = pct >= 90 ? RED : pct >= 70 ? YLW : GRN;
|
|
642
|
-
const label = contextPctDisplayLabel(pct);
|
|
643
|
-
// Keep a full-width bar wherever there is room for one. Below 80 cols the bar
|
|
644
|
-
// is dropped (label-only) so the footer never overflows a narrow terminal;
|
|
645
|
-
// at 80+ it stays a fixed 14 cells instead of shrinking to 8, which read as
|
|
646
|
-
// too small/cramped on mid-width terminals.
|
|
647
|
-
const cells = cols >= 80 ? 14 : 0;
|
|
648
|
-
if (!cells) return `${fill}${label}%${R}`;
|
|
649
|
-
const bar = makeBar(barPct, cells);
|
|
650
|
-
const filled = bar.replace(/░/g, '');
|
|
651
|
-
const empty = bar.replace(/▓/g, '');
|
|
652
|
-
return `${fill}${filled}${R}${D}${empty}${R} ${label}%`;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
function makeBar(pct, cells) {
|
|
656
|
-
let filled = Math.floor((Number(pct) || 0) * cells / 100);
|
|
657
|
-
if (filled < 0) filled = 0;
|
|
658
|
-
if (filled > cells) filled = cells;
|
|
659
|
-
if (pct >= 1 && filled === 0) filled = 1;
|
|
660
|
-
return '▓'.repeat(filled) + '░'.repeat(cells - filled);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function colourPct(p) {
|
|
664
|
-
if (p >= 90) return `${RED}${p}%${R}`;
|
|
665
|
-
if (p >= 70) return `${YLW}${p}%${R}`;
|
|
666
|
-
return `${GRN}${p}%${R}`;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
function epochMsToHHMM(ms) {
|
|
670
|
-
const d = new Date(Number(ms));
|
|
671
|
-
if (!Number.isFinite(d.getTime())) return '';
|
|
672
|
-
return d.toLocaleTimeString('sv-SE', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
function classifyAgentWorkers(workers = []) {
|
|
676
|
-
const maintenance = [];
|
|
677
|
-
const runningWorkers = [];
|
|
678
|
-
const seenMaintenance = new Set();
|
|
679
|
-
const seenRunning = new Set();
|
|
680
|
-
for (const w of Array.isArray(workers) ? workers : []) {
|
|
681
|
-
const tag = String(w?.tag || '').trim();
|
|
682
|
-
if (!tag) continue;
|
|
683
|
-
const maint = hiddenWorkerLabel(w);
|
|
684
|
-
if (maint) {
|
|
685
|
-
if (w.status !== 'idle' && !seenMaintenance.has(maint)) {
|
|
686
|
-
seenMaintenance.add(maint);
|
|
687
|
-
maintenance.push(`${GRN}↻${R} ${B}${maint}${R}`);
|
|
688
|
-
}
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
|
-
if (w.status !== 'idle' && !seenRunning.has(tag)) {
|
|
692
|
-
seenRunning.add(tag);
|
|
693
|
-
runningWorkers.push(w);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
return { maintenance, runningWorkers };
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
function hiddenWorkerLabel(worker = {}) {
|
|
700
|
-
const agent = String(worker?.agent || '').trim();
|
|
701
|
-
const tag = String(worker?.tag || '').trim();
|
|
702
|
-
return maintenanceLabel(agent) || (!agent ? maintenanceLabel(tag) : '');
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function hiddenStatuslineAgents() {
|
|
706
|
-
if (_hiddenStatuslineAgents) return _hiddenStatuslineAgents;
|
|
707
|
-
const agents = new Set(DEFAULT_HIDDEN_STATUSLINE_AGENTS);
|
|
708
|
-
try {
|
|
709
|
-
for (const agent of listHiddenAgentNames()) {
|
|
710
|
-
const clean = String(agent || '').trim();
|
|
711
|
-
if (clean) agents.add(clean);
|
|
712
|
-
}
|
|
713
|
-
} catch {}
|
|
714
|
-
_hiddenStatuslineAgents = agents;
|
|
715
|
-
return agents;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
function isActiveHiddenStatus(statusText) {
|
|
719
|
-
return /^(connecting|requesting|streaming|tool_running|running)$/i.test(String(statusText || '').trim());
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
function activeHiddenAgentWorkers({ sessionId = '', clientHostPid = 0 } = {}) {
|
|
723
|
-
const agents = hiddenStatuslineAgents();
|
|
724
|
-
const ownerPid = positiveInt(clientHostPid);
|
|
725
|
-
const ownerSessionId = String(sessionId || '').trim();
|
|
726
|
-
const rows = [];
|
|
727
|
-
try {
|
|
728
|
-
for (const [runtimeSessionId, entry] of forEachSessionRuntime() || []) {
|
|
729
|
-
if (!entry || entry.closed === true) continue;
|
|
730
|
-
const session = entry.session || null;
|
|
731
|
-
if (!session || session.closed === true) continue;
|
|
732
|
-
const agent = String(session?.agent || '').trim();
|
|
733
|
-
if (!agent || !agents.has(agent)) continue;
|
|
734
|
-
const id = session?.id || runtimeSessionId || null;
|
|
735
|
-
if (ownerSessionId && id === ownerSessionId) continue;
|
|
736
|
-
const sessionOwnerId = String(session?.ownerSessionId || '').trim();
|
|
737
|
-
if (sessionOwnerId && ownerSessionId && sessionOwnerId !== ownerSessionId) continue;
|
|
738
|
-
const pid = positiveInt(session?.clientHostPid);
|
|
739
|
-
if (ownerPid && pid && pid !== ownerPid) continue;
|
|
740
|
-
const stage = String(entry.stage || session?.stage || session?.status || '').trim().toLowerCase();
|
|
741
|
-
const status = String(session?.status || stage || '').trim().toLowerCase();
|
|
742
|
-
if (!isActiveHiddenStatus(stage || status)) continue;
|
|
743
|
-
rows.push({
|
|
744
|
-
tag: String(session?.agentTag || `${agent}:${id || rows.length}`).trim(),
|
|
745
|
-
agent,
|
|
746
|
-
status: 'running',
|
|
747
|
-
stage: stage || status || 'running',
|
|
748
|
-
sessionId: id,
|
|
749
|
-
provider: session?.provider || null,
|
|
750
|
-
model: session?.model || null,
|
|
751
|
-
});
|
|
752
|
-
}
|
|
753
|
-
} catch {}
|
|
754
|
-
return rows;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
function isTerminalBridgeStatus(statusText) {
|
|
758
|
-
return /idle|done|complete|success|closed|error|fail|cancel|killed|timeout/.test(String(statusText || '').toLowerCase());
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
function timeMs(value) {
|
|
762
|
-
if (typeof value === 'number' && Number.isFinite(value) && value > 0) return value;
|
|
763
|
-
const n = Date.parse(String(value || ''));
|
|
764
|
-
return Number.isFinite(n) ? n : 0;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function maintenanceLabel(tag) {
|
|
768
|
-
switch (tag) {
|
|
769
|
-
case 'cycle1-agent': return 'cycle1';
|
|
770
|
-
case 'cycle2-agent': return 'cycle2';
|
|
771
|
-
case 'cycle3-agent': return 'cycle3';
|
|
772
|
-
case 'scheduler-task': return 'scheduler';
|
|
773
|
-
case 'webhook-handler': return 'webhook';
|
|
774
|
-
case 'explorer': return 'explorer';
|
|
775
|
-
default: return '';
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function shellJobsStatus({ clientHostPid } = {}) {
|
|
780
|
-
return _shellJobsStatusImpl({ clientHostPid });
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Memory cycle L2 segment source. The memory daemon writes
|
|
784
|
-
// data/memory-cycle-state.json on every cycle start/finish (index.mjs
|
|
785
|
-
// _writeCycleStateFile) — no HTTP call from the statusline path. Cached at
|
|
786
|
-
// the same 1s cadence as the shell segment. Shows a single unified "Memory"
|
|
787
|
-
// segment: running (spinner + elapsed) or backlog warning (yellow count).
|
|
788
|
-
let _memoryCycleSegmentCache = { at: 0, value: null };
|
|
789
|
-
const MEMORY_CYCLE_SEGMENT_CACHE_MS = 1000;
|
|
790
|
-
const MEMORY_CYCLE_BACKLOG_WARN = 500;
|
|
791
|
-
function memoryCycleStatus() {
|
|
792
|
-
const now = Date.now();
|
|
793
|
-
if (now - _memoryCycleSegmentCache.at < MEMORY_CYCLE_SEGMENT_CACHE_MS) {
|
|
794
|
-
return _memoryCycleSegmentCache.value;
|
|
795
|
-
}
|
|
796
|
-
let value = null;
|
|
797
|
-
try {
|
|
798
|
-
const p = join(dataDir(), 'memory-cycle-state.json');
|
|
799
|
-
if (existsSync(p)) {
|
|
800
|
-
const state = JSON.parse(readFileSync(p, 'utf-8'));
|
|
801
|
-
const running = state?.running || null;
|
|
802
|
-
const backlog = state?.backlog || {};
|
|
803
|
-
// Stale-file guard: a daemon that died mid-run leaves running set —
|
|
804
|
-
// ignore anything not refreshed in the last 10 minutes.
|
|
805
|
-
const fresh = Number(state?.updatedAt) > now - 10 * 60_000;
|
|
806
|
-
if (fresh && running?.cycle && Number(running.started_at) > 0) {
|
|
807
|
-
value = { kind: 'running', startedAt: Number(running.started_at) };
|
|
808
|
-
} else if (fresh) {
|
|
809
|
-
const pending = Math.max(Number(backlog?.unchunked) || 0, Number(backlog?.cycle2_pending) || 0);
|
|
810
|
-
if (pending > MEMORY_CYCLE_BACKLOG_WARN) value = { kind: 'backlog', pending };
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
} catch { value = null; }
|
|
814
|
-
_memoryCycleSegmentCache = { at: now, value };
|
|
815
|
-
return value;
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
function _shellJobsStatusImpl({ clientHostPid } = {}) {
|
|
819
|
-
const ownerPid = positiveInt(clientHostPid);
|
|
820
|
-
const empty = { count: 0, elapsedLabel: '' };
|
|
821
|
-
if (!ownerPid) return empty;
|
|
822
|
-
const now = Date.now();
|
|
823
|
-
if (_shellJobsSegmentCache.ownerPid === ownerPid && now - _shellJobsSegmentCache.at < SHELL_JOBS_SEGMENT_CACHE_MS) {
|
|
824
|
-
return _shellJobsSegmentCache.value || empty;
|
|
825
|
-
}
|
|
826
|
-
let value = empty;
|
|
827
|
-
try {
|
|
828
|
-
const dir = join(dataDir(), 'shell-jobs');
|
|
829
|
-
if (!existsSync(dir)) {
|
|
830
|
-
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
831
|
-
return value;
|
|
832
|
-
}
|
|
833
|
-
const names = readdirSync(dir);
|
|
834
|
-
const done = new Set(names.filter((n) => n.endsWith('.done')).map((n) => n.slice(0, -5)));
|
|
835
|
-
const ownerByJob = new Map();
|
|
836
|
-
for (const n of names) {
|
|
837
|
-
const i = n.lastIndexOf('.owner-');
|
|
838
|
-
if (i > 0) {
|
|
839
|
-
const pid = positiveInt(n.slice(i + 7));
|
|
840
|
-
if (pid) ownerByJob.set(n.slice(0, i), pid);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
const ids = names
|
|
844
|
-
.filter((n) => n.endsWith('.json'))
|
|
845
|
-
.map((n) => n.slice(0, -5))
|
|
846
|
-
.filter((id) => !done.has(id) && ownerByJob.get(id) === ownerPid)
|
|
847
|
-
.sort((a, b) => jobStampMs(b) - jobStampMs(a))
|
|
848
|
-
.slice(0, 30);
|
|
849
|
-
let count = 0;
|
|
850
|
-
let oldestMs = Infinity;
|
|
851
|
-
for (const id of ids) {
|
|
852
|
-
const p = join(dir, `${id}.json`);
|
|
853
|
-
let detail;
|
|
854
|
-
try { detail = JSON.parse(readFileSync(p, 'utf-8')); } catch { continue; }
|
|
855
|
-
if (!isShellJobAlive(detail, p, dir, id)) continue;
|
|
856
|
-
count++;
|
|
857
|
-
try {
|
|
858
|
-
const st = statSync(p);
|
|
859
|
-
if (st.mtimeMs < oldestMs) oldestMs = st.mtimeMs;
|
|
860
|
-
} catch {}
|
|
861
|
-
}
|
|
862
|
-
if (!count) {
|
|
863
|
-
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
864
|
-
return value;
|
|
865
|
-
}
|
|
866
|
-
const elapsedLabel = Number.isFinite(oldestMs) ? formatElapsed(Date.now() - oldestMs) : '';
|
|
867
|
-
value = { count, elapsedLabel };
|
|
868
|
-
} catch {
|
|
869
|
-
value = empty;
|
|
870
|
-
}
|
|
871
|
-
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
872
|
-
return value;
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
function isShellJobAlive(detail, detailPath, dir, id) {
|
|
876
|
-
const pid = positiveInt(detail?.pid);
|
|
877
|
-
if (!pid) return false;
|
|
878
|
-
try {
|
|
879
|
-
const st = statSync(detailPath);
|
|
880
|
-
const timeoutMs = Number(detail?.timeoutMs);
|
|
881
|
-
const enforced = detail?.timeoutEnforced === true || existsSync(join(dir, `${id}.enforced`));
|
|
882
|
-
if (enforced && Number.isFinite(timeoutMs) && timeoutMs > 0 && Date.now() - st.mtimeMs > timeoutMs + 30 * 60_000) {
|
|
883
|
-
return false;
|
|
884
|
-
}
|
|
885
|
-
} catch {
|
|
886
|
-
return false;
|
|
887
|
-
}
|
|
888
|
-
try {
|
|
889
|
-
process.kill(pid, 0);
|
|
890
|
-
return true;
|
|
891
|
-
} catch (e) {
|
|
892
|
-
return e?.code === 'EPERM';
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
function jobStampMs(id) {
|
|
897
|
-
const m = /^job_(\d+)/.exec(String(id || ''));
|
|
898
|
-
return m ? Number(m[1]) : 0;
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
function positiveInt(value) {
|
|
902
|
-
const n = Number(value);
|
|
903
|
-
return Number.isInteger(n) && n > 0 ? n : 0;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
// Byte-identical replica of src/tui/time-format.mjs formatDuration() with
|
|
907
|
-
// DEFAULT options (no mostSignificantOnly / hideTrailingZeros), wrapped to drop
|
|
908
|
-
// sub-1s like formatElapsed there. Output shape: '' (<1s), `Xs`, `Xm Ys`,
|
|
909
|
-
// `Xh Ym Zs`, `Xd Yh Zm`. statusline.mjs is a standalone UI module that should
|
|
910
|
-
// not depend on the React/ink TUI tree, so the algorithm is replicated rather
|
|
911
|
-
// than imported. Used for ALL L2 elapsed (Agents/Explore/Search/Shell).
|
|
912
|
-
function formatElapsed(ms) {
|
|
913
|
-
if (!Number.isFinite(Number(ms))) return '';
|
|
914
|
-
const value = Math.max(0, Number(ms) || 0);
|
|
915
|
-
if (value < 60_000) {
|
|
916
|
-
if (value < 1_000) return '';
|
|
917
|
-
return `${Math.floor(value / 1000)}s`;
|
|
918
|
-
}
|
|
919
|
-
const days = Math.floor(value / 86_400_000);
|
|
920
|
-
const hours = Math.floor((value % 86_400_000) / 3_600_000);
|
|
921
|
-
const minutes = Math.floor((value % 3_600_000) / 60_000);
|
|
922
|
-
const seconds = Math.floor((value % 60_000) / 1000);
|
|
923
|
-
if (days > 0) return `${days}d ${hours}h ${minutes}m`;
|
|
924
|
-
if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`;
|
|
925
|
-
if (minutes > 0) return `${minutes}m ${seconds}s`;
|
|
926
|
-
return `${seconds}s`;
|
|
927
|
-
}
|
|
928
|
-
|
|
929
527
|
/** Minimal one-line footer used when the vendored renderer is unavailable. */
|
|
930
528
|
export function fallbackStatusline({
|
|
931
529
|
provider = '', model = '', effort = '', fast = false, cwd = '', stats, contextWindow = 0,
|
|
@@ -962,25 +560,3 @@ function fallbackLine({
|
|
|
962
560
|
if (!parts.length) return statusSubtle('> mixdog');
|
|
963
561
|
return parts.join(sep);
|
|
964
562
|
}
|
|
965
|
-
|
|
966
|
-
function num(v) {
|
|
967
|
-
const n = Number(v);
|
|
968
|
-
return Number.isFinite(n) ? n : 0;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
function clampPct(v) {
|
|
972
|
-
const n = Number(v);
|
|
973
|
-
if (!Number.isFinite(n)) return 0;
|
|
974
|
-
return Math.max(0, Math.min(100, n));
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
/** Human-friendly token count: 1234 -> 1.2k. */
|
|
978
|
-
function fmt(n) {
|
|
979
|
-
const v = Number(n) || 0;
|
|
980
|
-
if (v < 1000) return String(Math.round(v));
|
|
981
|
-
if (v < 1_000_000) {
|
|
982
|
-
const k = Math.round(v / 1000);
|
|
983
|
-
return k >= 1000 ? '1M' : `${k}k`;
|
|
984
|
-
}
|
|
985
|
-
return (v / 1_000_000).toFixed(1) + 'M';
|
|
986
|
-
}
|