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,471 @@
|
|
|
1
|
+
// Session compaction runner — recall-fasttrack / semantic / prune-fallback.
|
|
2
|
+
// Extracted verbatim from manager.mjs (behavior-preserving). Self-contained:
|
|
3
|
+
// operates on a live `session` object + opts, using pure compact/context
|
|
4
|
+
// helpers. No runtime-liveness (_runtimeState) coupling — manager.mjs still
|
|
5
|
+
// owns scheduling / stage gating and simply calls runSessionCompaction().
|
|
6
|
+
import { createHash } from 'crypto';
|
|
7
|
+
import { getProvider } from '../../providers/registry.mjs';
|
|
8
|
+
import {
|
|
9
|
+
recallFastTrackCompactMessages,
|
|
10
|
+
semanticCompactMessages,
|
|
11
|
+
pruneToolOutputsUnanchored,
|
|
12
|
+
effectiveBudget as compactEffectiveBudget,
|
|
13
|
+
compactTypeIsRecallFastTrack,
|
|
14
|
+
compactTypeIsSemantic,
|
|
15
|
+
drainSessionCycle1,
|
|
16
|
+
} from '../compact.mjs';
|
|
17
|
+
import { estimateMessagesTokens, estimateRequestReserveTokens, estimateTranscriptContextUsage, resolveCompactBufferRatio } from '../context-utils.mjs';
|
|
18
|
+
import { executeInternalTool } from '../../internal-tools.mjs';
|
|
19
|
+
import {
|
|
20
|
+
positiveContextWindow,
|
|
21
|
+
compactTriggerForSession,
|
|
22
|
+
compactTargetBudget,
|
|
23
|
+
semanticCompactionEnabledForSession,
|
|
24
|
+
compactTypeForSession,
|
|
25
|
+
} from './context-meta.mjs';
|
|
26
|
+
import { uncachedInputTokensForProvider } from './usage-metrics.mjs';
|
|
27
|
+
|
|
28
|
+
// 'compacting' is a transient in-flight stage written just before semantic /
|
|
29
|
+
// recall-fasttrack compaction runs. If the process crashes or only partially
|
|
30
|
+
// saves while it is set, a later load/resume reads a session that is NOT
|
|
31
|
+
// actually compacting but whose UI marker (App.jsx / ContextPanel) shows
|
|
32
|
+
// "Compacting conversation" permanently. Normalize that stale transient stage
|
|
33
|
+
// to 'interrupted' so the surface recovers. Terminal stages (post_turn /
|
|
34
|
+
// manual / auto_clear / *_failed / overflow_failed) are intentionally left as
|
|
35
|
+
// the durable record of the last real outcome.
|
|
36
|
+
export function normalizeStaleCompactingStage(session) {
|
|
37
|
+
const c = session?.compaction;
|
|
38
|
+
if (!c || typeof c !== 'object') return false;
|
|
39
|
+
if (c.lastStage !== 'compacting' && c.inProgress !== true) return false;
|
|
40
|
+
c.lastStage = 'interrupted';
|
|
41
|
+
c.inProgress = false;
|
|
42
|
+
c.lastCheckedAt = Date.now();
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
function addCompactUsageToSession(session, usage) {
|
|
46
|
+
if (!session || !usage) return;
|
|
47
|
+
const inputTokens = usage.inputTokens || 0;
|
|
48
|
+
const outputTokens = usage.outputTokens || 0;
|
|
49
|
+
const cachedTokens = usage.cachedTokens || 0;
|
|
50
|
+
const cacheWriteTokens = usage.cacheWriteTokens || 0;
|
|
51
|
+
const uncachedInputTokens = uncachedInputTokensForProvider(session.provider, inputTokens, cachedTokens, cacheWriteTokens);
|
|
52
|
+
session.totalInputTokens = (session.totalInputTokens || 0) + inputTokens;
|
|
53
|
+
session.totalOutputTokens = (session.totalOutputTokens || 0) + outputTokens;
|
|
54
|
+
session.totalCachedReadTokens = (session.totalCachedReadTokens || 0) + cachedTokens;
|
|
55
|
+
session.totalCacheWriteTokens = (session.totalCacheWriteTokens || 0) + cacheWriteTokens;
|
|
56
|
+
session.totalUncachedInputTokens = (session.totalUncachedInputTokens || 0) + uncachedInputTokens;
|
|
57
|
+
session.tokensCumulative = (session.tokensCumulative || 0) + inputTokens + outputTokens;
|
|
58
|
+
}
|
|
59
|
+
async function runRecallFastTrackForSession(session, messages, opts = {}) {
|
|
60
|
+
const sessionId = opts.sessionId || session?.id || null;
|
|
61
|
+
if (!sessionId) throw new Error('recall-fasttrack requires a session id');
|
|
62
|
+
const query = `session:${sessionId}:all-chunks`;
|
|
63
|
+
const querySha = createHash('sha256').update(query).digest('hex').slice(0, 16);
|
|
64
|
+
const callerCtx = {
|
|
65
|
+
callerSessionId: sessionId,
|
|
66
|
+
callerCwd: session?.cwd || undefined,
|
|
67
|
+
routingSessionId: sessionId,
|
|
68
|
+
clientHostPid: session?.clientHostPid,
|
|
69
|
+
signal: opts.signal || null,
|
|
70
|
+
};
|
|
71
|
+
const hydrateLimit = positiveContextWindow(session?.compaction?.recallIngestLimit)
|
|
72
|
+
|| Math.max(500, Math.min(5000, messages.length || 0));
|
|
73
|
+
try {
|
|
74
|
+
await executeInternalTool('memory', {
|
|
75
|
+
action: 'ingest_session',
|
|
76
|
+
sessionId,
|
|
77
|
+
messages,
|
|
78
|
+
cwd: session?.cwd,
|
|
79
|
+
limit: hydrateLimit,
|
|
80
|
+
}, callerCtx);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
try { process.stderr.write(`[session] recall-fasttrack ingest skipped (sess=${sessionId}): ${err?.message || err}\n`); } catch {}
|
|
83
|
+
}
|
|
84
|
+
const dumpArgs = {
|
|
85
|
+
action: 'dump_session_roots',
|
|
86
|
+
sessionId,
|
|
87
|
+
includeRaw: true,
|
|
88
|
+
limit: positiveContextWindow(session?.compaction?.recallChunkLimit ?? session?.compaction?.recallLimit) || hydrateLimit,
|
|
89
|
+
};
|
|
90
|
+
const runTool = (name, args) => executeInternalTool(name, args, callerCtx);
|
|
91
|
+
let recallText = await executeInternalTool('memory', dumpArgs, callerCtx);
|
|
92
|
+
let cycle1Text = '';
|
|
93
|
+
let cycle1Error = null;
|
|
94
|
+
const hasRawRows = /(?:^|\n)# raw_pending\s+\d+\s+id=/i.test(String(recallText || ''));
|
|
95
|
+
if (hasRawRows) {
|
|
96
|
+
try {
|
|
97
|
+
// Drain this session's cycle1 in window×concurrency units until no
|
|
98
|
+
// raw rows remain, so the injected root is fully chunked rather than
|
|
99
|
+
// carrying the unprocessed transcript tail (single-pass left raw in).
|
|
100
|
+
const drained = await drainSessionCycle1(runTool, {
|
|
101
|
+
sessionId,
|
|
102
|
+
dumpArgs,
|
|
103
|
+
deadlineMs: positiveContextWindow(session?.compaction?.recallCycle1DeadlineMs) || 120_000,
|
|
104
|
+
maxPasses: positiveContextWindow(session?.compaction?.recallCycle1MaxPasses) || 0,
|
|
105
|
+
cycleArgs: {
|
|
106
|
+
min_batch: 1,
|
|
107
|
+
session_cap: 1,
|
|
108
|
+
batch_size: positiveContextWindow(session?.compaction?.recallCycle1BatchSize) || 100,
|
|
109
|
+
rows_per_session: positiveContextWindow(session?.compaction?.recallRowsPerSession) || 100,
|
|
110
|
+
window_size: positiveContextWindow(session?.compaction?.recallWindowSize) || 20,
|
|
111
|
+
concurrency: positiveContextWindow(session?.compaction?.recallConcurrency) || 5,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
recallText = drained.recallText;
|
|
115
|
+
cycle1Text = drained.cycle1Text;
|
|
116
|
+
if (drained.error) {
|
|
117
|
+
cycle1Error = drained.error;
|
|
118
|
+
try { process.stderr.write(`[session] recall-fasttrack cycle1 error (sess=${sessionId}): ${drained.error}\n`); } catch {}
|
|
119
|
+
}
|
|
120
|
+
if (drained.rawRemaining > 0) {
|
|
121
|
+
try { process.stderr.write(`[session] recall-fasttrack drained passes=${drained.passes} rawRemaining=${drained.rawRemaining} (sess=${sessionId})\n`); } catch {}
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
try { process.stderr.write(`[session] recall-fasttrack cycle1 skipped (sess=${sessionId}): ${err?.message || err}\n`); } catch {}
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
cycle1Text = 'cycle1: skipped (session chunks already hydrated)';
|
|
128
|
+
}
|
|
129
|
+
return { query, querySha, cycle1Error, recallText: [`session_id=${sessionId}`, cycle1Text, recallText].map(v => String(v || '').trim()).filter(Boolean).join('\n\n') };
|
|
130
|
+
}
|
|
131
|
+
// Element-identity change detection (same approach as loop.mjs messagesArrayChanged): two
|
|
132
|
+
// arrays are "unchanged" only when same length AND every slot is the same object
|
|
133
|
+
// reference. Used to reject a no-op prune (which returns a fresh array whose
|
|
134
|
+
// elements are the untouched originals) from being accepted as a recovery.
|
|
135
|
+
function messagesChanged(before, after) {
|
|
136
|
+
if (!Array.isArray(before) || !Array.isArray(after)) return before !== after;
|
|
137
|
+
if (before.length !== after.length) return true;
|
|
138
|
+
for (let i = 0; i < before.length; i += 1) {
|
|
139
|
+
if (before[i] !== after[i]) return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
export async function runSessionCompaction(session, opts = {}) {
|
|
144
|
+
if (!session || session.closed === true) return null;
|
|
145
|
+
const mode = opts.mode === 'auto' ? 'auto' : 'manual';
|
|
146
|
+
const force = opts.force === true || mode === 'manual';
|
|
147
|
+
if (mode === 'auto' && session.compaction?.auto === false) return null;
|
|
148
|
+
const messages = Array.isArray(session.messages) ? session.messages : [];
|
|
149
|
+
if (messages.length < 3 && !force) return null;
|
|
150
|
+
const boundary = positiveContextWindow(session.compactBoundaryTokens)
|
|
151
|
+
|| positiveContextWindow(session.autoCompactTokenLimit)
|
|
152
|
+
|| positiveContextWindow(session.contextWindow);
|
|
153
|
+
if (!boundary) {
|
|
154
|
+
if (force) throw new Error('compact: no context window is available for this session');
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
// Reserve must mirror loop.mjs (buildCompactPolicy): request reserve (tool
|
|
158
|
+
// schema) PLUS the configured reserve (session.compaction.reservedTokens or
|
|
159
|
+
// MIXDOG_AGENT_COMPACT_RESERVED_TOKENS env). The old request-only value left
|
|
160
|
+
// the manual / auto-clear compact budget without the configured headroom the
|
|
161
|
+
// loop path reserves, so a compacted transcript could overflow on next send.
|
|
162
|
+
const requestReserveTokens = estimateRequestReserveTokens(session.tools || []);
|
|
163
|
+
const configuredReserveTokens = positiveContextWindow(session.compaction?.reservedTokens)
|
|
164
|
+
|| positiveContextWindow(process.env.MIXDOG_AGENT_COMPACT_RESERVED_TOKENS)
|
|
165
|
+
|| 0;
|
|
166
|
+
const reserveTokens = requestReserveTokens + configuredReserveTokens;
|
|
167
|
+
const beforeMessageTokens = estimateMessagesTokens(messages);
|
|
168
|
+
const triggerTokens = compactTriggerForSession(session, boundary)
|
|
169
|
+
|| positiveContextWindow(session.compaction?.triggerTokens)
|
|
170
|
+
|| boundary;
|
|
171
|
+
const bufferTokens = Math.max(0, boundary - triggerTokens);
|
|
172
|
+
const bufferRatio = boundary ? (bufferTokens / boundary) : resolveCompactBufferRatio(session.compaction || {});
|
|
173
|
+
const pressureTokens = estimateTranscriptContextUsage(messages, session.tools || []);
|
|
174
|
+
const beforeTokens = pressureTokens;
|
|
175
|
+
const compactType = compactTypeForSession(session);
|
|
176
|
+
if (!force && pressureTokens < triggerTokens) return {
|
|
177
|
+
changed: false,
|
|
178
|
+
reason: 'below threshold',
|
|
179
|
+
compactType,
|
|
180
|
+
beforeMessages: messages.length,
|
|
181
|
+
afterMessages: messages.length,
|
|
182
|
+
beforeTokens,
|
|
183
|
+
afterTokens: beforeTokens,
|
|
184
|
+
beforeMessageTokens,
|
|
185
|
+
afterMessageTokens: beforeMessageTokens,
|
|
186
|
+
pressureTokens,
|
|
187
|
+
triggerTokens,
|
|
188
|
+
bufferTokens,
|
|
189
|
+
bufferRatio,
|
|
190
|
+
boundaryTokens: boundary,
|
|
191
|
+
budgetTokens: boundary,
|
|
192
|
+
targetBudgetTokens: boundary,
|
|
193
|
+
reserveTokens,
|
|
194
|
+
semanticCompact: false,
|
|
195
|
+
};
|
|
196
|
+
const budgetSourceTokens = force ? Math.max(pressureTokens, triggerTokens) : pressureTokens;
|
|
197
|
+
const compactBudget = compactTargetBudget(boundary, reserveTokens, budgetSourceTokens);
|
|
198
|
+
const budget = compactBudget || boundary;
|
|
199
|
+
try { await opts.onStageChange?.('compacting'); } catch { /* best-effort */ }
|
|
200
|
+
const provider = opts.provider || getProvider(session.provider) || null;
|
|
201
|
+
let compacted;
|
|
202
|
+
let compactError = null;
|
|
203
|
+
let semanticCompactResult = null;
|
|
204
|
+
let semanticCompactError = null;
|
|
205
|
+
let recallFastTrackResult = null;
|
|
206
|
+
let recallFastTrackError = null;
|
|
207
|
+
if (compactTypeIsRecallFastTrack(compactType)) {
|
|
208
|
+
try {
|
|
209
|
+
const recallPayload = await runRecallFastTrackForSession(session, messages, opts);
|
|
210
|
+
recallFastTrackResult = recallFastTrackCompactMessages(messages, budget, {
|
|
211
|
+
reserveTokens,
|
|
212
|
+
force: true,
|
|
213
|
+
recallText: recallPayload.recallText,
|
|
214
|
+
query: recallPayload.query,
|
|
215
|
+
querySha: recallPayload.querySha,
|
|
216
|
+
// Ingest just ran on the live transcript, so an empty recall dump
|
|
217
|
+
// means the memory pipeline is broken — do NOT erase history
|
|
218
|
+
// behind an empty summary shell. Empty recall now throws and is
|
|
219
|
+
// handled by the semantic fallback below (or recorded failure).
|
|
220
|
+
allowEmptyRecall: false,
|
|
221
|
+
tailTurns: positiveContextWindow(session.compaction?.tailTurns) || 2,
|
|
222
|
+
keepTokens: positiveContextWindow(session.compaction?.keepTokens ?? session.compaction?.keep?.tokens),
|
|
223
|
+
preserveRecentTokens: positiveContextWindow(session.compaction?.preserveRecentTokens),
|
|
224
|
+
});
|
|
225
|
+
if (Array.isArray(recallFastTrackResult?.messages)) {
|
|
226
|
+
compacted = recallFastTrackResult.messages;
|
|
227
|
+
}
|
|
228
|
+
if (recallPayload.cycle1Error) {
|
|
229
|
+
recallFastTrackError = new Error(String(recallPayload.cycle1Error));
|
|
230
|
+
}
|
|
231
|
+
} catch (err) {
|
|
232
|
+
recallFastTrackError = err;
|
|
233
|
+
compactError = err;
|
|
234
|
+
try {
|
|
235
|
+
process.stderr.write(`[session] recall-fasttrack ${mode} compact failed (sess=${session.id || 'unknown'}): ${err?.message || err}\n`);
|
|
236
|
+
} catch { /* best-effort */ }
|
|
237
|
+
// Degraded-compact fallback: recall-fasttrack failed (empty recall,
|
|
238
|
+
// ingest error, fit failure). Before recording a hard failure, try
|
|
239
|
+
// the semantic path once so auto-clear/manual compaction still makes
|
|
240
|
+
// progress WITHOUT shipping an empty-recall summary. History is only
|
|
241
|
+
// replaced when the semantic summary actually succeeds.
|
|
242
|
+
if (semanticCompactionEnabledForSession(session)
|
|
243
|
+
&& provider && typeof provider.send === 'function') {
|
|
244
|
+
try {
|
|
245
|
+
semanticCompactResult = await semanticCompactMessages(
|
|
246
|
+
provider,
|
|
247
|
+
messages,
|
|
248
|
+
opts.model || session.model,
|
|
249
|
+
budget,
|
|
250
|
+
{
|
|
251
|
+
reserveTokens,
|
|
252
|
+
providerName: session.provider || provider?.name || null,
|
|
253
|
+
sessionId: opts.sessionId || session.id || null,
|
|
254
|
+
signal: opts.signal || null,
|
|
255
|
+
promptCacheKey: session.promptCacheKey || null,
|
|
256
|
+
providerCacheKey: session.promptCacheKey || null,
|
|
257
|
+
timeoutMs: positiveContextWindow(session.compaction?.timeoutMs) || 30_000,
|
|
258
|
+
tailTurns: positiveContextWindow(session.compaction?.tailTurns) || 2,
|
|
259
|
+
keepTokens: positiveContextWindow(session.compaction?.keepTokens ?? session.compaction?.keep?.tokens),
|
|
260
|
+
preserveRecentTokens: positiveContextWindow(session.compaction?.preserveRecentTokens),
|
|
261
|
+
force: true,
|
|
262
|
+
},
|
|
263
|
+
);
|
|
264
|
+
if (Array.isArray(semanticCompactResult?.messages)) {
|
|
265
|
+
compacted = semanticCompactResult.messages;
|
|
266
|
+
compactError = null;
|
|
267
|
+
addCompactUsageToSession(session, semanticCompactResult.usage);
|
|
268
|
+
try {
|
|
269
|
+
process.stderr.write(`[session] degraded compact: recall-fasttrack failed, semantic fallback succeeded (sess=${session.id || 'unknown'}, mode=${mode})\n`);
|
|
270
|
+
} catch { /* best-effort */ }
|
|
271
|
+
}
|
|
272
|
+
} catch (fallbackErr) {
|
|
273
|
+
semanticCompactError = fallbackErr;
|
|
274
|
+
try {
|
|
275
|
+
process.stderr.write(`[session] degraded compact: semantic fallback also failed (sess=${session.id || 'unknown'}): ${fallbackErr?.message || fallbackErr}\n`);
|
|
276
|
+
} catch { /* best-effort */ }
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} else if (compactTypeIsSemantic(compactType)) {
|
|
281
|
+
try {
|
|
282
|
+
if (!semanticCompactionEnabledForSession(session)) {
|
|
283
|
+
throw new Error('semantic compact is disabled for this session');
|
|
284
|
+
}
|
|
285
|
+
if (!provider || typeof provider.send !== 'function') {
|
|
286
|
+
throw new Error(`semantic compact provider unavailable: ${session.provider || 'unknown'}`);
|
|
287
|
+
}
|
|
288
|
+
semanticCompactResult = await semanticCompactMessages(
|
|
289
|
+
provider,
|
|
290
|
+
messages,
|
|
291
|
+
opts.model || session.model,
|
|
292
|
+
budget,
|
|
293
|
+
{
|
|
294
|
+
reserveTokens,
|
|
295
|
+
providerName: session.provider || provider?.name || null,
|
|
296
|
+
sessionId: opts.sessionId || session.id || null,
|
|
297
|
+
signal: opts.signal || null,
|
|
298
|
+
promptCacheKey: session.promptCacheKey || null,
|
|
299
|
+
providerCacheKey: session.promptCacheKey || null,
|
|
300
|
+
timeoutMs: positiveContextWindow(session.compaction?.timeoutMs) || 30_000,
|
|
301
|
+
tailTurns: positiveContextWindow(session.compaction?.tailTurns) || 2,
|
|
302
|
+
keepTokens: positiveContextWindow(session.compaction?.keepTokens ?? session.compaction?.keep?.tokens),
|
|
303
|
+
preserveRecentTokens: positiveContextWindow(session.compaction?.preserveRecentTokens),
|
|
304
|
+
force: true,
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
if (Array.isArray(semanticCompactResult?.messages)) {
|
|
308
|
+
compacted = semanticCompactResult.messages;
|
|
309
|
+
addCompactUsageToSession(session, semanticCompactResult.usage);
|
|
310
|
+
}
|
|
311
|
+
} catch (err) {
|
|
312
|
+
semanticCompactError = err;
|
|
313
|
+
compactError = err;
|
|
314
|
+
try {
|
|
315
|
+
process.stderr.write(`[session] semantic ${mode} compact failed (sess=${session.id || 'unknown'}): ${err?.message || err}\n`);
|
|
316
|
+
} catch { /* best-effort */ }
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!compacted && !compactError) {
|
|
320
|
+
compactError = new Error(`${compactType} compact produced no messages`);
|
|
321
|
+
}
|
|
322
|
+
// Anchor-independent prune safety net (mirror loop.mjs compact catch): when a
|
|
323
|
+
// non-recall (semantic) compact failed, try one non-LLM prune that needs no
|
|
324
|
+
// user anchor before recording failure, so Lead manual / auto-clear paths
|
|
325
|
+
// recover the same transcripts the loop path does. Gated off the recall
|
|
326
|
+
// path — a recall failure keeps its original contract (no silent prune).
|
|
327
|
+
if (!compacted && !recallFastTrackError) {
|
|
328
|
+
try {
|
|
329
|
+
const acceptThreshold = compactEffectiveBudget(budget, { reserveTokens });
|
|
330
|
+
const salvaged = pruneToolOutputsUnanchored(messages, budget, { reserveTokens });
|
|
331
|
+
// pruneToolOutputsUnanchored ALWAYS returns a fresh reconciled array
|
|
332
|
+
// (never the input identity), so `salvaged !== messages` is always
|
|
333
|
+
// true and cannot detect a no-op. Compare by element identity so a
|
|
334
|
+
// transcript that already fit (nothing pruned) is NOT falsely accepted
|
|
335
|
+
// as a recovery — that would clear compactError and unconditionally
|
|
336
|
+
// invalidate providerState for an unchanged transcript.
|
|
337
|
+
if (Array.isArray(salvaged)
|
|
338
|
+
&& messagesChanged(messages, salvaged)
|
|
339
|
+
&& estimateMessagesTokens(salvaged) <= acceptThreshold) {
|
|
340
|
+
compacted = salvaged;
|
|
341
|
+
compactError = null;
|
|
342
|
+
try {
|
|
343
|
+
process.stderr.write(`[session] compact fallback prune recovered (sess=${session.id || 'unknown'}, mode=${mode})\n`);
|
|
344
|
+
} catch { /* best-effort */ }
|
|
345
|
+
}
|
|
346
|
+
} catch { /* fall through to failure record */ }
|
|
347
|
+
}
|
|
348
|
+
if (!compacted) {
|
|
349
|
+
const now = Date.now();
|
|
350
|
+
session.compaction = {
|
|
351
|
+
...(session.compaction || {}),
|
|
352
|
+
auto: mode === 'auto' ? true : session.compaction?.auto !== false,
|
|
353
|
+
boundaryTokens: boundary,
|
|
354
|
+
triggerTokens,
|
|
355
|
+
bufferTokens,
|
|
356
|
+
bufferRatio,
|
|
357
|
+
reserveTokens,
|
|
358
|
+
lastStage: mode === 'auto' ? 'post_turn_failed' : 'manual_failed',
|
|
359
|
+
lastBeforeTokens: beforeTokens,
|
|
360
|
+
lastAfterTokens: beforeTokens,
|
|
361
|
+
lastBeforeMessageTokens: beforeMessageTokens,
|
|
362
|
+
lastAfterMessageTokens: beforeMessageTokens,
|
|
363
|
+
lastPressureTokens: pressureTokens,
|
|
364
|
+
lastCheckedAt: now,
|
|
365
|
+
lastChanged: false,
|
|
366
|
+
type: compactType,
|
|
367
|
+
compactType,
|
|
368
|
+
lastCompactType: compactType,
|
|
369
|
+
lastSemantic: false,
|
|
370
|
+
lastSemanticError: semanticCompactError?.message || null,
|
|
371
|
+
lastRecallFastTrack: false,
|
|
372
|
+
lastRecallFastTrackError: recallFastTrackError?.message || null,
|
|
373
|
+
lastError: compactError?.message || semanticCompactError?.message || recallFastTrackError?.message || String(compactError || semanticCompactError || recallFastTrackError || 'compact failed'),
|
|
374
|
+
};
|
|
375
|
+
return {
|
|
376
|
+
changed: false,
|
|
377
|
+
error: session.compaction.lastError,
|
|
378
|
+
compactType,
|
|
379
|
+
beforeMessages: messages.length,
|
|
380
|
+
afterMessages: messages.length,
|
|
381
|
+
beforeTokens,
|
|
382
|
+
afterTokens: beforeTokens,
|
|
383
|
+
beforeMessageTokens,
|
|
384
|
+
afterMessageTokens: beforeMessageTokens,
|
|
385
|
+
pressureTokens,
|
|
386
|
+
triggerTokens,
|
|
387
|
+
bufferTokens,
|
|
388
|
+
bufferRatio,
|
|
389
|
+
boundaryTokens: boundary,
|
|
390
|
+
budgetTokens: boundary,
|
|
391
|
+
targetBudgetTokens: budget,
|
|
392
|
+
reserveTokens,
|
|
393
|
+
semanticCompact: false,
|
|
394
|
+
semanticError: semanticCompactError?.message || null,
|
|
395
|
+
recallFastTrack: false,
|
|
396
|
+
recallFastTrackError: recallFastTrackError?.message || null,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
let beforeEncoded = '';
|
|
400
|
+
let afterEncoded = '';
|
|
401
|
+
try { beforeEncoded = JSON.stringify(messages); } catch { beforeEncoded = ''; }
|
|
402
|
+
try { afterEncoded = JSON.stringify(compacted); } catch { afterEncoded = ''; }
|
|
403
|
+
const afterMessageTokens = estimateMessagesTokens(compacted);
|
|
404
|
+
const afterTokens = afterMessageTokens + reserveTokens;
|
|
405
|
+
const changed = beforeEncoded && afterEncoded
|
|
406
|
+
? beforeEncoded !== afterEncoded
|
|
407
|
+
: (compacted.length !== messages.length || afterMessageTokens !== beforeMessageTokens);
|
|
408
|
+
const unchangedReason = changed ? null : (force ? 'nothing to compact' : 'below threshold');
|
|
409
|
+
const now = Date.now();
|
|
410
|
+
session.messages = compacted;
|
|
411
|
+
session.providerState = undefined;
|
|
412
|
+
session.compaction = {
|
|
413
|
+
...(session.compaction || {}),
|
|
414
|
+
auto: mode === 'auto' ? true : session.compaction?.auto !== false,
|
|
415
|
+
boundaryTokens: boundary,
|
|
416
|
+
triggerTokens,
|
|
417
|
+
bufferTokens,
|
|
418
|
+
bufferRatio,
|
|
419
|
+
reserveTokens,
|
|
420
|
+
type: compactType,
|
|
421
|
+
compactType,
|
|
422
|
+
lastCompactType: compactType,
|
|
423
|
+
lastStage: mode === 'auto' ? 'post_turn' : 'manual',
|
|
424
|
+
lastBeforeTokens: beforeTokens,
|
|
425
|
+
lastAfterTokens: afterTokens,
|
|
426
|
+
lastBeforeMessageTokens: beforeMessageTokens,
|
|
427
|
+
lastAfterMessageTokens: afterMessageTokens,
|
|
428
|
+
lastPressureTokens: pressureTokens,
|
|
429
|
+
lastCheckedAt: now,
|
|
430
|
+
lastChanged: changed,
|
|
431
|
+
lastChangedAt: changed ? now : session.compaction?.lastChangedAt || null,
|
|
432
|
+
lastCompactAt: changed ? now : session.compaction?.lastCompactAt || null,
|
|
433
|
+
lastSemantic: semanticCompactResult?.semantic === true,
|
|
434
|
+
lastSemanticError: semanticCompactError?.message || null,
|
|
435
|
+
lastRecallFastTrack: recallFastTrackResult?.recallFastTrack === true,
|
|
436
|
+
lastRecallFastTrackError: recallFastTrackError?.message || null,
|
|
437
|
+
lastRecallFastTrackQuerySha: recallFastTrackResult?.query ? createHash('sha256').update(recallFastTrackResult.query).digest('hex').slice(0, 16) : null,
|
|
438
|
+
lastSemanticUsage: semanticCompactResult?.usage ? {
|
|
439
|
+
inputTokens: semanticCompactResult.usage.inputTokens || 0,
|
|
440
|
+
outputTokens: semanticCompactResult.usage.outputTokens || 0,
|
|
441
|
+
cachedTokens: semanticCompactResult.usage.cachedTokens || 0,
|
|
442
|
+
cacheWriteTokens: semanticCompactResult.usage.cacheWriteTokens || 0,
|
|
443
|
+
} : null,
|
|
444
|
+
compactCount: (session.compaction?.compactCount || 0) + (changed ? 1 : 0),
|
|
445
|
+
};
|
|
446
|
+
if (changed && mode === 'auto') session.lastContextTokensStaleAfterCompact = true;
|
|
447
|
+
return {
|
|
448
|
+
changed,
|
|
449
|
+
reason: unchangedReason,
|
|
450
|
+
compactType,
|
|
451
|
+
beforeMessages: messages.length,
|
|
452
|
+
afterMessages: compacted.length,
|
|
453
|
+
beforeTokens,
|
|
454
|
+
afterTokens,
|
|
455
|
+
beforeMessageTokens,
|
|
456
|
+
afterMessageTokens,
|
|
457
|
+
pressureTokens,
|
|
458
|
+
triggerTokens,
|
|
459
|
+
bufferTokens,
|
|
460
|
+
bufferRatio,
|
|
461
|
+
boundaryTokens: boundary,
|
|
462
|
+
budgetTokens: boundary,
|
|
463
|
+
targetBudgetTokens: budget,
|
|
464
|
+
reserveTokens,
|
|
465
|
+
semanticCompact: semanticCompactResult?.semantic === true,
|
|
466
|
+
semanticError: semanticCompactError?.message || null,
|
|
467
|
+
recallFastTrack: recallFastTrackResult?.recallFastTrack === true,
|
|
468
|
+
recallFastTrackError: recallFastTrackError?.message || null,
|
|
469
|
+
usage: semanticCompactResult?.usage || null,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
@@ -35,14 +35,17 @@ const CONTEXT_WINDOWS = {
|
|
|
35
35
|
// runtime agree on the boundary the first time a model is routed. Local models
|
|
36
36
|
// (llama/mistral/phi/qwen/gemma) stay small so an unknown local id never claims
|
|
37
37
|
// a giant window.
|
|
38
|
-
|
|
38
|
+
const LOCAL_PROVIDERS = new Set(['ollama', 'lmstudio', 'llamacpp', 'llama.cpp', 'local', '']);
|
|
39
|
+
export function guessContextWindow(model, provider = null) {
|
|
39
40
|
if (CONTEXT_WINDOWS[model])
|
|
40
41
|
return CONTEXT_WINDOWS[model];
|
|
41
42
|
const m = String(model || '').toLowerCase();
|
|
43
|
+
const p = String(provider || '').toLowerCase();
|
|
44
|
+
const isLocalProvider = LOCAL_PROVIDERS.has(p);
|
|
42
45
|
// Local/self-hosted families — never inflate an unknown local id.
|
|
43
|
-
if (m.includes('llama') || m.includes('mistral') || m.includes('mixtral')
|
|
46
|
+
if (isLocalProvider && (m.includes('llama') || m.includes('mistral') || m.includes('mixtral')
|
|
44
47
|
|| m.includes('phi') || m.includes('qwen') || m.includes('gemma')
|
|
45
|
-
|| m.includes('deepseek-r1') || m.includes('codellama'))
|
|
48
|
+
|| m.includes('deepseek-r1') || m.includes('codellama')))
|
|
46
49
|
return 8192;
|
|
47
50
|
// Current hosted families by name pattern.
|
|
48
51
|
if (m.startsWith('claude-opus') || m.startsWith('claude-sonnet')) return 1000000;
|
|
@@ -148,7 +151,7 @@ export function resolveSessionContextMeta(provider, model, seed = {}) {
|
|
|
148
151
|
|| positiveContextWindow(seed.rawContextWindow)
|
|
149
152
|
|| positiveContextWindow(seed.raw_context_window)
|
|
150
153
|
|| positiveContextWindow(seed.contextWindow)
|
|
151
|
-
|| guessContextWindow(model);
|
|
154
|
+
|| guessContextWindow(model, providerNameOf(provider));
|
|
152
155
|
const effectiveContextWindowPercent = boundedPercent(
|
|
153
156
|
seed.effectiveContextWindowPercent
|
|
154
157
|
?? seed.effective_context_window_percent
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Prompt content + temporal helpers, extracted verbatim from manager.mjs
|
|
2
2
|
// (behavior-preserving). Pure string/date utilities with no session state.
|
|
3
3
|
import { isInternalRuntimeNotificationText as contractIsInternalRuntimeNotificationText } from '../../../../shared/tool-execution-contract.mjs';
|
|
4
|
+
import { SUMMARY_PREFIX } from '../compact.mjs';
|
|
4
5
|
|
|
5
6
|
export function promptContentText(content) {
|
|
6
7
|
if (typeof content === 'string') return content;
|
|
@@ -124,11 +125,22 @@ export function isProtectedContextUserMessage(message) {
|
|
|
124
125
|
&& message.content.trimStart().startsWith('<system-reminder>');
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
// Compact summary messages (role:'user', content startsWith SUMMARY_PREFIX)
|
|
129
|
+
// are synthetic anchors, not a real human turn — they must not count as
|
|
130
|
+
// "user conversation" or the post-clear/post-compact session-start block
|
|
131
|
+
// would be wrongly suppressed on the next real user turn.
|
|
132
|
+
function isSummaryAnchorMessage(message) {
|
|
133
|
+
return message?.role === 'user'
|
|
134
|
+
&& typeof message.content === 'string'
|
|
135
|
+
&& message.content.startsWith(SUMMARY_PREFIX);
|
|
136
|
+
}
|
|
137
|
+
|
|
127
138
|
export function hasUserConversationMessage(messages) {
|
|
128
139
|
return (Array.isArray(messages) ? messages : []).some((message) => (
|
|
129
140
|
message?.role === 'user'
|
|
130
141
|
&& !isProtectedContextUserMessage(message)
|
|
131
142
|
&& !isReferenceFilesMessage(message)
|
|
143
|
+
&& !isSummaryAnchorMessage(message)
|
|
132
144
|
));
|
|
133
145
|
}
|
|
134
146
|
|