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
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
// ── Runtime liveness map ──────────────────────────────────────────────
|
|
2
|
+
// In-memory only. Tracks per-session stage + stream heartbeat so agent type=list
|
|
3
|
+
// can surface whether a session is actually alive vs stuck. Never persisted —
|
|
4
|
+
// heartbeats would otherwise churn the session JSON on every SSE delta.
|
|
5
|
+
//
|
|
6
|
+
// Extracted from manager.mjs (pass-3). The Map and its timer companion are
|
|
7
|
+
// module-level singletons — preserving the baseline's single-process shape.
|
|
8
|
+
// manager.mjs owns askSession's controller/generation lifecycle and calls the
|
|
9
|
+
// accessors below via imports; a small set of accessors that need the session
|
|
10
|
+
// store / provider registry are injected through configureRuntimeLiveness() to
|
|
11
|
+
// avoid a circular import back into manager.mjs.
|
|
12
|
+
//
|
|
13
|
+
// Entry shape: {
|
|
14
|
+
// stage, lastStreamDeltaAt, lastToolCall, lastError, updatedAt,
|
|
15
|
+
// controller?: AbortController, // set while an ask is in flight
|
|
16
|
+
// generation?: number, // snapshot taken at ask start
|
|
17
|
+
// closed?: boolean, // flipped by closeSession()
|
|
18
|
+
// }
|
|
19
|
+
import { createAbortController } from '../../../../shared/abort-controller.mjs';
|
|
20
|
+
import { publishHeartbeat, deleteHeartbeat } from '../store.mjs';
|
|
21
|
+
import { DEFAULT_ACTIVITY_HEARTBEAT_MS } from '../../stall-policy.mjs';
|
|
22
|
+
import {
|
|
23
|
+
configureUsageMetricsRuntime,
|
|
24
|
+
dropMetricSeenState,
|
|
25
|
+
bumpUsageMetricsTurnId,
|
|
26
|
+
} from './usage-metrics.mjs';
|
|
27
|
+
|
|
28
|
+
const HEARTBEAT_THROTTLE_MS = 60_000; // 60s
|
|
29
|
+
|
|
30
|
+
const _runtimeState = new Map();
|
|
31
|
+
const _toolActivityHeartbeats = new Map();
|
|
32
|
+
const VALID_STAGES = new Set([
|
|
33
|
+
'connecting', 'requesting', 'streaming', 'tool_running', 'idle', 'error', 'done', 'cancelling',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// Injected deps that would otherwise pull manager.mjs's store/provider surface
|
|
37
|
+
// back into this module (circular). Wired once from manager.mjs at load time.
|
|
38
|
+
let _deps = {
|
|
39
|
+
loadSession: () => null,
|
|
40
|
+
saveSessionAsync: async () => {},
|
|
41
|
+
};
|
|
42
|
+
export function configureRuntimeLiveness(deps = {}) {
|
|
43
|
+
_deps = { ..._deps, ...deps };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function _touchRuntime(id) {
|
|
47
|
+
let entry = _runtimeState.get(id);
|
|
48
|
+
if (!entry) {
|
|
49
|
+
entry = { stage: 'idle', lastStreamDeltaAt: null, lastToolCall: null, lastError: null, updatedAt: Date.now() };
|
|
50
|
+
_runtimeState.set(id, entry);
|
|
51
|
+
}
|
|
52
|
+
return entry;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function _stopToolActivityHeartbeat(id) {
|
|
56
|
+
if (!id) return;
|
|
57
|
+
const timer = _toolActivityHeartbeats.get(id);
|
|
58
|
+
if (!timer) return;
|
|
59
|
+
try { clearInterval(timer); } catch { /* ignore */ }
|
|
60
|
+
_toolActivityHeartbeats.delete(id);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _touchSessionActivityProgress(id) {
|
|
64
|
+
const entry = _runtimeState.get(id);
|
|
65
|
+
if (!entry || entry.closed || entry.controller?.signal?.aborted) return;
|
|
66
|
+
if (entry.stage !== 'tool_running') return;
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
entry.lastProgressAt = now;
|
|
69
|
+
entry.updatedAt = now;
|
|
70
|
+
publishHeartbeat(id, now);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function _startToolActivityHeartbeat(id) {
|
|
74
|
+
_stopToolActivityHeartbeat(id);
|
|
75
|
+
if (!(DEFAULT_ACTIVITY_HEARTBEAT_MS > 0)) return;
|
|
76
|
+
const timer = setInterval(() => _touchSessionActivityProgress(id), DEFAULT_ACTIVITY_HEARTBEAT_MS);
|
|
77
|
+
if (typeof timer.unref === 'function') timer.unref();
|
|
78
|
+
_toolActivityHeartbeats.set(id, timer);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function updateSessionStage(id, stage) {
|
|
82
|
+
if (!id || !VALID_STAGES.has(stage)) return;
|
|
83
|
+
const entry = _touchRuntime(id);
|
|
84
|
+
const now = Date.now();
|
|
85
|
+
entry.stage = stage;
|
|
86
|
+
if (stage === 'connecting' || stage === 'requesting') {
|
|
87
|
+
entry.modelRequestStartedAt = now;
|
|
88
|
+
}
|
|
89
|
+
entry.lastProgressAt = now;
|
|
90
|
+
entry.updatedAt = now;
|
|
91
|
+
if (stage !== 'tool_running') _stopToolActivityHeartbeat(id);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Reset heartbeat-visible fields for a new ask. Preserves controller/generation/
|
|
96
|
+
* closed (lifecycle) but clears the previous run's streaming state so stale
|
|
97
|
+
* lastToolCall / lastStreamDeltaAt from the previous ask don't leak into the
|
|
98
|
+
* new one.
|
|
99
|
+
*/
|
|
100
|
+
export function markSessionAskStart(id) {
|
|
101
|
+
if (!id) return;
|
|
102
|
+
_stopToolActivityHeartbeat(id);
|
|
103
|
+
const entry = _touchRuntime(id);
|
|
104
|
+
entry.usageMetricsTurnIncremental = false;
|
|
105
|
+
const sessionForTurn = entry.session ?? _deps.loadSession(id);
|
|
106
|
+
if (sessionForTurn) bumpUsageMetricsTurnId(sessionForTurn);
|
|
107
|
+
entry.stage = 'connecting';
|
|
108
|
+
entry.lastStreamDeltaAt = null;
|
|
109
|
+
entry.lastToolCall = null;
|
|
110
|
+
entry.toolStartedAt = null;
|
|
111
|
+
entry.lastError = null;
|
|
112
|
+
// A new ask starts a fresh turn lifecycle — clear any stale empty-final
|
|
113
|
+
// classification from the prior turn so inspectBridgeEntry doesn't keep
|
|
114
|
+
// short-circuiting to 'empty-synthesis' (which would disable stall
|
|
115
|
+
// detection for the entire new turn).
|
|
116
|
+
entry.emptyFinal = false;
|
|
117
|
+
entry.emptyFinalAt = null;
|
|
118
|
+
// askStartedAt is the watchdog's fallback reference when a session
|
|
119
|
+
// hangs before any stream delta arrives. Without it, a provider that
|
|
120
|
+
// never returns a first token would stall forever because the watchdog
|
|
121
|
+
// keys solely on lastStreamDeltaAt.
|
|
122
|
+
const now = Date.now();
|
|
123
|
+
entry.askStartedAt = now;
|
|
124
|
+
entry.modelRequestStartedAt = now;
|
|
125
|
+
entry.lastProgressAt = now;
|
|
126
|
+
entry.updatedAt = now;
|
|
127
|
+
// Publish heartbeat immediately so the status aggregator picks the
|
|
128
|
+
// session up in the connecting / requesting window. Without this the
|
|
129
|
+
// .hb file only landed on the first stream chunk — producing a 3–10s
|
|
130
|
+
// (xhigh: 30s+) invisible gap where agent sessions ran but the CC
|
|
131
|
+
// statusline showed no maintenance/agent badge. STREAM_FRESH_MS (5 min)
|
|
132
|
+
// still drops a session whose provider truly never returns a chunk;
|
|
133
|
+
// markSessionStreamDelta keeps refreshing once chunks arrive.
|
|
134
|
+
publishHeartbeat(id, now);
|
|
135
|
+
}
|
|
136
|
+
export async function markSessionStreamDelta(id) {
|
|
137
|
+
if (!id) return;
|
|
138
|
+
// Non-creating lookup: a live ask ALWAYS has a runtime entry (markSessionAskStart
|
|
139
|
+
// creates it before streaming begins). _touchRuntime would instead resurrect a
|
|
140
|
+
// blank entry — and closeSession()/idle-sweep clear _runtimeState on a deferred
|
|
141
|
+
// tick while a detached provider stream may still be trickling deltas. A delta
|
|
142
|
+
// arriving after that clear must NOT re-create an entry or it would republish the
|
|
143
|
+
// .hb heartbeat that markSessionClosed deleted, orphaning a dead session's
|
|
144
|
+
// heartbeat indefinitely (the disk tombstone blocks ask resumption but not this
|
|
145
|
+
// path). Skip a missing, tombstoned, or aborted entry — never refresh liveness.
|
|
146
|
+
const entry = _runtimeState.get(id);
|
|
147
|
+
if (!entry || entry.closed || entry.controller?.signal?.aborted) return;
|
|
148
|
+
_stopToolActivityHeartbeat(id);
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
entry.lastStreamDeltaAt = now;
|
|
151
|
+
entry.lastProgressAt = now;
|
|
152
|
+
// Only promote to 'streaming' if we were in a pre-stream stage; never downgrade
|
|
153
|
+
// mid-tool (tool_running has its own delta source if the tool streams back).
|
|
154
|
+
if (entry.stage === 'connecting' || entry.stage === 'requesting') {
|
|
155
|
+
entry.stage = 'streaming';
|
|
156
|
+
}
|
|
157
|
+
// Lightweight heartbeat (≤5s self-throttled) for the status aggregator.
|
|
158
|
+
// Disk-side session.lastHeartbeatAt below is the heavy 60s zombie-reaper
|
|
159
|
+
// signal; the .hb file is the fast fresh-session signal consumed by the
|
|
160
|
+
// status line.
|
|
161
|
+
publishHeartbeat(id, now);
|
|
162
|
+
const session = entry.session;
|
|
163
|
+
if (session && now - (session.lastHeartbeatAt || 0) > HEARTBEAT_THROTTLE_MS) {
|
|
164
|
+
session.lastHeartbeatAt = now;
|
|
165
|
+
await _deps.saveSessionAsync(session, { expectedGeneration: session.generation });
|
|
166
|
+
}
|
|
167
|
+
entry.updatedAt = now;
|
|
168
|
+
}
|
|
169
|
+
export function markSessionToolCall(id, toolName) {
|
|
170
|
+
if (!id) return;
|
|
171
|
+
const entry = _touchRuntime(id);
|
|
172
|
+
entry.stage = 'tool_running';
|
|
173
|
+
entry.lastToolCall = toolName || null;
|
|
174
|
+
entry.toolStartedAt = Date.now();
|
|
175
|
+
entry.lastProgressAt = entry.toolStartedAt;
|
|
176
|
+
entry.updatedAt = entry.toolStartedAt;
|
|
177
|
+
publishHeartbeat(id, entry.toolStartedAt);
|
|
178
|
+
_startToolActivityHeartbeat(id);
|
|
179
|
+
}
|
|
180
|
+
// Parent AbortSignal listeners are dropped on askSession unwind (finally /
|
|
181
|
+
// terminal return) and on error/cancel/close — not in markSessionDone, which
|
|
182
|
+
// also runs between queued follow-up turns within one ask.
|
|
183
|
+
export function markSessionDone(id, { empty = false } = {}) {
|
|
184
|
+
if (!id) return;
|
|
185
|
+
_stopToolActivityHeartbeat(id);
|
|
186
|
+
const entry = _touchRuntime(id);
|
|
187
|
+
entry.stage = 'done';
|
|
188
|
+
entry.lastError = null;
|
|
189
|
+
entry.askStartedAt = null;
|
|
190
|
+
entry.toolStartedAt = null;
|
|
191
|
+
// Non-empty completion: drop any stale empty-final flag so a subsequent
|
|
192
|
+
// ask on the same reusable runtime entry starts clean. Empty-final
|
|
193
|
+
// completions preserve the flag (set by markSessionEmptyFinal just prior).
|
|
194
|
+
if (!empty) {
|
|
195
|
+
entry.emptyFinal = false;
|
|
196
|
+
entry.emptyFinalAt = null;
|
|
197
|
+
}
|
|
198
|
+
const doneTs = Date.now();
|
|
199
|
+
entry.doneAt = doneTs;
|
|
200
|
+
entry.lastProgressAt = doneTs;
|
|
201
|
+
entry.updatedAt = doneTs;
|
|
202
|
+
// Terminal stage — drop the heartbeat so the status badge releases
|
|
203
|
+
// immediately. A subsequent ask on the same session re-publishes via
|
|
204
|
+
// markSessionStreamDelta on the first chunk.
|
|
205
|
+
deleteHeartbeat(id);
|
|
206
|
+
}
|
|
207
|
+
// Tag a session as having completed with empty final synthesis (no
|
|
208
|
+
// content/reasoning). Distinct from `markSessionDone`: still a success
|
|
209
|
+
// (no abort), but the stall watchdog and post-mortem tools can
|
|
210
|
+
// distinguish "finished empty" from "finished with content" without
|
|
211
|
+
// mistaking the silence for a stall.
|
|
212
|
+
export function markSessionEmptyFinal(id) {
|
|
213
|
+
if (!id) return;
|
|
214
|
+
const entry = _touchRuntime(id);
|
|
215
|
+
entry.emptyFinal = true;
|
|
216
|
+
entry.emptyFinalAt = Date.now();
|
|
217
|
+
}
|
|
218
|
+
export function markSessionError(id, msg) {
|
|
219
|
+
if (!id) return;
|
|
220
|
+
_stopToolActivityHeartbeat(id);
|
|
221
|
+
const entry = _touchRuntime(id);
|
|
222
|
+
entry.stage = 'error';
|
|
223
|
+
entry.lastError = msg ? String(msg).slice(0, 200) : null;
|
|
224
|
+
entry.askStartedAt = null;
|
|
225
|
+
entry.toolStartedAt = null;
|
|
226
|
+
// Error path is a non-empty completion (we have an error message, not a
|
|
227
|
+
// silent empty final). Clear the flag so the next ask starts clean.
|
|
228
|
+
entry.emptyFinal = false;
|
|
229
|
+
entry.emptyFinalAt = null;
|
|
230
|
+
const errTs = Date.now();
|
|
231
|
+
entry.doneAt = errTs;
|
|
232
|
+
entry.lastProgressAt = errTs;
|
|
233
|
+
entry.updatedAt = errTs;
|
|
234
|
+
deleteHeartbeat(id);
|
|
235
|
+
_unlinkParentAbortListener(entry);
|
|
236
|
+
}
|
|
237
|
+
export function markSessionCancelled(id) {
|
|
238
|
+
if (!id) return;
|
|
239
|
+
_stopToolActivityHeartbeat(id);
|
|
240
|
+
const entry = _touchRuntime(id);
|
|
241
|
+
entry.stage = 'done';
|
|
242
|
+
entry.lastError = null;
|
|
243
|
+
entry.askStartedAt = null;
|
|
244
|
+
entry.toolStartedAt = null;
|
|
245
|
+
entry.emptyFinal = false;
|
|
246
|
+
entry.emptyFinalAt = null;
|
|
247
|
+
const doneTs = Date.now();
|
|
248
|
+
entry.doneAt = doneTs;
|
|
249
|
+
entry.lastProgressAt = doneTs;
|
|
250
|
+
entry.updatedAt = doneTs;
|
|
251
|
+
deleteHeartbeat(id);
|
|
252
|
+
_unlinkParentAbortListener(entry);
|
|
253
|
+
}
|
|
254
|
+
export function getSessionRuntime(id) {
|
|
255
|
+
return id ? (_runtimeState.get(id) || null) : null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const _COMPACTION_BLOCKED_STAGES = new Set([
|
|
259
|
+
'connecting', 'requesting', 'streaming', 'tool_running', 'cancelling',
|
|
260
|
+
]);
|
|
261
|
+
|
|
262
|
+
export function isSessionCompactionBlocked(sessionId) {
|
|
263
|
+
if (!sessionId) return false;
|
|
264
|
+
const entry = _runtimeState.get(sessionId);
|
|
265
|
+
if (!entry || entry.closed === true) return false;
|
|
266
|
+
if (entry.controller && !entry.controller.signal?.aborted) return true;
|
|
267
|
+
return _COMPACTION_BLOCKED_STAGES.has(entry.stage);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function getSessionProgressSnapshot(sessionId) {
|
|
271
|
+
const entry = _runtimeState.get(sessionId);
|
|
272
|
+
if (!entry) return null;
|
|
273
|
+
const askStartedAt = entry.askStartedAt || 0;
|
|
274
|
+
const modelRequestStartedAt = entry.modelRequestStartedAt || askStartedAt;
|
|
275
|
+
const firstActivityAt = Math.max(
|
|
276
|
+
entry.lastStreamDeltaAt || 0,
|
|
277
|
+
entry.toolStartedAt || 0,
|
|
278
|
+
);
|
|
279
|
+
const stage = entry.stage || 'idle';
|
|
280
|
+
const waitingForFirstActivity = Boolean(
|
|
281
|
+
modelRequestStartedAt
|
|
282
|
+
&& (stage === 'connecting' || stage === 'requesting')
|
|
283
|
+
&& firstActivityAt <= modelRequestStartedAt
|
|
284
|
+
);
|
|
285
|
+
return {
|
|
286
|
+
stage,
|
|
287
|
+
askStartedAt,
|
|
288
|
+
modelRequestStartedAt,
|
|
289
|
+
firstActivityAt,
|
|
290
|
+
lastStreamDeltaAt: entry.lastStreamDeltaAt || 0,
|
|
291
|
+
toolStartedAt: entry.toolStartedAt || 0,
|
|
292
|
+
lastProgressAt: entry.lastProgressAt || 0,
|
|
293
|
+
updatedAt: entry.updatedAt || 0,
|
|
294
|
+
hasFirstActivity: Boolean(firstActivityAt && (!askStartedAt || firstActivityAt >= askStartedAt)),
|
|
295
|
+
waitingForFirstActivity,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Iterate all active session runtimes. Used by the stream watchdog.
|
|
301
|
+
* Returns an iterable of [sessionId, entry] pairs; consumers should
|
|
302
|
+
* treat entries as read-only snapshots and avoid mutating them.
|
|
303
|
+
*/
|
|
304
|
+
export function forEachSessionRuntime() {
|
|
305
|
+
return _runtimeState.entries();
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Wire the usage-metrics runtime accessor to this module's _runtimeState so
|
|
309
|
+
// persistIterationMetrics can read the live in-memory session and flag
|
|
310
|
+
// usageMetricsTurnIncremental. (Moved with the map from manager.mjs.)
|
|
311
|
+
configureUsageMetricsRuntime({ getRuntimeEntry: (id) => _runtimeState.get(id) });
|
|
312
|
+
|
|
313
|
+
/** Mark session hidden so listSessions() filters it out (runtime-only). */
|
|
314
|
+
export function hideSessionFromList(sessionId) {
|
|
315
|
+
if (!sessionId) return;
|
|
316
|
+
const entry = _runtimeState.get(sessionId);
|
|
317
|
+
if (entry) entry.listHidden = true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function getSessionAbortSignal(sessionId) {
|
|
321
|
+
return _runtimeState.get(sessionId)?.controller?.signal ?? null;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Return the most recent "session is making progress" timestamp.
|
|
326
|
+
*
|
|
327
|
+
* Combines three independent progress signals so an idle watchdog can stay
|
|
328
|
+
* alive across both streaming and long tool calls:
|
|
329
|
+
* - lastStreamDeltaAt: provider stream chunk landed
|
|
330
|
+
* - toolStartedAt: a tool call just kicked off (nested tool work may
|
|
331
|
+
* stall the outer stream for a while; this keeps the watchdog from
|
|
332
|
+
* killing legitimate sub-agent runs)
|
|
333
|
+
* - askStartedAt: ask just started; covers the pre-stream connect window
|
|
334
|
+
*
|
|
335
|
+
* Returns 0 when the runtime entry is unknown so callers can decide to
|
|
336
|
+
* either skip the watchdog or treat 0 as "no progress yet".
|
|
337
|
+
*/
|
|
338
|
+
export function getSessionLastProgressAt(sessionId) {
|
|
339
|
+
const entry = _runtimeState.get(sessionId);
|
|
340
|
+
if (!entry) return 0;
|
|
341
|
+
return Math.max(
|
|
342
|
+
entry.lastProgressAt || 0,
|
|
343
|
+
entry.lastStreamDeltaAt || 0,
|
|
344
|
+
entry.toolStartedAt || 0,
|
|
345
|
+
entry.askStartedAt || 0,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Link a parent AbortSignal to a sub-session's controller so that aborting
|
|
351
|
+
* the parent (fan-out deadline or caller ESC) tears down the agent role's
|
|
352
|
+
* provider call promptly. Safe to call after prepareAgentSession but before
|
|
353
|
+
* askSession completes. No-op if the session runtime isn't found.
|
|
354
|
+
*
|
|
355
|
+
* @param {string} sessionId — the sub-session to abort
|
|
356
|
+
* @param {AbortSignal} parentSignal — upstream signal (from fan-out coordinator)
|
|
357
|
+
*/
|
|
358
|
+
export function linkParentSignalToSession(sessionId, parentSignal) {
|
|
359
|
+
if (!(parentSignal instanceof AbortSignal)) return;
|
|
360
|
+
const entry = _touchRuntime(sessionId);
|
|
361
|
+
if (!entry.controller) entry.controller = createAbortController();
|
|
362
|
+
const abortReason = () => {
|
|
363
|
+
const reason = parentSignal.reason;
|
|
364
|
+
if (reason instanceof Error) return reason;
|
|
365
|
+
if (reason !== undefined && reason !== null && reason !== '') return new Error(String(reason));
|
|
366
|
+
return new Error('parent signal aborted');
|
|
367
|
+
};
|
|
368
|
+
if (parentSignal.aborted) {
|
|
369
|
+
_unlinkParentAbortListener(entry);
|
|
370
|
+
try { entry.controller.abort(abortReason()); } catch { /* ignore */ }
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
_unlinkParentAbortListener(entry);
|
|
374
|
+
const onParentAbort = () => {
|
|
375
|
+
try { entry.controller?.abort(abortReason()); } catch { /* ignore */ }
|
|
376
|
+
};
|
|
377
|
+
parentSignal.addEventListener('abort', onParentAbort, { once: true });
|
|
378
|
+
entry.parentAbortLink = { signal: parentSignal, listener: onParentAbort };
|
|
379
|
+
}
|
|
380
|
+
export function _unlinkParentAbortListener(entry) {
|
|
381
|
+
const link = entry?.parentAbortLink;
|
|
382
|
+
if (!link) return;
|
|
383
|
+
try { link.signal.removeEventListener('abort', link.listener); } catch { /* ignore */ }
|
|
384
|
+
entry.parentAbortLink = null;
|
|
385
|
+
}
|
|
386
|
+
export function _clearSessionRuntime(id) {
|
|
387
|
+
if (id) {
|
|
388
|
+
_stopToolActivityHeartbeat(id);
|
|
389
|
+
_unlinkParentAbortListener(_runtimeState.get(id));
|
|
390
|
+
_runtimeState.delete(id);
|
|
391
|
+
// R15: also drop the per-session metric-idempotency Set; otherwise it
|
|
392
|
+
// grows O(sessions x iterations) for the whole server lifetime since
|
|
393
|
+
// nothing else deletes from _metricSeenIter on session close.
|
|
394
|
+
dropMetricSeenState(id);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Direct-Map accessors for manager.mjs internals (askSession / closeSession /
|
|
399
|
+
// idle-sweep) that mutate entries in place. Kept as thin exports so the Map
|
|
400
|
+
// stays private to this module.
|
|
401
|
+
export function _getRuntimeEntry(id) {
|
|
402
|
+
return _runtimeState.get(id);
|
|
403
|
+
}
|
|
404
|
+
export function _runtimeEntries() {
|
|
405
|
+
return _runtimeState.entries();
|
|
406
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Standalone-status route info + gateway usage telemetry for sessions.
|
|
2
|
+
// Extracted verbatim from manager.mjs (behavior-preserving). Fire-and-forget
|
|
3
|
+
// statusline telemetry — must never affect the model turn.
|
|
4
|
+
import { getProvider } from '../../providers/registry.mjs';
|
|
5
|
+
import { fetchOAuthUsageSnapshot } from '../../providers/oauth-usage.mjs';
|
|
6
|
+
import {
|
|
7
|
+
buildGatewayLimits,
|
|
8
|
+
recordGatewayUsageEvent,
|
|
9
|
+
summarizeGatewayUsage,
|
|
10
|
+
} from '../../providers/statusline-route-meta.mjs';
|
|
11
|
+
import { estimateTranscriptContextUsage } from '../context-utils.mjs';
|
|
12
|
+
|
|
13
|
+
export function standaloneStatusRouteInfo(session) {
|
|
14
|
+
if (!session) return null;
|
|
15
|
+
// autoCompactTokenLimit is an EXPLICIT sub-boundary auto-compact limit only.
|
|
16
|
+
// Do NOT fall back to compactBoundaryTokens/contextWindow here — that
|
|
17
|
+
// labels a derived full-window value as an explicit limit, which the
|
|
18
|
+
// runtime would treat as the compaction trigger and collapse the buffer.
|
|
19
|
+
// The boundary/window stays available via contextWindow/rawContextWindow.
|
|
20
|
+
const _boundary = Number(session.compactBoundaryTokens || session.contextWindow || 0);
|
|
21
|
+
const _limit = Number(session.autoCompactTokenLimit || 0);
|
|
22
|
+
const explicitAutoCompactTokenLimit = _limit > 0 && (!_boundary || _limit < _boundary) ? _limit : null;
|
|
23
|
+
return {
|
|
24
|
+
provider: session.provider,
|
|
25
|
+
model: session.model,
|
|
26
|
+
modelDisplay: session.modelDisplay || session.displayName || session.model,
|
|
27
|
+
effort: session.effort || '',
|
|
28
|
+
fast: session.fast === true,
|
|
29
|
+
contextWindow: session.contextWindow || null,
|
|
30
|
+
rawContextWindow: session.rawContextWindow || session.contextWindow || null,
|
|
31
|
+
effectiveContextWindowPercent: session.effectiveContextWindowPercent || null,
|
|
32
|
+
autoCompactTokenLimit: explicitAutoCompactTokenLimit,
|
|
33
|
+
presetId: session.presetId || null,
|
|
34
|
+
presetName: session.presetName || null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function recordStandaloneStatusTelemetry(session, result, durationMs) {
|
|
39
|
+
if (!session || !result?.usage) return;
|
|
40
|
+
const routeInfo = standaloneStatusRouteInfo(session);
|
|
41
|
+
if (!routeInfo?.provider || !routeInfo?.model) return;
|
|
42
|
+
const providerOut = {
|
|
43
|
+
usage: result.usage,
|
|
44
|
+
model: result.model,
|
|
45
|
+
serviceTier: result.serviceTier,
|
|
46
|
+
};
|
|
47
|
+
// The transcript estimate is the SSOT for the displayed context footprint.
|
|
48
|
+
// agentLoop()'s result has no `compact` field, so build a synthetic compact
|
|
49
|
+
// arg carrying the live monotonic estimate (estimateMessagesTokens+reserve)
|
|
50
|
+
// as afterTokens. This lights up summarizeGatewayUsage's estimate-based
|
|
51
|
+
// contextUsedPct branch (provider input_tokens swing wildly / unbounded on
|
|
52
|
+
// e.g. OpenAI gpt-5.5), and lets a genuine >100% pass through.
|
|
53
|
+
const _estTokens = estimateTranscriptContextUsage(session.messages, session.tools || []);
|
|
54
|
+
const _compactArg = { ...(result.compact && typeof result.compact === 'object' ? result.compact : {}), afterTokens: _estTokens };
|
|
55
|
+
try {
|
|
56
|
+
const summary = {
|
|
57
|
+
...summarizeGatewayUsage(routeInfo, providerOut, _compactArg, durationMs),
|
|
58
|
+
requestKind: 'chat',
|
|
59
|
+
sessionId: session.id || null,
|
|
60
|
+
toolCount: result.toolCallsTotal ?? null,
|
|
61
|
+
messageCount: Array.isArray(session.messages) ? session.messages.length : null,
|
|
62
|
+
cacheStrategy: session.providerCacheOpts?.cacheStrategy || null,
|
|
63
|
+
};
|
|
64
|
+
recordGatewayUsageEvent(summary);
|
|
65
|
+
} catch {
|
|
66
|
+
// Statusline telemetry must never affect the model turn.
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const provider = getProvider(routeInfo.provider);
|
|
70
|
+
if (!provider) return;
|
|
71
|
+
fetchOAuthUsageSnapshot(routeInfo, provider, (message) => {
|
|
72
|
+
if (process.env.MIXDOG_STATUSLINE_TRACE) {
|
|
73
|
+
process.stderr.write(`[statusline] ${message}\n`);
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
.then((snapshot) => {
|
|
77
|
+
try { buildGatewayLimits(routeInfo, providerOut, snapshot); } catch {}
|
|
78
|
+
})
|
|
79
|
+
.catch(() => {});
|
|
80
|
+
}
|