mixdog 0.9.2 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/telegram-format.mjs +19 -22
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +6 -14
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +259 -80
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +56 -588
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -462
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// Extracted from index.mjs — env/config flag helpers (pure, poll-on-use).
|
|
2
|
+
// These read process.env and the on-disk config section each call so runtime
|
|
3
|
+
// toggles (recap, secondary mode, cycle kill-switches) take effect without a
|
|
4
|
+
// daemon restart. No module-level mutable state; safe to import anywhere.
|
|
5
|
+
import os from 'node:os'
|
|
6
|
+
import path from 'node:path'
|
|
7
|
+
import fs from 'node:fs'
|
|
8
|
+
import { readSection } from '../../shared/config.mjs'
|
|
9
|
+
|
|
10
|
+
export function readMainConfig() {
|
|
11
|
+
return readSection('memory')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Recap toggle lives in the `agent` config section (agent.recap.enabled,
|
|
15
|
+
// default true) — the same file the session runtime persists via
|
|
16
|
+
// saveConfigAndAdopt. The memory daemon runs as a detached cross-process HTTP
|
|
17
|
+
// worker, so it re-reads this section from disk each cycle tick (poll-on-use)
|
|
18
|
+
// rather than relying on IPC. A legacy `agent.modules.memory === false` flag is
|
|
19
|
+
// still honored as recap off (migration folds it on the session-runtime side).
|
|
20
|
+
export function readRecapEnabled() {
|
|
21
|
+
try {
|
|
22
|
+
let agent = readSection('agent')
|
|
23
|
+
// The agent section may be double-wrapped ({ agent: { providers, ... } })
|
|
24
|
+
// in legacy config files; loadConfig() unwraps the same way (config.mjs
|
|
25
|
+
// `raw.agent && raw.agent.providers`). Mirror it here or a migrated/toggled
|
|
26
|
+
// recap flag written inside the wrapper would be invisible to the daemon.
|
|
27
|
+
if (agent?.agent && agent.agent.providers) agent = agent.agent
|
|
28
|
+
const recap = agent?.recap
|
|
29
|
+
if (recap && typeof recap === 'object' && recap.enabled === false) return false
|
|
30
|
+
const legacyMemory = agent?.modules?.memory
|
|
31
|
+
if (legacyMemory === false) return false
|
|
32
|
+
if (legacyMemory && typeof legacyMemory === 'object' && legacyMemory.enabled === false) return false
|
|
33
|
+
return true
|
|
34
|
+
} catch {
|
|
35
|
+
return true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function embeddingWarmupEnabled() {
|
|
40
|
+
const raw = String(process.env.MIXDOG_EMBED_WARMUP ?? '1').trim().toLowerCase()
|
|
41
|
+
return !(raw === '0' || raw === 'false' || raw === 'off' || raw === 'no')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function envFlagEnabled(name) {
|
|
45
|
+
const raw = String(process.env[name] ?? '').trim().toLowerCase()
|
|
46
|
+
return raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function memorySecondaryMode() {
|
|
50
|
+
return envFlagEnabled('MIXDOG_MEMORY_SECONDARY')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function embeddingWarmupCanStart() {
|
|
54
|
+
return embeddingWarmupEnabled() && !memorySecondaryMode()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function memoryLlmWorkerEnabled() {
|
|
58
|
+
return !memorySecondaryMode() && !envFlagEnabled('MIXDOG_MEMORY_DISABLE_LLM_WORKER')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function memoryCyclesEnabled() {
|
|
62
|
+
// Background cycles run only when: not secondary mode, the env hard-override
|
|
63
|
+
// is not set, AND the user-facing recap toggle is on. The recap flag is
|
|
64
|
+
// re-read from disk here so toggling it at runtime takes effect without a
|
|
65
|
+
// daemon restart (checkCycles polls this each tick). The env override stays a
|
|
66
|
+
// hard kill switch regardless of recap.
|
|
67
|
+
return !memorySecondaryMode()
|
|
68
|
+
&& !envFlagEnabled('MIXDOG_MEMORY_DISABLE_CYCLES')
|
|
69
|
+
&& readRecapEnabled()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function secondaryPgAdvertised(dataDir) {
|
|
73
|
+
if (!memorySecondaryMode()) return true
|
|
74
|
+
const runtimeRoot = process.env.MIXDOG_RUNTIME_ROOT
|
|
75
|
+
? path.resolve(process.env.MIXDOG_RUNTIME_ROOT)
|
|
76
|
+
: path.join(os.tmpdir(), 'mixdog')
|
|
77
|
+
try {
|
|
78
|
+
const cur = JSON.parse(fs.readFileSync(path.join(runtimeRoot, 'active-instance.json'), 'utf8'))
|
|
79
|
+
const port = Number(cur?.pg_port)
|
|
80
|
+
const pgdata = cur?.pg_pgdata ? path.resolve(String(cur.pg_pgdata)) : ''
|
|
81
|
+
return Number.isInteger(port) && port > 0 && pgdata === path.resolve(path.join(dataDir, 'pgdata'))
|
|
82
|
+
} catch {
|
|
83
|
+
return false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function assertSecondaryPgAttachable(dataDir) {
|
|
88
|
+
if (!secondaryPgAdvertised(dataDir)) {
|
|
89
|
+
throw new Error('memory-service: secondary mode requires an existing primary PG instance')
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Barrel — original memory-cycle module split into memory-embed / memory-cycle1 / memory-cycle2.
|
|
2
2
|
// External callers import from this path; the split is transparent.
|
|
3
3
|
export {
|
|
4
|
-
syncRootEmbedding, deleteRootEmbedding, flushEmbeddingDirty, inferChunkProjectId,
|
|
4
|
+
syncRootEmbedding, deleteRootEmbedding, flushEmbeddingDirty, flushRawEmbeddings, inferChunkProjectId,
|
|
5
5
|
} from './memory-embed.mjs'
|
|
6
6
|
export { runCycle1 } from './memory-cycle1.mjs'
|
|
7
7
|
export {
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
// Unified-gate cluster extracted from memory-cycle2.mjs: prompt formatting,
|
|
2
|
+
// pipe-verdict parsing/validation, the rules-digest cache, the single LLM
|
|
3
|
+
// gate pass (runUnifiedGate) and the Sonnet re-judge cascade. Facade
|
|
4
|
+
// (memory-cycle2.mjs) re-exports the public members unchanged.
|
|
5
|
+
import { existsSync, readFileSync } from 'fs'
|
|
6
|
+
import { join } from 'path'
|
|
7
|
+
import { resolveMaintenancePreset } from '../../shared/llm/index.mjs'
|
|
8
|
+
import { callAgentDispatch } from './agent-ipc.mjs'
|
|
9
|
+
import { listCore } from './core-memory-store.mjs'
|
|
10
|
+
import { __mixdogMemoryLog, throwIfAborted, resourceDir } from './memory-cycle2-shared.mjs'
|
|
11
|
+
|
|
12
|
+
export const CYCLE2_ACTIVE_TARGET_CAP = 100
|
|
13
|
+
|
|
14
|
+
// Status-based verb whitelist. 3-tier policy: pending → active/archived,
|
|
15
|
+
// active → active/archived/update/merge.
|
|
16
|
+
const STATUS_ALLOWED_VERBS = {
|
|
17
|
+
pending: new Set(['active', 'archived']),
|
|
18
|
+
active: new Set(['active', 'archived', 'update', 'merge']),
|
|
19
|
+
}
|
|
20
|
+
const NON_ARCHIVE_VERBS = new Set(['active', 'update', 'merge'])
|
|
21
|
+
// Union of every primary (status) verb across all statuses, plus the two
|
|
22
|
+
// non-verb line kinds. Used by the stray-index shift guard to decide whether
|
|
23
|
+
// a `idx|id|verb` line had a leading row index prepended by the LLM.
|
|
24
|
+
const ALL_PRIMARY_VERBS = new Set(['active', 'archived', 'update', 'merge'])
|
|
25
|
+
const isShiftFollowToken = (tok) => {
|
|
26
|
+
const v = String(tok ?? '').trim().toLowerCase()
|
|
27
|
+
return ALL_PRIMARY_VERBS.has(v) || v === 'why' || v === 'core'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function invokeLlm(prompt, mode, preset, timeout, llmCall = callAgentDispatch) {
|
|
31
|
+
return await llmCall({
|
|
32
|
+
agent: 'cycle2-agent',
|
|
33
|
+
taskType: 'maintenance',
|
|
34
|
+
mode,
|
|
35
|
+
preset,
|
|
36
|
+
timeout,
|
|
37
|
+
cwd: null,
|
|
38
|
+
}, prompt)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildPidMap(rowSets) {
|
|
42
|
+
const pids = [...new Set(rowSets.flat().map(r => r.project_id).filter(Boolean))].sort()
|
|
43
|
+
return new Map(pids.map((p, i) => [p, `P${i + 1}`]))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function formatEntriesForPromotePrompt(rows, pidMap, opts = {}) {
|
|
47
|
+
if (!rows || rows.length === 0) return '(none)'
|
|
48
|
+
const map = pidMap ?? buildPidMap([rows])
|
|
49
|
+
// When numbered, prefix each row with its 1-based prompt-order ordinal so the
|
|
50
|
+
// gate LLM can echo a row number it can see, instead of inventing one. The
|
|
51
|
+
// ordinal domain (1..N) and the 5-digit batch-id domain must stay disjoint —
|
|
52
|
+
// see the ordinalToId invariant in runUnifiedGate.
|
|
53
|
+
const numbered = opts.numbered === true
|
|
54
|
+
const lines = rows.map((r, i) => {
|
|
55
|
+
const tag = r.project_id ? (map.get(r.project_id) ?? 'C') : 'C'
|
|
56
|
+
const stat = r.status ? `[${r.status}]` : '[?]'
|
|
57
|
+
const prefix = numbered ? `${i + 1}. ` : '- '
|
|
58
|
+
return `${prefix}id:${r.id} ${stat} ${tag} ${r.category} s:${r.score ?? 'n'} el:${r.element} sm:${String(r.summary || '').slice(0, 100)}`
|
|
59
|
+
})
|
|
60
|
+
if (map.size === 0) return lines.join('\n')
|
|
61
|
+
const legend = [...map.entries()].map(([p, t]) => `${t}=${p}`).concat('C=COMMON').join(', ')
|
|
62
|
+
return `# pid: ${legend}\n` + lines.join('\n')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// User-curated rows from core_entries — id-less, no status, no score; the
|
|
66
|
+
// LLM only needs element + summary + project tag to detect overlap with
|
|
67
|
+
// candidate entries below. Format kept terse so the prompt budget stays small.
|
|
68
|
+
function formatUserCoreForPrompt(rows, pidMap) {
|
|
69
|
+
if (!rows || rows.length === 0) return '(none)'
|
|
70
|
+
const map = pidMap ?? new Map()
|
|
71
|
+
return rows.map(r => {
|
|
72
|
+
const tag = r.project_id ? (map.get(r.project_id) ?? 'C') : 'C'
|
|
73
|
+
const sm = String(r.summary || '').slice(0, 200)
|
|
74
|
+
return `- ${tag} ${r.category}: ${r.element}${sm && sm !== r.element ? ` — ${sm}` : ''}`
|
|
75
|
+
}).join('\n')
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Parse pipe-format unified verdicts. Each line: <id>|<verb> [|...].
|
|
79
|
+
// Verbs validated against the row's current status via STATUS_ALLOWED_VERBS.
|
|
80
|
+
// Returns { actions, rejected } or null when no parseable lines.
|
|
81
|
+
function parseUnifiedFormat(raw, statusById, ordinalToId = null) {
|
|
82
|
+
if (raw == null) return null
|
|
83
|
+
const text = String(raw).trim()
|
|
84
|
+
if (!text) return { actions: [], rejected: new Set() }
|
|
85
|
+
const lines = text.split('\n')
|
|
86
|
+
const actions = []
|
|
87
|
+
const rejected = new Set()
|
|
88
|
+
const support = new Map()
|
|
89
|
+
let sawValid = false
|
|
90
|
+
// Resolve a first-field/merge token to a real batch id. The gate may echo
|
|
91
|
+
// either the exact 5-digit batch id OR the 1-based row ordinal shown in the
|
|
92
|
+
// numbered Entries block. The two domains are disjoint (asserted in
|
|
93
|
+
// runUnifiedGate), so an exact-id hit always wins and an unmatched value
|
|
94
|
+
// falls back to ordinal lookup; anything else is NaN (line treated invalid).
|
|
95
|
+
const resolveId = (tok) => {
|
|
96
|
+
const n = Number(String(tok ?? '').trim())
|
|
97
|
+
if (!Number.isFinite(n)) return NaN
|
|
98
|
+
if (statusById.has(n)) return n
|
|
99
|
+
if (ordinalToId && ordinalToId.has(n)) return ordinalToId.get(n)
|
|
100
|
+
return NaN
|
|
101
|
+
}
|
|
102
|
+
for (const rawLine of lines) {
|
|
103
|
+
const line = rawLine.trim()
|
|
104
|
+
if (!line) continue
|
|
105
|
+
if (line.startsWith('//') || line.startsWith('#')) continue
|
|
106
|
+
if (line.startsWith('```')) continue
|
|
107
|
+
const parts = line.split('|')
|
|
108
|
+
if (parts.length < 2) continue
|
|
109
|
+
// LLM sometimes prefixes a row index, emitting `idx|id|verdict` instead of
|
|
110
|
+
// `id|verdict`; parts[0] (the index) is a stray token and the line must be
|
|
111
|
+
// shifted before parsing. Strict invariant so a real 2-field `id|verdict`
|
|
112
|
+
// is never shifted into a 1-field line (which would throw on parts[1]):
|
|
113
|
+
// parts.length >= 3 AND parts[1] is a known batch id AND parts[2] is a
|
|
114
|
+
// valid primary verb / why / core (the shifted verdict slot).
|
|
115
|
+
// Trigger when EITHER parts[0] is not a known id (classic stray index) OR
|
|
116
|
+
// parts[0] IS known but parts[1] is not itself a valid verb — that covers
|
|
117
|
+
// `1|1|active`, where the stray index collides with a real batch id and the
|
|
118
|
+
// un-shifted reading would verb-reject the wrong row.
|
|
119
|
+
if (
|
|
120
|
+
parts.length >= 3 &&
|
|
121
|
+
statusById.has(Number(parts[1].trim())) &&
|
|
122
|
+
isShiftFollowToken(parts[2]) &&
|
|
123
|
+
(!statusById.has(Number(parts[0].trim())) || !isShiftFollowToken(parts[1]))
|
|
124
|
+
) {
|
|
125
|
+
parts.shift()
|
|
126
|
+
}
|
|
127
|
+
const entryId = resolveId(parts[0])
|
|
128
|
+
const action = parts[1].trim().toLowerCase()
|
|
129
|
+
if (!Number.isFinite(entryId) || !action) continue
|
|
130
|
+
const status = statusById.get(entryId)
|
|
131
|
+
if (!status) continue
|
|
132
|
+
// Only mark as parse-ok when the id is known to the batch; a response
|
|
133
|
+
// composed entirely of unknown ids would otherwise return parse-ok with
|
|
134
|
+
// zero actions/rejections, leaving the rows un-reviewed and re-queued.
|
|
135
|
+
sawValid = true
|
|
136
|
+
if (action === 'core') {
|
|
137
|
+
actions.push({ entry_id: entryId, action: 'core', core_summary: parts.slice(2).join('|').trim().slice(0, 120) })
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
if (action === 'why') {
|
|
141
|
+
const kind = (parts[2] ?? '').trim().toUpperCase()
|
|
142
|
+
const reason = parts.slice(3).join('|').replace(/\s+/g, ' ').trim().slice(0, 240)
|
|
143
|
+
if ((kind === 'A' || kind === 'B') && reason) {
|
|
144
|
+
support.set(entryId, { kind, reason })
|
|
145
|
+
}
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
const allowed = STATUS_ALLOWED_VERBS[status]
|
|
149
|
+
if (!allowed || !allowed.has(action)) {
|
|
150
|
+
__mixdogMemoryLog(`[cycle2] verb rejected: id=${entryId} status=${status} verb=${action}\n`)
|
|
151
|
+
rejected.add(entryId)
|
|
152
|
+
continue
|
|
153
|
+
}
|
|
154
|
+
if (action === 'update') {
|
|
155
|
+
actions.push({
|
|
156
|
+
entry_id: entryId, action,
|
|
157
|
+
element: (parts[2] ?? '').trim(),
|
|
158
|
+
summary: parts.slice(3).join('|').trim(),
|
|
159
|
+
})
|
|
160
|
+
} else if (action === 'merge') {
|
|
161
|
+
const targetId = resolveId(parts[2])
|
|
162
|
+
const sourceIds = [...new Set((parts[3] ?? '').split(',').map(s => resolveId(s)).filter(Number.isFinite))]
|
|
163
|
+
if (!Number.isFinite(targetId) || sourceIds.length === 0) {
|
|
164
|
+
__mixdogMemoryLog(`[cycle2] merge rejected: id=${entryId} invalid target/sources\n`)
|
|
165
|
+
rejected.add(entryId)
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
if (targetId !== entryId && !sourceIds.includes(entryId)) {
|
|
169
|
+
__mixdogMemoryLog(
|
|
170
|
+
`[cycle2] merge rejected: id=${entryId} must be target or listed source (target=${targetId} sources=${sourceIds.join(',')})\n`,
|
|
171
|
+
)
|
|
172
|
+
rejected.add(entryId)
|
|
173
|
+
continue
|
|
174
|
+
}
|
|
175
|
+
actions.push({
|
|
176
|
+
entry_id: entryId, action,
|
|
177
|
+
target_id: targetId,
|
|
178
|
+
source_ids: sourceIds,
|
|
179
|
+
element: (parts[4] ?? '').trim(),
|
|
180
|
+
summary: parts.slice(5).join('|').trim(),
|
|
181
|
+
})
|
|
182
|
+
} else {
|
|
183
|
+
actions.push({ entry_id: entryId, action })
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!sawValid && rejected.size === 0) return null
|
|
187
|
+
return { actions, rejected, support }
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ─── Current rules digest cache ──────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
let _currentRulesDigest = null
|
|
193
|
+
let _currentRulesDigestTs = 0
|
|
194
|
+
export function loadCurrentRulesDigest() {
|
|
195
|
+
const now = Date.now()
|
|
196
|
+
if (_currentRulesDigest && now - _currentRulesDigestTs < 60_000) return _currentRulesDigest
|
|
197
|
+
const sources = [
|
|
198
|
+
join(resourceDir(), 'rules', 'shared', '01-general.md'),
|
|
199
|
+
join(resourceDir(), 'rules', 'shared', '01-tool.md'),
|
|
200
|
+
join(resourceDir(), 'rules', 'shared', '04-memory.md'),
|
|
201
|
+
join(resourceDir(), 'rules', 'shared', '06-team.md'),
|
|
202
|
+
join(resourceDir(), 'rules', 'shared', '07-workflow.md'),
|
|
203
|
+
]
|
|
204
|
+
const parts = []
|
|
205
|
+
for (const p of sources) {
|
|
206
|
+
try {
|
|
207
|
+
if (!existsSync(p)) continue
|
|
208
|
+
const txt = readFileSync(p, 'utf8').trim()
|
|
209
|
+
if (txt) parts.push(`# Source: ${p}\n${txt}`)
|
|
210
|
+
} catch {}
|
|
211
|
+
}
|
|
212
|
+
const joined = parts.join('\n\n---\n\n')
|
|
213
|
+
const CAP = 40_000
|
|
214
|
+
_currentRulesDigest = joined.length > CAP ? joined.slice(0, CAP) + '\n…[truncated]' : joined
|
|
215
|
+
_currentRulesDigestTs = now
|
|
216
|
+
return _currentRulesDigest
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function uniqueIds(values) {
|
|
220
|
+
return [...new Set(values
|
|
221
|
+
.map(id => Number(id))
|
|
222
|
+
.filter(id => Number.isFinite(id)))]
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function validateUnifiedGate(parsed, statusById) {
|
|
226
|
+
const actions = Array.isArray(parsed?.actions) ? parsed.actions : []
|
|
227
|
+
const primary = actions.filter(a => a?.action !== 'core')
|
|
228
|
+
const verdictCounts = new Map()
|
|
229
|
+
for (const action of primary) {
|
|
230
|
+
const id = Number(action?.entry_id)
|
|
231
|
+
if (!Number.isFinite(id)) continue
|
|
232
|
+
verdictCounts.set(id, (verdictCounts.get(id) || 0) + 1)
|
|
233
|
+
}
|
|
234
|
+
const expectedIds = [...statusById.keys()]
|
|
235
|
+
const missingVerdictIds = expectedIds.filter(id => !verdictCounts.has(id))
|
|
236
|
+
const duplicateVerdictIds = [...verdictCounts.entries()]
|
|
237
|
+
.filter(([, count]) => count > 1)
|
|
238
|
+
.map(([id]) => id)
|
|
239
|
+
const support = parsed?.support instanceof Map ? parsed.support : new Map()
|
|
240
|
+
const coreIds = new Set(actions
|
|
241
|
+
.filter(a => a?.action === 'core')
|
|
242
|
+
.map(a => Number(a.entry_id))
|
|
243
|
+
.filter(id => Number.isFinite(id)))
|
|
244
|
+
const missingSupportIds = []
|
|
245
|
+
const missingCoreIds = []
|
|
246
|
+
for (const action of primary) {
|
|
247
|
+
if (!NON_ARCHIVE_VERBS.has(action?.action)) continue
|
|
248
|
+
const id = Number(action.entry_id)
|
|
249
|
+
if (!Number.isFinite(id)) continue
|
|
250
|
+
const coreId = action.action === 'merge' && Number.isFinite(Number(action.target_id))
|
|
251
|
+
? Number(action.target_id)
|
|
252
|
+
: id
|
|
253
|
+
const hasSupport = support.has(id) || (action.action === 'merge' && support.has(coreId))
|
|
254
|
+
if (!hasSupport) missingSupportIds.push(id)
|
|
255
|
+
if (!coreIds.has(coreId)) missingCoreIds.push(id)
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
missingVerdictIds: uniqueIds(missingVerdictIds),
|
|
259
|
+
duplicateVerdictIds: uniqueIds(duplicateVerdictIds),
|
|
260
|
+
missingSupportIds: uniqueIds(missingSupportIds),
|
|
261
|
+
missingCoreIds: uniqueIds(missingCoreIds),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function gateQualitySummary(quality) {
|
|
266
|
+
const parts = []
|
|
267
|
+
if (quality?.missingVerdictIds?.length) parts.push(`missing verdict ids=${quality.missingVerdictIds.join(',')}`)
|
|
268
|
+
if (quality?.duplicateVerdictIds?.length) parts.push(`duplicate verdict ids=${quality.duplicateVerdictIds.join(',')}`)
|
|
269
|
+
if (quality?.missingSupportIds?.length) parts.push(`missing why ids=${quality.missingSupportIds.join(',')}`)
|
|
270
|
+
if (quality?.missingCoreIds?.length) parts.push(`missing core ids=${quality.missingCoreIds.join(',')}`)
|
|
271
|
+
return parts.join('; ')
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function stripUnsupportedPromotions(parsed, unsupportedIds) {
|
|
275
|
+
const ids = new Set(uniqueIds(unsupportedIds))
|
|
276
|
+
if (ids.size === 0) return parsed
|
|
277
|
+
const rejected = new Set(parsed?.rejected || [])
|
|
278
|
+
for (const id of ids) rejected.add(id)
|
|
279
|
+
const actions = (parsed?.actions || []).filter(a => {
|
|
280
|
+
if (a?.action === 'core') return true
|
|
281
|
+
return !ids.has(Number(a?.entry_id))
|
|
282
|
+
})
|
|
283
|
+
return { ...parsed, actions, rejected }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function requiredCoreIdForAction(action) {
|
|
287
|
+
if (action?.action === 'merge' && Number.isFinite(Number(action.target_id))) {
|
|
288
|
+
return Number(action.target_id)
|
|
289
|
+
}
|
|
290
|
+
return Number(action?.entry_id)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ─── Unified gate ────────────────────────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
// Single LLM pass over rows whose status is in {pending, active}.
|
|
296
|
+
// Returns { actions, rejected, parseOk } following parseUnifiedFormat shape.
|
|
297
|
+
export async function runUnifiedGate(db, rows, activeContext, config = {}, options = {}) {
|
|
298
|
+
const signal = options?.signal
|
|
299
|
+
throwIfAborted(signal)
|
|
300
|
+
if (!rows || rows.length === 0) return { actions: [], rejected: new Set(), parseOk: true }
|
|
301
|
+
const promptPath = join(resourceDir(), 'defaults', 'memory-promote-prompt.md')
|
|
302
|
+
if (!existsSync(promptPath)) {
|
|
303
|
+
throw new Error(`runCycle2: prompt file missing at ${promptPath}`)
|
|
304
|
+
}
|
|
305
|
+
const template = readFileSync(promptPath, 'utf8')
|
|
306
|
+
const userCoreRows = options.dataDir ? await listCore(options.dataDir, '*').catch(() => []) : []
|
|
307
|
+
throwIfAborted(signal)
|
|
308
|
+
const sharedPidMap = buildPidMap([activeContext ?? [], rows ?? [], userCoreRows ?? []])
|
|
309
|
+
const rulesDigest = loadCurrentRulesDigest() || '(no current rules digest available)'
|
|
310
|
+
const activeCount = activeContext?.length ?? 0
|
|
311
|
+
const activeCap = options.activeCap ?? CYCLE2_ACTIVE_TARGET_CAP
|
|
312
|
+
|
|
313
|
+
const prompt = template
|
|
314
|
+
.replace('{{CURRENT_RULES}}', rulesDigest)
|
|
315
|
+
.replace('{{USER_CORE}}', formatUserCoreForPrompt(userCoreRows, sharedPidMap))
|
|
316
|
+
.replace('{{CORE_MEMORY}}', formatEntriesForPromotePrompt(activeContext, sharedPidMap))
|
|
317
|
+
.replace('{{ITEMS}}', formatEntriesForPromotePrompt(rows, sharedPidMap, { numbered: true }))
|
|
318
|
+
.replace('{{ACTIVE_COUNT}}', String(activeCount))
|
|
319
|
+
.replace('{{ACTIVE_CAP}}', String(activeCap))
|
|
320
|
+
|
|
321
|
+
const preset = options.preset || resolveMaintenancePreset('memory')
|
|
322
|
+
const timeout = Number(config?.cycle2?.timeout ?? 600000)
|
|
323
|
+
const mode = 'cycle2-unified'
|
|
324
|
+
|
|
325
|
+
const previewRaw = (raw) => String(raw ?? '').replace(/\s+/g, ' ').slice(0, 200)
|
|
326
|
+
const callOnce = async (extraTag) => {
|
|
327
|
+
throwIfAborted(signal)
|
|
328
|
+
const p = extraTag ? `${prompt}\n\n[retry:${extraTag}]` : prompt
|
|
329
|
+
const raw = await invokeLlm(p, mode, preset, timeout, options.callLlm)
|
|
330
|
+
throwIfAborted(signal)
|
|
331
|
+
return raw
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const statusById = new Map(rows.map(r => [Number(r.id), String(r.status)]))
|
|
335
|
+
// Ordinal → batch-id map, keyed by 1-based prompt order (the same order the
|
|
336
|
+
// numbered Entries block uses). The gate may echo either the real 5-digit
|
|
337
|
+
// batch id or the row ordinal 1..N; the parser resolves both. The two domains
|
|
338
|
+
// MUST be disjoint or an ordinal could shadow a real id (ids are 5-digit,
|
|
339
|
+
// ordinals are <= rows.length, so disjointness always holds in practice).
|
|
340
|
+
// On a violation a row-number line is indistinguishable from an exact-id
|
|
341
|
+
// line, so no safe interpretation exists — skip this batch (gate failure)
|
|
342
|
+
// rather than risk applying a verdict to the wrong entry. The cycle itself
|
|
343
|
+
// proceeds; the batch re-queues for a later run.
|
|
344
|
+
const ordinalToId = new Map(rows.map((r, i) => [i + 1, Number(r.id)]))
|
|
345
|
+
const minBatchId = Math.min(...[...statusById.keys()])
|
|
346
|
+
if (Number.isFinite(minBatchId) && minBatchId <= rows.length) {
|
|
347
|
+
__mixdogMemoryLog(`[cycle2] batch id ${minBatchId} collides with ordinal range 1..${rows.length} — skipping batch (no safe id resolution)\n`)
|
|
348
|
+
return { actions: null, rejected: new Set(), parseOk: false }
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
__mixdogMemoryLog(`[cycle2-diag] unified prompt=${prompt.length} bytes; rows=${rows.length}\n`)
|
|
352
|
+
|
|
353
|
+
let raw
|
|
354
|
+
try {
|
|
355
|
+
raw = await callOnce(null)
|
|
356
|
+
} catch (err) {
|
|
357
|
+
if (signal?.aborted) throw signal.reason ?? err
|
|
358
|
+
__mixdogMemoryLog(`[cycle2] unified LLM error: ${err.message}\n`)
|
|
359
|
+
return { actions: null, rejected: new Set(), parseOk: false }
|
|
360
|
+
}
|
|
361
|
+
throwIfAborted(signal)
|
|
362
|
+
__mixdogMemoryLog(`[cycle2-diag] unified raw (first 1500): ${String(raw ?? '').replace(/\n/g, '⏎').slice(0, 1500)}\n`)
|
|
363
|
+
|
|
364
|
+
let parsed = parseUnifiedFormat(raw, statusById, ordinalToId)
|
|
365
|
+
let quality = parsed ? validateUnifiedGate(parsed, statusById) : null
|
|
366
|
+
const qualityIssue = () => gateQualitySummary(quality)
|
|
367
|
+
if (!parsed || qualityIssue()) {
|
|
368
|
+
throwIfAborted(signal)
|
|
369
|
+
const issue = parsed ? qualityIssue() : `unparseable (${previewRaw(raw)})`
|
|
370
|
+
__mixdogMemoryLog(`[cycle2] unified quality retry: ${issue}\n`)
|
|
371
|
+
// Preserve the first pass before retrying. A retry fired for a mere quality
|
|
372
|
+
// issue (e.g. a few missing verdicts) must not throw away an otherwise-valid
|
|
373
|
+
// first-pass parse if the retry comes back unparseable.
|
|
374
|
+
const firstParsed = parsed
|
|
375
|
+
const firstQuality = quality
|
|
376
|
+
try {
|
|
377
|
+
const retryTag = parsed
|
|
378
|
+
? 'complete-verdicts-with-why-and-core-lines'
|
|
379
|
+
: 'first-field-must-be-the-listed-row-number'
|
|
380
|
+
const raw2 = await callOnce(retryTag)
|
|
381
|
+
const retryParsed = parseUnifiedFormat(raw2, statusById, ordinalToId)
|
|
382
|
+
if (retryParsed) {
|
|
383
|
+
parsed = retryParsed
|
|
384
|
+
quality = validateUnifiedGate(retryParsed, statusById)
|
|
385
|
+
} else if (firstParsed) {
|
|
386
|
+
__mixdogMemoryLog(`[cycle2] unparseable after retry — falling back to first-pass parse (${previewRaw(raw2)})\n`)
|
|
387
|
+
parsed = firstParsed
|
|
388
|
+
quality = firstQuality
|
|
389
|
+
} else {
|
|
390
|
+
__mixdogMemoryLog(`[cycle2] unparseable after retry — skipping batch (${previewRaw(raw2)})\n`)
|
|
391
|
+
return { actions: null, rejected: new Set(), parseOk: false }
|
|
392
|
+
}
|
|
393
|
+
} catch (err) {
|
|
394
|
+
if (signal?.aborted) throw signal.reason ?? err
|
|
395
|
+
if (firstParsed) {
|
|
396
|
+
__mixdogMemoryLog(`[cycle2] retry LLM error: ${err.message} — falling back to first-pass parse\n`)
|
|
397
|
+
parsed = firstParsed
|
|
398
|
+
quality = firstQuality
|
|
399
|
+
} else {
|
|
400
|
+
__mixdogMemoryLog(`[cycle2] retry LLM error: ${err.message}\n`)
|
|
401
|
+
return { actions: null, rejected: new Set(), parseOk: false }
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const finalIssue = gateQualitySummary(quality)
|
|
406
|
+
// duplicateVerdictIds are genuinely ambiguous (the same row got two conflicting
|
|
407
|
+
// verbs) — keep the full-skip. missingVerdictIds, by contrast, used to skip the
|
|
408
|
+
// WHOLE batch, so a handful of persistently-missing poison rows could livelock
|
|
409
|
+
// the gate. Partial-apply instead: keep the valid verdicts we did receive, just
|
|
410
|
+
// log the missing ids and leave those rows for a later run.
|
|
411
|
+
if (quality?.duplicateVerdictIds?.length) {
|
|
412
|
+
__mixdogMemoryLog(`[cycle2] duplicate verdict coverage after retry — skipping batch (${finalIssue})\n`)
|
|
413
|
+
return { actions: null, rejected: new Set(), parseOk: false }
|
|
414
|
+
}
|
|
415
|
+
if (quality?.missingVerdictIds?.length) {
|
|
416
|
+
__mixdogMemoryLog(`[cycle2] missing verdicts after retry — partial apply, leaving ids=${quality.missingVerdictIds.join(',')} for a later run (${finalIssue})\n`)
|
|
417
|
+
}
|
|
418
|
+
// A response made up solely of why/core lines parses "ok" yet carries zero
|
|
419
|
+
// primary (status-verb) verdicts. Without this guard parseOk stays true and
|
|
420
|
+
// the caller treats the batch as a clean no-op, masking the coverage failure
|
|
421
|
+
// and marking the rows reviewed. Fail the parse so the rows are re-queued.
|
|
422
|
+
const primaryCount = (parsed.actions || []).filter(a => a?.action !== 'core').length
|
|
423
|
+
if (rows.length > 0 && primaryCount === 0) {
|
|
424
|
+
__mixdogMemoryLog(`[cycle2] gate produced zero primary verdicts for ${rows.length} rows — failing parse\n`)
|
|
425
|
+
return { actions: null, rejected: new Set(), parseOk: false, missingIds: [...statusById.keys()] }
|
|
426
|
+
}
|
|
427
|
+
const incompletePromotionIds = uniqueIds([
|
|
428
|
+
...(quality?.missingSupportIds || []),
|
|
429
|
+
...(quality?.missingCoreIds || []),
|
|
430
|
+
])
|
|
431
|
+
if (incompletePromotionIds.length > 0) {
|
|
432
|
+
__mixdogMemoryLog(`[cycle2] incomplete non-archive verdicts rejected after retry ids=${incompletePromotionIds.join(',')} (${finalIssue})\n`)
|
|
433
|
+
parsed = stripUnsupportedPromotions(parsed, incompletePromotionIds)
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
actions: parsed.actions,
|
|
437
|
+
rejected: parsed.rejected,
|
|
438
|
+
parseOk: true,
|
|
439
|
+
missingIds: quality?.missingVerdictIds || [],
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ─── Sonnet cascade ──────────────────────────────────────────────────────────
|
|
444
|
+
|
|
445
|
+
// Sonnet re-judge over first-pass keep verdicts. Sonnet sees rules + summary
|
|
446
|
+
// and returns binary keep/drop. Failures fail-open (preserve first-pass).
|
|
447
|
+
export async function sonnetCascade(candidates, rulesDigest, options = {}) {
|
|
448
|
+
const signal = options?.signal
|
|
449
|
+
throwIfAborted(signal)
|
|
450
|
+
if (!candidates || candidates.length === 0) return new Map()
|
|
451
|
+
const lines = candidates.map(c =>
|
|
452
|
+
`id:${c.id} status:${c.status} verb:${c.verb} cat:${c.category} el:${c.element} sm:${String(c.summary || '').slice(0, 200)}${c.core ? ` core:${String(c.core).slice(0, 200)}` : ''}`,
|
|
453
|
+
).join('\n')
|
|
454
|
+
const prompt = [
|
|
455
|
+
`Final gate over first-pass keep verdicts.`,
|
|
456
|
+
`Keep a candidate ONLY if it lands in one of three layers: L1 relationship/communication`,
|
|
457
|
+
`(user identity, address form, reply-style preferences, disliked patterns); L2 behavior rules`,
|
|
458
|
+
`(principles the user corrected/insisted on, hard safety boundaries, quality bars); or L3 current`,
|
|
459
|
+
`map (one-line project-landscape summaries, live long-running goals, environment anchors documented`,
|
|
460
|
+
`nowhere else). For a past decision/failure, keep only the one-line lesson that still constrains`,
|
|
461
|
+
`behavior, else drop. DROP anything whose source of truth is code, rules files, or skill docs, plus`,
|
|
462
|
+
`implementation specs, code-internal constants, measurements, resolved-bug stories, status snapshots,`,
|
|
463
|
+
`and duplicates of source-of-truth rules.`,
|
|
464
|
+
`When a candidate has a core: field, judge THAT extracted one-line lesson (the entry will live as`,
|
|
465
|
+
`that line), not the raw narrative in el:/sm:.`,
|
|
466
|
+
``,
|
|
467
|
+
`Source-of-truth rules (excerpt — DO NOT duplicate in memory):`,
|
|
468
|
+
String(rulesDigest || '').slice(0, 4000),
|
|
469
|
+
``,
|
|
470
|
+
`Candidates:`,
|
|
471
|
+
lines,
|
|
472
|
+
``,
|
|
473
|
+
`Reply one line per id: "<id>|keep" to retain, "<id>|drop" to reject.`,
|
|
474
|
+
`NO prose, NO preamble, NO meta-commentary. First character must be a digit.`,
|
|
475
|
+
].join('\n')
|
|
476
|
+
|
|
477
|
+
// Hardcoded — resolveMaintenancePreset falls back to first preset (HAIKU)
|
|
478
|
+
// when no binding exists, which would defeat the cascade. SONNET HIGH
|
|
479
|
+
// matches the worker pool's default preset id from agent-config.
|
|
480
|
+
const preset = options.cascadePreset || 'SONNET HIGH'
|
|
481
|
+
const llmCall = typeof options?.callLlm === 'function' ? options.callLlm : callAgentDispatch
|
|
482
|
+
let raw
|
|
483
|
+
try {
|
|
484
|
+
raw = await llmCall({
|
|
485
|
+
agent: 'cycle2-agent',
|
|
486
|
+
taskType: 'maintenance',
|
|
487
|
+
mode: 'cycle2-cascade',
|
|
488
|
+
preset,
|
|
489
|
+
timeout: 600000,
|
|
490
|
+
cwd: null,
|
|
491
|
+
}, prompt)
|
|
492
|
+
} catch (err) {
|
|
493
|
+
if (signal?.aborted) throw signal.reason ?? err
|
|
494
|
+
__mixdogMemoryLog(`[cycle2] cascade error: ${err.message} — fail-open\n`)
|
|
495
|
+
return new Map()
|
|
496
|
+
}
|
|
497
|
+
throwIfAborted(signal)
|
|
498
|
+
|
|
499
|
+
const verdicts = new Map()
|
|
500
|
+
for (const line of String(raw ?? '').split('\n')) {
|
|
501
|
+
throwIfAborted(signal)
|
|
502
|
+
const trimmed = line.trim()
|
|
503
|
+
if (!trimmed) continue
|
|
504
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('#') || trimmed.startsWith('```')) continue
|
|
505
|
+
const parts = trimmed.split('|')
|
|
506
|
+
if (parts.length < 2) continue
|
|
507
|
+
const id = Number(parts[0].trim())
|
|
508
|
+
const v = parts[1].trim().toLowerCase()
|
|
509
|
+
if (Number.isFinite(id) && (v === 'keep' || v === 'drop')) verdicts.set(id, v)
|
|
510
|
+
}
|
|
511
|
+
__mixdogMemoryLog(`[cycle2] cascade evaluated=${candidates.length} drops=${[...verdicts.values()].filter(v => v === 'drop').length}\n`)
|
|
512
|
+
return verdicts
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export { NON_ARCHIVE_VERBS, requiredCoreIdForAction }
|