mixdog 0.9.1 → 0.9.3
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 +9 -1
- package/scripts/_bench-cwc.json +20 -0
- package/scripts/agent-loop-policy-test.mjs +37 -0
- package/scripts/agent-parallel-smoke.mjs +54 -10
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/background-task-meta-smoke.mjs +1 -1
- package/scripts/bench-run.mjs +262 -0
- package/scripts/build-tui.mjs +13 -1
- package/scripts/compact-smoke.mjs +12 -0
- package/scripts/compact-trigger-migration-smoke.mjs +67 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
- package/scripts/internal-comms-bench.mjs +727 -0
- package/scripts/internal-comms-smoke.mjs +75 -0
- package/scripts/lead-workflow-smoke.mjs +4 -4
- package/scripts/live-worker-smoke.mjs +9 -9
- package/scripts/output-style-bench.mjs +285 -0
- package/scripts/output-style-smoke.mjs +13 -10
- package/scripts/patch-replay.mjs +90 -0
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-stream-stall-test.mjs +276 -0
- package/scripts/provider-toolcall-test.mjs +599 -1
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/routing-corpus.mjs +281 -0
- package/scripts/session-bench.mjs +1526 -0
- package/scripts/session-diag.mjs +595 -0
- package/scripts/task-bench.mjs +207 -0
- package/scripts/tool-failures.mjs +6 -6
- package/scripts/tool-smoke.mjs +310 -67
- package/scripts/toolcall-args-test.mjs +81 -0
- package/src/agents/debugger/AGENT.md +4 -4
- package/src/agents/heavy-worker/AGENT.md +20 -9
- package/src/agents/reviewer/AGENT.md +4 -4
- package/src/agents/worker/AGENT.md +17 -9
- package/src/app.mjs +10 -6
- package/src/defaults/{hidden-roles.json → agents.json} +7 -7
- package/src/examples/schedules/SCHEDULE.example.md +32 -0
- package/src/examples/webhooks/WEBHOOK.example.md +40 -0
- package/src/headless-role.mjs +14 -14
- package/src/help.mjs +1 -0
- package/src/lib/rules-builder.cjs +32 -54
- package/src/mixdog-session-runtime.mjs +1040 -2036
- package/src/output-styles/default.md +12 -7
- package/src/output-styles/minimal.md +25 -0
- package/src/output-styles/oneline.md +21 -0
- package/src/output-styles/simple.md +10 -9
- package/src/repl.mjs +17 -7
- package/src/rules/agent/00-common.md +7 -5
- package/src/rules/agent/30-explorer.md +8 -12
- package/src/rules/lead/01-general.md +3 -1
- package/src/rules/lead/lead-tool.md +13 -2
- package/src/rules/shared/01-tool.md +23 -12
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
- package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
- package/src/runtime/agent/orchestrator/config.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
- package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
- package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
- package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
- package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +49 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +513 -1000
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +497 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1192 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
- package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/channels/backends/discord.mjs +99 -9
- package/src/runtime/channels/backends/telegram.mjs +501 -0
- package/src/runtime/channels/index.mjs +437 -1439
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/config.mjs +54 -2
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/format.mjs +4 -2
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
- package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +280 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
- package/src/runtime/channels/lib/webhook.mjs +59 -31
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +465 -345
- package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
- package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +121 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/recall-format.mjs +183 -0
- package/src/runtime/memory/lib/session-ingest.mjs +107 -0
- package/src/runtime/memory/lib/trace-store.mjs +69 -22
- package/src/runtime/memory/tool-defs.mjs +10 -7
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/channel-notification-routing.mjs +12 -0
- package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
- package/src/runtime/shared/config.mjs +9 -0
- package/src/runtime/shared/llm/http-agent.mjs +12 -5
- package/src/runtime/shared/schedules-store.mjs +21 -19
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-surface.mjs +98 -13
- package/src/runtime/shared/transcript-writer.mjs +156 -0
- package/src/runtime/shared/update-checker.mjs +214 -0
- package/src/session-runtime/config-helpers.mjs +209 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/output-styles.mjs +124 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool.mjs +302 -117
- package/src/standalone/channel-admin.mjs +133 -40
- package/src/standalone/channel-worker.mjs +10 -292
- package/src/standalone/explore-tool.mjs +12 -5
- package/src/standalone/hook-bus.mjs +165 -8
- package/src/standalone/memory-runtime-proxy.mjs +3 -1
- package/src/standalone/opencode-go-login.mjs +121 -0
- package/src/standalone/provider-admin.mjs +25 -3
- package/src/standalone/usage-dashboard.mjs +1 -1
- package/src/tui/App.jsx +2883 -778
- package/src/tui/components/ConfirmBar.jsx +47 -0
- package/src/tui/components/ContextPanel.jsx +5 -3
- package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
- package/src/tui/components/Markdown.jsx +22 -98
- package/src/tui/components/Message.jsx +14 -35
- package/src/tui/components/Picker.jsx +87 -12
- package/src/tui/components/PromptInput.jsx +355 -22
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +8 -5
- package/src/tui/components/Spinner.jsx +7 -7
- package/src/tui/components/StatusLine.jsx +40 -21
- package/src/tui/components/TextEntryPanel.jsx +51 -7
- package/src/tui/components/ToolExecution.jsx +183 -101
- package/src/tui/components/TurnDone.jsx +4 -4
- package/src/tui/components/UsagePanel.jsx +1 -1
- package/src/tui/components/tool-output-format.mjs +161 -23
- package/src/tui/components/tool-output-format.test.mjs +87 -0
- package/src/tui/display-width.mjs +69 -0
- package/src/tui/display-width.test.mjs +35 -0
- package/src/tui/dist/index.mjs +6731 -2333
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +569 -948
- package/src/tui/index.jsx +117 -7
- package/src/tui/input-editing.mjs +58 -8
- package/src/tui/input-editing.selection.test.mjs +75 -0
- package/src/tui/keyboard-protocol.mjs +42 -0
- package/src/tui/lib/voice-recorder.mjs +469 -0
- package/src/tui/markdown/format-token.mjs +128 -76
- package/src/tui/markdown/format-token.test.mjs +61 -19
- package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
- package/src/tui/markdown/render-ansi.test.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +167 -0
- package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
- package/src/tui/markdown/table-layout.mjs +9 -9
- package/src/tui/paste-attachments.mjs +36 -9
- package/src/tui/paste-fix.test.mjs +119 -0
- package/src/tui/prompt-history-store.mjs +129 -0
- package/src/tui/prompt-history-store.test.mjs +52 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
- package/src/tui/theme.mjs +41 -657
- package/src/tui/themes/base.mjs +86 -0
- package/src/tui/themes/basic.mjs +85 -0
- package/src/tui/themes/catppuccin.mjs +72 -0
- package/src/tui/themes/dracula.mjs +70 -0
- package/src/tui/themes/everforest.mjs +71 -0
- package/src/tui/themes/gruvbox.mjs +71 -0
- package/src/tui/themes/index.mjs +71 -0
- package/src/tui/themes/indigo.mjs +78 -0
- package/src/tui/themes/kanagawa.mjs +80 -0
- package/src/tui/themes/light.mjs +81 -0
- package/src/tui/themes/nord.mjs +72 -0
- package/src/tui/themes/onedark.mjs +16 -0
- package/src/tui/themes/rosepine.mjs +70 -0
- package/src/tui/themes/teal.mjs +80 -0
- package/src/tui/themes/tokyonight.mjs +79 -0
- package/src/tui/themes/utils.mjs +106 -0
- package/src/tui/themes/warm.mjs +79 -0
- package/src/tui/transcript-tool-failures.mjs +13 -2
- package/src/ui/markdown.mjs +1 -1
- package/src/ui/model-display.mjs +2 -2
- package/src/ui/statusline.mjs +75 -27
- package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
- package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
- package/src/workflows/default/WORKFLOW.md +46 -12
- package/src/workflows/sequential/WORKFLOW.md +51 -0
- package/src/workflows/solo/WORKFLOW.md +12 -1
- package/vendor/ink/build/display-width.js +62 -0
- package/vendor/ink/build/ink.js +100 -12
- package/vendor/ink/build/measure-text.js +4 -1
- package/vendor/ink/build/output.js +115 -9
- package/vendor/ink/build/render-node-to-output.js +4 -1
- package/vendor/ink/build/render.js +4 -0
- package/src/output-styles/extreme-simple.md +0 -20
- package/src/rules/lead/04-workflow.md +0 -51
- package/src/workflows/default/workflow.json +0 -13
- package/src/workflows/solo/workflow.json +0 -7
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// responds with OK_PARTIAL plus a hex-encoded failures payload that
|
|
28
28
|
// the JS side surfaces per-entry.
|
|
29
29
|
|
|
30
|
-
import { existsSync, readFileSync, realpathSync, statSync, mkdirSync } from 'node:fs';
|
|
30
|
+
import { existsSync, readFileSync, realpathSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
31
31
|
import { unlink } from 'node:fs/promises';
|
|
32
32
|
import { spawn } from 'node:child_process';
|
|
33
33
|
import { createInterface } from 'node:readline';
|
|
@@ -581,6 +581,41 @@ function isPatchErrorText(text) {
|
|
|
581
581
|
return /^Error:/i.test(String(text ?? '').trimStart());
|
|
582
582
|
}
|
|
583
583
|
|
|
584
|
+
// ── UI-only unified-diff side-channel ───────────────────────────────────────
|
|
585
|
+
// apply_patch's MODEL-VISIBLE result stays the compact summary ("Applied 1
|
|
586
|
+
// File (Native)\n OK Modify path — +1 Line · -1 Line"). The standard unified
|
|
587
|
+
// diff it builds internally (nativePatchStr) is stashed here, keyed by the
|
|
588
|
+
// tool_use id, so the agent loop can attach it to the TUI tool-result message
|
|
589
|
+
// as a UI-only field (never sent to the model). The TUI's expanded (ctrl+o)
|
|
590
|
+
// raw view then colorizes it via the existing formatExpandedResult/diff path.
|
|
591
|
+
// Size-capped per entry and FIFO-bounded so a huge patch (or a leaked entry
|
|
592
|
+
// whose take() never ran) cannot grow the transcript item or this Map.
|
|
593
|
+
const APPLY_PATCH_UI_DIFF_MAX_CHARS = 64 * 1024;
|
|
594
|
+
const APPLY_PATCH_UI_DIFF_REGISTRY_MAX = 64;
|
|
595
|
+
const _applyPatchUiDiffByCallId = new Map();
|
|
596
|
+
|
|
597
|
+
function registerApplyPatchUiDiff(callId, diff) {
|
|
598
|
+
if (!callId || typeof diff !== 'string' || !diff.trim()) return;
|
|
599
|
+
let text = diff;
|
|
600
|
+
if (text.length > APPLY_PATCH_UI_DIFF_MAX_CHARS) {
|
|
601
|
+
text = `${text.slice(0, APPLY_PATCH_UI_DIFF_MAX_CHARS)}\n… [diff truncated for display]`;
|
|
602
|
+
}
|
|
603
|
+
if (_applyPatchUiDiffByCallId.size >= APPLY_PATCH_UI_DIFF_REGISTRY_MAX) {
|
|
604
|
+
const oldest = _applyPatchUiDiffByCallId.keys().next().value;
|
|
605
|
+
if (oldest !== undefined) _applyPatchUiDiffByCallId.delete(oldest);
|
|
606
|
+
}
|
|
607
|
+
_applyPatchUiDiffByCallId.set(callId, text);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Consume (read + remove) the UI-only unified diff for a tool_use id. Returns
|
|
611
|
+
// null when none was registered (error/rename-only/partial-fail/dry-skip).
|
|
612
|
+
export function takeApplyPatchUiDiff(callId) {
|
|
613
|
+
if (!callId) return null;
|
|
614
|
+
const value = _applyPatchUiDiffByCallId.get(callId) || null;
|
|
615
|
+
if (value != null) _applyPatchUiDiffByCallId.delete(callId);
|
|
616
|
+
return value;
|
|
617
|
+
}
|
|
618
|
+
|
|
584
619
|
// Count how many source lines a hunk consumes vs produces so we can
|
|
585
620
|
// surface a concise `lines_changed` figure without re-diffing.
|
|
586
621
|
function countHunkChanges(hunks) {
|
|
@@ -1332,7 +1367,7 @@ function prepareInput(patchStr) {
|
|
|
1332
1367
|
return String(patchStr).replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
|
|
1333
1368
|
}
|
|
1334
1369
|
|
|
1335
|
-
function
|
|
1370
|
+
function isApplyPatchEnvelope(patchStr) {
|
|
1336
1371
|
const text = prepareInput(patchStr).trimStart();
|
|
1337
1372
|
return text.startsWith('*** Begin Patch')
|
|
1338
1373
|
|| text.startsWith('*** Add File:')
|
|
@@ -1342,7 +1377,7 @@ function isCodexApplyPatchEnvelope(patchStr) {
|
|
|
1342
1377
|
|
|
1343
1378
|
function isV4APatchInput(patchStr, format) {
|
|
1344
1379
|
return String(format || '').toLowerCase() === 'v4a'
|
|
1345
|
-
||
|
|
1380
|
+
|| isApplyPatchEnvelope(patchStr);
|
|
1346
1381
|
}
|
|
1347
1382
|
|
|
1348
1383
|
const UNIFIED_HUNK_HEADER_RE = /^@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@/;
|
|
@@ -2806,6 +2841,14 @@ async function apply_patch(args, cwd, options = {}) {
|
|
|
2806
2841
|
if (renameLines.length > 0 && !isPatchErrorText(nativeResult)) {
|
|
2807
2842
|
combined = `${renameLines.join('\n')}\n${nativeResult}`;
|
|
2808
2843
|
}
|
|
2844
|
+
// UI-only: stash the standard unified diff (nativePatchStr) keyed by the
|
|
2845
|
+
// tool_use id so the agent loop can attach it to the TUI tool-result for the
|
|
2846
|
+
// expanded (ctrl+o) colored-diff view. NEVER added to the model-visible
|
|
2847
|
+
// result string. Only on clean success — partial/failed cases surface as an
|
|
2848
|
+
// Error card with the failure text instead.
|
|
2849
|
+
if (!isPatchErrorText(combined) && options?.toolCallId) {
|
|
2850
|
+
registerApplyPatchUiDiff(options.toolCallId, nativePatchStr);
|
|
2851
|
+
}
|
|
2809
2852
|
if (!isPatchErrorText(combined) && rejectedV4AHunks.length > 0) {
|
|
2810
2853
|
const tail = [
|
|
2811
2854
|
'',
|
|
@@ -2824,6 +2867,74 @@ async function apply_patch(args, cwd, options = {}) {
|
|
|
2824
2867
|
export const __patchTestHooks = { findFirstFailingUnifiedHunk, computeUnifiedChangeBand, collectUnifiedOps, unifiedOldLinesMatchAt, splitBufferLinesForPatch };
|
|
2825
2868
|
|
|
2826
2869
|
export async function executePatchTool(name, args, cwd, options = {}) {
|
|
2870
|
+
// --- Opt-in failure replay capture -------------------------------------
|
|
2871
|
+
// When MIXDOG_PATCH_REPLAY_CAPTURE=1, a FAILED apply_patch (throw or
|
|
2872
|
+
// "Error:" body) is frozen to a replay file with the ORIGINAL patch args,
|
|
2873
|
+
// cwd, error, and a full snapshot of every target file's current bytes, so
|
|
2874
|
+
// scripts/patch-replay.mjs can re-run the exact failing call against updated
|
|
2875
|
+
// code. Local dev only; the patch body is stored verbatim (no redaction).
|
|
2876
|
+
return _executePatchTool(name, args, cwd, options);
|
|
2877
|
+
}
|
|
2878
|
+
|
|
2879
|
+
function patchReplayDir() {
|
|
2880
|
+
const base = process.env.MIXDOG_PATCH_REPLAY_DIR
|
|
2881
|
+
|| pathJoin(getPluginDataDir(), 'history', 'patch-replays');
|
|
2882
|
+
return base;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
function getPluginDataDir() {
|
|
2886
|
+
try { return getPluginData(); } catch { /* fall through */ }
|
|
2887
|
+
return process.env.MIXDOG_DATA_DIR || pathJoin(process.env.USERPROFILE || process.env.HOME || '.', '.mixdog', 'data');
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
function patchTargetPaths(patchStr, basePath) {
|
|
2891
|
+
const out = [];
|
|
2892
|
+
const re = /^\*\*\* (?:Update|Add|Delete) File:\s*(.+)$/gm;
|
|
2893
|
+
let m;
|
|
2894
|
+
while ((m = re.exec(String(patchStr || '')))) {
|
|
2895
|
+
const rel = m[1].trim();
|
|
2896
|
+
if (rel) out.push(rel);
|
|
2897
|
+
}
|
|
2898
|
+
// unified headers
|
|
2899
|
+
const ure = /^\+\+\+ (?:b\/)?(.+)$/gm;
|
|
2900
|
+
while ((m = ure.exec(String(patchStr || '')))) {
|
|
2901
|
+
const rel = m[1].trim();
|
|
2902
|
+
if (rel && rel !== '/dev/null') out.push(rel);
|
|
2903
|
+
}
|
|
2904
|
+
return [...new Set(out)];
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
function maybeCapturePatchReplay(args, cwd, errorText) {
|
|
2908
|
+
if (process.env.MIXDOG_PATCH_REPLAY_CAPTURE !== '1') return;
|
|
2909
|
+
try {
|
|
2910
|
+
const patchStr = typeof args?.patch === 'string' ? args.patch : '';
|
|
2911
|
+
const basePath = pathResolve(String(args?.base_path || cwd || process.cwd()));
|
|
2912
|
+
const dir = patchReplayDir();
|
|
2913
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
2914
|
+
const rels = patchTargetPaths(patchStr, basePath);
|
|
2915
|
+
const files = {};
|
|
2916
|
+
for (const rel of rels) {
|
|
2917
|
+
try {
|
|
2918
|
+
const abs = isAbsolute(rel) ? rel : pathResolve(basePath, rel);
|
|
2919
|
+
files[rel] = existsSync(abs) ? readFileSync(abs, 'utf8') : null;
|
|
2920
|
+
} catch { files[rel] = null; }
|
|
2921
|
+
}
|
|
2922
|
+
const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
2923
|
+
const record = {
|
|
2924
|
+
id,
|
|
2925
|
+
ts: Date.now(),
|
|
2926
|
+
tool: 'apply_patch',
|
|
2927
|
+
args: { patch: patchStr, base_path: args?.base_path ?? null, format: args?.format ?? null, dry_run: args?.dry_run ?? null, fuzzy: args?.fuzzy ?? null, reject_partial: args?.reject_partial ?? null },
|
|
2928
|
+
cwd: basePath,
|
|
2929
|
+
error_first_line: String(errorText || '').split('\n')[0].slice(0, 400),
|
|
2930
|
+
targets: rels,
|
|
2931
|
+
file_snapshots: files,
|
|
2932
|
+
};
|
|
2933
|
+
writeFileSync(pathJoin(dir, `${id}.json`), JSON.stringify(record, null, 2), { mode: 0o600 });
|
|
2934
|
+
} catch { /* capture is best-effort; never affect the tool result */ }
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
async function _executePatchTool(name, args, cwd, options = {}) {
|
|
2827
2938
|
const effectiveCwd = cwd || process.cwd();
|
|
2828
2939
|
switch (name) {
|
|
2829
2940
|
case 'apply_patch': {
|
|
@@ -2831,9 +2942,12 @@ export async function executePatchTool(name, args, cwd, options = {}) {
|
|
|
2831
2942
|
try {
|
|
2832
2943
|
result = await apply_patch(args || {}, effectiveCwd, options);
|
|
2833
2944
|
} catch (err) {
|
|
2834
|
-
|
|
2945
|
+
const errText = `Error: ${err?.message || String(err)}`;
|
|
2946
|
+
maybeCapturePatchReplay(args, effectiveCwd, errText);
|
|
2947
|
+
return errText;
|
|
2835
2948
|
}
|
|
2836
|
-
|
|
2949
|
+
if (isPatchErrorText(String(result))) maybeCapturePatchReplay(args, effectiveCwd, String(result));
|
|
2950
|
+
// ② completion progress ("Found N" summary line). Best-effort, no-op
|
|
2837
2951
|
// when onProgress is absent (no progressToken). Never throws — only
|
|
2838
2952
|
// emits on success (an "Error:" body is left to the tool result alone).
|
|
2839
2953
|
if (typeof options?.onProgress === 'function') {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// Per-tool live-status message builder
|
|
2
|
-
// generalization / getActivityDescription). Dependency-light and
|
|
1
|
+
// Per-tool live-status message builder. Dependency-light and
|
|
3
2
|
// side-effect-free: given a tool name + its raw args, return a short
|
|
4
3
|
// human-readable "what's happening now" string. Used by the central dispatch
|
|
5
4
|
// path to emit a single start-of-tool progress notification. Every tool gets a
|
|
@@ -53,8 +53,7 @@ const SHELL_OUTPUT_DISK_CAP = 100 * 1024 * 1024;
|
|
|
53
53
|
|
|
54
54
|
// Background-task disk watchdog cadence. The size guard polls the spilled
|
|
55
55
|
// stdout/stderr files every interval and SIGKILLs the child once the
|
|
56
|
-
// combined size exceeds SHELL_OUTPUT_DISK_CAP
|
|
57
|
-
// upstream cadence — short enough that a runaway loop is caught within a
|
|
56
|
+
// combined size exceeds SHELL_OUTPUT_DISK_CAP — short enough that a runaway loop is caught within a
|
|
58
57
|
// few seconds, long enough that the stat overhead is negligible.
|
|
59
58
|
const SIZE_WATCHDOG_INTERVAL_MS = 1_000;
|
|
60
59
|
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
// pyenv / mise / asdf / direnv setup the user gets in their interactive
|
|
9
9
|
// terminal — without paying a fresh login-shell startup on every call.
|
|
10
10
|
//
|
|
11
|
-
//
|
|
12
|
-
// createAndSaveSnapshot). Simpler scope: bash and zsh only, no embedded
|
|
11
|
+
// Scope: bash and zsh only, no embedded
|
|
13
12
|
// search-tool injection (mixdog ships its own grep/glob helpers).
|
|
14
13
|
|
|
15
14
|
import { spawn } from 'node:child_process';
|
|
@@ -34,8 +33,7 @@ const _cache = new Map();
|
|
|
34
33
|
// command. Cleared on process exit (process-scoped Set).
|
|
35
34
|
const _failedShells = new Set();
|
|
36
35
|
|
|
37
|
-
//
|
|
38
|
-
// cleanupRegistry.ts + ShellSnapshot.ts:534-545). Snapshot files are
|
|
36
|
+
// Snapshot files are
|
|
39
37
|
// session-scoped and must be unlinked on graceful shutdown — otherwise
|
|
40
38
|
// they pile up forever (each rc-file mtime change creates a new file).
|
|
41
39
|
const _activeSnapshots = new Set();
|
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
realpathSync
|
|
17
17
|
} from "fs";
|
|
18
18
|
import { join, sep } from "path";
|
|
19
|
-
import {
|
|
19
|
+
import { createHash } from "crypto";
|
|
20
|
+
import { chunk, formatForDiscord, MAX_DISCORD_MESSAGE } from "../lib/format.mjs";
|
|
20
21
|
import { withConfigLock } from "../lib/config-lock.mjs";
|
|
21
22
|
import { readSection, updateSection } from "../../shared/config.mjs";
|
|
22
|
-
const MAX_CHUNK_LIMIT =
|
|
23
|
+
const MAX_CHUNK_LIMIT = MAX_DISCORD_MESSAGE;
|
|
23
24
|
const MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
24
25
|
const RECENT_SENT_CAP = 200;
|
|
25
26
|
function defaultAccess() {
|
|
@@ -52,6 +53,7 @@ function safeAttName(att) {
|
|
|
52
53
|
}
|
|
53
54
|
class DiscordBackend {
|
|
54
55
|
name = "discord";
|
|
56
|
+
MAX_MESSAGE_LENGTH = MAX_DISCORD_MESSAGE;
|
|
55
57
|
onMessage = null;
|
|
56
58
|
onInteraction = null;
|
|
57
59
|
onModalRequest = null;
|
|
@@ -77,6 +79,9 @@ class DiscordBackend {
|
|
|
77
79
|
this.initialAccess = normalizeAccess(config.access);
|
|
78
80
|
this.client = null;
|
|
79
81
|
}
|
|
82
|
+
formatOutgoing(text) {
|
|
83
|
+
return formatForDiscord(text);
|
|
84
|
+
}
|
|
80
85
|
// ── Lifecycle ──────────────────────────────────────────────────────
|
|
81
86
|
async connect() {
|
|
82
87
|
// Re-entry guard: if a connect() is already in-flight or completed, return
|
|
@@ -335,33 +340,118 @@ class DiscordBackend {
|
|
|
335
340
|
}
|
|
336
341
|
}
|
|
337
342
|
if (files.length > 10) throw new Error("max 10 attachments per message");
|
|
338
|
-
|
|
343
|
+
// Resume-token support (opaque to callers). A partial-send failure hands the
|
|
344
|
+
// caller a token { hash, nextChunkIdx, sentIds, prefixed }; a requeued retry
|
|
345
|
+
// passes it back so we resume at the failed chunk instead of re-sending
|
|
346
|
+
// chunks that already landed.
|
|
347
|
+
//
|
|
348
|
+
// The "\u3164\n" prefix is applied from this.sendCount, which advances when
|
|
349
|
+
// ANY backend send fully succeeds. Between the original partial failure and
|
|
350
|
+
// this retry, an unrelated send (scheduler/lifecycle/permission) can flip
|
|
351
|
+
// sendCount from 0 to >0, which would change the prefix — and therefore the
|
|
352
|
+
// chunked text and its hash — breaking the token match and forcing a
|
|
353
|
+
// duplicate full-resend. To keep the retry byte-identical to the original
|
|
354
|
+
// attempt, freeze the prefix decision in the token and reuse it here instead
|
|
355
|
+
// of recomputing from the (possibly changed) current sendCount.
|
|
356
|
+
const resumeToken = opts?.resumeToken;
|
|
357
|
+
const applyPrefix = (resumeToken && typeof resumeToken.prefixed === "boolean")
|
|
358
|
+
? resumeToken.prefixed
|
|
359
|
+
: (text && this.sendCount > 0 ? true : false);
|
|
360
|
+
if (text && applyPrefix) {
|
|
339
361
|
text = "\u3164\n" + text;
|
|
340
362
|
}
|
|
341
363
|
const access = this.loadAccess();
|
|
342
364
|
const limit = Math.max(1, Math.min(access.textChunkLimit ?? MAX_CHUNK_LIMIT, MAX_CHUNK_LIMIT));
|
|
343
365
|
const replyMode = access.replyToMode ?? "off";
|
|
344
366
|
const chunks = chunk(text, limit);
|
|
367
|
+
// Hash the FINAL prefixed text so the token pins to the exact bytes sent.
|
|
368
|
+
const contentHash = createHash("md5").update(text).digest("hex");
|
|
369
|
+
let startIdx = 0;
|
|
345
370
|
const sentIds = [];
|
|
371
|
+
// Resume only when BOTH the text hash AND the effective chunk limit match.
|
|
372
|
+
// The token's nextChunkIdx is an index into the chunk array, which is only
|
|
373
|
+
// meaningful for the same `limit`; if access.textChunkLimit changed during
|
|
374
|
+
// the retry window the same text can split differently, so a stale index
|
|
375
|
+
// could skip or duplicate a range. On mismatch fall through to startIdx=0
|
|
376
|
+
// (full, safe resend).
|
|
377
|
+
if (resumeToken && resumeToken.hash === contentHash && resumeToken.limit === limit) {
|
|
378
|
+
startIdx = Math.max(0, Math.min(resumeToken.nextChunkIdx ?? 0, chunks.length));
|
|
379
|
+
// Seed sentIds with the already-delivered ids so the returned list stays
|
|
380
|
+
// complete across the resume boundary.
|
|
381
|
+
if (Array.isArray(resumeToken.sentIds)) sentIds.push(...resumeToken.sentIds);
|
|
382
|
+
}
|
|
346
383
|
try {
|
|
347
|
-
for (let i =
|
|
384
|
+
for (let i = startIdx; i < chunks.length; i++) {
|
|
348
385
|
const shouldReplyTo = replyTo != null && replyMode !== "off" && (replyMode === "all" || i === 0);
|
|
349
386
|
const embeds = i === 0 ? opts?.embeds ?? [] : [];
|
|
350
387
|
const components = i === 0 ? opts?.components ?? [] : [];
|
|
351
|
-
const
|
|
388
|
+
const payload = {
|
|
352
389
|
content: chunks[i],
|
|
353
390
|
...embeds.length > 0 ? { embeds } : {},
|
|
354
391
|
...components.length > 0 ? { components } : {},
|
|
355
392
|
...i === 0 && files.length > 0 ? { files } : {},
|
|
356
393
|
...shouldReplyTo ? { reply: { messageReference: replyTo, failIfNotExists: false } } : {}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
394
|
+
};
|
|
395
|
+
// Per-chunk send with retry. On 429 honour Retry-After; on other
|
|
396
|
+
// transient errors do a short backoff. Cap at 3 attempts per chunk;
|
|
397
|
+
// after the cap throw an error carrying a resume token so the caller
|
|
398
|
+
// (output-forwarder) requeues the WHOLE item and the next sendMessage
|
|
399
|
+
// resumes at this chunk index instead of re-sending 0..i-1.
|
|
400
|
+
let attempt = 0;
|
|
401
|
+
for (;;) {
|
|
402
|
+
try {
|
|
403
|
+
const sent = await ch.send(payload);
|
|
404
|
+
this.noteSent(sent.id);
|
|
405
|
+
sentIds.push(sent.id);
|
|
406
|
+
break;
|
|
407
|
+
} catch (err) {
|
|
408
|
+
attempt++;
|
|
409
|
+
const status = err?.status ?? err?.code ?? err?.httpStatus;
|
|
410
|
+
// Classify: PERMANENT = a 4xx client error (unknown channel/message,
|
|
411
|
+
// missing access/permissions, 404 …) that will never succeed on
|
|
412
|
+
// retry. TRANSIENT = 429, any 5xx, or network error (no status). A
|
|
413
|
+
// permanent error must NOT retry-loop and must NOT carry a resume
|
|
414
|
+
// token — throw it immediately flagged so the forwarder drops the
|
|
415
|
+
// item instead of requeuing forever.
|
|
416
|
+
const isPermanent = typeof status === "number" && status >= 400 && status < 500 && status !== 429;
|
|
417
|
+
if (isPermanent) {
|
|
418
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
419
|
+
e.permanent = true;
|
|
420
|
+
throw e;
|
|
421
|
+
}
|
|
422
|
+
if (attempt >= 3) {
|
|
423
|
+
// Attach an opaque resume token pointing at the chunk that failed
|
|
424
|
+
// (i). sentIds holds every chunk already delivered (including any
|
|
425
|
+
// seeded from a prior token).
|
|
426
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
427
|
+
e.resumeToken = { hash: contentHash, nextChunkIdx: i, sentIds: [...sentIds], prefixed: applyPrefix, limit };
|
|
428
|
+
throw e;
|
|
429
|
+
}
|
|
430
|
+
if (status === 429) {
|
|
431
|
+
// @discordjs/rest RateLimitError.retryAfter is ALWAYS in ms
|
|
432
|
+
// (no unit guess). Clamp to [0, 60000]; fall back to 1s when the
|
|
433
|
+
// field is absent/invalid. (discord.js also auto-sleeps rate
|
|
434
|
+
// limits internally, so we rarely even reach this branch.)
|
|
435
|
+
const retryAfterMs = Number(err?.retryAfter);
|
|
436
|
+
const ms = Number.isFinite(retryAfterMs) && retryAfterMs > 0 ? Math.min(retryAfterMs, 60_000) : 1000;
|
|
437
|
+
await new Promise((r) => setTimeout(r, ms));
|
|
438
|
+
} else {
|
|
439
|
+
// Other transient error (5xx / network): short backoff.
|
|
440
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
360
444
|
}
|
|
361
445
|
this.sendCount += sentIds.length;
|
|
362
446
|
} catch (err) {
|
|
363
447
|
const msg = err instanceof Error ? err.message : String(err);
|
|
364
|
-
|
|
448
|
+
const wrapped = new Error(`send failed after ${sentIds.length}/${chunks.length} chunk(s): ${msg}`);
|
|
449
|
+
// Preserve the opaque resume token across the rewrap so the caller can
|
|
450
|
+
// persist it on the queue item and resume on retry.
|
|
451
|
+
if (err?.resumeToken) wrapped.resumeToken = err.resumeToken;
|
|
452
|
+
// Propagate the permanent flag so the forwarder drops (not requeues) it.
|
|
453
|
+
if (err?.permanent) wrapped.permanent = true;
|
|
454
|
+
throw wrapped;
|
|
365
455
|
}
|
|
366
456
|
return { sentIds };
|
|
367
457
|
}
|