mixdog 0.9.95 → 0.9.97
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/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/MIT.txt +56 -0
- package/LICENSES/codex-NOTICE.txt +6 -0
- package/NOTICE.md +46 -9
- package/README.md +0 -17
- package/package.json +16 -12
- package/scripts/.tmp-coverage-report.mjs +96 -0
- package/scripts/build-tui.mjs +17 -1
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- package/scripts/fixtures/patch-replay-corpus.json +98 -0
- package/src/defaults/mixdog-config.template.json +5 -8
- package/src/defaults/skills/setup/SKILL.md +1 -1
- package/src/lib/keychain-cjs.cjs +1 -1
- package/src/lib/mixdog-debug.cjs +2 -3
- package/src/rules/agent/30-explorer.md +7 -4
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/rules/shared/01-tool.md +21 -29
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/config.mjs +246 -67
- package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
- package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
- package/src/runtime/channels/lib/config.mjs +34 -34
- package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
- package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
- package/src/runtime/channels/lib/memory-client.mjs +31 -157
- package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
- package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
- package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
- package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
- package/src/runtime/channels/lib/scheduler.mjs +2 -2
- package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
- package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
- package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
- package/src/runtime/channels/lib/worker-main.mjs +38 -38
- package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
- package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +76 -248
- package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
- package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
- package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
- package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
- package/src/runtime/memory/lib/http-router.mjs +9 -6
- package/src/runtime/memory/lib/ko-morph.mjs +3 -20
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
- package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
- package/src/runtime/memory/lib/memory-log.mjs +1 -1
- package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +32 -9
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/memory/tool-defs.mjs +1 -5
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/search/tool-defs.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/agent-route-config.mjs +119 -0
- package/src/runtime/shared/atomic-file.mjs +80 -4
- package/src/runtime/shared/channel-notification-routing.mjs +2 -2
- package/src/runtime/shared/child-guardian.mjs +204 -108
- package/src/runtime/shared/child-spawn-gate.mjs +139 -72
- package/src/runtime/shared/config.mjs +120 -47
- package/src/runtime/shared/llm/index.mjs +1 -1
- package/src/runtime/shared/owner-fair-gate.mjs +134 -0
- package/src/runtime/shared/resource-admission.mjs +150 -100
- package/src/runtime/shared/stream-progress.mjs +6 -0
- package/src/runtime/shared/tool-execution-owner.mjs +12 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +91 -27
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +47 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
- package/src/session-runtime/channel-config-api.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +39 -37
- package/src/session-runtime/config-lifecycle.mjs +27 -27
- package/src/session-runtime/lifecycle-api.mjs +22 -3
- package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
- package/src/session-runtime/model-route-api.mjs +7 -5
- package/src/session-runtime/prewarm.mjs +1 -1
- package/src/session-runtime/provider-auth-api.mjs +0 -7
- package/src/session-runtime/provider-models.mjs +1 -1
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +63 -39
- package/src/session-runtime/runtime-tunables.mjs +4 -4
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +45 -17
- package/src/session-runtime/session-title.mjs +1 -1
- package/src/session-runtime/session-turn-api.mjs +96 -24
- package/src/session-runtime/settings-api.mjs +12 -24
- package/src/session-runtime/tool-defs.mjs +3 -3
- package/src/session-runtime/workflow-agents-api.mjs +55 -81
- package/src/session-runtime/workflow.mjs +14 -24
- package/src/standalone/agent-dispatch-broker.mjs +198 -0
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
- package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
- package/src/standalone/agent-tool/tool-def.mjs +0 -14
- package/src/standalone/channel-admin.mjs +53 -64
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
- package/src/standalone/explore-tool.mjs +187 -70
- package/src/standalone/fair-call-scheduler.mjs +264 -0
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/session-client.mjs +1134 -0
- package/src/standalone/session-protocol.mjs +192 -0
- package/src/standalone/session-runtime-pool.mjs +393 -0
- package/src/standalone/session-runtime-record.mjs +20 -0
- package/src/standalone/session-runtime-worker.mjs +203 -0
- package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +34 -21
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/app-view.jsx +5 -2
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/core-memory-picker.mjs +19 -19
- package/src/tui/app/doctor.mjs +5 -11
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/message-selector.mjs +103 -0
- package/src/tui/app/onboarding-steps.mjs +3 -4
- package/src/tui/app/project-picker.mjs +80 -56
- package/src/tui/app/prompt-submit.mjs +48 -43
- package/src/tui/app/resume-picker.mjs +2 -2
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker.mjs +31 -33
- package/src/tui/app/transcript-window.mjs +38 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
- package/src/tui/app/use-prompt-handlers.mjs +86 -21
- package/src/tui/app/use-prompt-hint.mjs +3 -2
- package/src/tui/app/use-prompt-queue-history.mjs +10 -3
- package/src/tui/app/use-transcript-scroll.mjs +10 -3
- package/src/tui/app/use-transcript-window.mjs +50 -17
- package/src/tui/components/PromptInput.jsx +71 -38
- package/src/tui/components/Spinner.jsx +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
- package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
- package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
- package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
- package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +1031 -1676
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +12 -13
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
- package/src/tui/{engine → session}/boot-profile.mjs +2 -2
- package/src/tui/{engine → session}/context-state.mjs +3 -3
- package/src/tui/{engine → session}/labels.mjs +2 -2
- package/src/tui/{engine → session}/live-share.mjs +38 -4
- package/src/tui/{engine → session}/notice-text.mjs +2 -2
- package/src/tui/{engine → session}/notification-plan.mjs +3 -3
- package/src/tui/session/oauth-flows.mjs +140 -0
- package/src/tui/{engine → session}/prompt-history.mjs +3 -3
- package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
- package/src/tui/{engine → session}/session-api.mjs +110 -93
- package/src/tui/{engine → session}/session-flow.mjs +8 -2
- package/src/tui/{engine → session}/session-stats.mjs +3 -3
- package/src/tui/{engine → session}/tool-approval.mjs +3 -3
- package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
- package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +38 -176
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
- package/src/tui/session.mjs +22 -0
- package/src/tui/spinner-meta.mjs +4 -4
- package/src/tui/spinner-verbs.mjs +51 -3
- package/src/ui/statusline-segments.mjs +1 -1
- package/src/ui/statusline.mjs +1 -1
- package/vendor/ink/build/components/App.js +10 -1
- package/src/session-runtime/memory-daemon-probe.mjs +0 -61
- package/src/standalone/engine-daemon-client.mjs +0 -894
- package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
- package/src/standalone/engine-daemon-protocol.mjs +0 -33
- package/src/tui/engine/turn-watchdog.mjs +0 -92
- package/src/tui/engine.mjs +0 -22
- package/src/tui/hooks/useEngine.mjs +0 -16
- /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
- /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
- /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
- /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
- /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
// Stub
|
|
2
|
-
// src/standalone/
|
|
1
|
+
// Stub channel service for the flip smoke: same lifecycle contract as
|
|
2
|
+
// src/standalone/daemon.mjs (pid-verified singleton claim, HTTP+SSE
|
|
3
3
|
// transport, discovery file, ready handshake, client-grace self-shutdown) but
|
|
4
4
|
// with a STUB runtime instead of worker-main — no Discord token needed. The
|
|
5
5
|
// real channel-worker.mjs spawn-or-attach path forks THIS via
|
|
6
|
-
//
|
|
6
|
+
// MIXDOG_DAEMON_ENTRY, so the flip is exercised end to end.
|
|
7
7
|
process.env.MIXDOG_WORKER_MODE = process.env.MIXDOG_WORKER_MODE || '1';
|
|
8
8
|
|
|
9
9
|
import os from 'node:os';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import { writeJsonAtomicSync } from '../src/runtime/shared/atomic-file.mjs';
|
|
12
13
|
import { claimSingletonOwner, releaseSingletonOwner } from '../src/runtime/shared/singleton-owner.mjs';
|
|
13
14
|
import { safeIpcSend } from '../src/runtime/shared/safe-ipc-send.mjs';
|
|
14
|
-
import {
|
|
15
|
+
import { createChannelTransport } from '../src/standalone/channel-transport.mjs';
|
|
15
16
|
|
|
16
17
|
function runtimeRoot() {
|
|
17
18
|
return process.env.MIXDOG_RUNTIME_ROOT
|
|
@@ -20,8 +21,8 @@ function runtimeRoot() {
|
|
|
20
21
|
}
|
|
21
22
|
const RUNTIME_ROOT = runtimeRoot();
|
|
22
23
|
const DATA_DIR = process.env.MIXDOG_DATA_DIR ? path.resolve(process.env.MIXDOG_DATA_DIR) : RUNTIME_ROOT;
|
|
23
|
-
const DISCOVERY_PATH = path.join(RUNTIME_ROOT, '
|
|
24
|
-
const OWNER_PATH = path.join(DATA_DIR, '
|
|
24
|
+
const DISCOVERY_PATH = path.join(RUNTIME_ROOT, 'daemon.json');
|
|
25
|
+
const OWNER_PATH = path.join(DATA_DIR, 'daemon-owner.json');
|
|
25
26
|
|
|
26
27
|
function log(line) { if (process.env.DAEMON_SMOKE_VERBOSE) process.stderr.write(`[stub-daemon] ${line}\n`); }
|
|
27
28
|
|
|
@@ -31,23 +32,24 @@ async function shutdown(reason, code = 0) {
|
|
|
31
32
|
if (shuttingDown) return;
|
|
32
33
|
shuttingDown = true;
|
|
33
34
|
try { await transport?.stop?.(); } catch {}
|
|
35
|
+
try { rmSync(DISCOVERY_PATH, { force: true }); } catch {}
|
|
34
36
|
try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {}
|
|
35
37
|
process.exit(code);
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
async function main() {
|
|
39
41
|
try { mkdirSync(RUNTIME_ROOT, { recursive: true }); } catch {}
|
|
40
|
-
const claim = claimSingletonOwner(OWNER_PATH, { kind: '
|
|
42
|
+
const claim = claimSingletonOwner(OWNER_PATH, { kind: 'mixdog-daemon', pid: process.pid, meta: { cwd: process.cwd() } });
|
|
41
43
|
if (!claim.owned) { process.exit(0); } // race loser → spawner attaches to winner
|
|
42
44
|
process.on('exit', () => { try { releaseSingletonOwner(OWNER_PATH, process.pid); } catch {} });
|
|
43
45
|
|
|
44
46
|
// Optional env dump for the tool smoke: proves daemonEnv() spawned us with
|
|
45
|
-
// the
|
|
47
|
+
// the host-mode flags (MIXDOG_DAEMON_HOST=1, MIXDOG_CLI_OWNED=0).
|
|
46
48
|
if (process.env.SMOKE_CHANNEL_ENV_OUT) {
|
|
47
49
|
try {
|
|
48
50
|
writeFileSync(process.env.SMOKE_CHANNEL_ENV_OUT, JSON.stringify({
|
|
49
51
|
cliOwned: process.env.MIXDOG_CLI_OWNED,
|
|
50
|
-
|
|
52
|
+
host: process.env.MIXDOG_DAEMON_HOST,
|
|
51
53
|
}));
|
|
52
54
|
} catch { /* smoke-only, best-effort */ }
|
|
53
55
|
}
|
|
@@ -61,15 +63,19 @@ async function main() {
|
|
|
61
63
|
return { ok: true, name, args, leadPid: ctx.leadPid };
|
|
62
64
|
};
|
|
63
65
|
|
|
64
|
-
transport =
|
|
66
|
+
transport = createChannelTransport({
|
|
65
67
|
handleCall,
|
|
66
|
-
discoveryPath: DISCOVERY_PATH,
|
|
67
68
|
clientGraceMs: 250,
|
|
68
69
|
sweepMs: 1000,
|
|
69
70
|
log,
|
|
70
71
|
onClientsEmpty: () => { void shutdown('no live clients'); },
|
|
71
72
|
});
|
|
72
73
|
const { port, token } = await transport.start();
|
|
74
|
+
writeJsonAtomicSync(DISCOVERY_PATH, {
|
|
75
|
+
pid: process.pid,
|
|
76
|
+
startedAt: Date.now(),
|
|
77
|
+
endpoints: { channel: { port, token } },
|
|
78
|
+
}, { compact: true });
|
|
73
79
|
safeIpcSend(process, { type: 'ready', port, token });
|
|
74
80
|
log(`ready port=${port} pid=${process.pid}`);
|
|
75
81
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "eof-marker-midfile",
|
|
4
|
+
"note": "observed: a mid-file hunk carried *** End of File; the EOF-anchored seek missed and every recovery tier was gated off, so a byte-perfect context failed",
|
|
5
|
+
"expect": "applied",
|
|
6
|
+
"expect_content": { "a.txt": "one\nTWO\nthree\nfour\nfive\n" },
|
|
7
|
+
"file_snapshots": { "a.txt": "one\ntwo\nthree\nfour\nfive\n" },
|
|
8
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: a.txt\n@@\n one\n-two\n+TWO\n three\n*** End of File\n*** End Patch\n" }
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "deletion-line-one-char-off",
|
|
12
|
+
"note": "observed: `first divergent line` hints whose expected/actual differ by <= 1 character (retyped from memory) on a deletion line",
|
|
13
|
+
"expect": "applied",
|
|
14
|
+
"expect_content": { "src.js": "alpha\nconst total = count + 2;\nbeta\ngamma\n" },
|
|
15
|
+
"file_snapshots": { "src.js": "alpha\nconst total = count + 1;\nbeta\ngamma\n" },
|
|
16
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: src.js\n@@\n alpha\n-const total = cout + 1;\n+const total = count + 2;\n beta\n*** End Patch\n" }
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"id": "stale-outer-context-line",
|
|
20
|
+
"note": "observed: one unrelated context line copied just outside the real edit",
|
|
21
|
+
"expect": "applied",
|
|
22
|
+
"expect_content": { "tools.js": "const tools = {\n shell_step: {\n command: 'node test.js',\n },\n};\n" },
|
|
23
|
+
"file_snapshots": { "tools.js": "const tools = {\n shell_step: {\n command: 'node test.js',\n cwd: root,\n timeout: 30000,\n },\n};\n" },
|
|
24
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: tools.js\n@@\n stale outer context\n shell_step: {\n command: 'node test.js',\n- cwd: root,\n- timeout: 30000,\n },\n*** End Patch\n" }
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"id": "context-retyped-from-memory",
|
|
28
|
+
"note": "observed (dominant stale-context shape): the surrounding context was retyped from memory while the edited line itself is current and unique",
|
|
29
|
+
"expect": "applied",
|
|
30
|
+
"expect_content": { "cfg.js": "l1\nl2\nl3\nconst flag = false;\nl5\nl6\nl7\n" },
|
|
31
|
+
"file_snapshots": { "cfg.js": "l1\nl2\nl3\nconst flag = true;\nl5\nl6\nl7\n" },
|
|
32
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: cfg.js\n@@\n remembered header\n another stale line\n-const flag = true;\n+const flag = false;\n stale trailer\n*** End Patch\n" }
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "retyped-context-with-duplicate-core",
|
|
36
|
+
"note": "guard: the same rescue must refuse when the edited line occurs more than once",
|
|
37
|
+
"expect": "rejected",
|
|
38
|
+
"expect_error": "context not found",
|
|
39
|
+
"file_snapshots": { "dupcore.js": "a\nconst flag = true;\nb\nc\nconst flag = true;\nd\n" },
|
|
40
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: dupcore.js\n@@\n stale one\n-const flag = true;\n+const flag = false;\n stale two\n*** End Patch\n" }
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "decomposed-unicode-context",
|
|
44
|
+
"note": "observed: context authored in decomposed Unicode against composed on-disk text",
|
|
45
|
+
"expect": "applied",
|
|
46
|
+
"expect_content": { "label.js": "head\nconst label = \"tea\";\ntail\n" },
|
|
47
|
+
"file_snapshots": { "label.js": "head\nconst label = \"caf\u00e9\";\ntail\n" },
|
|
48
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: label.js\n@@\n head\n-const label = \"cafe\u0301\";\n+const label = \"tea\";\n tail\n*** End Patch\n" }
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"id": "stacked-at-anchors",
|
|
52
|
+
"note": "V4A anchor chain (@@ class + @@ def): consecutive headers must narrow ONE hunk, not silently resolve against the first occurrence",
|
|
53
|
+
"expect": "applied",
|
|
54
|
+
"expect_content": { "dup.py": "class A:\n def run():\n return 1\n\nclass B:\n def run():\n return 2\n" },
|
|
55
|
+
"file_snapshots": { "dup.py": "class A:\n def run():\n return 1\n\nclass B:\n def run():\n return 1\n" },
|
|
56
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: dup.py\n@@ class B:\n@@ def run():\n- return 1\n+ return 2\n*** End Patch\n" }
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "divergent-line-far-off",
|
|
60
|
+
"note": "observed (dominant class, 65 of 91 hinted misses): the quoted context is genuinely different content — must stay a hard miss with a divergence hint",
|
|
61
|
+
"expect": "rejected",
|
|
62
|
+
"expect_error": "first divergent line|context not found",
|
|
63
|
+
"file_snapshots": { "app.js": "head\nawait engine.startWork();\ntail\n" },
|
|
64
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: app.js\n@@\n head\n-unsubscribe();\n+cleanup();\n tail\n*** End Patch\n" }
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "shifted-context-first-line-only",
|
|
68
|
+
"note": "observed (62 misses): the first old line exists but the block does not — must reject and point at the nearest line",
|
|
69
|
+
"expect": "rejected",
|
|
70
|
+
"expect_error": "nearest line|context not found",
|
|
71
|
+
"file_snapshots": { "shift.js": "open();\nmiddle();\nclose();\n" },
|
|
72
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: shift.js\n@@\n open();\n-gone();\n-also gone();\n+replacement();\n close();\n*** End Patch\n" }
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"id": "near-miss-in-two-places",
|
|
76
|
+
"note": "guard: a near-miss context that fits two windows must never be applied to a guessed one",
|
|
77
|
+
"expect": "rejected",
|
|
78
|
+
"expect_error": "context not found",
|
|
79
|
+
"file_snapshots": { "dup.js": "alpha\nvalue = 1;\nbeta\nalpha\nvalue = 1;\nbeta\n" },
|
|
80
|
+
"args": { "patch": "*** Begin Patch\n*** Update File: dup.js\n@@\n alpha\n-value = 7;\n+value = 2;\n beta\n*** End Patch\n" }
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "compacted-history-placeholder",
|
|
84
|
+
"note": "observed (39 rows): the patch argument was replaced by the history-compaction marker",
|
|
85
|
+
"expect": "rejected",
|
|
86
|
+
"expect_error": "compacted-history placeholder",
|
|
87
|
+
"file_snapshots": { "a.txt": "one\ntwo\n" },
|
|
88
|
+
"args": { "patch": "[mixdog compacted patch: 15983 chars, sha256:9ae407d696803e2a; already applied to a.txt - do not copy]" }
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"id": "missing-patch-argument",
|
|
92
|
+
"note": "observed (783 rows, single harness burst): apply_patch called with no patch argument — must be a stated contract error, never a runtime type crash",
|
|
93
|
+
"expect": "rejected",
|
|
94
|
+
"expect_error": "\"patch\" is required",
|
|
95
|
+
"file_snapshots": {},
|
|
96
|
+
"args": { "base_path": null }
|
|
97
|
+
}
|
|
98
|
+
]
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"outputStyle": "default",
|
|
3
|
-
"
|
|
4
|
-
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"targetPath": ""
|
|
8
|
-
}
|
|
3
|
+
"agent": {
|
|
4
|
+
"mcpServers": {},
|
|
5
|
+
"profile": { "title": "", "language": "system" },
|
|
6
|
+
"recap": { "enabled": true }
|
|
9
7
|
},
|
|
8
|
+
"channels": {},
|
|
10
9
|
"memory": {
|
|
11
|
-
"enabled": true,
|
|
12
|
-
"user": { "title": "" },
|
|
13
10
|
"cycle1": { "interval": "10m" },
|
|
14
11
|
"cycle2": { "interval": "1h" }
|
|
15
12
|
}
|
|
@@ -132,7 +132,7 @@ apply it, and verify the result.
|
|
|
132
132
|
### Discord / Telegram / voice channels
|
|
133
133
|
|
|
134
134
|
1. Check `/channels` (or `/setting` → Channel) and runtime channel status.
|
|
135
|
-
2. Update the requested
|
|
135
|
+
2. Update the requested provider, credential, or main channel/chat setting.
|
|
136
136
|
3. Restart/reconnect only when the channel implementation requires it.
|
|
137
137
|
4. Verify channel presence and a non-secret status signal.
|
|
138
138
|
|
package/src/lib/keychain-cjs.cjs
CHANGED
|
@@ -6,7 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
const { resolvePluginData } = require('./plugin-paths.cjs');
|
|
7
7
|
|
|
8
8
|
const SERVICE = 'mixdog';
|
|
9
|
-
// Shared bound for every synchronous keychain
|
|
9
|
+
// Shared bound for every synchronous keychain provider (DPAPI/PowerShell on
|
|
10
10
|
// Windows, security(1) on macOS). A single env override keeps them consistent.
|
|
11
11
|
const KEYCHAIN_TIMEOUT_MS = Number(process.env.MIXDOG_KEYCHAIN_TIMEOUT_MS || 15000);
|
|
12
12
|
const POWERSHELL_TIMEOUT_MS = KEYCHAIN_TIMEOUT_MS;
|
package/src/lib/mixdog-debug.cjs
CHANGED
|
@@ -102,8 +102,7 @@ const CANONICAL_PLUGIN_LOG_NAMES = new Set([
|
|
|
102
102
|
'webhook.log',
|
|
103
103
|
'perf.log',
|
|
104
104
|
'tool-events.log',
|
|
105
|
-
'
|
|
106
|
-
'channels-worker-standalone.log',
|
|
105
|
+
'daemon.log',
|
|
107
106
|
'mixdog-tui.stderr.log',
|
|
108
107
|
]);
|
|
109
108
|
|
|
@@ -183,7 +182,7 @@ function rotateBoundedLog(filePath, maxBytes, keepBytes) {
|
|
|
183
182
|
}
|
|
184
183
|
|
|
185
184
|
// Shared bound for unbounded per-writer plugin logs (tool-events,
|
|
186
|
-
// memory
|
|
185
|
+
// memory runtime and daemon logs). Keep a tail so recent
|
|
187
186
|
// context survives rotation while the file stays under the 10 MB cap.
|
|
188
187
|
const PLUGIN_LOG_MAX_BYTES = 10 * 1024 * 1024;
|
|
189
188
|
const PLUGIN_LOG_KEEP_BYTES = 2 * 1024 * 1024;
|
|
@@ -32,11 +32,14 @@ or concept synonym; never a prose phrase. Spaces and non-ASCII are allowed
|
|
|
32
32
|
only in verbatim quoted error/log literals. Translate other non-English
|
|
33
33
|
queries to English identifiers.
|
|
34
34
|
|
|
35
|
-
Scope is
|
|
35
|
+
Scope is every `<roots><root>…</root></roots>` entry when supplied, otherwise
|
|
36
|
+
session cwd. Search every supplied root in the turn-1 batch: grep/glob batch
|
|
37
|
+
`path[]`, while find uses one sibling call per root. Never silently fall back to
|
|
38
|
+
cwd or omit a supplied root. A find result is relative to its exact root; prefix
|
|
39
|
+
that root when returning a path outside cwd. For unverified `src` paths, use
|
|
36
40
|
`find` first; never guess or invent directories or pair `path:"."` with guessed
|
|
37
|
-
`src/**`. Scoped grep/glob may use only an exact find-returned
|
|
38
|
-
|
|
39
|
-
paths.
|
|
41
|
+
`src/**`. Scoped grep/glob may use only a supplied root or an exact find-returned
|
|
42
|
+
path. After zero hits, change tokens or scope, never wording or guessed paths.
|
|
40
43
|
|
|
41
44
|
An anchor is a `path:line` containing a query token or synonym, including a
|
|
42
45
|
code_graph hit. Generic terms without query specificity are zero. Never
|
|
@@ -7,12 +7,11 @@
|
|
|
7
7
|
- Destructive/hard-to-reverse action needs explicit confirmation and explicit
|
|
8
8
|
validated target paths — never `~`, a root, or unresolved variables/globs;
|
|
9
9
|
report material deletions with recoverability.
|
|
10
|
-
-
|
|
10
|
+
- Ask only for decisions.
|
|
11
11
|
- Build only what the task requires; trust internal and framework guarantees.
|
|
12
12
|
- Mid-task input: a replacement supersedes current work, an addition folds
|
|
13
13
|
into it, a status question gets a brief answer while work continues; after
|
|
14
14
|
context compaction continue from the summary — never restart or redo
|
|
15
15
|
finished work.
|
|
16
|
-
- When blocked, exhaust safe in-scope checks once and report the blocker.
|
|
17
16
|
- Your final message ends the turn: answer only when the work is done. After a
|
|
18
17
|
failed tool call, fix and re-run it, or state plainly that it is unresolved.
|
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
# Lead Tools
|
|
2
2
|
|
|
3
|
-
- Write-role agents self-verify with `shell`. Lead uses `shell` only for git,
|
|
4
|
-
benches, and cross-scope verification no retrieval tool can produce;
|
|
5
|
-
inspection stays on `read`/`grep`/`glob`/`list`/`find`/`code_graph`.
|
|
6
3
|
- Use the current project/workspace unless the request or tool requires another.
|
|
@@ -1,31 +1,23 @@
|
|
|
1
1
|
# Tool Use
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
is final; on failure fix and rerun what failed. Diagnostics non-fatal; report
|
|
25
|
-
verified vs assumed.
|
|
26
|
-
- `apply_patch` is the primary edit tool: once path and content are known it
|
|
27
|
-
joins the current batch — all files and hunks in one call, never one turn per
|
|
28
|
-
file (the format contract lives in its tool description).
|
|
29
|
-
- After a background task starts or reports, end the turn — its notification
|
|
30
|
-
resumes the work. Never poll or block; wait only for what the turn cannot
|
|
31
|
-
proceed without. Long commands whose output the next step ignores go async.
|
|
3
|
+
- Unknown coordinates → one `explore` call with every unknown facet, sent
|
|
4
|
+
alone. Then batch every anchored retrieval needed to determine the complete
|
|
5
|
+
edit: partial path/name→`find`; exact directory entries→`list`; wildcard→
|
|
6
|
+
`glob`; text/regex-anchored source blocks→`grep`; anchorless known file/range→
|
|
7
|
+
`read`; symbol/relation→`code_graph`; web/current→`search`; returned URL body→
|
|
8
|
+
`web_fetch`; prior work→`recall`; durable compact English memory→`memory`;
|
|
9
|
+
explicit project change→`cwd`; explicit user-requested conversation reset→
|
|
10
|
+
`session_manage`; process/env, git, build/run/test→`shell`. Call `shell` only
|
|
11
|
+
when the task actually requires one of those operations; do not treat it as
|
|
12
|
+
a routine investigation or completion step. Never use shell equivalents for
|
|
13
|
+
file discovery or content retrieval.
|
|
14
|
+
- Use verified paths (cwd/project/user/tool); explicit paths may be outside cwd;
|
|
15
|
+
guessed fragments use `find`. Maximize fan-out across routed retrieval tools
|
|
16
|
+
and fetch all information needed in one batch; never re-fetch an unchanged
|
|
17
|
+
span.
|
|
18
|
+
- Once the edit is determined, finish in one assistant turn with one
|
|
19
|
+
`apply_patch` for all edits. If final verification actually requires `shell`,
|
|
20
|
+
use one verification chain after the edit; otherwise finish without it.
|
|
21
|
+
- After a call returns a background `task_id`, end the turn; its completion
|
|
22
|
+
notification resumes work. Never poll; use task control only for recovery or
|
|
23
|
+
a required blocking result.
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
abortAgentProgressWatchdog,
|
|
37
37
|
agentWatchdogPolicyActive,
|
|
38
38
|
evaluateAgentWatchdogAbort,
|
|
39
|
+
partialHandoffTextFromSession,
|
|
39
40
|
resolveAgentWatchdogPolicy,
|
|
40
41
|
resolveHandoffMessageStartIndex,
|
|
41
42
|
watchdogPartialHandoffFromError,
|
|
@@ -65,6 +66,17 @@ function formatCompactElapsedSeconds(ms) {
|
|
|
65
66
|
return `${Math.max(1, Math.ceil(value / 1000))}s`;
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
// True when an abort explicitly opted into partial salvage — the error object
|
|
70
|
+
// or the abort reason carries `salvagePartial: true`. A DEADLINE-driven caller
|
|
71
|
+
// (explore hard timeout) sets it so the anchors the sub-agent already produced
|
|
72
|
+
// are returned instead of discarded; user cancellation (ESC) never sets it and
|
|
73
|
+
// keeps the throw-everything behaviour.
|
|
74
|
+
function salvagePartialRequested(error, signal) {
|
|
75
|
+
if (error && typeof error === 'object' && error.salvagePartial === true) return true;
|
|
76
|
+
const reason = signal?.reason;
|
|
77
|
+
return !!(reason && typeof reason === 'object' && reason.salvagePartial === true);
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
function agentCompactEventLabel(event = {}) {
|
|
69
81
|
const status = String(event.status || '').toLowerCase();
|
|
70
82
|
const reactive = String(event.trigger || '').toLowerCase() === 'reactive';
|
|
@@ -229,7 +241,7 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
229
241
|
}
|
|
230
242
|
const agent = opts.agent;
|
|
231
243
|
|
|
232
|
-
return async function agentDispatch({ prompt, preset: presetArg, sourceName: sourceNameArg, parentSignal: callParentSignal, idleTimeoutMs: callIdleTimeoutMs, cwd: callCwd }) {
|
|
244
|
+
return async function agentDispatch({ prompt, preset: presetArg, sourceName: sourceNameArg, parentSignal: callParentSignal, idleTimeoutMs: callIdleTimeoutMs, cwd: callCwd, sessionId: callSessionId }) {
|
|
233
245
|
if (typeof prompt !== 'string' || !prompt) {
|
|
234
246
|
throw new Error(`[agent-dispatch] prompt required for agent "${agent}"`);
|
|
235
247
|
}
|
|
@@ -243,6 +255,7 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
243
255
|
lease = await admission.acquire('agent', {
|
|
244
256
|
signal: admissionAbortLink.signal,
|
|
245
257
|
label: agent,
|
|
258
|
+
ownerKey: callSessionId || opts.ownerSessionId || opts.parentSessionId || opts.sessionId || null,
|
|
246
259
|
});
|
|
247
260
|
} catch (error) {
|
|
248
261
|
admissionAbortLink.dispose();
|
|
@@ -470,7 +483,10 @@ export function makeAgentDispatch(opts = {}) {
|
|
|
470
483
|
try { closeSession(session.id, 'ephemeral-done'); } catch { /* ignore */ }
|
|
471
484
|
return out;
|
|
472
485
|
} catch (err) {
|
|
473
|
-
const partial = watchdogPartialHandoffFromError(err, getSession(session.id), _handoffMsgStart)
|
|
486
|
+
const partial = watchdogPartialHandoffFromError(err, getSession(session.id), _handoffMsgStart)
|
|
487
|
+
?? (salvagePartialRequested(err, _abortLink?.signal)
|
|
488
|
+
? partialHandoffTextFromSession(getSession(session.id), _handoffMsgStart)
|
|
489
|
+
: null);
|
|
474
490
|
if (partial) {
|
|
475
491
|
terminalStatus = 'idle';
|
|
476
492
|
try { closeSession(session.id, 'ephemeral-done'); } catch { /* ignore */ }
|
|
@@ -7,10 +7,26 @@
|
|
|
7
7
|
import { appendAgentTrace } from '../agent-trace-io.mjs';
|
|
8
8
|
import { getHiddenAgent } from '../internal-agents.mjs';
|
|
9
9
|
import {
|
|
10
|
+
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
11
|
+
PROVIDER_WS_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
12
|
+
STALL_TICK_MS,
|
|
10
13
|
resolveAgentStallThresholds,
|
|
11
14
|
resolveAgentToolThresholdSeconds,
|
|
12
15
|
} from '../stall-policy.mjs';
|
|
13
16
|
|
|
17
|
+
// Ordering guarantee, stated in stall-policy.mjs: the provider layer — which
|
|
18
|
+
// can retry in place or fall back to non-streaming — must fire STRICTLY before
|
|
19
|
+
// the agent watchdog's terminal abort. Role abort budgets (worker/reviewer
|
|
20
|
+
// 300s, explore 240s) sat at or BELOW the provider semantic-idle window
|
|
21
|
+
// (300s), inverting that order: the watchdog aborted the shared signal first,
|
|
22
|
+
// so the provider's recovery never ran and the `agent_stall` failure — which
|
|
23
|
+
// the classifier calls retryable — died on throwIfAborted instead. Hold the
|
|
24
|
+
// role-derived idle budget at one watchdog tick above the provider window.
|
|
25
|
+
const PROVIDER_RECOVERY_FLOOR_MS = Math.max(
|
|
26
|
+
PROVIDER_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
27
|
+
PROVIDER_WS_SEMANTIC_IDLE_TIMEOUT_MS,
|
|
28
|
+
) + STALL_TICK_MS;
|
|
29
|
+
|
|
14
30
|
const WATCHDOG_ABORT_RE = /^agent (?:first (?:transport|semantic response|response) stale|task stale|tool running stale)\s*\(/;
|
|
15
31
|
|
|
16
32
|
/**
|
|
@@ -123,6 +139,16 @@ export function watchdogPartialHandoffFromError(error, session, messageStartInde
|
|
|
123
139
|
return text.trim() ? text : null;
|
|
124
140
|
}
|
|
125
141
|
|
|
142
|
+
// Salvage path for NON-watchdog aborts that explicitly opt in (the abort error
|
|
143
|
+
// / abort reason carries `salvagePartial: true` — e.g. the explore wall-clock
|
|
144
|
+
// hard timeout). Same collection rule as the watchdog handoff: only assistant
|
|
145
|
+
// text appended during this run. Plain user cancellation never opts in, so ESC
|
|
146
|
+
// still discards the run.
|
|
147
|
+
export function partialHandoffTextFromSession(session, messageStartIndex = 0) {
|
|
148
|
+
const text = collectSessionAssistantHandoffText(session, messageStartIndex);
|
|
149
|
+
return text.trim() ? text : null;
|
|
150
|
+
}
|
|
151
|
+
|
|
126
152
|
function resolveWatchdogAbortElapsedMs({ error, snapshot, policy, now, anchorTs, lastProgressAt }) {
|
|
127
153
|
if (snapshot && policy) {
|
|
128
154
|
if (snapshot.waitingForFirstActivity) {
|
|
@@ -223,7 +249,8 @@ export function resolveAgentWatchdogPolicy(agent, overrides = {}) {
|
|
|
223
249
|
idleStaleMs = Math.floor(overrides.idleTimeoutMs);
|
|
224
250
|
} else if (getHiddenAgent(agent)) {
|
|
225
251
|
const { abort } = resolveAgentStallThresholds(agent);
|
|
226
|
-
|
|
252
|
+
// Role budget, floored so the provider recovery window always wins.
|
|
253
|
+
idleStaleMs = Math.max(abort * 1000, PROVIDER_RECOVERY_FLOOR_MS);
|
|
227
254
|
} else {
|
|
228
255
|
// Part B: the primary mid-stream stall catch is now the provider-level
|
|
229
256
|
// SEMANTIC idle abort (~120s, ping-immune). This public-agent idle is a
|
|
@@ -238,6 +265,9 @@ export function resolveAgentWatchdogPolicy(agent, overrides = {}) {
|
|
|
238
265
|
idleStaleMs = backstopMs > 0
|
|
239
266
|
? Math.min(DEFAULT_STALE_TIMEOUT_MS, backstopMs)
|
|
240
267
|
: DEFAULT_STALE_TIMEOUT_MS;
|
|
268
|
+
// Same floor for the public backstop: a workflow role (worker 300s,
|
|
269
|
+
// explore 240s) must not undercut the provider window either.
|
|
270
|
+
idleStaleMs = Math.max(idleStaleMs, PROVIDER_RECOVERY_FLOOR_MS);
|
|
241
271
|
}
|
|
242
272
|
|
|
243
273
|
const idleSec = idleStaleMs / 1000;
|
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { configuredAgentRouteCandidates } from '../../../shared/agent-route-config.mjs';
|
|
2
2
|
import { loadConfig } from '../config.mjs';
|
|
3
3
|
import { getHiddenAgent } from '../internal-agents.mjs';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function normalizeMaintenanceCandidate(candidate, config) {
|
|
5
|
+
function normalizeMaintenanceCandidate(candidate) {
|
|
8
6
|
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) return candidate || null;
|
|
9
|
-
const
|
|
10
|
-
const fallbackProvider = isKnownProvider(configuredProvider)
|
|
11
|
-
? configuredProvider
|
|
12
|
-
: DEFAULT_AGENT_ROUTE_PROVIDER;
|
|
13
|
-
const provider = String(candidate.provider || fallbackProvider).trim();
|
|
7
|
+
const provider = String(candidate.provider || '').trim();
|
|
14
8
|
const model = String(candidate.model || '').trim();
|
|
15
9
|
if (!provider || !model) return null;
|
|
16
10
|
return {
|
|
@@ -34,20 +28,15 @@ export function resolveMaintenanceRoute({ preset, optsPreset, agent, config: cfg
|
|
|
34
28
|
if (hidden) {
|
|
35
29
|
try {
|
|
36
30
|
const config = cfgIn || loadConfig({ secrets: false });
|
|
37
|
-
const maint = config?.maintenance || {};
|
|
38
31
|
const key = hidden.maintKey || hidden.slot;
|
|
39
32
|
const role = key === 'explore' ? 'explore' : (key === 'memory' ? 'maintainer' : '');
|
|
40
|
-
|
|
41
|
-
if (!role) return maint[key] ?? null;
|
|
33
|
+
if (!role) return config?.maintenance?.[key] ?? null;
|
|
42
34
|
const candidates = [
|
|
43
|
-
|
|
44
|
-
key === 'memory' ? config?.agents?.maintenance : null,
|
|
45
|
-
workflowSlot ? config?.workflowRoutes?.[workflowSlot] : null,
|
|
46
|
-
maint[key],
|
|
35
|
+
...configuredAgentRouteCandidates(config, role),
|
|
47
36
|
role ? config?.default : null,
|
|
48
37
|
];
|
|
49
38
|
for (const candidate of candidates) {
|
|
50
|
-
const route = normalizeMaintenanceCandidate(candidate
|
|
39
|
+
const route = normalizeMaintenanceCandidate(candidate);
|
|
51
40
|
if (route) return route;
|
|
52
41
|
}
|
|
53
42
|
return null;
|
|
@@ -31,7 +31,16 @@ function compactRecoveredErrorMessage(value) {
|
|
|
31
31
|
: message;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function traceAgentLoop({
|
|
34
|
+
function traceAgentLoop({
|
|
35
|
+
sessionId,
|
|
36
|
+
iteration,
|
|
37
|
+
sendMs,
|
|
38
|
+
preSendMs,
|
|
39
|
+
toolResumeMs,
|
|
40
|
+
messageCount,
|
|
41
|
+
bodyBytesEst,
|
|
42
|
+
agent = null,
|
|
43
|
+
}) {
|
|
35
44
|
// Two emit modes, no behavior change either way:
|
|
36
45
|
// VERBOSE=1 → full loop row incl. body_bytes_est (payload serialized).
|
|
37
46
|
// TIMING=1 → lightweight send-latency attribution for high-fanout
|
|
@@ -45,6 +54,8 @@ function traceAgentLoop({ sessionId, iteration, sendMs, messageCount, bodyBytesE
|
|
|
45
54
|
kind: 'loop',
|
|
46
55
|
agent: agent || null,
|
|
47
56
|
send_ms: sendMs,
|
|
57
|
+
pre_send_ms: preSendMs ?? null,
|
|
58
|
+
tool_resume_ms: toolResumeMs ?? null,
|
|
48
59
|
message_count: messageCount,
|
|
49
60
|
body_bytes_est: bodyBytesEst ?? null,
|
|
50
61
|
});
|
|
@@ -9,7 +9,8 @@ import { isDiagnosticIOEnabled } from '../../../lib/mixdog-debug.cjs';
|
|
|
9
9
|
const WARNED_KEYS = new Set();
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// In-memory buffer
|
|
12
|
+
// In-memory buffer. Product sessions flush directly into the daemon-hosted
|
|
13
|
+
// memory module; the standalone path can still use its local HTTP service.
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
let _buffer = [];
|
|
15
16
|
const _BUFFER_MAX = 2000;
|
|
@@ -225,21 +226,11 @@ try {
|
|
|
225
226
|
function _resolveServiceUrl() {
|
|
226
227
|
if (_serviceUrl) return _serviceUrl;
|
|
227
228
|
try {
|
|
228
|
-
//
|
|
229
|
-
// pid-validated; fall back to legacy active-instance.json memory_port.
|
|
229
|
+
// Standalone memory HTTP uses its pid-validated discovery advert.
|
|
230
230
|
const advertPort = readServicePort('memory', { requirePid: false });
|
|
231
231
|
if (advertPort) { _serviceAdvertPort = advertPort; _serviceUrl = `http://127.0.0.1:${advertPort}`; return _serviceUrl; }
|
|
232
232
|
_serviceAdvertPort = null;
|
|
233
|
-
|
|
234
|
-
? join(process.env.MIXDOG_RUNTIME_ROOT)
|
|
235
|
-
: join(os.tmpdir(), 'mixdog');
|
|
236
|
-
const activeFile = join(runtimeRoot, 'active-instance.json');
|
|
237
|
-
if (!existsSync(activeFile)) return null;
|
|
238
|
-
const active = JSON.parse(readFileSync(activeFile, 'utf-8'));
|
|
239
|
-
const port = Number(active && active.memory_port);
|
|
240
|
-
if (!Number.isFinite(port) || port <= 0) return null;
|
|
241
|
-
_serviceUrl = `http://127.0.0.1:${port}`;
|
|
242
|
-
return _serviceUrl;
|
|
233
|
+
return null;
|
|
243
234
|
} catch {
|
|
244
235
|
return null;
|
|
245
236
|
}
|