mixdog 0.7.1
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/.claude-plugin/marketplace.json +31 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.gitattributes +34 -0
- package/.mcp.json +14 -0
- package/ARCHITECTURE.md +77 -0
- package/CHANGELOG.md +7 -0
- package/CONTRIBUTING.md +45 -0
- package/DATA-FLOW.md +79 -0
- package/LICENSE +21 -0
- package/README.md +389 -0
- package/SECURITY.md +138 -0
- package/UNINSTALL.md +112 -0
- package/agents/maintenance.md +5 -0
- package/agents/memory-classification.md +30 -0
- package/agents/scheduler-task.md +18 -0
- package/agents/webhook-handler.md +27 -0
- package/agents/worker.md +24 -0
- package/bin/bridge +133 -0
- package/bin/statusline-launcher.mjs +78 -0
- package/bin/statusline-lib.mjs +550 -0
- package/bin/statusline.mjs +607 -0
- package/bun.lock +802 -0
- package/commands/config.md +16 -0
- package/commands/doctor.md +13 -0
- package/commands/setup.md +17 -0
- package/defaults/cycle3-review-prompt.md +90 -0
- package/defaults/hidden-roles.json +65 -0
- package/defaults/memory-chunk-prompt.md +63 -0
- package/defaults/memory-promote-prompt.md +135 -0
- package/defaults/mixdog-config.template.json +27 -0
- package/defaults/user-workflow.json +8 -0
- package/defaults/user-workflow.md +12 -0
- package/hooks/hooks.json +73 -0
- package/hooks/lib/active-instance.cjs +77 -0
- package/hooks/lib/permission-evaluator.cjs +411 -0
- package/hooks/lib/permission-route.cjs +63 -0
- package/hooks/lib/permission-rules.cjs +170 -0
- package/hooks/lib/settings-loader.cjs +116 -0
- package/hooks/post-tool-use.cjs +84 -0
- package/hooks/pre-mcp-sandbox.cjs +158 -0
- package/hooks/pre-tool-subagent.cjs +253 -0
- package/hooks/session-start.cjs +1372 -0
- package/hooks/turn-timer.cjs +82 -0
- package/lib/claude-md-writer.cjs +386 -0
- package/lib/config-cjs.cjs +61 -0
- package/lib/hook-pipe-path.cjs +10 -0
- package/lib/keychain-cjs.cjs +263 -0
- package/lib/plugin-paths.cjs +61 -0
- package/lib/rules-builder.cjs +241 -0
- package/lib/text-utils.cjs +61 -0
- package/native/README.md +117 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +107 -0
- package/prompts/code-review.txt +16 -0
- package/prompts/security-audit.txt +17 -0
- package/rules/bridge/00-common.md +39 -0
- package/rules/bridge/20-skip-protocol.md +18 -0
- package/rules/bridge/30-explorer.md +33 -0
- package/rules/bridge/40-cycle1-agent.md +52 -0
- package/rules/bridge/41-cycle2-agent.md +62 -0
- package/rules/bridge/42-cycle3-agent.md +44 -0
- package/rules/lead/00-tool-lead.md +61 -0
- package/rules/lead/01-general.md +23 -0
- package/rules/lead/02-channels.md +49 -0
- package/rules/lead/03-team.md +27 -0
- package/rules/lead/04-workflow.md +20 -0
- package/rules/shared/00-language.md +14 -0
- package/rules/shared/01-tool.md +138 -0
- package/scripts/bootstrap.mjs +184 -0
- package/scripts/bridge-unify-smoke.mjs +308 -0
- package/scripts/build-runtime-linux.sh +348 -0
- package/scripts/build-runtime-macos.sh +217 -0
- package/scripts/build-runtime-windows.ps1 +242 -0
- package/scripts/builtin-utils-smoke.mjs +392 -0
- package/scripts/check-json.mjs +45 -0
- package/scripts/check-syntax-changed.mjs +102 -0
- package/scripts/check-syntax.mjs +58 -0
- package/scripts/code-graph-batch.test.mjs +33 -0
- package/scripts/config-preserve-smoke.mjs +180 -0
- package/scripts/doctor.mjs +484 -0
- package/scripts/edit-normalize-fuzz.mjs +130 -0
- package/scripts/edit-normalize-smoke.mjs +401 -0
- package/scripts/edit-operation-smoke.mjs +369 -0
- package/scripts/edit2-smoke.mjs +63 -0
- package/scripts/fuzzy-e2e.mjs +28 -0
- package/scripts/fuzzy-smoke.mjs +26 -0
- package/scripts/generate-runtime-manifest.mjs +166 -0
- package/scripts/guard-smoke.mjs +66 -0
- package/scripts/hidden-role-schema-smoke.mjs +162 -0
- package/scripts/hook-routing-smoke.mjs +29 -0
- package/scripts/inject-input.ps1 +204 -0
- package/scripts/io-complex-smoke.mjs +667 -0
- package/scripts/io-explore-bench.mjs +424 -0
- package/scripts/io-guardrails-smoke.mjs +205 -0
- package/scripts/io-mini-bench-baseline.json +11 -0
- package/scripts/io-mini-bench.mjs +216 -0
- package/scripts/io-route-harness.mjs +933 -0
- package/scripts/io-telemetry-report.mjs +691 -0
- package/scripts/mutation-bench.mjs +564 -0
- package/scripts/mutation-io-smoke.mjs +1081 -0
- package/scripts/native-patch-bridge-smoke.mjs +288 -0
- package/scripts/native-patch-smoke.mjs +304 -0
- package/scripts/patch-interior-context-smoke.mjs +49 -0
- package/scripts/patch-newline-utf8-smoke.mjs +157 -0
- package/scripts/perf-hook-smoke.mjs +71 -0
- package/scripts/permission-eval-smoke.mjs +426 -0
- package/scripts/prep-patch.mjs +53 -0
- package/scripts/prep-shim.mjs +96 -0
- package/scripts/provider-cache-smoke.mjs +687 -0
- package/scripts/report-runtime-health.mjs +132 -0
- package/scripts/run-mcp.mjs +1547 -0
- package/scripts/salvage-v4a-shatter.test.mjs +58 -0
- package/scripts/scoped-cache-io-smoke.mjs +103 -0
- package/scripts/shell-policy-round3-smoke.mjs +46 -0
- package/scripts/smoke-runtime-negative.ps1 +100 -0
- package/scripts/smoke-runtime-negative.sh +95 -0
- package/scripts/stall-policy-smoke.mjs +50 -0
- package/scripts/start-memory-worker.mjs +23 -0
- package/scripts/statusline-launcher-smoke.mjs +82 -0
- package/scripts/stress-atomic-write.mjs +1028 -0
- package/scripts/test-config-rmw-restore.mjs +122 -0
- package/scripts/test-fault-inject.mjs +164 -0
- package/scripts/test-large-file.mjs +174 -0
- package/scripts/tool-edge-smoke.mjs +209 -0
- package/scripts/uninstall.mjs +201 -0
- package/scripts/webhook-selfheal-smoke.mjs +29 -0
- package/scripts/write-overwrite-guard-smoke.mjs +56 -0
- package/server-main.mjs +3055 -0
- package/server.mjs +468 -0
- package/setup/config-merge.mjs +254 -0
- package/setup/install.mjs +120 -0
- package/setup/launch-core.mjs +507 -0
- package/setup/launch.mjs +101 -0
- package/setup/setup-server.mjs +3206 -0
- package/setup/setup.html +3693 -0
- package/skills/retro-skill-proposer/SKILL.md +92 -0
- package/skills/schedule-add/SKILL.md +77 -0
- package/skills/setup/SKILL.md +346 -0
- package/skills/webhook-add/SKILL.md +81 -0
- package/src/agent/bridge-stall-watchdog.mjs +337 -0
- package/src/agent/index.mjs +2138 -0
- package/src/agent/orchestrator/activity-bus.mjs +38 -0
- package/src/agent/orchestrator/ai-wrapped-dispatch.mjs +1010 -0
- package/src/agent/orchestrator/bridge-retry.mjs +220 -0
- package/src/agent/orchestrator/bridge-trace.mjs +583 -0
- package/src/agent/orchestrator/cache-mtime.mjs +58 -0
- package/src/agent/orchestrator/config.mjs +358 -0
- package/src/agent/orchestrator/context/collect.mjs +651 -0
- package/src/agent/orchestrator/dispatch-persist.mjs +549 -0
- package/src/agent/orchestrator/drain-registry.mjs +50 -0
- package/src/agent/orchestrator/explore-validator.mjs +8 -0
- package/src/agent/orchestrator/internal-roles.mjs +118 -0
- package/src/agent/orchestrator/internal-tools.mjs +88 -0
- package/src/agent/orchestrator/jobs.mjs +116 -0
- package/src/agent/orchestrator/mcp/client.mjs +364 -0
- package/src/agent/orchestrator/providers/anthropic-betas.mjs +21 -0
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +1745 -0
- package/src/agent/orchestrator/providers/anthropic.mjs +437 -0
- package/src/agent/orchestrator/providers/gemini.mjs +1175 -0
- package/src/agent/orchestrator/providers/grok-oauth.mjs +782 -0
- package/src/agent/orchestrator/providers/model-catalog.mjs +241 -0
- package/src/agent/orchestrator/providers/openai-compat.mjs +1467 -0
- package/src/agent/orchestrator/providers/openai-oauth-ws.mjs +1890 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +1307 -0
- package/src/agent/orchestrator/providers/openai-ws.mjs +104 -0
- package/src/agent/orchestrator/providers/registry.mjs +192 -0
- package/src/agent/orchestrator/providers/retry-classifier.mjs +325 -0
- package/src/agent/orchestrator/session/abort-lookup.mjs +13 -0
- package/src/agent/orchestrator/session/cache/post-edit-marks.mjs +42 -0
- package/src/agent/orchestrator/session/cache/prefetch-cache.mjs +142 -0
- package/src/agent/orchestrator/session/cache/read-cache.mjs +319 -0
- package/src/agent/orchestrator/session/cache/scoped-cache-outcome.mjs +11 -0
- package/src/agent/orchestrator/session/cache/scoped-cache.mjs +361 -0
- package/src/agent/orchestrator/session/cache/util.mjs +49 -0
- package/src/agent/orchestrator/session/loop.mjs +1478 -0
- package/src/agent/orchestrator/session/manager.mjs +1975 -0
- package/src/agent/orchestrator/session/read-dedup.mjs +6 -0
- package/src/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/agent/orchestrator/session/save-session-worker.mjs +18 -0
- package/src/agent/orchestrator/session/store.mjs +624 -0
- package/src/agent/orchestrator/session/stream-watchdog.mjs +130 -0
- package/src/agent/orchestrator/session/tool-result-offload.mjs +166 -0
- package/src/agent/orchestrator/session/trim.mjs +491 -0
- package/src/agent/orchestrator/smart-bridge/CACHE-SHARD.md +115 -0
- package/src/agent/orchestrator/smart-bridge/bridge-llm.mjs +327 -0
- package/src/agent/orchestrator/smart-bridge/cache-obs.mjs +150 -0
- package/src/agent/orchestrator/smart-bridge/cache-strategy.mjs +228 -0
- package/src/agent/orchestrator/smart-bridge/index.mjs +215 -0
- package/src/agent/orchestrator/smart-bridge/profiles.mjs +37 -0
- package/src/agent/orchestrator/smart-bridge/registry.mjs +348 -0
- package/src/agent/orchestrator/smart-bridge/session-builder.mjs +116 -0
- package/src/agent/orchestrator/stall-policy.mjs +195 -0
- package/src/agent/orchestrator/tool-loop-guard.mjs +75 -0
- package/src/agent/orchestrator/tools/bash-policy-scan.mjs +77 -0
- package/src/agent/orchestrator/tools/bash-session.mjs +721 -0
- package/src/agent/orchestrator/tools/builtin/advisory-lock.mjs +171 -0
- package/src/agent/orchestrator/tools/builtin/arg-guard.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/atomic-write.mjs +236 -0
- package/src/agent/orchestrator/tools/builtin/bash-tool.mjs +480 -0
- package/src/agent/orchestrator/tools/builtin/binary-file.mjs +76 -0
- package/src/agent/orchestrator/tools/builtin/builtin-tools.mjs +256 -0
- package/src/agent/orchestrator/tools/builtin/cache-layers.mjs +386 -0
- package/src/agent/orchestrator/tools/builtin/cwd-utils.mjs +37 -0
- package/src/agent/orchestrator/tools/builtin/device-paths.mjs +154 -0
- package/src/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +292 -0
- package/src/agent/orchestrator/tools/builtin/diff-utils.mjs +109 -0
- package/src/agent/orchestrator/tools/builtin/edit-base-guard.mjs +58 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-plan.mjs +240 -0
- package/src/agent/orchestrator/tools/builtin/edit-byte-utils.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/edit-commit.mjs +74 -0
- package/src/agent/orchestrator/tools/builtin/edit-context-utils.mjs +242 -0
- package/src/agent/orchestrator/tools/builtin/edit-diagnostics.mjs +211 -0
- package/src/agent/orchestrator/tools/builtin/edit-engine.mjs +1364 -0
- package/src/agent/orchestrator/tools/builtin/edit-failure-context.mjs +126 -0
- package/src/agent/orchestrator/tools/builtin/edit-hint.mjs +141 -0
- package/src/agent/orchestrator/tools/builtin/edit-match-utils.mjs +194 -0
- package/src/agent/orchestrator/tools/builtin/edit-partial-write.mjs +60 -0
- package/src/agent/orchestrator/tools/builtin/edit-stale-refresh.mjs +168 -0
- package/src/agent/orchestrator/tools/builtin/edit-tool.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/edit-utf8-guard.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fs-reachability.mjs +48 -0
- package/src/agent/orchestrator/tools/builtin/fuzzy-match.mjs +99 -0
- package/src/agent/orchestrator/tools/builtin/glob-walk.mjs +170 -0
- package/src/agent/orchestrator/tools/builtin/grep-formatting.mjs +113 -0
- package/src/agent/orchestrator/tools/builtin/hash-utils.mjs +6 -0
- package/src/agent/orchestrator/tools/builtin/list-formatting.mjs +7 -0
- package/src/agent/orchestrator/tools/builtin/list-tool.mjs +593 -0
- package/src/agent/orchestrator/tools/builtin/native-edit-runner.mjs +89 -0
- package/src/agent/orchestrator/tools/builtin/notebook-edit-tool.mjs +300 -0
- package/src/agent/orchestrator/tools/builtin/open-config-tool.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/path-diagnostics.mjs +152 -0
- package/src/agent/orchestrator/tools/builtin/path-locks.mjs +35 -0
- package/src/agent/orchestrator/tools/builtin/path-utils.mjs +201 -0
- package/src/agent/orchestrator/tools/builtin/read-args.mjs +103 -0
- package/src/agent/orchestrator/tools/builtin/read-batch.mjs +172 -0
- package/src/agent/orchestrator/tools/builtin/read-constants.mjs +40 -0
- package/src/agent/orchestrator/tools/builtin/read-formatting.mjs +118 -0
- package/src/agent/orchestrator/tools/builtin/read-image-resize.mjs +189 -0
- package/src/agent/orchestrator/tools/builtin/read-image.mjs +88 -0
- package/src/agent/orchestrator/tools/builtin/read-lines.mjs +12 -0
- package/src/agent/orchestrator/tools/builtin/read-mode-tool.mjs +455 -0
- package/src/agent/orchestrator/tools/builtin/read-open.mjs +190 -0
- package/src/agent/orchestrator/tools/builtin/read-range-index.mjs +271 -0
- package/src/agent/orchestrator/tools/builtin/read-ranges.mjs +26 -0
- package/src/agent/orchestrator/tools/builtin/read-single-tool.mjs +728 -0
- package/src/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +173 -0
- package/src/agent/orchestrator/tools/builtin/read-special-files.mjs +268 -0
- package/src/agent/orchestrator/tools/builtin/read-streaming.mjs +602 -0
- package/src/agent/orchestrator/tools/builtin/read-tool.mjs +530 -0
- package/src/agent/orchestrator/tools/builtin/read-windows.mjs +107 -0
- package/src/agent/orchestrator/tools/builtin/rename-tool.mjs +196 -0
- package/src/agent/orchestrator/tools/builtin/rg-runner.mjs +422 -0
- package/src/agent/orchestrator/tools/builtin/search-builders.mjs +158 -0
- package/src/agent/orchestrator/tools/builtin/search-tool.mjs +869 -0
- package/src/agent/orchestrator/tools/builtin/shell-analysis.mjs +653 -0
- package/src/agent/orchestrator/tools/builtin/shell-jobs.mjs +936 -0
- package/src/agent/orchestrator/tools/builtin/shell-output.mjs +36 -0
- package/src/agent/orchestrator/tools/builtin/shell-runtime.mjs +214 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-helpers.mjs +143 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-store.mjs +206 -0
- package/src/agent/orchestrator/tools/builtin/snapshot-validation.mjs +98 -0
- package/src/agent/orchestrator/tools/builtin/text-stats.mjs +69 -0
- package/src/agent/orchestrator/tools/builtin/windows-roots.mjs +23 -0
- package/src/agent/orchestrator/tools/builtin/write-tool.mjs +401 -0
- package/src/agent/orchestrator/tools/builtin.mjs +500 -0
- package/src/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +39 -0
- package/src/agent/orchestrator/tools/code-graph-tool-defs.mjs +24 -0
- package/src/agent/orchestrator/tools/code-graph.mjs +4095 -0
- package/src/agent/orchestrator/tools/cwd-tool.mjs +298 -0
- package/src/agent/orchestrator/tools/destructive-warning.mjs +323 -0
- package/src/agent/orchestrator/tools/edit-normalize.mjs +603 -0
- package/src/agent/orchestrator/tools/env-scrub.mjs +100 -0
- package/src/agent/orchestrator/tools/graph-binary-fetcher.mjs +144 -0
- package/src/agent/orchestrator/tools/graph-manifest.json +26 -0
- package/src/agent/orchestrator/tools/host-input.mjs +204 -0
- package/src/agent/orchestrator/tools/mutation-content-cache.mjs +67 -0
- package/src/agent/orchestrator/tools/mutation-planner.mjs +75 -0
- package/src/agent/orchestrator/tools/next-call-utils.mjs +48 -0
- package/src/agent/orchestrator/tools/patch-binary-fetcher.mjs +133 -0
- package/src/agent/orchestrator/tools/patch-manifest.json +26 -0
- package/src/agent/orchestrator/tools/patch-tool-defs.mjs +20 -0
- package/src/agent/orchestrator/tools/patch.mjs +2754 -0
- package/src/agent/orchestrator/tools/progress-message.mjs +118 -0
- package/src/agent/orchestrator/tools/result-compression.mjs +279 -0
- package/src/agent/orchestrator/tools/shell-command.mjs +865 -0
- package/src/agent/orchestrator/tools/shell-exec-policy.mjs +89 -0
- package/src/agent/orchestrator/tools/shell-policy-danger-target.mjs +27 -0
- package/src/agent/orchestrator/tools/shell-policy-imports.mjs +7 -0
- package/src/agent/orchestrator/tools/shell-policy.mjs +345 -0
- package/src/agent/orchestrator/tools/shell-snapshot.mjs +313 -0
- package/src/agent/orchestrator/workflow-store.mjs +93 -0
- package/src/agent/tool-defs.mjs +103 -0
- package/src/channels/backends/discord.mjs +784 -0
- package/src/channels/data/voice-runtime-manifest.json +138 -0
- package/src/channels/index.mjs +3229 -0
- package/src/channels/lib/cli-worker-host.mjs +12 -0
- package/src/channels/lib/config-lock.mjs +13 -0
- package/src/channels/lib/config.mjs +292 -0
- package/src/channels/lib/drop-trace.mjs +71 -0
- package/src/channels/lib/event-pipeline.mjs +81 -0
- package/src/channels/lib/event-queue.mjs +345 -0
- package/src/channels/lib/executor.mjs +168 -0
- package/src/channels/lib/format.mjs +188 -0
- package/src/channels/lib/holidays.mjs +138 -0
- package/src/channels/lib/hook-pipe-server.mjs +802 -0
- package/src/channels/lib/interaction-workflows.mjs +184 -0
- package/src/channels/lib/memory-client.mjs +149 -0
- package/src/channels/lib/output-forwarder.mjs +765 -0
- package/src/channels/lib/runtime-paths.mjs +479 -0
- package/src/channels/lib/scheduler.mjs +723 -0
- package/src/channels/lib/session-control.mjs +36 -0
- package/src/channels/lib/session-discovery.mjs +103 -0
- package/src/channels/lib/settings.mjs +11 -0
- package/src/channels/lib/state-file.mjs +68 -0
- package/src/channels/lib/status-snapshot.mjs +219 -0
- package/src/channels/lib/tool-format.mjs +140 -0
- package/src/channels/lib/transcript-discovery.mjs +195 -0
- package/src/channels/lib/voice-runtime-fetcher.mjs +734 -0
- package/src/channels/lib/webhook.mjs +1179 -0
- package/src/channels/lib/whisper-server.mjs +477 -0
- package/src/channels/tool-defs.mjs +170 -0
- package/src/daemon/host.mjs +118 -0
- package/src/daemon/mcp-transport.mjs +47 -0
- package/src/daemon/session.mjs +100 -0
- package/src/daemon/thin-client.mjs +71 -0
- package/src/daemon/transport.mjs +163 -0
- package/src/memory/data/runtime-manifest.json +40 -0
- package/src/memory/index.mjs +3305 -0
- package/src/memory/lib/agent-ipc.mjs +93 -0
- package/src/memory/lib/bridge-trace-queries.mjs +120 -0
- package/src/memory/lib/core-memory-store.mjs +330 -0
- package/src/memory/lib/embedding-provider.mjs +269 -0
- package/src/memory/lib/embedding-worker.mjs +323 -0
- package/src/memory/lib/llm-worker-host.mjs +17 -0
- package/src/memory/lib/memory-cycle.mjs +11 -0
- package/src/memory/lib/memory-cycle1.mjs +641 -0
- package/src/memory/lib/memory-cycle2.mjs +1284 -0
- package/src/memory/lib/memory-cycle3.mjs +540 -0
- package/src/memory/lib/memory-embed.mjs +299 -0
- package/src/memory/lib/memory-extraction.mjs +5 -0
- package/src/memory/lib/memory-maintenance-store.mjs +32 -0
- package/src/memory/lib/memory-ops-policy.mjs +190 -0
- package/src/memory/lib/memory-recall-id-patch.mjs +15 -0
- package/src/memory/lib/memory-recall-read-query.mjs +7 -0
- package/src/memory/lib/memory-recall-scope-filter.mjs +63 -0
- package/src/memory/lib/memory-recall-store.mjs +621 -0
- package/src/memory/lib/memory-retrievers.mjs +112 -0
- package/src/memory/lib/memory-score.mjs +71 -0
- package/src/memory/lib/memory-text-utils.mjs +58 -0
- package/src/memory/lib/memory.mjs +412 -0
- package/src/memory/lib/model-profile.mjs +85 -0
- package/src/memory/lib/pg/adapter.mjs +308 -0
- package/src/memory/lib/pg/process.mjs +360 -0
- package/src/memory/lib/pg/supervisor.mjs +396 -0
- package/src/memory/lib/project-id-resolver.mjs +86 -0
- package/src/memory/lib/runtime-fetcher.mjs +442 -0
- package/src/memory/lib/trace-store.mjs +728 -0
- package/src/memory/tool-defs.mjs +79 -0
- package/src/search/index.mjs +1173 -0
- package/src/search/lib/backends/anthropic-oauth.mjs +98 -0
- package/src/search/lib/backends/exa.mjs +50 -0
- package/src/search/lib/backends/firecrawl.mjs +61 -0
- package/src/search/lib/backends/gemini-api.mjs +83 -0
- package/src/search/lib/backends/grok-oauth.mjs +86 -0
- package/src/search/lib/backends/index.mjs +150 -0
- package/src/search/lib/backends/openai-api.mjs +144 -0
- package/src/search/lib/backends/openai-oauth.mjs +98 -0
- package/src/search/lib/backends/openai-web-search.mjs +76 -0
- package/src/search/lib/backends/tavily.mjs +55 -0
- package/src/search/lib/backends/xai-api.mjs +113 -0
- package/src/search/lib/cache.mjs +131 -0
- package/src/search/lib/config.mjs +192 -0
- package/src/search/lib/formatter.mjs +115 -0
- package/src/search/lib/provider-usage.mjs +67 -0
- package/src/search/lib/providers.mjs +47 -0
- package/src/search/lib/search-intent.mjs +109 -0
- package/src/search/lib/setup-handler.mjs +261 -0
- package/src/search/lib/state.mjs +201 -0
- package/src/search/lib/web-tools.mjs +1207 -0
- package/src/search/tool-defs.mjs +83 -0
- package/src/setup/defender-exclusion.mjs +183 -0
- package/src/shared/abort-controller.mjs +15 -0
- package/src/shared/atomic-file.mjs +420 -0
- package/src/shared/config.mjs +350 -0
- package/src/shared/daemon-recycle.mjs +108 -0
- package/src/shared/disable-claude-builtins.mjs +88 -0
- package/src/shared/err-text.mjs +12 -0
- package/src/shared/llm/cost.mjs +66 -0
- package/src/shared/llm/http-agent.mjs +123 -0
- package/src/shared/llm/index.mjs +41 -0
- package/src/shared/llm/pid-cleanup.mjs +27 -0
- package/src/shared/llm/usage-log.mjs +47 -0
- package/src/shared/plugin-paths.mjs +58 -0
- package/src/shared/schedules-store.mjs +70 -0
- package/src/shared/seed.mjs +119 -0
- package/src/shared/user-cwd.mjs +213 -0
- package/src/shared/user-data-guard.mjs +238 -0
- package/src/status/aggregator.mjs +584 -0
- package/src/status/server.mjs +413 -0
- package/tools.json +1653 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdtemp, readFile, readdir, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
import { spawnSync } from 'node:child_process';
|
|
7
|
+
|
|
8
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
const MANIFEST = join(ROOT, 'native/mixdog-patch/Cargo.toml');
|
|
10
|
+
|
|
11
|
+
process.env.CLAUDE_PLUGIN_ROOT ||= ROOT;
|
|
12
|
+
// SAFETY: own an isolated temp data dir; never `||=` the LIVE CLAUDE_PLUGIN_DATA,
|
|
13
|
+
// which the finally below would then rm.
|
|
14
|
+
const SMOKE_PLUGIN_DATA = join(tmpdir(), `mixdog-native-patch-bridge-data-${process.pid}`);
|
|
15
|
+
process.env.CLAUDE_PLUGIN_DATA = SMOKE_PLUGIN_DATA;
|
|
16
|
+
process.env.MIXDOG_PATCH_NATIVE = 'server';
|
|
17
|
+
process.env.MIXDOG_PATCH_NATIVE_STRICT = '1';
|
|
18
|
+
|
|
19
|
+
function run(cmd, args, opts = {}) {
|
|
20
|
+
const result = spawnSync(cmd, args, { encoding: 'utf8', stdio: 'pipe', ...opts });
|
|
21
|
+
if (result.status !== 0) {
|
|
22
|
+
throw new Error(`${cmd} ${args.join(' ')} failed\n${result.stdout || ''}\n${result.stderr || ''}`);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function patchText(lines) {
|
|
28
|
+
return `${lines.join('\n')}\n`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
run('cargo', ['build', '--release', '--manifest-path', MANIFEST]);
|
|
32
|
+
|
|
33
|
+
const { executePatchTool, closeNativePatchServerForTests } = await import(pathToFileURL(join(ROOT, 'src/agent/orchestrator/tools/patch.mjs')).href);
|
|
34
|
+
|
|
35
|
+
const dir = await mkdtemp(join(tmpdir(), 'mixdog-native-patch-bridge-'));
|
|
36
|
+
try {
|
|
37
|
+
await writeFile(join(dir, 'bridge.txt'), 'alpha\nbeta\ngamma\n', 'utf8');
|
|
38
|
+
const out = await executePatchTool('apply_patch', {
|
|
39
|
+
base_path: dir,
|
|
40
|
+
patch: patchText([
|
|
41
|
+
'--- a/bridge.txt',
|
|
42
|
+
'+++ b/bridge.txt',
|
|
43
|
+
'@@ -2,1 +2,1 @@',
|
|
44
|
+
'-beta',
|
|
45
|
+
'+BETA',
|
|
46
|
+
]),
|
|
47
|
+
}, dir, { readStateScope: 'native-bridge-smoke' });
|
|
48
|
+
if (!String(out).includes('(native)')) throw new Error(`native bridge was not used: ${out}`);
|
|
49
|
+
const got = await readFile(join(dir, 'bridge.txt'), 'utf8');
|
|
50
|
+
if (got !== 'alpha\nBETA\ngamma\n') throw new Error(`unexpected bridge.txt: ${JSON.stringify(got)}`);
|
|
51
|
+
const tempResidue = (await readdir(dir)).filter((name) => name.includes('.mixdog-tmp-') || name.includes('.mixdog-new-'));
|
|
52
|
+
if (tempResidue.length > 0) throw new Error(`native atomic temp residue: ${tempResidue.join(', ')}`);
|
|
53
|
+
|
|
54
|
+
const aborted = new AbortController();
|
|
55
|
+
aborted.abort(new Error('native abort smoke'));
|
|
56
|
+
let abortOk = false;
|
|
57
|
+
try {
|
|
58
|
+
await executePatchTool('apply_patch', {
|
|
59
|
+
base_path: dir,
|
|
60
|
+
patch: patchText([
|
|
61
|
+
'--- a/bridge.txt',
|
|
62
|
+
'+++ b/bridge.txt',
|
|
63
|
+
'@@ -1,1 +1,1 @@',
|
|
64
|
+
'-alpha',
|
|
65
|
+
'+ABORTED',
|
|
66
|
+
]),
|
|
67
|
+
}, dir, { readStateScope: 'native-bridge-smoke-abort', signal: aborted.signal });
|
|
68
|
+
} catch (err) {
|
|
69
|
+
abortOk = err?.name === 'AbortError' || /native abort smoke|aborted/i.test(String(err?.message || err));
|
|
70
|
+
}
|
|
71
|
+
if (!abortOk) throw new Error('pre-aborted native patch signal did not reject cleanly');
|
|
72
|
+
|
|
73
|
+
await writeFile(join(dir, 'v4a-bridge.txt'), 'class Foo\n function bar\n return x;\n', 'utf8');
|
|
74
|
+
const v4aOut = await executePatchTool('apply_patch', {
|
|
75
|
+
base_path: dir,
|
|
76
|
+
format: 'v4a',
|
|
77
|
+
patch: patchText([
|
|
78
|
+
'*** Begin Patch',
|
|
79
|
+
'*** Update File: v4a-bridge.txt',
|
|
80
|
+
'@@ class Foo',
|
|
81
|
+
'@@ function bar',
|
|
82
|
+
'- return x;',
|
|
83
|
+
'+ return x + 1;',
|
|
84
|
+
'*** End Patch',
|
|
85
|
+
]),
|
|
86
|
+
}, dir, { readStateScope: 'native-bridge-smoke-v4a' });
|
|
87
|
+
if (!String(v4aOut).includes('(native)')) throw new Error(`native V4A bridge was not used: ${v4aOut}`);
|
|
88
|
+
const v4aGot = await readFile(join(dir, 'v4a-bridge.txt'), 'utf8');
|
|
89
|
+
if (v4aGot !== 'class Foo\n function bar\n return x + 1;\n') throw new Error(`unexpected v4a-bridge.txt: ${JSON.stringify(v4aGot)}`);
|
|
90
|
+
|
|
91
|
+
await writeFile(join(dir, 'v4a-anchor-line.txt'), 'before\nconst target = "old";\nafter\n', 'utf8');
|
|
92
|
+
const v4aAnchorLineOut = await executePatchTool('apply_patch', {
|
|
93
|
+
base_path: dir,
|
|
94
|
+
format: 'v4a',
|
|
95
|
+
patch: patchText([
|
|
96
|
+
'*** Begin Patch',
|
|
97
|
+
'*** Update File: v4a-anchor-line.txt',
|
|
98
|
+
'@@ const target',
|
|
99
|
+
'-const target = "old";',
|
|
100
|
+
'+const target = "new";',
|
|
101
|
+
'*** End Patch',
|
|
102
|
+
]),
|
|
103
|
+
}, dir, { readStateScope: 'native-bridge-smoke-v4a-anchor-line' });
|
|
104
|
+
if (!String(v4aAnchorLineOut).includes('(native)')) throw new Error(`native V4A anchor-line bridge was not used: ${v4aAnchorLineOut}`);
|
|
105
|
+
const v4aAnchorLineGot = await readFile(join(dir, 'v4a-anchor-line.txt'), 'utf8');
|
|
106
|
+
if (v4aAnchorLineGot !== 'before\nconst target = "new";\nafter\n') throw new Error(`unexpected v4a-anchor-line.txt: ${JSON.stringify(v4aAnchorLineGot)}`);
|
|
107
|
+
|
|
108
|
+
await writeFile(join(dir, 'strict-fuzzy.txt'), 'intro\nactual-before\nold\nactual-after\n', 'utf8');
|
|
109
|
+
const fuzzyNativeOut = await executePatchTool('apply_patch', {
|
|
110
|
+
base_path: dir,
|
|
111
|
+
patch: patchText([
|
|
112
|
+
'--- a/strict-fuzzy.txt',
|
|
113
|
+
'+++ b/strict-fuzzy.txt',
|
|
114
|
+
'@@ -1,4 +1,4 @@',
|
|
115
|
+
' intro',
|
|
116
|
+
' expected-before',
|
|
117
|
+
'-old',
|
|
118
|
+
'+NEW',
|
|
119
|
+
' expected-after',
|
|
120
|
+
]),
|
|
121
|
+
}, dir, { readStateScope: 'native-bridge-smoke-fuzzy-default' });
|
|
122
|
+
if (!String(fuzzyNativeOut).includes('(native)')) throw new Error(`native fuzzy bridge was not used: ${fuzzyNativeOut}`);
|
|
123
|
+
const fuzzyGot = await readFile(join(dir, 'strict-fuzzy.txt'), 'utf8');
|
|
124
|
+
if (fuzzyGot !== 'intro\nactual-before\nNEW\nactual-after\n') throw new Error(`unexpected strict-fuzzy.txt: ${JSON.stringify(fuzzyGot)}`);
|
|
125
|
+
|
|
126
|
+
await writeFile(join(dir, 'strict-fuzzy.txt'), 'intro\nactual-before\nold\nactual-after\n', 'utf8');
|
|
127
|
+
const strictOut = await executePatchTool('apply_patch', {
|
|
128
|
+
base_path: dir,
|
|
129
|
+
fuzzy: false,
|
|
130
|
+
patch: patchText([
|
|
131
|
+
'--- a/strict-fuzzy.txt',
|
|
132
|
+
'+++ b/strict-fuzzy.txt',
|
|
133
|
+
'@@ -1,4 +1,4 @@',
|
|
134
|
+
' intro',
|
|
135
|
+
' expected-before',
|
|
136
|
+
'-old',
|
|
137
|
+
'+NEW',
|
|
138
|
+
' expected-after',
|
|
139
|
+
]),
|
|
140
|
+
}, dir, { readStateScope: 'native-bridge-smoke-fuzzy-disabled' });
|
|
141
|
+
if (!String(strictOut).startsWith('Error:')) throw new Error(`fuzzy:false should reject context drift: ${strictOut}`);
|
|
142
|
+
const strictGot = await readFile(join(dir, 'strict-fuzzy.txt'), 'utf8');
|
|
143
|
+
if (strictGot !== 'intro\nactual-before\nold\nactual-after\n') throw new Error(`fuzzy:false changed file: ${JSON.stringify(strictGot)}`);
|
|
144
|
+
|
|
145
|
+
await writeFile(join(dir, 'fuzzy-trailing-ws.txt'), 'c1 \nold\nc2\t\nc3 \n', 'utf8');
|
|
146
|
+
const fuzzyWsOut = await executePatchTool('apply_patch', {
|
|
147
|
+
base_path: dir,
|
|
148
|
+
patch: patchText([
|
|
149
|
+
'--- a/fuzzy-trailing-ws.txt',
|
|
150
|
+
'+++ b/fuzzy-trailing-ws.txt',
|
|
151
|
+
'@@ -1,4 +1,4 @@',
|
|
152
|
+
' c1',
|
|
153
|
+
'-old',
|
|
154
|
+
'+NEW',
|
|
155
|
+
' c2',
|
|
156
|
+
' c3',
|
|
157
|
+
]),
|
|
158
|
+
}, dir, { readStateScope: 'native-bridge-smoke-fuzzy-ws' });
|
|
159
|
+
if (!String(fuzzyWsOut).includes('(native)')) throw new Error(`native fuzzy whitespace bridge was not used: ${fuzzyWsOut}`);
|
|
160
|
+
const fuzzyWsGot = await readFile(join(dir, 'fuzzy-trailing-ws.txt'), 'utf8');
|
|
161
|
+
if (fuzzyWsGot !== 'c1 \nNEW\nc2\t\nc3 \n') throw new Error(`unexpected fuzzy-trailing-ws.txt: ${JSON.stringify(fuzzyWsGot)}`);
|
|
162
|
+
|
|
163
|
+
await writeFile(join(dir, 'fuzzy-trailing-ws.txt'), 'c1 \nold\nc2\t\nc3 \n', 'utf8');
|
|
164
|
+
const strictWsOut = await executePatchTool('apply_patch', {
|
|
165
|
+
base_path: dir,
|
|
166
|
+
fuzzy: false,
|
|
167
|
+
patch: patchText([
|
|
168
|
+
'--- a/fuzzy-trailing-ws.txt',
|
|
169
|
+
'+++ b/fuzzy-trailing-ws.txt',
|
|
170
|
+
'@@ -1,4 +1,4 @@',
|
|
171
|
+
' c1',
|
|
172
|
+
'-old',
|
|
173
|
+
'+NEW',
|
|
174
|
+
' c2',
|
|
175
|
+
' c3',
|
|
176
|
+
]),
|
|
177
|
+
}, dir, { readStateScope: 'native-bridge-smoke-strict-ws' });
|
|
178
|
+
if (!String(strictWsOut).startsWith('Error:')) throw new Error(`fuzzy:false should reject trailing whitespace context drift: ${strictWsOut}`);
|
|
179
|
+
const strictWsGot = await readFile(join(dir, 'fuzzy-trailing-ws.txt'), 'utf8');
|
|
180
|
+
if (strictWsGot !== 'c1 \nold\nc2\t\nc3 \n') throw new Error(`fuzzy:false whitespace changed file: ${JSON.stringify(strictWsGot)}`);
|
|
181
|
+
|
|
182
|
+
const multiOut = await executePatchTool('apply_patch', {
|
|
183
|
+
base_path: dir,
|
|
184
|
+
patch: patchText([
|
|
185
|
+
'--- /dev/null',
|
|
186
|
+
'+++ b/new.txt',
|
|
187
|
+
'@@ -0,0 +1,1 @@',
|
|
188
|
+
'+created',
|
|
189
|
+
'--- a/bridge.txt',
|
|
190
|
+
'+++ b/bridge.txt',
|
|
191
|
+
'@@ -1,1 +1,1 @@',
|
|
192
|
+
'-alpha',
|
|
193
|
+
'+ALPHA',
|
|
194
|
+
]),
|
|
195
|
+
}, dir, { readStateScope: 'native-bridge-smoke-multi' });
|
|
196
|
+
if (!String(multiOut).includes('(native)')) throw new Error(`native multi bridge was not used: ${multiOut}`);
|
|
197
|
+
const newGot = await readFile(join(dir, 'new.txt'), 'utf8');
|
|
198
|
+
if (newGot !== 'created\n') throw new Error(`unexpected new.txt: ${JSON.stringify(newGot)}`);
|
|
199
|
+
const changedGot = await readFile(join(dir, 'bridge.txt'), 'utf8');
|
|
200
|
+
if (changedGot !== 'ALPHA\nBETA\ngamma\n') throw new Error(`unexpected bridge.txt after multi: ${JSON.stringify(changedGot)}`);
|
|
201
|
+
|
|
202
|
+
const dryOut = await executePatchTool('apply_patch', {
|
|
203
|
+
base_path: dir,
|
|
204
|
+
dry_run: true,
|
|
205
|
+
patch: patchText([
|
|
206
|
+
'--- a/new.txt',
|
|
207
|
+
'+++ /dev/null',
|
|
208
|
+
'@@ -1,1 +0,0 @@',
|
|
209
|
+
'-created',
|
|
210
|
+
]),
|
|
211
|
+
}, dir, { readStateScope: 'native-bridge-smoke-dry' });
|
|
212
|
+
if (!String(dryOut).includes('(native)')) throw new Error(`native dry-run bridge was not used: ${dryOut}`);
|
|
213
|
+
const dryGot = await readFile(join(dir, 'new.txt'), 'utf8');
|
|
214
|
+
if (dryGot !== 'created\n') throw new Error(`dry-run changed new.txt: ${JSON.stringify(dryGot)}`);
|
|
215
|
+
|
|
216
|
+
await writeFile(join(dir, 'no-newline.txt'), 'a\nb', 'utf8');
|
|
217
|
+
const noNewlineOut = await executePatchTool('apply_patch', {
|
|
218
|
+
base_path: dir,
|
|
219
|
+
patch: patchText([
|
|
220
|
+
'--- a/no-newline.txt',
|
|
221
|
+
'+++ b/no-newline.txt',
|
|
222
|
+
'@@ -1,2 +1,2 @@',
|
|
223
|
+
' a',
|
|
224
|
+
'-b',
|
|
225
|
+
'\',
|
|
226
|
+
'+B',
|
|
227
|
+
'\',
|
|
228
|
+
]),
|
|
229
|
+
}, dir, { readStateScope: 'native-bridge-smoke-no-newline' });
|
|
230
|
+
if (!String(noNewlineOut).includes('(native)')) throw new Error(`native no-newline bridge was not used: ${noNewlineOut}`);
|
|
231
|
+
const noNewlineGot = await readFile(join(dir, 'no-newline.txt'), 'utf8');
|
|
232
|
+
if (noNewlineGot !== 'a\nB') throw new Error(`unexpected no-newline.txt: ${JSON.stringify(noNewlineGot)}`);
|
|
233
|
+
|
|
234
|
+
await writeFile(join(dir, 'native-fail.txt'), 'left\nright\n', 'utf8');
|
|
235
|
+
const failedOut = await executePatchTool('apply_patch', {
|
|
236
|
+
base_path: dir,
|
|
237
|
+
patch: patchText([
|
|
238
|
+
'--- a/native-fail.txt',
|
|
239
|
+
'+++ b/native-fail.txt',
|
|
240
|
+
'@@ -1,2 +1,2 @@',
|
|
241
|
+
' left',
|
|
242
|
+
'-missing',
|
|
243
|
+
'+MISSING',
|
|
244
|
+
]),
|
|
245
|
+
}, dir, { readStateScope: 'native-bridge-smoke-fail' });
|
|
246
|
+
if (!String(failedOut).startsWith('Error: native patch failed')) {
|
|
247
|
+
throw new Error(`native failure did not stop at native engine: ${failedOut}`);
|
|
248
|
+
}
|
|
249
|
+
const failedGot = await readFile(join(dir, 'native-fail.txt'), 'utf8');
|
|
250
|
+
if (failedGot !== 'left\nright\n') throw new Error(`native failure changed file: ${JSON.stringify(failedGot)}`);
|
|
251
|
+
|
|
252
|
+
await writeFile(join(dir, 'misattr-b.txt'), 'beta line 1\nbeta line 2\n', 'utf8');
|
|
253
|
+
await writeFile(join(dir, 'misattr-a.txt'), 'alpha line 1\nalpha line 2\nalpha actual later\n', 'utf8');
|
|
254
|
+
const misattrOut = await executePatchTool('apply_patch', {
|
|
255
|
+
base_path: dir,
|
|
256
|
+
fuzzy: false,
|
|
257
|
+
patch: patchText([
|
|
258
|
+
'--- a/misattr-b.txt',
|
|
259
|
+
'+++ b/misattr-b.txt',
|
|
260
|
+
'@@ -1,1 +1,1 @@',
|
|
261
|
+
'-beta line 1',
|
|
262
|
+
'+BETA line 1',
|
|
263
|
+
'--- a/misattr-a.txt',
|
|
264
|
+
'+++ b/misattr-a.txt',
|
|
265
|
+
'@@ -1,1 +1,1 @@',
|
|
266
|
+
'-alpha line 1',
|
|
267
|
+
'+ALPHA line 1',
|
|
268
|
+
'@@ -3,1 +3,1 @@',
|
|
269
|
+
'-alpha expected later',
|
|
270
|
+
'+ALPHA expected later',
|
|
271
|
+
]),
|
|
272
|
+
}, dir, { readStateScope: 'native-bridge-smoke-misattribution' });
|
|
273
|
+
if (!String(misattrOut).startsWith('Error: native patch failed')) throw new Error(`misattribution patch should fail natively: ${misattrOut}`);
|
|
274
|
+
if (String(misattrOut).includes('"beta line 1"')) throw new Error(`failure context quoted the first file: ${misattrOut}`);
|
|
275
|
+
if (!String(misattrOut).includes('misattr-a.txt') || !String(misattrOut).includes('"alpha expected later"') || !String(misattrOut).includes('nearest line 3: "alpha actual later"')) {
|
|
276
|
+
throw new Error(`failure context did not name the failing hunk: ${misattrOut}`);
|
|
277
|
+
}
|
|
278
|
+
const misattrAGot = await readFile(join(dir, 'misattr-a.txt'), 'utf8');
|
|
279
|
+
const misattrBGot = await readFile(join(dir, 'misattr-b.txt'), 'utf8');
|
|
280
|
+
if (misattrAGot !== 'alpha line 1\nalpha line 2\nalpha actual later\n' || misattrBGot !== 'beta line 1\nbeta line 2\n') {
|
|
281
|
+
throw new Error('misattribution failure mutated files despite reject_partial=true');
|
|
282
|
+
}
|
|
283
|
+
console.log('native-patch bridge smoke passed');
|
|
284
|
+
} finally {
|
|
285
|
+
await closeNativePatchServerForTests();
|
|
286
|
+
await rm(dir, { recursive: true, force: true });
|
|
287
|
+
await rm(SMOKE_PLUGIN_DATA, { recursive: true, force: true });
|
|
288
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
7
|
+
import { createInterface } from 'node:readline';
|
|
8
|
+
|
|
9
|
+
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const MANIFEST = join(ROOT, 'native/mixdog-patch/Cargo.toml');
|
|
11
|
+
const BIN = join(ROOT, 'native/mixdog-patch/target/release', process.platform === 'win32' ? 'mixdog-patch.exe' : 'mixdog-patch');
|
|
12
|
+
|
|
13
|
+
function run(cmd, args, opts = {}) {
|
|
14
|
+
const res = spawnSync(cmd, args, { encoding: 'utf8', ...opts });
|
|
15
|
+
if (res.status !== 0) {
|
|
16
|
+
throw new Error(`${cmd} ${args.join(' ')} failed\nstdout=${res.stdout}\nstderr=${res.stderr}`);
|
|
17
|
+
}
|
|
18
|
+
return res;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function patchText(lines) {
|
|
22
|
+
return `${lines.join('\n')}\n`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function expectFile(dir, path, expected) {
|
|
26
|
+
const got = await readFile(join(dir, path), 'utf8');
|
|
27
|
+
if (got !== expected) throw new Error(`${path}: got ${JSON.stringify(got)}, expected ${JSON.stringify(expected)}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function expectMissing(dir, path) {
|
|
31
|
+
try {
|
|
32
|
+
await readFile(join(dir, path), 'utf8');
|
|
33
|
+
throw new Error(`${path}: expected missing, but file exists`);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
if (err?.code !== 'ENOENT') throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function applyNative(dir, patch) {
|
|
40
|
+
run(BIN, ['--base', dir], { input: patch });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function dryRunNative(dir, patch) {
|
|
44
|
+
run(BIN, ['--base', dir, '--dry-run'], { input: patch });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function applyNativeError(dir, patch) {
|
|
48
|
+
const result = spawnSync(BIN, ['--base', dir], { input: patch, encoding: 'utf8', stdio: 'pipe' });
|
|
49
|
+
if (result.status === 0) {
|
|
50
|
+
throw new Error(`native patch unexpectedly succeeded\nstdout=${result.stdout || ''}\nstderr=${result.stderr || ''}`);
|
|
51
|
+
}
|
|
52
|
+
return `${result.stdout || ''}${result.stderr || ''}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class NativePatchServer {
|
|
56
|
+
constructor() {
|
|
57
|
+
this.child = spawn(BIN, ['--server'], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
58
|
+
this.stderr = '';
|
|
59
|
+
this.lines = [];
|
|
60
|
+
this.waiters = [];
|
|
61
|
+
this.child.stderr.setEncoding('utf8');
|
|
62
|
+
this.child.stderr.on('data', (chunk) => { this.stderr += chunk; });
|
|
63
|
+
this.rl = createInterface({ input: this.child.stdout });
|
|
64
|
+
this.rl.on('line', (line) => {
|
|
65
|
+
const waiter = this.waiters.shift();
|
|
66
|
+
if (waiter) waiter(line);
|
|
67
|
+
else this.lines.push(line);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
nextLine() {
|
|
72
|
+
if (this.lines.length > 0) return Promise.resolve(this.lines.shift());
|
|
73
|
+
return new Promise((resolve) => this.waiters.push(resolve));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async apply(dir, patch) {
|
|
77
|
+
const baseBuf = Buffer.from(dir, 'utf8');
|
|
78
|
+
const patchBuf = Buffer.from(patch, 'utf8');
|
|
79
|
+
const linePromise = this.nextLine();
|
|
80
|
+
this.child.stdin.write(`APPLY ${baseBuf.length} ${patchBuf.length} 1 0 2 1\n`);
|
|
81
|
+
this.child.stdin.write(baseBuf);
|
|
82
|
+
this.child.stdin.write(patchBuf);
|
|
83
|
+
const line = await linePromise;
|
|
84
|
+
if (!line.startsWith('OK\t')) {
|
|
85
|
+
throw new Error(`native server failed: ${line}\nstderr=${this.stderr}`);
|
|
86
|
+
}
|
|
87
|
+
const contentHash = line.split('\t')[7];
|
|
88
|
+
if (!/^[a-f0-9]{64}$/i.test(contentHash || '')) {
|
|
89
|
+
throw new Error(`native server missing content hash: ${line}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async close() {
|
|
94
|
+
this.child.stdin.end('QUIT\n');
|
|
95
|
+
await new Promise((resolve) => this.child.once('exit', resolve));
|
|
96
|
+
this.rl.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
run('cargo', ['build', '--release', '--manifest-path', MANIFEST]);
|
|
101
|
+
|
|
102
|
+
const dir = await mkdtemp(join(tmpdir(), 'mixdog-native-patch-smoke-'));
|
|
103
|
+
try {
|
|
104
|
+
await writeFile(join(dir, 'replace.txt'), 'a\nb\nc\n', 'utf8');
|
|
105
|
+
await applyNative(dir, patchText([
|
|
106
|
+
'--- a/replace.txt',
|
|
107
|
+
'+++ b/replace.txt',
|
|
108
|
+
'@@ -2,1 +2,1 @@',
|
|
109
|
+
'-b',
|
|
110
|
+
'+B',
|
|
111
|
+
]));
|
|
112
|
+
await expectFile(dir, 'replace.txt', 'a\nB\nc\n');
|
|
113
|
+
|
|
114
|
+
await writeFile(join(dir, 'insert.txt'), 'a\nc\n', 'utf8');
|
|
115
|
+
await applyNative(dir, patchText([
|
|
116
|
+
'--- a/insert.txt',
|
|
117
|
+
'+++ b/insert.txt',
|
|
118
|
+
'@@ -1,0 +2,1 @@',
|
|
119
|
+
'+b',
|
|
120
|
+
]));
|
|
121
|
+
await expectFile(dir, 'insert.txt', 'a\nb\nc\n');
|
|
122
|
+
|
|
123
|
+
await writeFile(join(dir, 'crlf.txt'), 'a\r\nb\r\nc\r\n', 'utf8');
|
|
124
|
+
await applyNative(dir, patchText([
|
|
125
|
+
'--- a/crlf.txt',
|
|
126
|
+
'+++ b/crlf.txt',
|
|
127
|
+
'@@ -2,1 +2,1 @@',
|
|
128
|
+
'-b',
|
|
129
|
+
'+B',
|
|
130
|
+
]));
|
|
131
|
+
await expectFile(dir, 'crlf.txt', 'a\r\nB\r\nc\r\n');
|
|
132
|
+
|
|
133
|
+
await writeFile(join(dir, 'multi.txt'), 'h\nm\nt\n', 'utf8');
|
|
134
|
+
await applyNative(dir, patchText([
|
|
135
|
+
'--- a/multi.txt',
|
|
136
|
+
'+++ b/multi.txt',
|
|
137
|
+
'@@ -1,1 +1,1 @@',
|
|
138
|
+
'-h',
|
|
139
|
+
'+H',
|
|
140
|
+
'@@ -3,1 +3,1 @@',
|
|
141
|
+
'-t',
|
|
142
|
+
'+T',
|
|
143
|
+
]));
|
|
144
|
+
await expectFile(dir, 'multi.txt', 'H\nm\nT\n');
|
|
145
|
+
|
|
146
|
+
await writeFile(join(dir, 'fuzzy-offset.txt'), 'intro\nkeep\nold\ntrail\n', 'utf8');
|
|
147
|
+
await applyNative(dir, patchText([
|
|
148
|
+
'--- a/fuzzy-offset.txt',
|
|
149
|
+
'+++ b/fuzzy-offset.txt',
|
|
150
|
+
'@@ -20,3 +20,3 @@',
|
|
151
|
+
' keep',
|
|
152
|
+
'-old',
|
|
153
|
+
'+NEW',
|
|
154
|
+
' trail',
|
|
155
|
+
]));
|
|
156
|
+
await expectFile(dir, 'fuzzy-offset.txt', 'intro\nkeep\nNEW\ntrail\n');
|
|
157
|
+
|
|
158
|
+
await writeFile(join(dir, 'fuzzy-context.txt'), 'intro\nactual-before\nold\nactual-after\n', 'utf8');
|
|
159
|
+
await applyNative(dir, patchText([
|
|
160
|
+
'--- a/fuzzy-context.txt',
|
|
161
|
+
'+++ b/fuzzy-context.txt',
|
|
162
|
+
'@@ -1,4 +1,4 @@',
|
|
163
|
+
' intro',
|
|
164
|
+
' expected-before',
|
|
165
|
+
'-old',
|
|
166
|
+
'+NEW',
|
|
167
|
+
' expected-after',
|
|
168
|
+
]));
|
|
169
|
+
await expectFile(dir, 'fuzzy-context.txt', 'intro\nactual-before\nNEW\nactual-after\n');
|
|
170
|
+
|
|
171
|
+
await writeFile(join(dir, 'fuzzy-trailing-ws.txt'), 'c1 \nold\nc2\t\nc3 \n', 'utf8');
|
|
172
|
+
await applyNative(dir, patchText([
|
|
173
|
+
'--- a/fuzzy-trailing-ws.txt',
|
|
174
|
+
'+++ b/fuzzy-trailing-ws.txt',
|
|
175
|
+
'@@ -1,4 +1,4 @@',
|
|
176
|
+
' c1',
|
|
177
|
+
'-old',
|
|
178
|
+
'+NEW',
|
|
179
|
+
' c2',
|
|
180
|
+
' c3',
|
|
181
|
+
]));
|
|
182
|
+
await expectFile(dir, 'fuzzy-trailing-ws.txt', 'c1 \nNEW\nc2\t\nc3 \n');
|
|
183
|
+
|
|
184
|
+
await writeFile(join(dir, 'no-newline.txt'), 'a\nb', 'utf8');
|
|
185
|
+
await applyNative(dir, patchText([
|
|
186
|
+
'--- a/no-newline.txt',
|
|
187
|
+
'+++ b/no-newline.txt',
|
|
188
|
+
'@@ -1,2 +1,2 @@',
|
|
189
|
+
' a',
|
|
190
|
+
'-b',
|
|
191
|
+
'\',
|
|
192
|
+
'+B',
|
|
193
|
+
'\',
|
|
194
|
+
]));
|
|
195
|
+
await expectFile(dir, 'no-newline.txt', 'a\nB');
|
|
196
|
+
|
|
197
|
+
await writeFile(join(dir, 'add-newline.txt'), 'a\nb', 'utf8');
|
|
198
|
+
await applyNative(dir, patchText([
|
|
199
|
+
'--- a/add-newline.txt',
|
|
200
|
+
'+++ b/add-newline.txt',
|
|
201
|
+
'@@ -1,2 +1,2 @@',
|
|
202
|
+
' a',
|
|
203
|
+
'-b',
|
|
204
|
+
'\',
|
|
205
|
+
'+B',
|
|
206
|
+
]));
|
|
207
|
+
await expectFile(dir, 'add-newline.txt', 'a\nB\n');
|
|
208
|
+
|
|
209
|
+
await applyNative(dir, patchText([
|
|
210
|
+
'--- /dev/null',
|
|
211
|
+
'+++ b/created.txt',
|
|
212
|
+
'@@ -0,0 +1,2 @@',
|
|
213
|
+
'+one',
|
|
214
|
+
'+two',
|
|
215
|
+
]));
|
|
216
|
+
await expectFile(dir, 'created.txt', 'one\ntwo\n');
|
|
217
|
+
|
|
218
|
+
await writeFile(join(dir, 'deleted.txt'), 'gone\n', 'utf8');
|
|
219
|
+
await applyNative(dir, patchText([
|
|
220
|
+
'--- a/deleted.txt',
|
|
221
|
+
'+++ /dev/null',
|
|
222
|
+
'@@ -1,1 +0,0 @@',
|
|
223
|
+
'-gone',
|
|
224
|
+
]));
|
|
225
|
+
await expectMissing(dir, 'deleted.txt');
|
|
226
|
+
|
|
227
|
+
await writeFile(join(dir, 'empty-delete.txt'), '', 'utf8');
|
|
228
|
+
await applyNative(dir, patchText([
|
|
229
|
+
'--- a/empty-delete.txt',
|
|
230
|
+
'+++ /dev/null',
|
|
231
|
+
'@@ -1,0 +0,0 @@',
|
|
232
|
+
]));
|
|
233
|
+
await expectMissing(dir, 'empty-delete.txt');
|
|
234
|
+
|
|
235
|
+
await writeFile(join(dir, 'nonempty-zero-delete.txt'), 'still\n', 'utf8');
|
|
236
|
+
const nonemptyZeroDelete = await applyNativeError(dir, patchText([
|
|
237
|
+
'--- a/nonempty-zero-delete.txt',
|
|
238
|
+
'+++ /dev/null',
|
|
239
|
+
'@@ -1,0 +0,0 @@',
|
|
240
|
+
]));
|
|
241
|
+
if (!nonemptyZeroDelete.includes('residual byte')) {
|
|
242
|
+
throw new Error(`native non-empty zero delete lacked residual hint: ${nonemptyZeroDelete}`);
|
|
243
|
+
}
|
|
244
|
+
await expectFile(dir, 'nonempty-zero-delete.txt', 'still\n');
|
|
245
|
+
|
|
246
|
+
await writeFile(join(dir, 'bad-range.txt'), 'x\n', 'utf8');
|
|
247
|
+
const badRange = await applyNativeError(dir, patchText([
|
|
248
|
+
'--- a/bad-range.txt',
|
|
249
|
+
'+++ b/bad-range.txt',
|
|
250
|
+
'@@ anchor',
|
|
251
|
+
'-x',
|
|
252
|
+
'+X',
|
|
253
|
+
]));
|
|
254
|
+
if (!badRange.includes('@@ -A,B +C,D @@')) {
|
|
255
|
+
throw new Error(`native bad range lacked counted hunk hint: ${badRange}`);
|
|
256
|
+
}
|
|
257
|
+
await expectFile(dir, 'bad-range.txt', 'x\n');
|
|
258
|
+
|
|
259
|
+
await writeFile(join(dir, 'dry.txt'), 'stay\n', 'utf8');
|
|
260
|
+
await dryRunNative(dir, patchText([
|
|
261
|
+
'--- a/dry.txt',
|
|
262
|
+
'+++ b/dry.txt',
|
|
263
|
+
'@@ -1,1 +1,1 @@',
|
|
264
|
+
'-stay',
|
|
265
|
+
'+changed',
|
|
266
|
+
]));
|
|
267
|
+
await expectFile(dir, 'dry.txt', 'stay\n');
|
|
268
|
+
|
|
269
|
+
await writeFile(join(dir, 'a.txt'), 'a\n', 'utf8');
|
|
270
|
+
await writeFile(join(dir, 'b.txt'), 'b\n', 'utf8');
|
|
271
|
+
await applyNative(dir, patchText([
|
|
272
|
+
'--- a/a.txt',
|
|
273
|
+
'+++ b/a.txt',
|
|
274
|
+
'@@ -1,1 +1,1 @@',
|
|
275
|
+
'-a',
|
|
276
|
+
'+A',
|
|
277
|
+
'--- a/b.txt',
|
|
278
|
+
'+++ b/b.txt',
|
|
279
|
+
'@@ -1,1 +1,1 @@',
|
|
280
|
+
'-b',
|
|
281
|
+
'+B',
|
|
282
|
+
]));
|
|
283
|
+
await expectFile(dir, 'a.txt', 'A\n');
|
|
284
|
+
await expectFile(dir, 'b.txt', 'B\n');
|
|
285
|
+
|
|
286
|
+
const server = new NativePatchServer();
|
|
287
|
+
try {
|
|
288
|
+
await writeFile(join(dir, 'server.txt'), 'one\ntwo\n', 'utf8');
|
|
289
|
+
await server.apply(dir, patchText([
|
|
290
|
+
'--- a/server.txt',
|
|
291
|
+
'+++ b/server.txt',
|
|
292
|
+
'@@ -2,1 +2,1 @@',
|
|
293
|
+
'-two',
|
|
294
|
+
'+TWO',
|
|
295
|
+
]));
|
|
296
|
+
await expectFile(dir, 'server.txt', 'one\nTWO\n');
|
|
297
|
+
} finally {
|
|
298
|
+
await server.close();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log('native-patch smoke passed');
|
|
302
|
+
} finally {
|
|
303
|
+
await rm(dir, { recursive: true, force: true });
|
|
304
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Regression: the JS diagnostic matcher must mirror native
|
|
2
|
+
// evaluate_fuzzy_candidate's change-band logic. Content drift on INTERIOR
|
|
3
|
+
// context (strictly between the first and last change) must NOT be
|
|
4
|
+
// fuzz-consumable -> findFirstFailingUnifiedHunk reports the hunk as failing.
|
|
5
|
+
// Content drift on OUTER context (before first change / after last change),
|
|
6
|
+
// within the fuzz budget, still matches -> no failing hunk.
|
|
7
|
+
import { __patchTestHooks } from '../src/agent/orchestrator/tools/patch.mjs';
|
|
8
|
+
|
|
9
|
+
const { findFirstFailingUnifiedHunk, computeUnifiedChangeBand, collectUnifiedOps } = __patchTestHooks;
|
|
10
|
+
|
|
11
|
+
function assert(cond, msg) {
|
|
12
|
+
if (!cond) throw new Error(`assertion failed: ${msg}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Hunk shape used by the diagnostic matcher: { oldStart, lines: ['<tag><text>'] }.
|
|
16
|
+
// keep1 (outer ctx) / del1 / mid (interior ctx) / del2 / keep2 (outer ctx).
|
|
17
|
+
function makeHunk() {
|
|
18
|
+
return {
|
|
19
|
+
oldStart: 1,
|
|
20
|
+
lines: [' keep1', '-del1', ' mid', '-del2', ' keep2'],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// 1) Band sanity: changes (deletes) bound an interior context line.
|
|
25
|
+
{
|
|
26
|
+
const ops = collectUnifiedOps(makeHunk());
|
|
27
|
+
assert(JSON.stringify(ops) === JSON.stringify(['context', 'delete', 'context', 'delete', 'context']), 'ops sequence');
|
|
28
|
+
const band = computeUnifiedChangeBand(ops);
|
|
29
|
+
// del1 -> (1+1)*2=4, del2 -> (3+1)*2=8; mid offset 2 -> (2+1)*2=6 is interior.
|
|
30
|
+
assert(band.first === 4 && band.last === 8, `band ${JSON.stringify(band)}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2) INTERIOR context content drift must NOT match (hunk reported as failing).
|
|
34
|
+
{
|
|
35
|
+
const entry = { oldFileName: 'a/x', hunks: [makeHunk()] };
|
|
36
|
+
const source = ['keep1', 'del1', 'midDRIFTED', 'del2', 'keep2'];
|
|
37
|
+
const failing = findFirstFailingUnifiedHunk(entry, source, 2);
|
|
38
|
+
assert(failing === entry.hunks[0], 'interior drift must produce a failing hunk');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 3) OUTER context content drift within fuzz budget still matches (no failure).
|
|
42
|
+
{
|
|
43
|
+
const entry = { oldFileName: 'a/x', hunks: [makeHunk()] };
|
|
44
|
+
const source = ['keep1DRIFTED', 'del1', 'mid', 'del2', 'keep2'];
|
|
45
|
+
const failing = findFirstFailingUnifiedHunk(entry, source, 1);
|
|
46
|
+
assert(failing === null, 'outer drift within budget must match (no failing hunk)');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log('patch-interior-context-smoke OK');
|