mixdog 0.9.3 → 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 +7 -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/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/freevar-smoke.mjs +95 -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/provider-toolcall-test.mjs +7 -3
- 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 +23 -63
- 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 +6 -5
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/worker/AGENT.md +8 -4
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +632 -2042
- 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/rules/agent/00-common.md +2 -0
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +0 -11
- 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 +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -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 +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +297 -2123
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +130 -1002
- 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/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 +182 -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 +251 -397
- 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/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 +32 -41
- 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 +12 -292
- package/src/runtime/channels/index.mjs +229 -663
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -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/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/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 +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/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +201 -1948
- 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/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 +22 -2
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +780 -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/launcher-control.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 +84 -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 +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/settings-api.mjs +319 -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/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 +110 -671
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +30 -9
- 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 +945 -8094
- 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 +23 -101
- 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 +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 +19652 -18630
- 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 +122 -562
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- 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 +67 -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 +1 -1
- package/src/workflows/sequential/WORKFLOW.md +1 -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/external-tool-adapters.test.mjs +0 -162
- 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/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
|
@@ -213,6 +213,155 @@ export async function flushEmbeddingDirty(db, options = {}) {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
// Embeds raw transcript rows (is_root = 0, non-chunk-member) so the dense
|
|
217
|
+
// recall leg can rank them alongside root/summary entries. Mirrors
|
|
218
|
+
// flushEmbeddingDirty's SKIP LOCKED batch-claim structure but sources the
|
|
219
|
+
// embed text from `content` (raw rows have no element/summary) and honors
|
|
220
|
+
// `limit` as a total cap across the whole call rather than an unbounded loop.
|
|
221
|
+
//
|
|
222
|
+
// Stale-embedding safety when recap toggles back ON: cycle1's promotion
|
|
223
|
+
// UPDATE (memory-cycle1.mjs chunk commit) SETs summary/element, which fires
|
|
224
|
+
// trg_entries_embedding_invalidate (memory.mjs — BEFORE UPDATE OF content,
|
|
225
|
+
// summary, element; NEW.is_root=1 AND summary IS DISTINCT FROM OLD.summary)
|
|
226
|
+
// and NULLs the raw-content embedding written here; cycle1's end-of-cycle
|
|
227
|
+
// flushEmbeddingDirty (is_root=1 AND embedding IS NULL) then re-embeds the
|
|
228
|
+
// root from its fresh element/summary. Member leaves keep their content
|
|
229
|
+
// embedding — the dense recall leg resolves members to their chunk root
|
|
230
|
+
// (memory-recall-store.mjs member→root resolution), so that is leaf-level
|
|
231
|
+
// dense matching, not staleness.
|
|
232
|
+
export async function flushRawEmbeddings(db, options = {}) {
|
|
233
|
+
const { limit = 200, signal } = options ?? {}
|
|
234
|
+
throwIfAborted(signal)
|
|
235
|
+
await ensureEmbCacheTable(db)
|
|
236
|
+
throwIfAborted(signal)
|
|
237
|
+
|
|
238
|
+
let attempted = 0
|
|
239
|
+
let embedded = 0
|
|
240
|
+
let cursor = 0
|
|
241
|
+
|
|
242
|
+
while (attempted < limit) {
|
|
243
|
+
throwIfAborted(signal)
|
|
244
|
+
const batchCap = Math.min(BATCH_SIZE, limit - attempted)
|
|
245
|
+
const client = await db._pool.connect()
|
|
246
|
+
let ids
|
|
247
|
+
try {
|
|
248
|
+
throwIfAborted(signal)
|
|
249
|
+
await client.query('BEGIN')
|
|
250
|
+
const res = await client.query(
|
|
251
|
+
`SELECT id FROM memory.entries
|
|
252
|
+
WHERE is_root = 0 AND chunk_root IS NULL AND embedding IS NULL
|
|
253
|
+
AND NULLIF(btrim(session_id), '') IS NOT NULL
|
|
254
|
+
AND content IS NOT NULL
|
|
255
|
+
AND id > $2
|
|
256
|
+
ORDER BY id
|
|
257
|
+
LIMIT $1
|
|
258
|
+
FOR UPDATE SKIP LOCKED`,
|
|
259
|
+
[batchCap, cursor],
|
|
260
|
+
)
|
|
261
|
+
ids = res.rows.map(r => Number(r.id))
|
|
262
|
+
throwIfAborted(signal)
|
|
263
|
+
} catch (err) {
|
|
264
|
+
try { await client.query('ROLLBACK') } catch {}
|
|
265
|
+
client.release()
|
|
266
|
+
if (signal?.aborted) throw signal.reason ?? err
|
|
267
|
+
__mixdogMemoryLog(`[embed] raw flush SKIP LOCKED claim failed: ${err.message}\n`)
|
|
268
|
+
break
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (ids.length === 0) {
|
|
272
|
+
try { await client.query('COMMIT') } catch {}
|
|
273
|
+
client.release()
|
|
274
|
+
break
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
cursor = ids[ids.length - 1]
|
|
278
|
+
attempted += ids.length
|
|
279
|
+
let batchDone = false
|
|
280
|
+
try {
|
|
281
|
+
throwIfAborted(signal)
|
|
282
|
+
const writtenIds = await syncRawBatchEmbeddings(client, ids, { signal })
|
|
283
|
+
embedded += writtenIds.length
|
|
284
|
+
batchDone = true
|
|
285
|
+
} catch (err) {
|
|
286
|
+
if (signal?.aborted) throw signal.reason ?? err
|
|
287
|
+
__mixdogMemoryLog(`[embed] raw batch failed (ids=${ids[0]}..${ids[ids.length-1]}): ${err.message}\n`)
|
|
288
|
+
} finally {
|
|
289
|
+
try {
|
|
290
|
+
if (batchDone) await client.query('COMMIT')
|
|
291
|
+
else await client.query('ROLLBACK')
|
|
292
|
+
} catch {}
|
|
293
|
+
client.release()
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (ids.length < batchCap) break
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
return { attempted, embedded }
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Batch-embed raw transcript rows: content is the embed source text (raw
|
|
303
|
+
// rows have no element/summary). Mirrors syncBatchEmbeddings' shape.
|
|
304
|
+
async function syncRawBatchEmbeddings(db, ids, options = {}) {
|
|
305
|
+
const signal = options?.signal
|
|
306
|
+
throwIfAborted(signal)
|
|
307
|
+
const rows = (await db.query(
|
|
308
|
+
`SELECT id, content FROM memory.entries
|
|
309
|
+
WHERE id = ANY($1::bigint[]) AND is_root = 0 AND chunk_root IS NULL`,
|
|
310
|
+
[ids],
|
|
311
|
+
)).rows
|
|
312
|
+
throwIfAborted(signal)
|
|
313
|
+
if (rows.length === 0) return []
|
|
314
|
+
|
|
315
|
+
const dimsRow = (await db.query(`SELECT value FROM memory.meta WHERE key = 'embedding.current_dims'`)).rows[0]
|
|
316
|
+
throwIfAborted(signal)
|
|
317
|
+
const expected = Number(dimsRow?.value ?? 0)
|
|
318
|
+
|
|
319
|
+
const texts = rows.map(row => (typeof row.content === 'string' ? row.content.trim() : ''))
|
|
320
|
+
const vectors = await cachedEmbedTextBatch(db, texts.filter(Boolean), { signal })
|
|
321
|
+
throwIfAborted(signal)
|
|
322
|
+
let vecIdx = 0
|
|
323
|
+
const updates = [] // { id, content, vector }
|
|
324
|
+
for (let i = 0; i < rows.length; i++) {
|
|
325
|
+
throwIfAborted(signal)
|
|
326
|
+
const row = rows[i]
|
|
327
|
+
if (!texts[i]) continue
|
|
328
|
+
const vector = vectors[vecIdx++]
|
|
329
|
+
if (!Array.isArray(vector) || vector.length === 0) continue
|
|
330
|
+
if (Number.isFinite(expected) && expected > 0 && vector.length !== expected) {
|
|
331
|
+
__mixdogMemoryLog(`[embed] raw dim mismatch (id=${row.id} got=${vector.length} expected=${expected})\n`)
|
|
332
|
+
continue
|
|
333
|
+
}
|
|
334
|
+
updates.push({ id: Number(row.id), content: row.content, vector })
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (updates.length === 0) return []
|
|
338
|
+
|
|
339
|
+
const valClauses = updates.map((u, i) => {
|
|
340
|
+
const base = i * 3
|
|
341
|
+
return `($${base+1}::bigint, $${base+2}::halfvec, $${base+3}::text)`
|
|
342
|
+
})
|
|
343
|
+
const params = []
|
|
344
|
+
for (const u of updates) {
|
|
345
|
+
params.push(u.id, embeddingToSql(u.vector), u.content)
|
|
346
|
+
}
|
|
347
|
+
const res = await db.query(
|
|
348
|
+
`UPDATE memory.entries AS e
|
|
349
|
+
SET embedding = t.vector
|
|
350
|
+
FROM (VALUES ${valClauses.join(',')}) AS t(id, vector, content)
|
|
351
|
+
WHERE e.id = t.id AND e.is_root = 0 AND e.chunk_root IS NULL
|
|
352
|
+
AND e.embedding IS NULL
|
|
353
|
+
AND e.content IS NOT DISTINCT FROM t.content
|
|
354
|
+
RETURNING e.id`,
|
|
355
|
+
params,
|
|
356
|
+
)
|
|
357
|
+
throwIfAborted(signal)
|
|
358
|
+
const writtenIds = (res.rows || []).map(r => Number(r.id))
|
|
359
|
+
if (writtenIds.length < updates.length) {
|
|
360
|
+
__mixdogMemoryLog(`[embed-sync] ${updates.length - writtenIds.length} raw stale-write(s) skipped — content changed since read\n`)
|
|
361
|
+
}
|
|
362
|
+
return writtenIds
|
|
363
|
+
}
|
|
364
|
+
|
|
216
365
|
// Batch-embed all ids in one DB round-trip for fetching, one API call per unique text,
|
|
217
366
|
// then one VALUES UPDATE for all results.
|
|
218
367
|
// Returns the list of ids that were actually UPDATEd with a fresh embedding.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Extracted from index.mjs — single/multi-instance process-lock + owner-election
|
|
2
|
+
// helpers. Pure functions parameterized on lock-file paths so index.mjs retains
|
|
3
|
+
// ownership of DATA_DIR-derived constants and the daemon logger.
|
|
4
|
+
import fs from 'node:fs'
|
|
5
|
+
import { execFileSync } from 'node:child_process'
|
|
6
|
+
|
|
7
|
+
export function parsePositivePid(value) {
|
|
8
|
+
const pid = Number(value)
|
|
9
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isPidAliveLocal(pid) {
|
|
13
|
+
if (!Number.isFinite(pid) || pid <= 0) return false
|
|
14
|
+
try { process.kill(pid, 0); return true }
|
|
15
|
+
catch (e) { return e.code !== 'ESRCH' }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function tryAcquireMemoryOwnerLock(ownerLockFile, log = () => {}) {
|
|
19
|
+
// Returns true on success (this process now owns memory worker for the data
|
|
20
|
+
// dir), false when a live peer holds the lock. Stale locks (dead PID) are
|
|
21
|
+
// unlinked and retried atomically. Throws on unexpected fs errors so callers
|
|
22
|
+
// surface lock-system corruption rather than silently downgrading.
|
|
23
|
+
//
|
|
24
|
+
// EPERM/EBUSY/EACCES at openSync are transient — AV scanners (SignKorea /
|
|
25
|
+
// SKCert / ezPDFWS etc) briefly lock newly-created files during inspection.
|
|
26
|
+
// The 0.1.x baseline threw immediately and the worker promoted to
|
|
27
|
+
// permanentlyDegraded, killing memory tools for the rest of the session.
|
|
28
|
+
// Treat the AV error codes as retryable with bounded backoff (~750ms total)
|
|
29
|
+
// before giving up and rethrowing.
|
|
30
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
31
|
+
try {
|
|
32
|
+
const fd = fs.openSync(ownerLockFile, 'wx')
|
|
33
|
+
fs.writeSync(fd, String(process.pid))
|
|
34
|
+
fs.closeSync(fd)
|
|
35
|
+
return true
|
|
36
|
+
} catch (e) {
|
|
37
|
+
if (e.code === 'EEXIST') {
|
|
38
|
+
let ownerPid = NaN
|
|
39
|
+
try { ownerPid = Number(fs.readFileSync(ownerLockFile, 'utf8').trim()) } catch {}
|
|
40
|
+
if (isPidAliveLocal(ownerPid)) return false
|
|
41
|
+
// Stale lock: dead owner — unlink and retry exclusive create.
|
|
42
|
+
try { fs.unlinkSync(ownerLockFile) } catch {}
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
const transient = e.code === 'EPERM' || e.code === 'EBUSY' || e.code === 'EACCES'
|
|
46
|
+
if (transient && attempt < 4) {
|
|
47
|
+
// Sync busy-wait acceptable here: this runs on memory worker boot
|
|
48
|
+
// path, once per process; the parent handler is not blocked.
|
|
49
|
+
const end = Date.now() + 50 * (attempt + 1)
|
|
50
|
+
while (Date.now() < end) {}
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
throw e
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function releaseMemoryOwnerLock(ownerLockFile) {
|
|
60
|
+
try {
|
|
61
|
+
const ownerPid = Number(fs.readFileSync(ownerLockFile, 'utf8').trim())
|
|
62
|
+
if (ownerPid === process.pid) fs.unlinkSync(ownerLockFile)
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function killPreviousServer(pid, log = () => {}) {
|
|
67
|
+
if (pid <= 0 || pid === process.pid) return false
|
|
68
|
+
if (process.platform === 'win32') {
|
|
69
|
+
try {
|
|
70
|
+
execFileSync('taskkill', ['/F', '/T', '/PID', String(pid)], { encoding: 'utf8', timeout: 5000, windowsHide: true })
|
|
71
|
+
log(`[memory-service] Killed previous server PID ${pid}\n`)
|
|
72
|
+
return true
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Exit code 128 = process not found; treat stale lock as already-dead = success.
|
|
75
|
+
// Status 128 reliably means "process not found" regardless of locale; no text match needed.
|
|
76
|
+
// Status 1 with English text match handles edge cases on some Windows versions.
|
|
77
|
+
const notFoundText = /not found|no running instance/i.test(e.stdout || '')
|
|
78
|
+
|| /not found|no running instance/i.test(e.stderr || '')
|
|
79
|
+
|| /not found|no running instance/i.test(e.message || '')
|
|
80
|
+
const alreadyDead = e.status === 128 || (e.status === 1 && notFoundText)
|
|
81
|
+
if (alreadyDead) {
|
|
82
|
+
log(`[memory-service] PID ${pid} already dead (stale lock), proceeding\n`)
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
log(`[memory-service] taskkill failed for PID ${pid}: ${e.message}\n`)
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Pre-flight: if the process is already gone, treat stale lock as success.
|
|
90
|
+
try {
|
|
91
|
+
process.kill(pid, 0)
|
|
92
|
+
} catch (e) {
|
|
93
|
+
if (e.code === 'ESRCH') {
|
|
94
|
+
log(`[memory-service] PID ${pid} already dead (stale lock), proceeding\n`)
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
try { process.kill(pid, 'SIGTERM') } catch {}
|
|
99
|
+
try { process.kill(pid, 'SIGKILL') } catch {}
|
|
100
|
+
// Poll for death up to 2s
|
|
101
|
+
const deadline = Date.now() + 2000
|
|
102
|
+
while (Date.now() < deadline) {
|
|
103
|
+
try {
|
|
104
|
+
process.kill(pid, 0)
|
|
105
|
+
} catch (e) {
|
|
106
|
+
if (e.code === 'ESRCH') {
|
|
107
|
+
log(`[memory-service] Killed previous server PID ${pid}\n`)
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Synchronous 50ms sleep via shared buffer spin
|
|
112
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50)
|
|
113
|
+
}
|
|
114
|
+
log(`[memory-service] PID ${pid} still alive after SIGKILL\n`)
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function acquireLock(lockFile, log = () => {}) {
|
|
120
|
+
// Multi-instance guard. In multi-terminal mode the lock owner is a *peer*
|
|
121
|
+
// memory worker serving recall for another CC session. killPreviousServer
|
|
122
|
+
// would taskkill /F that healthy peer mid-flight, then this fork-proxy
|
|
123
|
+
// mode wouldn't even need a lock anyway. Skip the entire kill-the-previous
|
|
124
|
+
// protocol; fork-proxy detection in init() takes priority. If neither
|
|
125
|
+
// proxy nor lock-owner path applies (race window during simultaneous
|
|
126
|
+
// boot), the worker simply continues without the lock — server-main /
|
|
127
|
+
// PG / port-listen handle the actual conflict cases.
|
|
128
|
+
if (process.env.MIXDOG_MULTI_INSTANCE === '1') return
|
|
129
|
+
try {
|
|
130
|
+
if (fs.existsSync(lockFile)) {
|
|
131
|
+
const lockedPid = Number(fs.readFileSync(lockFile, 'utf8').trim())
|
|
132
|
+
if (lockedPid > 0 && lockedPid !== process.pid) {
|
|
133
|
+
const killed = killPreviousServer(lockedPid, log)
|
|
134
|
+
if (!killed) {
|
|
135
|
+
log(`[memory-service] Could not kill previous server PID ${lockedPid}, aborting\n`)
|
|
136
|
+
process.exit(1)
|
|
137
|
+
}
|
|
138
|
+
try { fs.unlinkSync(lockFile) } catch {}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const fd = fs.openSync(lockFile, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o600)
|
|
142
|
+
try {
|
|
143
|
+
fs.writeSync(fd, String(process.pid))
|
|
144
|
+
} finally {
|
|
145
|
+
fs.closeSync(fd)
|
|
146
|
+
}
|
|
147
|
+
} catch (e) {
|
|
148
|
+
if (e.code === 'EEXIST') {
|
|
149
|
+
log(`[memory-service] Lock file exists (EEXIST) — another instance is already running, exiting\n`)
|
|
150
|
+
process.exit(0)
|
|
151
|
+
}
|
|
152
|
+
log(`[memory-service] Lock acquisition failed: ${e.message}\n`)
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function releaseLock(lockFile) {
|
|
158
|
+
try {
|
|
159
|
+
const content = fs.readFileSync(lockFile, 'utf8').trim()
|
|
160
|
+
if (Number(content) === process.pid) fs.unlinkSync(lockFile)
|
|
161
|
+
} catch {}
|
|
162
|
+
}
|
|
@@ -15,6 +15,18 @@ import { recallReadQuery } from './memory-recall-read-query.mjs'
|
|
|
15
15
|
const _MV_HOT_ACTIVE_TTL_MS = 60_000
|
|
16
16
|
const _mvHotActiveCache = new WeakMap() // db → { populated: boolean, ts: number }
|
|
17
17
|
const SEMANTIC_ONLY_MIN_SIM = 0.72
|
|
18
|
+
// Cross-language semantic rescue: a query in one language against rows in
|
|
19
|
+
// another rarely clears SEMANTIC_ONLY_MIN_SIM on a small (384-dim) embedding
|
|
20
|
+
// model, yet the TOP dense ranks are still overwhelmingly relevant. Accept
|
|
21
|
+
// semantic-only rows that are both top-ranked in the dense leg AND above a
|
|
22
|
+
// softer floor, instead of dropping everything below 0.72.
|
|
23
|
+
const SEMANTIC_TOP_RANK_MAX = 3
|
|
24
|
+
// Floor for the top-rank rescue. 384-dim MiniLM-class embeddings put UNRELATED
|
|
25
|
+
// text at ~0.65-0.75 sim (measured: gibberish query "zzqqxx ... 999" scored
|
|
26
|
+
// 0.749 against real rows), so anything below ~0.70 is noise regardless of
|
|
27
|
+
// rank; cross-language relevant pairs measured 0.67-0.74. 0.70 keeps most of
|
|
28
|
+
// the cross-language rescue while rejecting pure-noise top ranks.
|
|
29
|
+
const SEMANTIC_TOP_RANK_MIN_SIM = 0.70
|
|
18
30
|
const SHORT_QUERY_TOKEN_MAX = 2
|
|
19
31
|
|
|
20
32
|
function buildExactTerms(query) {
|
|
@@ -62,7 +74,12 @@ function hasFullQueryTextMatch(query, row) {
|
|
|
62
74
|
function hasQueryTokenCoverage(row, queryTokenCount) {
|
|
63
75
|
if (queryTokenCount <= SHORT_QUERY_TOKEN_MAX) return true
|
|
64
76
|
const hits = Number(row?.exact_hits)
|
|
65
|
-
|
|
77
|
+
// Majority coverage, not full coverage. Requiring EVERY query token to hit
|
|
78
|
+
// made mixed-language queries ("usage spike 원인") return nothing when one
|
|
79
|
+
// token has no lexical counterpart in the row — ~60% of tokens matching is
|
|
80
|
+
// strong enough lexical evidence alongside the semantic leg.
|
|
81
|
+
const required = Math.max(2, Math.ceil(queryTokenCount * 0.6))
|
|
82
|
+
return Number.isFinite(hits) && hits >= required
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
function exactTextBoost(query, row, exactHits) {
|
|
@@ -479,7 +496,10 @@ LEFT JOIN exact x ON x.id = c.id`
|
|
|
479
496
|
const scored = scoredAll.filter(({ row }) => {
|
|
480
497
|
const hasTextSupport = row.sparse_rank != null || row.trgm_rank != null || row.exact_rank != null
|
|
481
498
|
const sim = Number(row.dense_sim)
|
|
482
|
-
const
|
|
499
|
+
const denseRankNum = row.dense_rank != null ? Number(row.dense_rank) : null
|
|
500
|
+
const hasSemanticSupport = (Number.isFinite(sim) && sim >= SEMANTIC_ONLY_MIN_SIM)
|
|
501
|
+
|| (Number.isFinite(sim) && sim >= SEMANTIC_TOP_RANK_MIN_SIM
|
|
502
|
+
&& denseRankNum != null && denseRankNum <= SEMANTIC_TOP_RANK_MAX)
|
|
483
503
|
if (!hasTextSupport) {
|
|
484
504
|
if (hasSemanticSupport) return true
|
|
485
505
|
semanticOnlyDropped += 1
|
|
@@ -194,7 +194,7 @@ function patchActiveInstance(fields) {
|
|
|
194
194
|
else merged[k] = v;
|
|
195
195
|
}
|
|
196
196
|
return merged;
|
|
197
|
-
}, { compact: true, fsyncDir: true });
|
|
197
|
+
}, { compact: true, fsyncDir: true, renameFallback: 'truncate' });
|
|
198
198
|
} catch (e) {
|
|
199
199
|
__mixdogMemoryLog(`[supervisor-pg] patchActiveInstance failed: ${e?.message}\n`);
|
|
200
200
|
}
|