mixdog 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- 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/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- 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 +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- 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 +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- 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 +41 -1142
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- 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-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -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/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- 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 +3 -3
- 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 +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- 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 -87
- 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 +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- 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 +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/telegram-format.mjs +19 -22
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -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/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -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 +72 -837
- 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-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -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 +6 -14
- 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/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- 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 +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -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 +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- 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 +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +259 -80
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +56 -588
- 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/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -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/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- 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 -462
- 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 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- 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/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/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,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
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/ui/statusline-segments.mjs — filesystem-backed L2 segment sources.
|
|
3
|
+
*
|
|
4
|
+
* Extracted verbatim from statusline.mjs: the shell-jobs segment (owner-scoped
|
|
5
|
+
* job scan + liveness) and the memory-cycle segment (daemon state file). Both
|
|
6
|
+
* keep their own 1s process-local caches. No behavior change.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
9
|
+
import { homedir } from 'node:os';
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { formatElapsed } from './statusline-format.mjs';
|
|
12
|
+
|
|
13
|
+
const DEFAULT_MIXDOG_HOME = process.env.MIXDOG_HOME || join(homedir(), '.mixdog');
|
|
14
|
+
const DEFAULT_STANDALONE_DATA_DIR = join(DEFAULT_MIXDOG_HOME, 'data');
|
|
15
|
+
const SHELL_JOBS_SEGMENT_CACHE_MS = 1000;
|
|
16
|
+
|
|
17
|
+
let _shellJobsSegmentCache = { ownerPid: 0, at: 0, value: { count: 0, elapsedLabel: '' } };
|
|
18
|
+
|
|
19
|
+
function dataDir() {
|
|
20
|
+
return process.env.MIXDOG_DATA_DIR || DEFAULT_STANDALONE_DATA_DIR;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function shellJobsStatus({ clientHostPid } = {}) {
|
|
24
|
+
return _shellJobsStatusImpl({ clientHostPid });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Memory cycle L2 segment source. The memory daemon writes
|
|
28
|
+
// data/memory-cycle-state.json on every cycle start/finish (index.mjs
|
|
29
|
+
// _writeCycleStateFile) — no HTTP call from the statusline path. Cached at
|
|
30
|
+
// the same 1s cadence as the shell segment. Shows a single unified "Memory"
|
|
31
|
+
// segment: running (spinner + elapsed) or backlog warning (yellow count).
|
|
32
|
+
let _memoryCycleSegmentCache = { at: 0, value: null };
|
|
33
|
+
const MEMORY_CYCLE_SEGMENT_CACHE_MS = 1000;
|
|
34
|
+
const MEMORY_CYCLE_BACKLOG_WARN = 500;
|
|
35
|
+
export function memoryCycleStatus() {
|
|
36
|
+
const now = Date.now();
|
|
37
|
+
if (now - _memoryCycleSegmentCache.at < MEMORY_CYCLE_SEGMENT_CACHE_MS) {
|
|
38
|
+
return _memoryCycleSegmentCache.value;
|
|
39
|
+
}
|
|
40
|
+
let value = null;
|
|
41
|
+
try {
|
|
42
|
+
const p = join(dataDir(), 'memory-cycle-state.json');
|
|
43
|
+
if (existsSync(p)) {
|
|
44
|
+
const state = JSON.parse(readFileSync(p, 'utf-8'));
|
|
45
|
+
const running = state?.running || null;
|
|
46
|
+
const backlog = state?.backlog || {};
|
|
47
|
+
// Stale-file guard: a daemon that died mid-run leaves running set —
|
|
48
|
+
// ignore anything not refreshed in the last 10 minutes.
|
|
49
|
+
const fresh = Number(state?.updatedAt) > now - 10 * 60_000;
|
|
50
|
+
if (fresh && running?.cycle && Number(running.started_at) > 0) {
|
|
51
|
+
value = { kind: 'running', startedAt: Number(running.started_at) };
|
|
52
|
+
} else if (fresh) {
|
|
53
|
+
const pending = Math.max(Number(backlog?.unchunked) || 0, Number(backlog?.cycle2_pending) || 0);
|
|
54
|
+
if (pending > MEMORY_CYCLE_BACKLOG_WARN) value = { kind: 'backlog', pending };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch { value = null; }
|
|
58
|
+
_memoryCycleSegmentCache = { at: now, value };
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _shellJobsStatusImpl({ clientHostPid } = {}) {
|
|
63
|
+
const ownerPid = positiveInt(clientHostPid);
|
|
64
|
+
const empty = { count: 0, elapsedLabel: '' };
|
|
65
|
+
if (!ownerPid) return empty;
|
|
66
|
+
const now = Date.now();
|
|
67
|
+
if (_shellJobsSegmentCache.ownerPid === ownerPid && now - _shellJobsSegmentCache.at < SHELL_JOBS_SEGMENT_CACHE_MS) {
|
|
68
|
+
return _shellJobsSegmentCache.value || empty;
|
|
69
|
+
}
|
|
70
|
+
let value = empty;
|
|
71
|
+
try {
|
|
72
|
+
const dir = join(dataDir(), 'shell-jobs');
|
|
73
|
+
if (!existsSync(dir)) {
|
|
74
|
+
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
const names = readdirSync(dir);
|
|
78
|
+
const done = new Set(names.filter((n) => n.endsWith('.done')).map((n) => n.slice(0, -5)));
|
|
79
|
+
const ownerByJob = new Map();
|
|
80
|
+
for (const n of names) {
|
|
81
|
+
const i = n.lastIndexOf('.owner-');
|
|
82
|
+
if (i > 0) {
|
|
83
|
+
const pid = positiveInt(n.slice(i + 7));
|
|
84
|
+
if (pid) ownerByJob.set(n.slice(0, i), pid);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const ids = names
|
|
88
|
+
.filter((n) => n.endsWith('.json'))
|
|
89
|
+
.map((n) => n.slice(0, -5))
|
|
90
|
+
.filter((id) => !done.has(id) && ownerByJob.get(id) === ownerPid)
|
|
91
|
+
.sort((a, b) => jobStampMs(b) - jobStampMs(a))
|
|
92
|
+
.slice(0, 30);
|
|
93
|
+
let count = 0;
|
|
94
|
+
let oldestMs = Infinity;
|
|
95
|
+
for (const id of ids) {
|
|
96
|
+
const p = join(dir, `${id}.json`);
|
|
97
|
+
let detail;
|
|
98
|
+
try { detail = JSON.parse(readFileSync(p, 'utf-8')); } catch { continue; }
|
|
99
|
+
if (!isShellJobAlive(detail, p, dir, id)) continue;
|
|
100
|
+
count++;
|
|
101
|
+
try {
|
|
102
|
+
const st = statSync(p);
|
|
103
|
+
if (st.mtimeMs < oldestMs) oldestMs = st.mtimeMs;
|
|
104
|
+
} catch {}
|
|
105
|
+
}
|
|
106
|
+
if (!count) {
|
|
107
|
+
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
const elapsedLabel = Number.isFinite(oldestMs) ? formatElapsed(Date.now() - oldestMs) : '';
|
|
111
|
+
value = { count, elapsedLabel };
|
|
112
|
+
} catch {
|
|
113
|
+
value = empty;
|
|
114
|
+
}
|
|
115
|
+
_shellJobsSegmentCache = { ownerPid, at: now, value };
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isShellJobAlive(detail, detailPath, dir, id) {
|
|
120
|
+
const pid = positiveInt(detail?.pid);
|
|
121
|
+
if (!pid) return false;
|
|
122
|
+
try {
|
|
123
|
+
const st = statSync(detailPath);
|
|
124
|
+
const timeoutMs = Number(detail?.timeoutMs);
|
|
125
|
+
const enforced = detail?.timeoutEnforced === true || existsSync(join(dir, `${id}.enforced`));
|
|
126
|
+
if (enforced && Number.isFinite(timeoutMs) && timeoutMs > 0 && Date.now() - st.mtimeMs > timeoutMs + 30 * 60_000) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
process.kill(pid, 0);
|
|
134
|
+
return true;
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return e?.code === 'EPERM';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function jobStampMs(id) {
|
|
141
|
+
const m = /^job_(\d+)/.exec(String(id || ''));
|
|
142
|
+
return m ? Number(m[1]) : 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function positiveInt(value) {
|
|
146
|
+
const n = Number(value);
|
|
147
|
+
return Number.isInteger(n) && n > 0 ? n : 0;
|
|
148
|
+
}
|