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
|
@@ -30,7 +30,8 @@ const VALID_CAT = new Set([
|
|
|
30
30
|
const SIM_RECALL = 0.65
|
|
31
31
|
const CORE_DEDUP_TOP_K = 5
|
|
32
32
|
|
|
33
|
-
export const CORE_SUMMARY_MAX =
|
|
33
|
+
export const CORE_SUMMARY_MAX = 100
|
|
34
|
+
export const CORE_ELEMENT_MAX = 40
|
|
34
35
|
|
|
35
36
|
function trimOrNull(v) {
|
|
36
37
|
if (v == null) return null
|
|
@@ -204,6 +205,9 @@ export async function addCore(dataDir, { element, summary, category }, projectId
|
|
|
204
205
|
const el = trimOrNull(element)
|
|
205
206
|
const sm = trimOrNull(summary) ?? el
|
|
206
207
|
if (!el || !sm) throw new Error('add requires element and summary')
|
|
208
|
+
if (el.length > CORE_ELEMENT_MAX) {
|
|
209
|
+
throw new Error(`core element too long (${el.length} chars, max ${CORE_ELEMENT_MAX}) — element is a short key/title, not content.`)
|
|
210
|
+
}
|
|
207
211
|
if (sm.length > CORE_SUMMARY_MAX) {
|
|
208
212
|
throw new Error(`core summary too long (${sm.length} chars, max ${CORE_SUMMARY_MAX}) — core memory must be 1 fact in 1-2 sentences; procedures, multi-step, or code belong in recap or docs. Compress and retry.`)
|
|
209
213
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Cycle LLM dispatch adapters, extracted from index.mjs (pass 3).
|
|
2
|
+
//
|
|
3
|
+
// Each cycleN scheduler/handler routes its LLM work through an in-process
|
|
4
|
+
// makeAgentDispatch() adapter (the memory daemon runs the provider registry
|
|
5
|
+
// locally, so the dead IPC agent path is never used). The factory built per
|
|
6
|
+
// cycle is fixed (role/taskType), so it is memoized in a single-slot closure
|
|
7
|
+
// and reshaped to the cycle call signature:
|
|
8
|
+
// callLlm({ role, taskType, mode, preset, timeout, cwd }, userMessage) -> string
|
|
9
|
+
// while makeAgentDispatch's function takes a single { prompt } object. The
|
|
10
|
+
// adapter maps the two — preset/cwd resolution is handled inside
|
|
11
|
+
// makeAgentDispatch via role.
|
|
12
|
+
//
|
|
13
|
+
// Pure factory: index.mjs injects makeAgentDispatch. No import-time side
|
|
14
|
+
// effects, no db/config coupling.
|
|
15
|
+
export function createCycleLlmAdapters({ makeAgentDispatch }) {
|
|
16
|
+
function buildAdapter(agent) {
|
|
17
|
+
let dispatch = null
|
|
18
|
+
return async (opts = {}, userMessage) => {
|
|
19
|
+
if (!dispatch) {
|
|
20
|
+
dispatch = makeAgentDispatch({
|
|
21
|
+
agent,
|
|
22
|
+
taskType: 'maintenance',
|
|
23
|
+
sourceType: 'memory-cycle',
|
|
24
|
+
// The cycle agents parse the full raw line-format response; the
|
|
25
|
+
// agent brief cap (12KB) would truncate a large valid response and
|
|
26
|
+
// append prose, causing partial parsing / omitted / invalid chunks.
|
|
27
|
+
// Opt out so the no-truncation contract is preserved.
|
|
28
|
+
brief: false,
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
// Preserve the cycle timeout contract: the caller derives opts.timeout
|
|
32
|
+
// from config / caller deadline and expects it to bound the call.
|
|
33
|
+
// makeAgentDispatch takes it as a per-call idleTimeoutMs (stale
|
|
34
|
+
// watchdog). Map it through; omit when absent/0 so agent defaults apply.
|
|
35
|
+
const callTimeout = Number(opts?.timeout)
|
|
36
|
+
return dispatch({
|
|
37
|
+
prompt: String(userMessage ?? ''),
|
|
38
|
+
preset: opts?.preset || undefined,
|
|
39
|
+
...(Number.isFinite(callTimeout) && callTimeout > 0 ? { idleTimeoutMs: callTimeout } : {}),
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Callers (cycle-scheduler `callLlm: getCycle1CallLlm()`, index.mjs bench)
|
|
45
|
+
// treat these as FACTORIES: call once to obtain the callLlm adapter.
|
|
46
|
+
// Returning the adapter directly here made `getCycle1CallLlm()` execute an
|
|
47
|
+
// LLM dispatch with no arguments — empty prompt → agent-dispatch throw →
|
|
48
|
+
// unhandled rejection that killed the memory daemon. Keep the factory
|
|
49
|
+
// contract: each getter returns the (memoized) adapter function.
|
|
50
|
+
const cycle1 = buildAdapter('cycle1-agent')
|
|
51
|
+
const cycle2 = buildAdapter('cycle2-agent')
|
|
52
|
+
const cycle3 = buildAdapter('cycle3-agent')
|
|
53
|
+
return {
|
|
54
|
+
getCycle1CallLlm: () => cycle1,
|
|
55
|
+
getCycle2CallLlm: () => cycle2,
|
|
56
|
+
getCycle3CallLlm: () => cycle3,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
// Background cycle scheduling cluster, extracted from index.mjs (pass 3).
|
|
2
|
+
//
|
|
3
|
+
// This owns the mutually-referential cycle machinery that pass-2 flagged as
|
|
4
|
+
// entangled: cycle-health ledger (_cycleHealth), the run-state file, the
|
|
5
|
+
// cycle1 outer coalesce layer (_startCycle1Run/_awaitCycle1Run), the scheduled
|
|
6
|
+
// enqueue/retry paths for cycle1/2/3, checkCycles(), and the self-rescheduling
|
|
7
|
+
// tick loop. index.mjs keeps lifecycle ownership by injecting live getters
|
|
8
|
+
// (getDb/getConfig/setConfig) plus the cycle runners and LLM adapters.
|
|
9
|
+
//
|
|
10
|
+
// Factory contract — everything the extracted functions closed over in the
|
|
11
|
+
// facade is passed in:
|
|
12
|
+
// getDb() -> live db handle (null before _initStore)
|
|
13
|
+
// getConfig() -> live mainConfig
|
|
14
|
+
// setConfig(cfg) -> checkCycles re-reads config each tick (poll-on-use)
|
|
15
|
+
// dataDir -> DATA_DIR
|
|
16
|
+
// log -> __mixdogMemoryLog
|
|
17
|
+
// getCycleLastRun / setCycleLastRun -> meta-backed cycle timestamps
|
|
18
|
+
// readMainConfig / memoryCyclesEnabled -> config-flag helpers
|
|
19
|
+
// getCycle{1,2,3}CallLlm -> in-process LLM adapters
|
|
20
|
+
// runCycle1 / runCycle2 / runCycle3 / parseInterval / flushRawEmbeddings
|
|
21
|
+
// getInFlightCycle1 -> inner cycle1 guard handle (rebuild drain)
|
|
22
|
+
// claimAndMarkScheduledCycle / resolveCoalesceMaxRetries /
|
|
23
|
+
// scheduleCoalescedCycleRetry -> coalesced queue primitives
|
|
24
|
+
// scheduledCycle{1,2,3}Signature -> queue signatures
|
|
25
|
+
// cycleStateFile -> path to memory-cycle-state.json
|
|
26
|
+
import fs from 'node:fs'
|
|
27
|
+
|
|
28
|
+
const CYCLE1_HEALTH_OVERDUE_MS = 5 * 60_000
|
|
29
|
+
const CYCLE1_AUTO_RESTART_COOLDOWN_MS = 5 * 60_000
|
|
30
|
+
const BACKLOG_WARN_COOLDOWN_MS = 10 * 60_000
|
|
31
|
+
const BACKLOG_WARN_PENDING = 500
|
|
32
|
+
const BACKLOG_WARN_FAILURES = 5
|
|
33
|
+
|
|
34
|
+
export function createCycleScheduler(deps) {
|
|
35
|
+
const {
|
|
36
|
+
getDb,
|
|
37
|
+
getConfig,
|
|
38
|
+
setConfig,
|
|
39
|
+
dataDir,
|
|
40
|
+
log = () => {},
|
|
41
|
+
getCycleLastRun,
|
|
42
|
+
setCycleLastRun,
|
|
43
|
+
readMainConfig,
|
|
44
|
+
memoryCyclesEnabled,
|
|
45
|
+
getCycle1CallLlm,
|
|
46
|
+
getCycle2CallLlm,
|
|
47
|
+
getCycle3CallLlm,
|
|
48
|
+
runCycle1,
|
|
49
|
+
runCycle2,
|
|
50
|
+
runCycle3,
|
|
51
|
+
parseInterval,
|
|
52
|
+
flushRawEmbeddings,
|
|
53
|
+
getInFlightCycle1,
|
|
54
|
+
claimAndMarkScheduledCycle,
|
|
55
|
+
resolveCoalesceMaxRetries,
|
|
56
|
+
scheduleCoalescedCycleRetry,
|
|
57
|
+
scheduledCycle1Signature,
|
|
58
|
+
scheduledCycle2Signature,
|
|
59
|
+
scheduledCycle3Signature,
|
|
60
|
+
cycleStateFile,
|
|
61
|
+
} = deps
|
|
62
|
+
|
|
63
|
+
// ── Cycle health state ────────────────────────────────────────────────────
|
|
64
|
+
const _cycleHealth = {
|
|
65
|
+
cycle1: { last_success_at: 0, last_error_at: 0, last_error: null, consecutive_failures: 0 },
|
|
66
|
+
cycle2: { last_success_at: 0, last_error_at: 0, last_error: null, consecutive_failures: 0 },
|
|
67
|
+
cycle3: { last_success_at: 0, last_error_at: 0, last_error: null, consecutive_failures: 0 },
|
|
68
|
+
}
|
|
69
|
+
let _cycleRunning = null // { cycle, started_at }
|
|
70
|
+
let _cycleBacklogSnapshot = { unchunked: 0, cycle2_pending: 0, at: 0 }
|
|
71
|
+
let _lastBacklogWarnAt = 0
|
|
72
|
+
|
|
73
|
+
// ── Cycle1 outer coalesce layer + tick loop state ─────────────────────────
|
|
74
|
+
let _cycle1InFlight = null
|
|
75
|
+
let _cycle2InFlight = false
|
|
76
|
+
let _cycle3InFlight = false
|
|
77
|
+
let _rawEmbedFlushInFlight = false
|
|
78
|
+
let _checkCyclesInFlight = false
|
|
79
|
+
let _cyclesActive = false
|
|
80
|
+
let _cycleInterval = null
|
|
81
|
+
let _startupTimeout = null
|
|
82
|
+
|
|
83
|
+
function _writeCycleStateFile() {
|
|
84
|
+
try {
|
|
85
|
+
fs.writeFileSync(cycleStateFile, JSON.stringify({
|
|
86
|
+
running: _cycleRunning,
|
|
87
|
+
backlog: _cycleBacklogSnapshot,
|
|
88
|
+
cycles: _cycleHealth,
|
|
89
|
+
updatedAt: Date.now(),
|
|
90
|
+
}))
|
|
91
|
+
} catch { /* best-effort; statusline just shows nothing */ }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function markCycleRunning(cycle) {
|
|
95
|
+
_cycleRunning = { cycle, started_at: Date.now() }
|
|
96
|
+
_writeCycleStateFile()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function markCycleDone(cycle, ok, err = null) {
|
|
100
|
+
const h = _cycleHealth[cycle]
|
|
101
|
+
if (h) {
|
|
102
|
+
const now = Date.now()
|
|
103
|
+
if (ok) { h.last_success_at = now; h.consecutive_failures = 0; h.last_error = null }
|
|
104
|
+
else { h.last_error_at = now; h.consecutive_failures += 1; h.last_error = String(err || 'unknown').slice(0, 200) }
|
|
105
|
+
if (!ok && h.consecutive_failures >= BACKLOG_WARN_FAILURES) {
|
|
106
|
+
_warnCycleHealth(`${cycle} failing repeatedly (consecutive=${h.consecutive_failures}, last="${h.last_error}")`)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (_cycleRunning?.cycle === cycle) _cycleRunning = null
|
|
110
|
+
_writeCycleStateFile()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _warnCycleHealth(msg) {
|
|
114
|
+
const now = Date.now()
|
|
115
|
+
if (now - _lastBacklogWarnAt < BACKLOG_WARN_COOLDOWN_MS) return
|
|
116
|
+
_lastBacklogWarnAt = now
|
|
117
|
+
log(`[cycle-health] WARN ${msg}\n`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function recordCycle1Result(result) {
|
|
121
|
+
const now = Date.now()
|
|
122
|
+
await setCycleLastRun('cycle1_heartbeat', now)
|
|
123
|
+
const skipped = result?.skippedInFlight === true
|
|
124
|
+
const coalescedNoop = result?.coalescedRetryNoop === true
|
|
125
|
+
const allFailed = !skipped
|
|
126
|
+
&& Number(result?.chunks ?? 0) === 0
|
|
127
|
+
&& Number(result?.processed ?? 0) === 0
|
|
128
|
+
&& Number(result?.skipped ?? 0) > 0
|
|
129
|
+
if (!skipped && !coalescedNoop && !allFailed) {
|
|
130
|
+
await setCycleLastRun('cycle1', now)
|
|
131
|
+
}
|
|
132
|
+
if (!skipped && !coalescedNoop) markCycleDone('cycle1', !allFailed, allFailed ? 'all rows skipped' : null)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function _startCycle1Run(config = {}, options = {}) {
|
|
136
|
+
if (typeof options?.callLlm !== 'function') {
|
|
137
|
+
options = { ...options, callLlm: getCycle1CallLlm() }
|
|
138
|
+
}
|
|
139
|
+
markCycleRunning('cycle1')
|
|
140
|
+
_cycle1InFlight = (async () => {
|
|
141
|
+
try {
|
|
142
|
+
const result = await runCycle1(getDb(), config, options, dataDir)
|
|
143
|
+
if (typeof options?.onCoalescedSuccess !== 'function') {
|
|
144
|
+
await recordCycle1Result(result)
|
|
145
|
+
}
|
|
146
|
+
return result
|
|
147
|
+
} catch (err) {
|
|
148
|
+
markCycleDone('cycle1', false, err?.message || err)
|
|
149
|
+
throw err
|
|
150
|
+
} finally {
|
|
151
|
+
if (_cycleRunning?.cycle === 'cycle1') { _cycleRunning = null; _writeCycleStateFile() }
|
|
152
|
+
if (_cycle1InFlight === promise) _cycle1InFlight = null
|
|
153
|
+
}
|
|
154
|
+
})()
|
|
155
|
+
const promise = _cycle1InFlight
|
|
156
|
+
return _cycle1InFlight
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function _awaitCycle1Run(config = {}, options = {}) {
|
|
160
|
+
const target = _cycle1InFlight || _startCycle1Run(config, options)
|
|
161
|
+
const callerDeadlineMs = Number(options.callerDeadlineMs) || 0
|
|
162
|
+
if (callerDeadlineMs <= 0) return await target
|
|
163
|
+
let timer
|
|
164
|
+
const deadlinePromise = new Promise((resolve) => {
|
|
165
|
+
timer = setTimeout(() => {
|
|
166
|
+
resolve({
|
|
167
|
+
processed: 0,
|
|
168
|
+
chunks: 0,
|
|
169
|
+
skipped: 0,
|
|
170
|
+
sessions: 0,
|
|
171
|
+
skippedInFlight: true,
|
|
172
|
+
timedOutWaiting: true,
|
|
173
|
+
callerDeadlineMs,
|
|
174
|
+
})
|
|
175
|
+
}, callerDeadlineMs)
|
|
176
|
+
})
|
|
177
|
+
try {
|
|
178
|
+
return await Promise.race([target, deadlinePromise])
|
|
179
|
+
} finally {
|
|
180
|
+
clearTimeout(timer)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function periodicCycle1Config() {
|
|
185
|
+
return {
|
|
186
|
+
min_batch: 20,
|
|
187
|
+
session_cap: 2,
|
|
188
|
+
batch_size: 50,
|
|
189
|
+
concurrency: 2,
|
|
190
|
+
...(getConfig()?.cycle1 || {}),
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function enqueueScheduledCycle(kind, intervalMs, signature) {
|
|
195
|
+
const claim = await claimAndMarkScheduledCycle(getDb(), kind, intervalMs, signature, { reason: 'scheduled' })
|
|
196
|
+
return claim.claimed === true
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function enqueueScheduledCycle1(intervalMs, _reason = 'scheduled') {
|
|
200
|
+
const config = periodicCycle1Config()
|
|
201
|
+
const signature = scheduledCycle1Signature(config)
|
|
202
|
+
if (await enqueueScheduledCycle('cycle1', intervalMs, signature)) {
|
|
203
|
+
scheduleScheduledCycle1(config, signature)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function enqueueScheduledCycle2(intervalMs, _reason = 'scheduled') {
|
|
208
|
+
const config = getConfig()?.cycle2 || {}
|
|
209
|
+
const signature = scheduledCycle2Signature(config)
|
|
210
|
+
if (await enqueueScheduledCycle('cycle2', intervalMs, signature)) {
|
|
211
|
+
scheduleScheduledCycle2(config, signature)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function enqueueScheduledCycle3(intervalMs, _reason = 'scheduled') {
|
|
216
|
+
const config = getConfig() || {}
|
|
217
|
+
const signature = scheduledCycle3Signature(config)
|
|
218
|
+
if (await enqueueScheduledCycle('cycle3', intervalMs, signature)) {
|
|
219
|
+
scheduleScheduledCycle3(config, signature)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function scheduleScheduledCycle1(config, signature, attempt = 0) {
|
|
224
|
+
const maxRetries = resolveCoalesceMaxRetries(config, 3)
|
|
225
|
+
if (attempt > maxRetries) {
|
|
226
|
+
log('[cycle1] scheduled queue retry cap reached\n')
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
scheduleCoalescedCycleRetry(getDb(), 'cycle1', async () => {
|
|
230
|
+
if (!memoryCyclesEnabled()) return
|
|
231
|
+
if (_cycle1InFlight) {
|
|
232
|
+
scheduleScheduledCycle1(config, signature, attempt + 1)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
const result = await _awaitCycle1Run(config, {
|
|
236
|
+
coalescedRetry: true,
|
|
237
|
+
onCoalescedSuccess: recordCycle1Result,
|
|
238
|
+
})
|
|
239
|
+
if (result?.skippedInFlight) scheduleScheduledCycle1(config, signature, attempt + 1)
|
|
240
|
+
}, config, signature)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function scheduleScheduledCycle2(config, signature, attempt = 0) {
|
|
244
|
+
const maxRetries = resolveCoalesceMaxRetries(config, 3)
|
|
245
|
+
if (attempt > maxRetries) {
|
|
246
|
+
log('[cycle2] scheduled queue retry cap reached\n')
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
scheduleCoalescedCycleRetry(getDb(), 'cycle2', async () => {
|
|
250
|
+
if (!memoryCyclesEnabled()) return
|
|
251
|
+
if (_cycle2InFlight) {
|
|
252
|
+
scheduleScheduledCycle2(config, signature, attempt + 1)
|
|
253
|
+
return
|
|
254
|
+
}
|
|
255
|
+
_cycle2InFlight = true
|
|
256
|
+
markCycleRunning('cycle2')
|
|
257
|
+
try {
|
|
258
|
+
let c2Options = {
|
|
259
|
+
coalescedRetry: true,
|
|
260
|
+
onCoalescedSuccess: _finalizeCycle2Run,
|
|
261
|
+
}
|
|
262
|
+
if (typeof c2Options?.callLlm !== 'function') {
|
|
263
|
+
c2Options = { ...c2Options, callLlm: getCycle2CallLlm() }
|
|
264
|
+
}
|
|
265
|
+
const result = await runCycle2(getDb(), config, c2Options, dataDir)
|
|
266
|
+
if (result?.skippedInFlight) {
|
|
267
|
+
scheduleScheduledCycle2(config, signature, attempt + 1)
|
|
268
|
+
} else if (result?.coalescedRetryNoop) {
|
|
269
|
+
log('[cycle2] scheduled queue noop\n')
|
|
270
|
+
} else if (result?.ok === false) {
|
|
271
|
+
await _finalizeCycle2Run(result)
|
|
272
|
+
}
|
|
273
|
+
} catch (err) {
|
|
274
|
+
log(`[cycle2] scheduled queue failed: ${err?.message || err}\n`)
|
|
275
|
+
markCycleDone('cycle2', false, err?.message || err)
|
|
276
|
+
} finally {
|
|
277
|
+
_cycle2InFlight = false
|
|
278
|
+
if (_cycleRunning?.cycle === 'cycle2') { _cycleRunning = null; _writeCycleStateFile() }
|
|
279
|
+
}
|
|
280
|
+
}, config, signature)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function scheduleScheduledCycle3(config, signature, attempt = 0) {
|
|
284
|
+
const retryConfig = config?.cycle3 || config
|
|
285
|
+
const maxRetries = resolveCoalesceMaxRetries(retryConfig, 3)
|
|
286
|
+
if (attempt > maxRetries) {
|
|
287
|
+
log('[cycle3] scheduled queue retry cap reached\n')
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
scheduleCoalescedCycleRetry(getDb(), 'cycle3', async () => {
|
|
291
|
+
if (!memoryCyclesEnabled()) return
|
|
292
|
+
if (_cycle3InFlight) {
|
|
293
|
+
scheduleScheduledCycle3(config, signature, attempt + 1)
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
_cycle3InFlight = true
|
|
297
|
+
markCycleRunning('cycle3')
|
|
298
|
+
try {
|
|
299
|
+
let c3Options = {
|
|
300
|
+
coalescedRetry: true,
|
|
301
|
+
onCoalescedSuccess: async () => { await setCycleLastRun('cycle3', Date.now()); markCycleDone('cycle3', true) },
|
|
302
|
+
}
|
|
303
|
+
if (typeof c3Options?.callLlm !== 'function') {
|
|
304
|
+
c3Options = { ...c3Options, callLlm: getCycle3CallLlm() }
|
|
305
|
+
}
|
|
306
|
+
const result = await runCycle3(getDb(), config, dataDir, c3Options)
|
|
307
|
+
if (result?.skippedInFlight) {
|
|
308
|
+
scheduleScheduledCycle3(config, signature, attempt + 1)
|
|
309
|
+
} else if (result?.coalescedRetryNoop) {
|
|
310
|
+
log('[cycle3] scheduled queue noop\n')
|
|
311
|
+
}
|
|
312
|
+
} catch (err) {
|
|
313
|
+
log(`[cycle3] scheduled queue failed: ${err?.message || err}\n`)
|
|
314
|
+
markCycleDone('cycle3', false, err?.message || err)
|
|
315
|
+
} finally {
|
|
316
|
+
_cycle3InFlight = false
|
|
317
|
+
if (_cycleRunning?.cycle === 'cycle3') { _cycleRunning = null; _writeCycleStateFile() }
|
|
318
|
+
}
|
|
319
|
+
}, retryConfig, signature)
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function _finalizeCycle2Run(result) {
|
|
323
|
+
if (result?.skippedInFlight) {
|
|
324
|
+
log('[cycle2] skipped: in flight\n')
|
|
325
|
+
return
|
|
326
|
+
}
|
|
327
|
+
if (result.ok) {
|
|
328
|
+
await setCycleLastRun('cycle2', Date.now())
|
|
329
|
+
await setCycleLastRun('cycle2_last_error', '')
|
|
330
|
+
log('[cycle2] completed\n')
|
|
331
|
+
markCycleDone('cycle2', true)
|
|
332
|
+
} else {
|
|
333
|
+
await setCycleLastRun('cycle2_last_error', result.error || 'unknown error')
|
|
334
|
+
log(`[cycle2] failed: ${result.error}\n`)
|
|
335
|
+
markCycleDone('cycle2', false, result.error || 'unknown error')
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
async function checkCycles() {
|
|
340
|
+
const mainConfig = readMainConfig()
|
|
341
|
+
setConfig(mainConfig)
|
|
342
|
+
const cyclesOn = memoryCyclesEnabled()
|
|
343
|
+
const db = getDb()
|
|
344
|
+
|
|
345
|
+
const cycle1Ms = parseInterval(mainConfig?.cycle1?.interval || '10m')
|
|
346
|
+
const cycle2Ms = parseInterval(mainConfig?.cycle2?.interval || '1h')
|
|
347
|
+
const cycle3Ms = parseInterval(mainConfig?.cycle3?.interval || '24h')
|
|
348
|
+
|
|
349
|
+
const now = Date.now()
|
|
350
|
+
const last = await getCycleLastRun()
|
|
351
|
+
|
|
352
|
+
if (cyclesOn) {
|
|
353
|
+
const cycle1OverdueMs = last.cycle1 > 0
|
|
354
|
+
? Math.max(0, now - last.cycle1 - cycle1Ms)
|
|
355
|
+
: 0
|
|
356
|
+
if (cycle1OverdueMs > CYCLE1_HEALTH_OVERDUE_MS) {
|
|
357
|
+
const lastSeen = last.cycle1 ? new Date(last.cycle1).toISOString() : 'never'
|
|
358
|
+
log(
|
|
359
|
+
`[cycle1] overdue by ${Math.floor(cycle1OverdueMs / 60_000)}min `
|
|
360
|
+
+ `(last=${lastSeen}). Pool B Anthropic shard may be cold.\n`
|
|
361
|
+
)
|
|
362
|
+
const lastAutoRestart = last.cycle1_autoRestart || 0
|
|
363
|
+
if (now - lastAutoRestart >= CYCLE1_AUTO_RESTART_COOLDOWN_MS) {
|
|
364
|
+
await setCycleLastRun('cycle1_autoRestart_attempt', now)
|
|
365
|
+
try {
|
|
366
|
+
const result = await _awaitCycle1Run(periodicCycle1Config())
|
|
367
|
+
await setCycleLastRun('cycle1_autoRestart', Date.now())
|
|
368
|
+
log(
|
|
369
|
+
`[cycle1] auto-restart completed chunks=${result?.chunks ?? 0} processed=${result?.processed ?? 0}\n`
|
|
370
|
+
)
|
|
371
|
+
return
|
|
372
|
+
} catch (e) {
|
|
373
|
+
log(`[cycle1] auto-restart error: ${e.message}\n`)
|
|
374
|
+
return
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (now - last.cycle1 >= cycle1Ms) {
|
|
380
|
+
await enqueueScheduledCycle1(cycle1Ms, 'scheduled')
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (now - last.cycle2 >= cycle2Ms) {
|
|
384
|
+
await enqueueScheduledCycle2(cycle2Ms, 'scheduled')
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (now - last.cycle3 >= cycle3Ms) {
|
|
388
|
+
await enqueueScheduledCycle3(cycle3Ms, 'scheduled')
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const unchunked = Number((await db.query(
|
|
394
|
+
`SELECT COUNT(*) c FROM entries WHERE chunk_root IS NULL AND NULLIF(btrim(session_id), '') IS NOT NULL`,
|
|
395
|
+
)).rows[0]?.c ?? 0)
|
|
396
|
+
const cycle2Pending = Number((await db.query(
|
|
397
|
+
`SELECT COUNT(*) c FROM entries WHERE is_root = 1 AND status = 'pending'`,
|
|
398
|
+
)).rows[0]?.c ?? 0)
|
|
399
|
+
_cycleBacklogSnapshot = { unchunked, cycle2_pending: cycle2Pending, at: now }
|
|
400
|
+
_writeCycleStateFile()
|
|
401
|
+
if (unchunked > BACKLOG_WARN_PENDING || cycle2Pending > BACKLOG_WARN_PENDING) {
|
|
402
|
+
_warnCycleHealth(`backlog unchunked=${unchunked} cycle2_pending=${cycle2Pending}`)
|
|
403
|
+
}
|
|
404
|
+
if (unchunked > 0 && !_rawEmbedFlushInFlight) {
|
|
405
|
+
_rawEmbedFlushInFlight = true
|
|
406
|
+
flushRawEmbeddings(db, { limit: 200 })
|
|
407
|
+
.then((r) => {
|
|
408
|
+
if (r.attempted > 0) log(`[embed] raw fallback flush attempted=${r.attempted} embedded=${r.embedded}\n`)
|
|
409
|
+
})
|
|
410
|
+
.catch((err) => log(`[embed] raw fallback flush failed: ${err?.message || err}\n`))
|
|
411
|
+
.finally(() => { _rawEmbedFlushInFlight = false })
|
|
412
|
+
}
|
|
413
|
+
const SELF_KICK_STALE_MS = 2 * 3600_000
|
|
414
|
+
const c1Stale = (_cycleHealth.cycle1.last_success_at || last.cycle1 || 0) < now - SELF_KICK_STALE_MS
|
|
415
|
+
const c2Stale = (_cycleHealth.cycle2.last_success_at || last.cycle2 || 0) < now - SELF_KICK_STALE_MS
|
|
416
|
+
if (cyclesOn && c1Stale && unchunked > BACKLOG_WARN_PENDING) {
|
|
417
|
+
_warnCycleHealth(`cycle1 self-kick: no success 2h+, unchunked=${unchunked}`)
|
|
418
|
+
await enqueueScheduledCycle1(0, 'self-kick')
|
|
419
|
+
} else if (cyclesOn && c2Stale && cycle2Pending > BACKLOG_WARN_PENDING) {
|
|
420
|
+
_warnCycleHealth(`cycle2 self-kick: no success 2h+, pending=${cycle2Pending}`)
|
|
421
|
+
await enqueueScheduledCycle2(0, 'self-kick')
|
|
422
|
+
}
|
|
423
|
+
} catch { /* counts are best-effort; never fail the tick */ }
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function _runCheckCyclesGuarded() {
|
|
427
|
+
if (_checkCyclesInFlight) return
|
|
428
|
+
_checkCyclesInFlight = true
|
|
429
|
+
try { await checkCycles() }
|
|
430
|
+
catch (e) { log(`[cycle-tick] error: ${e.message}\n`) }
|
|
431
|
+
finally { _checkCyclesInFlight = false }
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function _scheduleNextCheck() {
|
|
435
|
+
_cycleInterval = setTimeout(async () => {
|
|
436
|
+
_cycleInterval = null
|
|
437
|
+
try {
|
|
438
|
+
await _runCheckCyclesGuarded()
|
|
439
|
+
} catch (e) {
|
|
440
|
+
log(`[cycle-tick] re-arm guard caught: ${e?.message || e}\n`)
|
|
441
|
+
} finally {
|
|
442
|
+
if (_cyclesActive) _scheduleNextCheck()
|
|
443
|
+
}
|
|
444
|
+
}, 60_000)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function startCycles() {
|
|
448
|
+
if (_cyclesActive) return
|
|
449
|
+
_cyclesActive = true
|
|
450
|
+
// Boot reset: a previous daemon that crashed mid-run leaves the state
|
|
451
|
+
// file's `running` marker set, and the statusline keeps showing a
|
|
452
|
+
// phantom "Memory cycle running" spinner until its 10-minute stale
|
|
453
|
+
// guard kicks in. No cycle can be running when this scheduler starts,
|
|
454
|
+
// so clear the marker (health/backlog reset to this process's state).
|
|
455
|
+
_cycleRunning = null
|
|
456
|
+
_writeCycleStateFile()
|
|
457
|
+
_scheduleNextCheck()
|
|
458
|
+
_startupTimeout = setTimeout(() => { void _runCheckCyclesGuarded() }, 30_000)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function stopCycles() {
|
|
462
|
+
_cyclesActive = false
|
|
463
|
+
if (_cycleInterval) { clearTimeout(_cycleInterval); _cycleInterval = null }
|
|
464
|
+
if (_startupTimeout) { clearTimeout(_startupTimeout); _startupTimeout = null }
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Full-shutdown reset — baseline stop() cleared these module-level flags so
|
|
468
|
+
// a later init() starts from a clean slate instead of coalescing onto (or
|
|
469
|
+
// skipping behind) pre-stop in-flight work / stale running state.
|
|
470
|
+
function resetInFlight() {
|
|
471
|
+
_cycle1InFlight = null
|
|
472
|
+
_cycle2InFlight = false
|
|
473
|
+
_cycle3InFlight = false
|
|
474
|
+
_checkCyclesInFlight = false
|
|
475
|
+
_rawEmbedFlushInFlight = false
|
|
476
|
+
_cycleRunning = null
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
// health/state accessors (HTTP /health, statusline)
|
|
481
|
+
getCycleHealth: () => _cycleHealth,
|
|
482
|
+
getCycleRunning: () => _cycleRunning,
|
|
483
|
+
getCycleBacklogSnapshot: () => _cycleBacklogSnapshot,
|
|
484
|
+
// cycle1 in-flight handle (rebuild drain in index.mjs)
|
|
485
|
+
getCycle1InFlight: () => _cycle1InFlight,
|
|
486
|
+
// run primitives used by MCP action handlers
|
|
487
|
+
startCycle1Run: _startCycle1Run,
|
|
488
|
+
awaitCycle1Run: _awaitCycle1Run,
|
|
489
|
+
finalizeCycle2Run: _finalizeCycle2Run,
|
|
490
|
+
periodicCycle1Config,
|
|
491
|
+
// lifecycle
|
|
492
|
+
startCycles,
|
|
493
|
+
stopCycles,
|
|
494
|
+
resetInFlight,
|
|
495
|
+
checkCycles,
|
|
496
|
+
}
|
|
497
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Boot-edge embedding warmup queue, extracted from index.mjs (pass 2).
|
|
2
|
+
//
|
|
3
|
+
// ONNX session creation on the embedding worker thread is CPU-heavy, so it must
|
|
4
|
+
// not overlap the worker's own init (DB open, schema, cycle wiring). The warmup
|
|
5
|
+
// is queued during _initStore and fired at the _initRuntime completion edge, so
|
|
6
|
+
// it starts the instant boot's CPU-heavy work is done — no magic-number delay.
|
|
7
|
+
//
|
|
8
|
+
// This is a small pure factory: index.mjs owns the DATA_DIR-derived meta path
|
|
9
|
+
// and the embedding-provider imports, injecting them here as callbacks. No
|
|
10
|
+
// import-time side effects.
|
|
11
|
+
export function createEmbeddingWarmup({
|
|
12
|
+
canStart,
|
|
13
|
+
warmup,
|
|
14
|
+
getDims,
|
|
15
|
+
persistMeta,
|
|
16
|
+
log = () => {},
|
|
17
|
+
}) {
|
|
18
|
+
let _pending = null
|
|
19
|
+
|
|
20
|
+
// Queue the warmup; fireDeferred() runs it once boot completes. Re-queuing
|
|
21
|
+
// overwrites any prior pending closure (matches the original single-slot
|
|
22
|
+
// module var). MIXDOG_EMBED_WARMUP=0 (checked via canStart) disables it.
|
|
23
|
+
function schedule(metaPath, metaKey) {
|
|
24
|
+
if (!canStart()) {
|
|
25
|
+
// Silent skip here previously made "warmup never ran" boots
|
|
26
|
+
// indistinguishable from failed warmups. One line, boot-edge only.
|
|
27
|
+
log('[memory-service] embedding warmup skipped (disabled by env/secondary mode) — recall stays lexical until first embed\n')
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
_pending = () => {
|
|
31
|
+
warmup()
|
|
32
|
+
.then(() => {
|
|
33
|
+
const measured = Number(getDims())
|
|
34
|
+
try {
|
|
35
|
+
persistMeta(metaPath, { ...metaKey, dims: measured })
|
|
36
|
+
} catch (e) {
|
|
37
|
+
log(`[memory-service] could not persist embedding-meta: ${e?.message || e}\n`)
|
|
38
|
+
}
|
|
39
|
+
})
|
|
40
|
+
.catch(err => {
|
|
41
|
+
log(`[memory-service] background warmup failed: ${err?.message || err}\n`)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fireDeferred() {
|
|
47
|
+
const fire = _pending
|
|
48
|
+
if (!fire) return
|
|
49
|
+
_pending = null
|
|
50
|
+
fire()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function reset() {
|
|
54
|
+
_pending = null
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { schedule, fireDeferred, reset }
|
|
58
|
+
}
|