mixdog 0.9.94 → 0.9.96
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 +109 -0
- package/package.json +26 -13
- package/scripts/.tmp-coverage-report.mjs +96 -0
- package/scripts/build-tui.mjs +17 -1
- package/scripts/fixtures/patch-replay-corpus.json +98 -0
- package/scripts/lib/isolated-root-cleanup.mjs +19 -0
- package/scripts/run-suite.mjs +100 -0
- package/src/defaults/mixdog-config.template.json +5 -8
- package/src/lib/rules-builder.cjs +4 -3
- package/src/output-styles/detailed.md +13 -15
- package/src/output-styles/extreme-minimal.md +7 -11
- package/src/output-styles/minimal.md +4 -7
- package/src/output-styles/simple.md +11 -13
- package/src/rules/agent/30-explorer.md +7 -4
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-brief.md +4 -5
- package/src/rules/lead/lead-tool.md +3 -2
- package/src/rules/shared/01-tool.md +20 -35
- 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/cache-strategy.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +13 -2
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +29 -0
- package/src/runtime/agent/orchestrator/config.mjs +246 -67
- package/src/runtime/agent/orchestrator/mcp/client.mjs +29 -14
- 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-effort.mjs +97 -7
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +11 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +4 -6
- 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/lib/stream-outcome.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +8 -8
- 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 +29 -16
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +14 -16
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-headers.mjs +5 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +13 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/registry.mjs +51 -4
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +82 -22
- 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 +40 -36
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +10 -11
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +5 -33
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +0 -14
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +9 -8
- 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 +1 -27
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +6 -10
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -4
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +168 -83
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +34 -18
- 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 +89 -11
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +6 -39
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -6
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +56 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +124 -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/lib/grep-context-expander.mjs +7 -16
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +10 -26
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-image-resize.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -23
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +24 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +20 -43
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +76 -3
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +13 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +10 -5
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +38 -13
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +98 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +8 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +138 -25
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +47 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +6 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +3 -1
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +9 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +40 -2
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +57 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +177 -16
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +21 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +217 -25
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +10 -10
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +14 -15
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +60 -8
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
- package/src/runtime/channels/backends/discord.mjs +5 -14
- package/src/runtime/channels/backends/telegram.mjs +0 -5
- package/src/runtime/channels/lib/config.mjs +2 -2
- package/src/runtime/channels/lib/inbound-handler.mjs +1 -2
- package/src/runtime/channels/lib/output-forwarder.mjs +24 -5
- package/src/runtime/channels/lib/scheduler.mjs +3 -3
- package/src/runtime/channels/lib/worker-main.mjs +1 -1
- package/src/runtime/media/renditions.mjs +21 -2
- package/src/runtime/memory/index.mjs +5 -38
- package/src/runtime/memory/lib/agent-ipc.mjs +145 -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 +12 -33
- 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 +8 -0
- 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-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-cycle3.mjs +3 -1
- package/src/runtime/memory/lib/pg/process.mjs +32 -9
- package/src/runtime/memory/tool-defs.mjs +7 -11
- package/src/runtime/search/tool-defs.mjs +2 -2
- package/src/runtime/shared/agent-route-config.mjs +119 -0
- package/src/runtime/shared/atomic-file.mjs +133 -4
- package/src/runtime/shared/background-tasks.mjs +6 -3
- 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 +60 -31
- package/src/runtime/shared/config.mjs +130 -44
- package/src/runtime/shared/resource-admission.mjs +146 -58
- package/src/runtime/shared/stream-progress.mjs +6 -0
- package/src/runtime/shared/tool-status.mjs +10 -1
- package/src/runtime/shared/tool-surface.mjs +2 -7
- package/src/runtime/shared/turn-snapshot.mjs +411 -21
- package/src/runtime/shared/turn-worktree-snapshot.mjs +552 -0
- package/src/session-runtime/config-helpers.mjs +39 -37
- package/src/session-runtime/context-status.mjs +9 -3
- package/src/session-runtime/lifecycle-api.mjs +35 -5
- package/src/session-runtime/mcp-glue.mjs +11 -6
- package/src/session-runtime/model-route-api.mjs +7 -5
- package/src/session-runtime/provider-auth-api.mjs +0 -7
- package/src/session-runtime/provider-models.mjs +94 -43
- package/src/session-runtime/runtime-core.mjs +57 -19
- package/src/session-runtime/runtime-tunables.mjs +4 -0
- package/src/session-runtime/self-update.mjs +33 -4
- package/src/session-runtime/session-lifecycle.mjs +11 -2
- package/src/session-runtime/session-text.mjs +2 -1
- package/src/session-runtime/session-turn-api.mjs +139 -23
- package/src/session-runtime/settings-api.mjs +5 -17
- 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 +25 -28
- 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/agent-tool.mjs +4 -4
- package/src/standalone/backend-daemon.mjs +631 -0
- package/src/standalone/channel-admin.mjs +12 -12
- package/src/standalone/channel-daemon-client.mjs +17 -1
- package/src/standalone/channel-daemon-transport.mjs +231 -9
- package/src/standalone/channel-worker.mjs +3 -2
- package/src/standalone/engine-daemon-client.mjs +1069 -0
- package/src/standalone/engine-daemon-protocol.mjs +32 -0
- package/src/standalone/engine-daemon-service.mjs +1108 -0
- package/src/standalone/engine-daemon-transport.mjs +794 -0
- package/src/standalone/explore-tool.mjs +187 -70
- package/src/standalone/fair-call-scheduler.mjs +264 -0
- package/src/standalone/session-protocol.mjs +188 -0
- package/src/tui/App.jsx +92 -64
- package/src/tui/app/app-format.mjs +4 -2
- package/src/tui/app/app-view.jsx +10 -2
- package/src/tui/app/channel-pickers.mjs +7 -6
- package/src/tui/app/core-memory-picker.mjs +20 -20
- package/src/tui/app/doctor.mjs +5 -11
- package/src/tui/app/extension-pickers.mjs +20 -18
- package/src/tui/app/maintenance-pickers.mjs +27 -27
- package/src/tui/app/message-selector.mjs +103 -0
- package/src/tui/app/onboarding-steps.mjs +24 -20
- package/src/tui/app/project-picker.mjs +80 -56
- package/src/tui/app/prompt-submit.mjs +59 -43
- package/src/tui/app/resume-picker.mjs +2 -2
- package/src/tui/app/route-pickers.mjs +16 -9
- package/src/tui/app/settings-picker.mjs +60 -65
- package/src/tui/app/slash-dispatch.mjs +32 -28
- package/src/tui/app/transcript-window.mjs +57 -0
- package/src/tui/app/usage-context-panels.mjs +22 -7
- package/src/tui/app/use-mouse-input.mjs +25 -3
- 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 +39 -17
- package/src/tui/app/use-transcript-scroll.mjs +39 -10
- package/src/tui/app/use-transcript-window.mjs +62 -14
- package/src/tui/app/use-welcome-prompt-hint.mjs +2 -2
- package/src/tui/components/PromptInput.jsx +81 -38
- package/src/tui/components/Spinner.jsx +89 -95
- package/src/tui/components/TextEntryPanel.jsx +14 -0
- package/src/tui/components/ToolExecution.jsx +2 -2
- 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/dist/index.mjs +1694 -10633
- package/src/tui/engine/agent-job-feed.mjs +2 -2
- package/src/tui/engine/live-share.mjs +110 -3
- package/src/tui/engine/session-api-ext.mjs +24 -5
- package/src/tui/engine/session-api.mjs +165 -55
- package/src/tui/engine/session-flow.mjs +50 -5
- package/src/tui/engine/tool-card-results.mjs +10 -2
- package/src/tui/engine/tool-result-text.mjs +10 -0
- package/src/tui/engine/turn.mjs +11 -64
- package/src/tui/engine-local-session.mjs +1124 -0
- package/src/tui/engine.mjs +16 -1065
- package/src/tui/index.jsx +41 -4
- package/src/tui/markdown/stream-fence.mjs +1 -1
- package/src/tui/spinner-meta.mjs +80 -0
- package/src/tui/spinner-verbs.mjs +83 -0
- package/src/ui/statusline-segments.mjs +43 -10
- package/src/ui/statusline.mjs +10 -1
- package/scripts/tmp-cdp-errors.mjs +0 -41
- package/scripts/tmp-cdp-inspect.mjs +0 -41
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +0 -176
- package/src/standalone/channel-daemon.mjs +0 -226
|
@@ -282,7 +282,7 @@ function summarizePromptCacheTools(tools) {
|
|
|
282
282
|
|
|
283
283
|
/**
|
|
284
284
|
* Build a stable, prefix-scoped prompt_cache_key for OpenAI-style key-prefix
|
|
285
|
-
* providers. OpenAI OAuth
|
|
285
|
+
* providers. OpenAI OAuth uses a thread-scoped key by default:
|
|
286
286
|
* prompt_cache_key is the session/thread identity, clamped to the backend's
|
|
287
287
|
* 64-character limit. Other OpenAI-style providers keep the older
|
|
288
288
|
* namespace+prefix-hash key shape, but no longer get a shard suffix unless an
|
|
@@ -400,7 +400,7 @@ function assignPromptCacheLaneSlot(provider, opts, shards, seed, { auto = false
|
|
|
400
400
|
|
|
401
401
|
/**
|
|
402
402
|
* Resolve an optional cache-lane slot for OpenAI-style prompt cache sharding.
|
|
403
|
-
*
|
|
403
|
+
* prompt_cache_key is not sharded by default, so every provider now gets
|
|
404
404
|
* one un-suffixed key unless an env/config override opts into shards.
|
|
405
405
|
*/
|
|
406
406
|
export function resolveProviderPromptCacheLane(provider, opts = {}, config = {}) {
|
|
@@ -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
|
});
|
|
@@ -278,7 +289,7 @@ export function parseGrepCoverage(resultText, toolName, toolArgs, resultKind) {
|
|
|
278
289
|
return out.length ? out : null;
|
|
279
290
|
}
|
|
280
291
|
|
|
281
|
-
//
|
|
292
|
+
// Patch failures all arrive as "apply_patch … failed" prose, but
|
|
282
293
|
// a malformed envelope, a rejected hunk, a preflight veto and a size/lock
|
|
283
294
|
// guard need different operator responses. Returning null means "nothing
|
|
284
295
|
// patch-specific here" and lets the generic rules (path/enoent, schema/args,
|
|
@@ -368,6 +368,10 @@ function _resolveToolFailurePath() {
|
|
|
368
368
|
if (process.env.MIXDOG_TOOL_FAILURE_LOG_DISABLE === '1') return null;
|
|
369
369
|
if (_toolFailurePath) return _toolFailurePath;
|
|
370
370
|
const explicit = process.env.MIXDOG_TOOL_FAILURE_LOG_PATH;
|
|
371
|
+
// The repo's own `node --test` suites drive intentional tool failures
|
|
372
|
+
// (patch ordering, arg guards, ...). Without an explicit path those rows
|
|
373
|
+
// land in the user's real failure log and read as production incidents.
|
|
374
|
+
if (!explicit && process.env.NODE_TEST_CONTEXT) return null;
|
|
371
375
|
// Ship-mode default: skip diagnostic tool-failure log file IO unless
|
|
372
376
|
// dev/debug, MIXDOG_DIAGNOSTICS, or an explicit path opts back in.
|
|
373
377
|
if (!explicit && !isDiagnosticIOEnabled()) return null;
|
|
@@ -158,6 +158,34 @@ function traceAgentSse({ sessionId, sseParseMs, ttftMs, provider, model, transpo
|
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
// Per-turn preflight timing (submit → provider request). The runtime also
|
|
162
|
+
// emits this as a `mixdog:turn-timing` process event for the daemon log, but
|
|
163
|
+
// that sink is process-local and invisible to trace tooling — the row below is
|
|
164
|
+
// what session-bench reads, so TTFT stage regressions stay measurable offline.
|
|
165
|
+
function traceTurnTiming({
|
|
166
|
+
sessionId, status, requestId, ttftMs, endToEndTtftMs,
|
|
167
|
+
queueMs, routeMs, preflightMs, mcpMs, providerMs,
|
|
168
|
+
}) {
|
|
169
|
+
const ms = (value) => (Number.isFinite(Number(value)) ? Math.round(Number(value)) : null);
|
|
170
|
+
const payload = {
|
|
171
|
+
status: status || 'unknown',
|
|
172
|
+
request_id: requestId || null,
|
|
173
|
+
ttft_ms: ms(ttftMs),
|
|
174
|
+
end_to_end_ttft_ms: ms(endToEndTtftMs),
|
|
175
|
+
queue_ms: ms(queueMs),
|
|
176
|
+
route_ms: ms(routeMs),
|
|
177
|
+
preflight_ms: ms(preflightMs),
|
|
178
|
+
mcp_ms: ms(mcpMs),
|
|
179
|
+
provider_ms: ms(providerMs),
|
|
180
|
+
};
|
|
181
|
+
appendAgentTrace({
|
|
182
|
+
sessionId,
|
|
183
|
+
kind: 'turn_timing',
|
|
184
|
+
...payload,
|
|
185
|
+
payload,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
161
189
|
function extractThinkingTokens(rawUsage) {
|
|
162
190
|
if (!rawUsage || typeof rawUsage !== 'object') return null;
|
|
163
191
|
const direct = Number(rawUsage.thinking_tokens ?? rawUsage.thinkingTokens);
|
|
@@ -294,6 +322,7 @@ export {
|
|
|
294
322
|
traceAgentToolFailure,
|
|
295
323
|
traceAgentCompact,
|
|
296
324
|
traceAgentUsage,
|
|
325
|
+
traceTurnTiming,
|
|
297
326
|
resolveTraceUsageInput,
|
|
298
327
|
grokCacheChainTraceFields,
|
|
299
328
|
traceAgentCompress,
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { resolvePluginData } from '../../shared/plugin-paths.mjs';
|
|
2
2
|
import { readSection, updateSection, updateSectionAsync, getAgentApiKey, AGENT_PROVIDER_ENV } from '../../shared/config.mjs';
|
|
3
|
+
import {
|
|
4
|
+
agentRouteStorageNeedsMigration,
|
|
5
|
+
canonicalizeAgentRouteStorage,
|
|
6
|
+
} from '../../shared/agent-route-config.mjs';
|
|
3
7
|
import { OPENAI_COMPAT_PRESETS } from './providers/openai-compat-presets.mjs';
|
|
4
8
|
import {
|
|
5
9
|
hasAnthropicOAuthCredentials,
|
|
@@ -7,20 +11,6 @@ import {
|
|
|
7
11
|
hasGrokOAuthCredentials,
|
|
8
12
|
} from './providers/oauth-credential-probes.mjs';
|
|
9
13
|
|
|
10
|
-
// Keep config loading free of standalone provider-admin and its provider
|
|
11
|
-
// runtimes. These identifiers mirror that module's static catalog, assembled
|
|
12
|
-
// solely from the lightweight config/provider constants already imported here.
|
|
13
|
-
const CONFIG_PROVIDER_IDS = new Set([
|
|
14
|
-
...Object.keys(AGENT_PROVIDER_ENV),
|
|
15
|
-
...Object.keys(OPENAI_COMPAT_PRESETS),
|
|
16
|
-
'openai-oauth',
|
|
17
|
-
'anthropic-oauth',
|
|
18
|
-
'grok-oauth',
|
|
19
|
-
]);
|
|
20
|
-
function isConfiguredProviderId(provider) {
|
|
21
|
-
return CONFIG_PROVIDER_IDS.has(String(provider || '').trim());
|
|
22
|
-
}
|
|
23
|
-
|
|
24
14
|
// Thin wrapper around resolvePluginData so callers in this orchestrator tree
|
|
25
15
|
// can import a single helper without reaching into shared/.
|
|
26
16
|
export function getPluginData() {
|
|
@@ -33,19 +23,13 @@ export function getPluginData() {
|
|
|
33
23
|
// llm/index.mjs and setup-server.mjs so UI/runtime cannot drift from config.
|
|
34
24
|
//
|
|
35
25
|
// Explore and Maintainer start without a route so they dynamically inherit the
|
|
36
|
-
// Main route.
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// (cycle1/2/3-agent, distinct slots and invokedBy) but resolve their model from
|
|
40
|
-
// `maint.memory` via the `maintKey` override on their hidden-role entries.
|
|
26
|
+
// Main route. Their explicit routes live canonically in `agents.explore` and
|
|
27
|
+
// `agents.maintainer`; load-time migration still accepts the older workflow /
|
|
28
|
+
// maintenance aliases.
|
|
41
29
|
// scheduler/webhook still let a per-entry config.json model win first (the
|
|
42
30
|
// caller passes it explicitly via opts.preset); the haiku default below only
|
|
43
31
|
// applies when an entry omits its own model.
|
|
44
|
-
//
|
|
45
|
-
// UI / allow-list view (GET cleanup + POST validation) and is intentionally a
|
|
46
|
-
// SUBSET of DEFAULT_MAINTENANCE: scheduler/webhook carry a per-entry model and
|
|
47
|
-
// are not shown as shared rows, but still inherit the haiku default above when
|
|
48
|
-
// an entry omits its own model.
|
|
32
|
+
// Legacy route slots accepted only at config ingress for migration.
|
|
49
33
|
const MAINTENANCE_SLOTS = Object.freeze(['explore', 'memory']);
|
|
50
34
|
|
|
51
35
|
// --- User profile (statusline /profile) -------------------------------------
|
|
@@ -213,6 +197,13 @@ function normalizeSearchRoute(route) {
|
|
|
213
197
|
return null;
|
|
214
198
|
const provider = normalizeAgentProviderId(route.provider);
|
|
215
199
|
const model = String(route.model || '').trim();
|
|
200
|
+
if (!provider && model) {
|
|
201
|
+
return {
|
|
202
|
+
provider: 'default',
|
|
203
|
+
model: 'default',
|
|
204
|
+
...(String(route.toolType || '').trim() ? { toolType: String(route.toolType).trim() } : {}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
216
207
|
if (!provider || !model)
|
|
217
208
|
return null;
|
|
218
209
|
const out = { provider, model };
|
|
@@ -230,17 +221,11 @@ function normalizeSearchRoute(route) {
|
|
|
230
221
|
// Normalize stored maintenance slots to the direct {provider, model} route
|
|
231
222
|
// shape (provider/model/effort/fast only). Non-route values are dropped so the
|
|
232
223
|
// DEFAULT_MAINTENANCE route fills the slot.
|
|
233
|
-
function normalizeMaintenanceRoutes(rawMaint
|
|
224
|
+
function normalizeMaintenanceRoutes(rawMaint) {
|
|
234
225
|
const out = {};
|
|
235
|
-
const configuredProvider = String(defaultProvider || '').trim();
|
|
236
|
-
const fallbackProvider = isConfiguredProviderId(configuredProvider)
|
|
237
|
-
? configuredProvider
|
|
238
|
-
: 'anthropic-oauth';
|
|
239
226
|
for (const [slot, value] of Object.entries(rawMaint || {})) {
|
|
240
227
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
241
|
-
const provider = normalizeAgentProviderId(
|
|
242
|
-
value.provider || ((slot === 'explore' || slot === 'memory') ? fallbackProvider : ''),
|
|
243
|
-
);
|
|
228
|
+
const provider = normalizeAgentProviderId(value.provider);
|
|
244
229
|
const model = String(value.model || '').trim();
|
|
245
230
|
if (provider && model) {
|
|
246
231
|
const route = { provider, model };
|
|
@@ -281,6 +266,174 @@ function normalizeRecapConfig(rawRecap) {
|
|
|
281
266
|
return recap;
|
|
282
267
|
}
|
|
283
268
|
|
|
269
|
+
function configObject(value) {
|
|
270
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? { ...value } : {};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function nonEmptyConfigObject(value) {
|
|
274
|
+
return Object.keys(value).length > 0 ? value : undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function normalizeStoredCompactionType(value) {
|
|
278
|
+
const raw = String(value ?? '').trim().toLowerCase().replace(/_/g, '-');
|
|
279
|
+
if (['1', 'type1', 'type-1', 'semantic', 'summary', 'default'].includes(raw)) return 'semantic';
|
|
280
|
+
if (['2', 'type2', 'type-2', 'recall', 'recall-fast', 'recall-fasttrack',
|
|
281
|
+
'recall-fast-track', 'fasttrack', 'fast-track'].includes(raw)) return 'recall-fasttrack';
|
|
282
|
+
return 'recall-fasttrack';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function canonicalizeAutoClearStorage(value) {
|
|
286
|
+
const raw = configObject(value);
|
|
287
|
+
const next = { ...raw };
|
|
288
|
+
const idleMs = Number(raw.idleMs ?? raw.thresholdMs ?? raw.idleMillis);
|
|
289
|
+
const providerSource = configObject(raw.providerIdleMs ?? raw.providerDefaults ?? raw.providers);
|
|
290
|
+
const providerIdleMs = {};
|
|
291
|
+
for (const [key, candidate] of Object.entries(providerSource)) {
|
|
292
|
+
const provider = String(key || '').trim().toLowerCase();
|
|
293
|
+
const duration = Number(candidate);
|
|
294
|
+
if (!provider || !Number.isFinite(duration) || duration <= 0) continue;
|
|
295
|
+
providerIdleMs[provider] = Math.max(60_000, Math.round(duration));
|
|
296
|
+
}
|
|
297
|
+
delete next.thresholdMs;
|
|
298
|
+
delete next.idleMillis;
|
|
299
|
+
delete next.providerDefaults;
|
|
300
|
+
delete next.providers;
|
|
301
|
+
delete next.custom;
|
|
302
|
+
if (Number.isFinite(idleMs) && idleMs > 0) next.idleMs = Math.max(60_000, Math.round(idleMs));
|
|
303
|
+
else delete next.idleMs;
|
|
304
|
+
if (Object.keys(providerIdleMs).length) next.providerIdleMs = providerIdleMs;
|
|
305
|
+
else delete next.providerIdleMs;
|
|
306
|
+
if (Object.prototype.hasOwnProperty.call(raw, 'enabled')) next.enabled = raw.enabled !== false;
|
|
307
|
+
if (Object.prototype.hasOwnProperty.call(raw, 'minContextPercent')) {
|
|
308
|
+
const percent = Number(raw.minContextPercent);
|
|
309
|
+
if (Number.isFinite(percent)) next.minContextPercent = Math.min(100, Math.max(0, Math.round(percent)));
|
|
310
|
+
else delete next.minContextPercent;
|
|
311
|
+
}
|
|
312
|
+
return nonEmptyConfigObject(next);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function canonicalizeCompactionStorage(value) {
|
|
316
|
+
const raw = configObject(value);
|
|
317
|
+
const next = { ...raw };
|
|
318
|
+
const hasType = ['type', 'compactType', 'compact_type']
|
|
319
|
+
.some((key) => Object.prototype.hasOwnProperty.call(raw, key));
|
|
320
|
+
if (hasType) next.type = normalizeStoredCompactionType(raw.type ?? raw.compactType ?? raw.compact_type);
|
|
321
|
+
if (Object.prototype.hasOwnProperty.call(raw, 'auto')
|
|
322
|
+
|| Object.prototype.hasOwnProperty.call(raw, 'enabled')) {
|
|
323
|
+
next.auto = raw.auto !== false && raw.enabled !== false;
|
|
324
|
+
}
|
|
325
|
+
delete next.compactType;
|
|
326
|
+
delete next.compact_type;
|
|
327
|
+
delete next.enabled;
|
|
328
|
+
return nonEmptyConfigObject(next);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function canonicalizeShellStorage(value) {
|
|
332
|
+
const raw = configObject(value);
|
|
333
|
+
const next = { ...raw };
|
|
334
|
+
const command = String(raw.command ?? raw.path ?? raw.executable ?? raw.shell ?? '').trim();
|
|
335
|
+
delete next.path;
|
|
336
|
+
delete next.executable;
|
|
337
|
+
delete next.shell;
|
|
338
|
+
if (command) next.command = command;
|
|
339
|
+
else delete next.command;
|
|
340
|
+
return nonEmptyConfigObject(next);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function canonicalizeModulesStorage(value) {
|
|
344
|
+
const modules = configObject(value);
|
|
345
|
+
delete modules.memory;
|
|
346
|
+
return nonEmptyConfigObject(modules);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function migrateLegacyModelSettings(raw = {}) {
|
|
350
|
+
const modelSettings = raw.modelSettings && typeof raw.modelSettings === 'object'
|
|
351
|
+
? { ...raw.modelSettings }
|
|
352
|
+
: {};
|
|
353
|
+
const legacyFastModels = raw.fastModels && typeof raw.fastModels === 'object'
|
|
354
|
+
? raw.fastModels
|
|
355
|
+
: {};
|
|
356
|
+
for (const [key, enabled] of Object.entries(legacyFastModels)) {
|
|
357
|
+
if (enabled !== true) continue;
|
|
358
|
+
const current = modelSettings[key];
|
|
359
|
+
if (!current || typeof current !== 'object' || Array.isArray(current)) {
|
|
360
|
+
modelSettings[key] = { fast: true };
|
|
361
|
+
} else if (!Object.prototype.hasOwnProperty.call(current, 'fast')) {
|
|
362
|
+
modelSettings[key] = { ...current, fast: true };
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return modelSettings;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function canonicalizeLegacyAgentStorage(value = {}) {
|
|
369
|
+
const next = canonicalizeAgentRouteStorage(value);
|
|
370
|
+
next.presets = Array.isArray(next.presets)
|
|
371
|
+
? next.presets.map((preset) => normalizePreset(preset)).filter(Boolean)
|
|
372
|
+
: [];
|
|
373
|
+
next.modelSettings = migrateLegacyModelSettings(value);
|
|
374
|
+
const autoClear = canonicalizeAutoClearStorage(next.autoClear);
|
|
375
|
+
if (autoClear) next.autoClear = autoClear;
|
|
376
|
+
else delete next.autoClear;
|
|
377
|
+
const compaction = canonicalizeCompactionStorage(next.compaction);
|
|
378
|
+
if (compaction) next.compaction = compaction;
|
|
379
|
+
else delete next.compaction;
|
|
380
|
+
const shell = canonicalizeShellStorage(next.shell);
|
|
381
|
+
if (shell) next.shell = shell;
|
|
382
|
+
else delete next.shell;
|
|
383
|
+
if (Object.prototype.hasOwnProperty.call(next, 'profile')) {
|
|
384
|
+
next.profile = normalizeProfileConfig(next.profile);
|
|
385
|
+
}
|
|
386
|
+
if (Object.prototype.hasOwnProperty.call(next, 'skills')) {
|
|
387
|
+
const skills = normalizeSkillsConfig(next.skills);
|
|
388
|
+
if (skills.disabled.length) next.skills = skills;
|
|
389
|
+
else delete next.skills;
|
|
390
|
+
}
|
|
391
|
+
if (!next.searchRoute && value?.search && typeof value.search === 'object') {
|
|
392
|
+
next.searchRoute = value.search;
|
|
393
|
+
}
|
|
394
|
+
next.searchRoute = normalizeSearchRoute(next.searchRoute);
|
|
395
|
+
const modules = next.modules && typeof next.modules === 'object' && !Array.isArray(next.modules)
|
|
396
|
+
? { ...next.modules }
|
|
397
|
+
: {};
|
|
398
|
+
if (Object.prototype.hasOwnProperty.call(modules, 'memory')) {
|
|
399
|
+
const legacyMemory = modules.memory;
|
|
400
|
+
if (!(next.recap && typeof next.recap === 'object' && Object.prototype.hasOwnProperty.call(next.recap, 'enabled'))) {
|
|
401
|
+
const enabled = legacyMemory && typeof legacyMemory === 'object'
|
|
402
|
+
? legacyMemory.enabled !== false
|
|
403
|
+
: legacyMemory !== false;
|
|
404
|
+
next.recap = { ...(next.recap || {}), enabled };
|
|
405
|
+
}
|
|
406
|
+
delete modules.memory;
|
|
407
|
+
}
|
|
408
|
+
if (Object.keys(modules).length) next.modules = modules;
|
|
409
|
+
else delete next.modules;
|
|
410
|
+
delete next.fastModels;
|
|
411
|
+
delete next.agentMaintenance;
|
|
412
|
+
delete next.runtime;
|
|
413
|
+
delete next.search;
|
|
414
|
+
delete next.capabilities;
|
|
415
|
+
delete next.defaultProvider;
|
|
416
|
+
delete next.guide;
|
|
417
|
+
return next;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function agentConfigStorageNeedsMigration(value = {}) {
|
|
421
|
+
const modules = value?.modules;
|
|
422
|
+
const presets = Array.isArray(value?.presets) ? value.presets : [];
|
|
423
|
+
const canonical = canonicalizeLegacyAgentStorage(value);
|
|
424
|
+
const normalizedFields = ['autoClear', 'compaction', 'shell', 'profile', 'skills', 'modules', 'guide'];
|
|
425
|
+
return agentRouteStorageNeedsMigration(value)
|
|
426
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'fastModels')
|
|
427
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'agentMaintenance')
|
|
428
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'runtime')
|
|
429
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'search')
|
|
430
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'capabilities')
|
|
431
|
+
|| Object.prototype.hasOwnProperty.call(value || {}, 'defaultProvider')
|
|
432
|
+
|| presets.some((preset) => !normalizePreset(preset))
|
|
433
|
+
|| (modules && typeof modules === 'object' && Object.prototype.hasOwnProperty.call(modules, 'memory'))
|
|
434
|
+
|| normalizedFields.some((key) => JSON.stringify(value?.[key]) !== JSON.stringify(canonical?.[key]));
|
|
435
|
+
}
|
|
436
|
+
|
|
284
437
|
export function loadConfig(options = {}) {
|
|
285
438
|
const includeSecrets = options.secrets !== false;
|
|
286
439
|
const sectionRaw = readSection('agent');
|
|
@@ -290,6 +443,8 @@ export function loadConfig(options = {}) {
|
|
|
290
443
|
if (raw.agent && raw.agent.providers) {
|
|
291
444
|
raw = raw.agent;
|
|
292
445
|
}
|
|
446
|
+
const storageNeedsMigration = agentConfigStorageNeedsMigration(raw);
|
|
447
|
+
raw = canonicalizeLegacyAgentStorage(raw);
|
|
293
448
|
const defaults = buildDefaultConfig({ detectCredentials: includeSecrets });
|
|
294
449
|
// Deep-merge provider subkeys: unknown per-provider values are
|
|
295
450
|
// preserved through save/load so future fields round-trip
|
|
@@ -371,27 +526,13 @@ export function loadConfig(options = {}) {
|
|
|
371
526
|
// without overriding an explicit modelSettings.fast value. Keep
|
|
372
527
|
// the compatibility read here, but never expose/persist the
|
|
373
528
|
// legacy container again.
|
|
374
|
-
const modelSettings = raw
|
|
375
|
-
? { ...raw.modelSettings }
|
|
376
|
-
: {};
|
|
377
|
-
const legacyFastModels = raw.fastModels && typeof raw.fastModels === 'object'
|
|
378
|
-
? raw.fastModels
|
|
379
|
-
: {};
|
|
380
|
-
for (const [key, enabled] of Object.entries(legacyFastModels)) {
|
|
381
|
-
if (enabled !== true) continue;
|
|
382
|
-
const current = modelSettings[key];
|
|
383
|
-
if (!current || typeof current !== 'object' || Array.isArray(current)) {
|
|
384
|
-
modelSettings[key] = { fast: true };
|
|
385
|
-
} else if (!Object.prototype.hasOwnProperty.call(current, 'fast')) {
|
|
386
|
-
modelSettings[key] = { ...current, fast: true };
|
|
387
|
-
}
|
|
388
|
-
}
|
|
529
|
+
const modelSettings = migrateLegacyModelSettings(raw);
|
|
389
530
|
const workflowRoutes = raw.workflowRoutes && typeof raw.workflowRoutes === 'object' ? { ...raw.workflowRoutes } : {};
|
|
390
531
|
delete workflowRoutes.search;
|
|
391
532
|
// Normalize maintenance slots to routes, then overlay onto the
|
|
392
533
|
// route-shaped defaults.
|
|
393
|
-
const normalizedMaint = normalizeMaintenanceRoutes(rawMaint
|
|
394
|
-
|
|
534
|
+
const normalizedMaint = normalizeMaintenanceRoutes(rawMaint);
|
|
535
|
+
const loaded = canonicalizeAgentRouteStorage({
|
|
395
536
|
providers: mergedProviders,
|
|
396
537
|
mcpServers,
|
|
397
538
|
mcpProjectOverrides: raw.mcpProjectOverrides && typeof raw.mcpProjectOverrides === 'object' && !Array.isArray(raw.mcpProjectOverrides)
|
|
@@ -418,7 +559,40 @@ export function loadConfig(options = {}) {
|
|
|
418
559
|
update: raw.update && typeof raw.update === 'object' ? { ...raw.update } : {},
|
|
419
560
|
recap: recapConfig,
|
|
420
561
|
modules: raw.modules && typeof raw.modules === 'object' ? { ...raw.modules } : {},
|
|
421
|
-
};
|
|
562
|
+
});
|
|
563
|
+
if (storageNeedsMigration) {
|
|
564
|
+
try {
|
|
565
|
+
// `readSection('agent')` sees cross-section migrations from
|
|
566
|
+
// canonicalizeUnifiedConfig (root autoClear/compaction/
|
|
567
|
+
// shell and memory.user.title), while the in-lock raw
|
|
568
|
+
// section does not. Seed only those missing/partial values
|
|
569
|
+
// from the canonical read snapshot; the current in-lock
|
|
570
|
+
// section still wins for concurrent edits.
|
|
571
|
+
persistAgentConfig((current) => {
|
|
572
|
+
const merged = { ...current };
|
|
573
|
+
for (const key of ['autoClear', 'compaction', 'shell', 'recap']) {
|
|
574
|
+
if (!Object.prototype.hasOwnProperty.call(current, key)
|
|
575
|
+
&& Object.prototype.hasOwnProperty.call(raw, key)) {
|
|
576
|
+
merged[key] = raw[key];
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (Object.prototype.hasOwnProperty.call(raw, 'profile')) {
|
|
580
|
+
const currentProfile = normalizeProfileConfig(current.profile);
|
|
581
|
+
const migratedProfile = normalizeProfileConfig(raw.profile);
|
|
582
|
+
merged.profile = {
|
|
583
|
+
...currentProfile,
|
|
584
|
+
...(!currentProfile.title && migratedProfile.title
|
|
585
|
+
? { title: migratedProfile.title }
|
|
586
|
+
: {}),
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
return canonicalizeLegacyAgentStorage(merged);
|
|
590
|
+
});
|
|
591
|
+
} catch (err) {
|
|
592
|
+
process.stderr.write(`[config] persist canonical agent config failed: ${err?.message}\n`);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return loaded;
|
|
422
596
|
}
|
|
423
597
|
catch { /* fall through */ }
|
|
424
598
|
}
|
|
@@ -430,7 +604,6 @@ export function loadConfig(options = {}) {
|
|
|
430
604
|
presets: DEFAULT_PRESETS.map(p => ({ ...p })),
|
|
431
605
|
default: null,
|
|
432
606
|
maintenance: { ...DEFAULT_MAINTENANCE },
|
|
433
|
-
workflowRoutes: {},
|
|
434
607
|
searchRoute: null,
|
|
435
608
|
modelSettings: {},
|
|
436
609
|
onboarding: {},
|
|
@@ -491,6 +664,7 @@ export async function patchSkillsDisabledAsync(disabledNames) {
|
|
|
491
664
|
}
|
|
492
665
|
|
|
493
666
|
function buildAgentSaveBuilder(config, appliedDirty) {
|
|
667
|
+
const canonicalRoutes = canonicalizeLegacyAgentStorage(config);
|
|
494
668
|
// Strip ephemeral defaults from providers but preserve any unknown
|
|
495
669
|
// per-provider subkey so future schema additions round-trip through the
|
|
496
670
|
// setup UI without changes here. apiKey is intentionally omitted —
|
|
@@ -514,13 +688,15 @@ function buildAgentSaveBuilder(config, appliedDirty) {
|
|
|
514
688
|
persistedProviders[name] = slim;
|
|
515
689
|
}
|
|
516
690
|
}
|
|
517
|
-
const
|
|
518
|
-
?
|
|
519
|
-
: {};
|
|
520
|
-
delete workflowRoutes.search;
|
|
521
|
-
const presets = Array.isArray(config.presets)
|
|
522
|
-
? config.presets.filter(p => p?.id !== 'workflow-search')
|
|
691
|
+
const presets = Array.isArray(canonicalRoutes.presets)
|
|
692
|
+
? canonicalRoutes.presets.filter(p => p?.id !== 'workflow-search')
|
|
523
693
|
: [];
|
|
694
|
+
const profile = normalizeProfileConfig(config.profile);
|
|
695
|
+
const skills = normalizeSkillsConfig(config.skills);
|
|
696
|
+
const autoClear = canonicalizeAutoClearStorage(config.autoClear);
|
|
697
|
+
const compaction = canonicalizeCompactionStorage(config.compaction);
|
|
698
|
+
const shell = canonicalizeShellStorage(config.shell);
|
|
699
|
+
const modules = canonicalizeModulesStorage(config.modules);
|
|
524
700
|
// Build the replacement from `existingRaw` — the section read INSIDE the
|
|
525
701
|
// file lock — not a snapshot taken before it, so unmanaged keys written by
|
|
526
702
|
// a concurrent instance survive the save (lost-update guard).
|
|
@@ -559,27 +735,25 @@ function buildAgentSaveBuilder(config, appliedDirty) {
|
|
|
559
735
|
}
|
|
560
736
|
const next = {
|
|
561
737
|
...existingRaw,
|
|
562
|
-
guide: config.guide || existingRaw.guide || undefined,
|
|
563
738
|
providers: persistedProviders,
|
|
564
739
|
mcpServers,
|
|
565
740
|
mcpProjectOverrides,
|
|
566
741
|
presets,
|
|
567
742
|
default: config.default || null,
|
|
568
|
-
maintenance:
|
|
569
|
-
workflowRoutes,
|
|
743
|
+
maintenance: canonicalRoutes.maintenance,
|
|
570
744
|
searchRoute: normalizeSearchRoute(config.searchRoute),
|
|
571
745
|
modelSettings: config.modelSettings || {},
|
|
572
746
|
onboarding: config.onboarding || {},
|
|
573
|
-
agents:
|
|
747
|
+
agents: canonicalRoutes.agents,
|
|
574
748
|
workflow: config.workflow || { active: 'default' },
|
|
575
|
-
profile
|
|
576
|
-
skills:
|
|
577
|
-
autoClear
|
|
578
|
-
compaction
|
|
579
|
-
shell
|
|
749
|
+
profile,
|
|
750
|
+
skills: skills.disabled.length ? skills : undefined,
|
|
751
|
+
autoClear,
|
|
752
|
+
compaction,
|
|
753
|
+
shell,
|
|
580
754
|
update: config.update || {},
|
|
581
755
|
recap: config.recap || {},
|
|
582
|
-
modules
|
|
756
|
+
modules,
|
|
583
757
|
};
|
|
584
758
|
// These keys were previously round-tripped despite having no runtime
|
|
585
759
|
// consumer. Explicitly remove them from the in-lock baseline so a
|
|
@@ -587,6 +761,11 @@ function buildAgentSaveBuilder(config, appliedDirty) {
|
|
|
587
761
|
delete next.fastModels;
|
|
588
762
|
delete next.agentMaintenance;
|
|
589
763
|
delete next.runtime;
|
|
764
|
+
delete next.workflowRoutes;
|
|
765
|
+
delete next.search;
|
|
766
|
+
delete next.capabilities;
|
|
767
|
+
delete next.defaultProvider;
|
|
768
|
+
delete next.guide;
|
|
590
769
|
return next;
|
|
591
770
|
};
|
|
592
771
|
}
|