mixdog 0.9.87 → 0.9.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -7
- package/scripts/tool-failures.mjs +60 -24
- package/src/agents/heavy-worker/AGENT.md +3 -4
- package/src/agents/worker/AGENT.md +1 -2
- package/src/cli.mjs +8 -0
- package/src/defaults/cycle3-review-prompt.md +4 -4
- package/src/rules/agent/00-core.md +3 -0
- package/src/rules/agent/42-cycle3-agent.md +5 -6
- package/src/rules/lead/01-general.md +2 -0
- package/src/rules/shared/01-tool.md +29 -35
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +3 -51
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +59 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +67 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +57 -4
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +2 -3
- package/src/runtime/agent/orchestrator/config.mjs +29 -11
- package/src/runtime/agent/orchestrator/context/collect.mjs +1 -1
- package/src/runtime/agent/orchestrator/internal-agents.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/client.mjs +0 -24
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +25 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +321 -22
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +35 -22
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +28 -23
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +10 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +0 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +0 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +2 -5
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +27 -0
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +329 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +23 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +107 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +4 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +124 -12
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +19 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +110 -75
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +96 -113
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +160 -11
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +18 -1
- package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +147 -117
- package/src/runtime/agent/orchestrator/session/loop/stop-hooks.mjs +88 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +50 -8
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +25 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +47 -6
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +485 -24
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +100 -2
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +80 -14
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +142 -2
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +128 -32
- package/src/runtime/agent/orchestrator/session/manager.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +77 -4
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +70 -39
- package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +91 -0
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +141 -64
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +211 -28
- package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +313 -0
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +622 -53
- package/src/runtime/agent/orchestrator/session/store/serialize.mjs +43 -10
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +53 -8
- package/src/runtime/agent/orchestrator/session/store/summary-rebuild-worker.mjs +20 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +113 -9
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +476 -52
- package/src/runtime/agent/orchestrator/session/store.mjs +721 -196
- package/src/runtime/agent/orchestrator/session/token-native.mjs +2 -4
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +4 -6
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +146 -90
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +6 -0
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +93 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +5 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +85 -5
- package/src/runtime/agent/orchestrator/tools/builtin/snapshot-store.mjs +52 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +54 -51
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +14 -15
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +132 -8
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +86 -3
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +210 -38
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +23 -1
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +87 -19
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +33 -8
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +12 -2
- package/src/runtime/media/lanes.mjs +2 -2
- package/src/runtime/media/renditions.mjs +101 -17
- package/src/runtime/media/renditions.test.mjs +54 -0
- package/src/runtime/media/store.mjs +111 -43
- package/src/runtime/media/store.test.mjs +53 -11
- package/src/runtime/memory/index.mjs +26 -50
- package/src/runtime/memory/lib/core-memory-candidates.mjs +2 -7
- package/src/runtime/memory/lib/core-memory-store.mjs +2 -9
- package/src/runtime/memory/lib/cycle-scheduler.mjs +10 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +3 -3
- package/src/runtime/memory/lib/embedding-worker.mjs +60 -15
- package/src/runtime/memory/lib/http-router.mjs +4 -0
- package/src/runtime/memory/lib/ko-morph.mjs +49 -6
- package/src/runtime/memory/lib/memory-action-handlers.mjs +21 -18
- package/src/runtime/memory/lib/memory-cycle2.mjs +3 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -3
- package/src/runtime/memory/lib/memory-recall-store.mjs +24 -0
- package/src/runtime/memory/lib/query-handlers.mjs +26 -28
- package/src/runtime/memory/tool-defs.mjs +3 -3
- package/src/runtime/shared/atomic-file.mjs +5 -1
- package/src/runtime/shared/child-guardian.mjs +73 -2
- package/src/runtime/shared/provider-api-key.mjs +22 -5
- package/src/runtime/shared/tool-execution-contract.mjs +53 -0
- package/src/session-runtime/config-helpers.mjs +6 -5
- package/src/session-runtime/lifecycle-api.mjs +16 -4
- package/src/session-runtime/media-api.mjs +31 -16
- package/src/session-runtime/prewarm.mjs +2 -24
- package/src/session-runtime/runtime-core.mjs +33 -15
- package/src/session-runtime/runtime-tunables.mjs +0 -3
- package/src/session-runtime/session-lifecycle.mjs +0 -6
- package/src/session-runtime/session-title.mjs +228 -0
- package/src/session-runtime/session-turn-api.mjs +11 -7
- package/src/session-runtime/workflow-agents-api.mjs +16 -0
- package/src/session-runtime/workflow.mjs +7 -5
- package/src/standalone/agent-tool/job-views.mjs +90 -36
- package/src/standalone/agent-tool/spawn-flow.mjs +25 -216
- package/src/standalone/agent-tool/worker-index.mjs +8 -0
- package/src/standalone/agent-tool/worker-rows.mjs +2 -1
- package/src/standalone/agent-tool.mjs +17 -17
- package/src/standalone/agent-watchdog-registry.mjs +19 -2
- package/src/standalone/channel-daemon.mjs +8 -0
- package/src/standalone/explore-tool.mjs +1 -1
- package/src/standalone/memory-runtime-proxy.mjs +4 -7
- package/src/standalone/projects.mjs +4 -1
- package/src/standalone/usage-dashboard.mjs +25 -3
- package/src/tui/App.jsx +5 -3
- package/src/tui/app/resume-picker.mjs +7 -2
- package/src/tui/app/route-pickers.mjs +11 -117
- package/src/tui/app/slash-dispatch.mjs +13 -3
- package/src/tui/app/transcript-row-estimate.mjs +1 -1
- package/src/tui/app/transcript-window.mjs +17 -93
- package/src/tui/app/use-transcript-scroll.mjs +32 -2
- package/src/tui/app/use-transcript-window.mjs +64 -114
- package/src/tui/components/Markdown.jsx +19 -2
- package/src/tui/components/Message.jsx +8 -3
- package/src/tui/components/ToolExecution.jsx +7 -3
- package/src/tui/dist/index.mjs +243 -223
- package/src/tui/engine/live-share.mjs +15 -5
- package/src/tui/engine/render-timing.mjs +2 -2
- package/src/tui/engine/session-api-ext.mjs +74 -4
- package/src/tui/engine/session-flow.mjs +3 -1
- package/src/tui/engine.mjs +2 -2
- package/src/tui/hooks/useSharedTick.mjs +0 -2
- package/src/tui/index.jsx +5 -5
- package/src/ui/statusline-agents.mjs +44 -11
- package/src/vendor/statusline/bin/statusline-route.mjs +26 -3
- package/src/workflows/default/WORKFLOW.md +3 -3
- package/src/workflows/solo/WORKFLOW.md +5 -3
- package/src/runtime/media/index.mjs +0 -18
- package/src/runtime/memory/lib/embedding-warmup.mjs +0 -68
- package/src/standalone/agent-shard/shard-child.mjs +0 -300
- package/src/standalone/agent-shard/shard-pool.mjs +0 -443
|
@@ -262,6 +262,44 @@ export function parseGrepCoverage(resultText, toolName, toolArgs, resultKind) {
|
|
|
262
262
|
return out.length ? out : null;
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
+
// Codex-style patch failures all arrive as "apply_patch … failed" prose, but
|
|
266
|
+
// a malformed envelope, a rejected hunk, a preflight veto and a size/lock
|
|
267
|
+
// guard need different operator responses. Returning null means "nothing
|
|
268
|
+
// patch-specific here" and lets the generic rules (path/enoent, schema/args,
|
|
269
|
+
// runtime/failure) finish the classification.
|
|
270
|
+
// Emitted by the sequence/wave reporters whenever section writes reached disk
|
|
271
|
+
// and were NOT rolled back cleanly (committed-by-design, partial mode, or a
|
|
272
|
+
// rollback that itself failed). Any of these means the working tree state is
|
|
273
|
+
// committed or uncertain, which outranks every other failure detail.
|
|
274
|
+
const PATCH_COMMITTED_WRITES_RE = /already applied to disk \(writes committed\)|applied to disk \(committed\) and left in place|applied \(committed to disk\)|rollback was incomplete|--- rollback incomplete ---|patch partially (?:applied|created|updated|written)/;
|
|
275
|
+
|
|
276
|
+
function classifyPatchFailure(text) {
|
|
277
|
+
// Writes already committed outranks every other patch detail: the tree is
|
|
278
|
+
// no longer in its pre-patch state and needs inspection before a retry.
|
|
279
|
+
if (PATCH_COMMITTED_WRITES_RE.test(text)) return 'patch/partial-apply';
|
|
280
|
+
// Resource guards (byte cap) are deliberate rejections, not tool defects.
|
|
281
|
+
if (/patch too large|byte cap/.test(text)) return 'patch/limit';
|
|
282
|
+
if (/parse failed|invalid patch|malformed patch|missing \*\*\* (?:begin|end) patch|patch body is empty|unsupported patch format/.test(text)
|
|
283
|
+
// `patch contained no file sections` / `contains an empty file path`:
|
|
284
|
+
// the envelope parsed but produced nothing applicable — a patch-text
|
|
285
|
+
// defect, not a context miss.
|
|
286
|
+
|| /contained no file sections|contains an empty file path/.test(text)) return 'patch/parse';
|
|
287
|
+
if (/hunk rejected|context not found|context mismatch|anchor not found|expected first old(?:\/context| line)/.test(text)) {
|
|
288
|
+
// Stale context requires REAL evidence that the region exists but has
|
|
289
|
+
// moved/changed (nearest line, divergent line, rejected hunk). The
|
|
290
|
+
// `expected first old line: …` fragment is printed for every context
|
|
291
|
+
// miss, so on its own it means only "the context is not there".
|
|
292
|
+
return /nearest line|first divergent line|hunk rejected/.test(text)
|
|
293
|
+
? 'patch/stale-context'
|
|
294
|
+
: 'patch/context';
|
|
295
|
+
}
|
|
296
|
+
// Missing/unreadable targets are path problems even when a preflight
|
|
297
|
+
// wrapper reports them; fall through to the generic path rules.
|
|
298
|
+
if (/enoent|no such file|target unreadable|source (?:missing or )?unreadable|destination unreadable|cannot find/.test(text)) return null;
|
|
299
|
+
if (/preflight rejected|does not support|cannot be combined|only one v4a rename|missing parsed entry|internal js dispatch|rollback snapshot target/.test(text)) return 'patch/verification';
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
265
303
|
function classifyToolFailure(resultText, toolName) {
|
|
266
304
|
const raw = String(resultText ?? '');
|
|
267
305
|
const text = raw.toLowerCase();
|
|
@@ -281,16 +319,31 @@ function classifyToolFailure(resultText, toolName) {
|
|
|
281
319
|
if (/\[signal:\s*[^\]]+/i.test(leading)) return 'process/signal';
|
|
282
320
|
return 'command-exit';
|
|
283
321
|
}
|
|
284
|
-
if (
|
|
285
|
-
if (
|
|
286
|
-
|
|
322
|
+
if (/compacted-history placeholder/.test(text)) return 'expected-preflight';
|
|
323
|
+
if (/\[tool-input-validation\]/.test(text)) return 'schema/args';
|
|
324
|
+
// `shell` results quote git/patch output verbatim (e.g. `git apply`
|
|
325
|
+
// rejects); only real patch surfaces may claim the patch taxonomy, so an
|
|
326
|
+
// ordinary command exit is never rewritten into a patch failure.
|
|
327
|
+
const patchSurface = String(toolName || '') === 'apply_patch'
|
|
328
|
+
|| (String(toolName || '') !== 'shell'
|
|
329
|
+
&& /apply_patch|native patch|v4a|\*\*\* begin patch|hunk rejected|context not found/.test(text));
|
|
330
|
+
// Committed/uncertain on-disk state outranks the lock or permission detail
|
|
331
|
+
// that triggered it: the operator must inspect the tree before retrying,
|
|
332
|
+
// and a "denied"/"lock held" word further down must not hide that.
|
|
333
|
+
if (patchSurface && PATCH_COMMITTED_WRITES_RE.test(text)) return 'patch/partial-apply';
|
|
334
|
+
// Write-lock contention is a resource guard, not a timeout or a patch bug.
|
|
335
|
+
if (/advisory lock (?:timeout|busy)|lock held by pid|\.mixdog-lock/.test(text)) return 'resource/lock';
|
|
336
|
+
if (/eacces|eperm|permission|denied|forbidden|operation not permitted/.test(text)) return 'path/permission';
|
|
337
|
+
if (patchSurface) {
|
|
338
|
+
const patchCategory = classifyPatchFailure(text);
|
|
339
|
+
if (patchCategory) return patchCategory;
|
|
340
|
+
}
|
|
287
341
|
if (/requires either|invalid arguments|unknown parameter|unknown memory action/.test(text)
|
|
288
342
|
|| /must be|schema|required|old_string is .*>?=/.test(text)) return 'schema/args';
|
|
289
343
|
if (/not in allow-list|not allowed/.test(text)) return 'permission';
|
|
290
344
|
if (String(toolName || '') === 'shell' || /^\s*\[exit code:\s*\d+\]/i.test(raw)) return 'command-exit';
|
|
291
345
|
if (/enoent|cannot find|not found at this path|path does not exist|no such file|file not found in graph|unreadable/.test(text)) return 'path/enoent';
|
|
292
346
|
if (/timed out|timeout|interrupted|aborted/.test(text)) return 'timeout/abort';
|
|
293
|
-
if (/permission|denied|forbidden/.test(text)) return 'permission';
|
|
294
347
|
if (/unknown tool|tool.*not.*available|missing.*tool/.test(text)) return 'tool-surface';
|
|
295
348
|
return 'runtime/failure';
|
|
296
349
|
}
|
|
@@ -305,8 +305,8 @@ function _scheduleFlush(immediate = false) {
|
|
|
305
305
|
// Never let the periodic trace flush keep the event loop alive: a
|
|
306
306
|
// ref'd 5s timer prevented short-lived processes (smoke scripts, agent
|
|
307
307
|
// tasks) from exiting naturally after their work was done — they hung
|
|
308
|
-
// until an external timeout/kill. exit/beforeExit
|
|
309
|
-
// the
|
|
308
|
+
// until an external timeout/kill. The synchronous exit/beforeExit drain
|
|
309
|
+
// still flushes the local spool; explicit shutdown awaits remote drain.
|
|
310
310
|
_flushTimer.unref?.();
|
|
311
311
|
}
|
|
312
312
|
}
|
|
@@ -331,7 +331,6 @@ async function drainAgentTrace() {
|
|
|
331
331
|
await _flush();
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
|
-
process.on('exit', drainAgentTrace);
|
|
335
334
|
|
|
336
335
|
function appendAgentTrace(record = {}) {
|
|
337
336
|
// Test isolation — when run-all-tests.mjs sets this env, skip entirely.
|
|
@@ -366,6 +366,26 @@ export function loadConfig(options = {}) {
|
|
|
366
366
|
.map(p => normalizePreset(p))
|
|
367
367
|
.filter(Boolean)
|
|
368
368
|
.filter(p => p.id !== 'workflow-search');
|
|
369
|
+
// `fastModels` was the pre-modelSettings Fast preference map.
|
|
370
|
+
// Fold any still-meaningful true entries into the canonical map,
|
|
371
|
+
// without overriding an explicit modelSettings.fast value. Keep
|
|
372
|
+
// the compatibility read here, but never expose/persist the
|
|
373
|
+
// legacy container again.
|
|
374
|
+
const modelSettings = raw.modelSettings && typeof raw.modelSettings === 'object'
|
|
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
|
+
}
|
|
369
389
|
const workflowRoutes = raw.workflowRoutes && typeof raw.workflowRoutes === 'object' ? { ...raw.workflowRoutes } : {};
|
|
370
390
|
delete workflowRoutes.search;
|
|
371
391
|
// Normalize maintenance slots to routes, then overlay onto the
|
|
@@ -382,12 +402,10 @@ export function loadConfig(options = {}) {
|
|
|
382
402
|
maintenance: { ...DEFAULT_MAINTENANCE, ...normalizedMaint },
|
|
383
403
|
workflowRoutes,
|
|
384
404
|
searchRoute: normalizeSearchRoute(raw.searchRoute),
|
|
385
|
-
|
|
386
|
-
modelSettings: raw.modelSettings && typeof raw.modelSettings === 'object' ? raw.modelSettings : {},
|
|
405
|
+
modelSettings,
|
|
387
406
|
onboarding: raw.onboarding && typeof raw.onboarding === 'object' ? raw.onboarding : {},
|
|
388
407
|
agents: raw.agents && typeof raw.agents === 'object' ? raw.agents : {},
|
|
389
408
|
workflow: raw.workflow && typeof raw.workflow === 'object' ? { active: String(raw.workflow.active || 'default') } : { active: 'default' },
|
|
390
|
-
agentMaintenance: { enabled: true, interval: '1h', ...raw.agentMaintenance },
|
|
391
409
|
profile: normalizeProfileConfig(raw.profile),
|
|
392
410
|
skills: normalizeSkillsConfig(raw.skills),
|
|
393
411
|
// No idleMs default here: absent idleMs means "provider default"
|
|
@@ -396,7 +414,6 @@ export function loadConfig(options = {}) {
|
|
|
396
414
|
// bypass the per-provider auto-clear table.
|
|
397
415
|
autoClear: { enabled: true, ...raw.autoClear },
|
|
398
416
|
compaction: raw.compaction && typeof raw.compaction === 'object' ? { ...raw.compaction } : {},
|
|
399
|
-
runtime: raw.runtime && typeof raw.runtime === 'object' ? raw.runtime : {},
|
|
400
417
|
shell: raw.shell && typeof raw.shell === 'object' ? raw.shell : {},
|
|
401
418
|
update: raw.update && typeof raw.update === 'object' ? { ...raw.update } : {},
|
|
402
419
|
recap: recapConfig,
|
|
@@ -415,17 +432,14 @@ export function loadConfig(options = {}) {
|
|
|
415
432
|
maintenance: { ...DEFAULT_MAINTENANCE },
|
|
416
433
|
workflowRoutes: {},
|
|
417
434
|
searchRoute: null,
|
|
418
|
-
fastModels: {},
|
|
419
435
|
modelSettings: {},
|
|
420
436
|
onboarding: {},
|
|
421
437
|
agents: {},
|
|
422
438
|
workflow: { active: 'default' },
|
|
423
|
-
agentMaintenance: { enabled: true, interval: '1h' },
|
|
424
439
|
profile: normalizeProfileConfig(null),
|
|
425
440
|
skills: normalizeSkillsConfig(null),
|
|
426
441
|
autoClear: { enabled: true },
|
|
427
442
|
compaction: {},
|
|
428
|
-
runtime: {},
|
|
429
443
|
shell: {},
|
|
430
444
|
update: {},
|
|
431
445
|
recap: { enabled: true },
|
|
@@ -543,7 +557,7 @@ function buildAgentSaveBuilder(config, appliedDirty) {
|
|
|
543
557
|
}
|
|
544
558
|
if (Object.keys(mergedProject).length > 0) mcpProjectOverrides[projectKey] = mergedProject;
|
|
545
559
|
}
|
|
546
|
-
|
|
560
|
+
const next = {
|
|
547
561
|
...existingRaw,
|
|
548
562
|
guide: config.guide || existingRaw.guide || undefined,
|
|
549
563
|
providers: persistedProviders,
|
|
@@ -554,22 +568,26 @@ function buildAgentSaveBuilder(config, appliedDirty) {
|
|
|
554
568
|
maintenance: config.maintenance || {},
|
|
555
569
|
workflowRoutes,
|
|
556
570
|
searchRoute: normalizeSearchRoute(config.searchRoute),
|
|
557
|
-
fastModels: config.fastModels || {},
|
|
558
571
|
modelSettings: config.modelSettings || {},
|
|
559
572
|
onboarding: config.onboarding || {},
|
|
560
573
|
agents: config.agents || {},
|
|
561
574
|
workflow: config.workflow || { active: 'default' },
|
|
562
|
-
agentMaintenance: config.agentMaintenance || {},
|
|
563
575
|
profile: normalizeProfileConfig(config.profile),
|
|
564
576
|
skills: normalizeSkillsConfig(config.skills),
|
|
565
577
|
autoClear: config.autoClear || {},
|
|
566
578
|
compaction: config.compaction || {},
|
|
567
|
-
runtime: config.runtime || {},
|
|
568
579
|
shell: config.shell || {},
|
|
569
580
|
update: config.update || {},
|
|
570
581
|
recap: config.recap || {},
|
|
571
582
|
modules: config.modules || existingRaw.modules || {},
|
|
572
583
|
};
|
|
584
|
+
// These keys were previously round-tripped despite having no runtime
|
|
585
|
+
// consumer. Explicitly remove them from the in-lock baseline so a
|
|
586
|
+
// normal settings save also finishes migration of older installs.
|
|
587
|
+
delete next.fastModels;
|
|
588
|
+
delete next.agentMaintenance;
|
|
589
|
+
delete next.runtime;
|
|
590
|
+
return next;
|
|
573
591
|
};
|
|
574
592
|
}
|
|
575
593
|
export function saveConfig(config) {
|
|
@@ -714,7 +714,7 @@ export function loadScopedRoleInstructions(agent, provider = null) {
|
|
|
714
714
|
if (useUnified) {
|
|
715
715
|
// Explicit-cache providers — every agent sees the same all-in-one
|
|
716
716
|
// instruction surface. Cross-agent calls hit the same provider-side prefix
|
|
717
|
-
//
|
|
717
|
+
// prefix, eliminating the cross-agent cache miss seen on Pool C
|
|
718
718
|
// transitions for openai-oauth/openai. This branch is disabled by the
|
|
719
719
|
// empty provider set above; BP2 remains the active agent surface.
|
|
720
720
|
agentRuleSectionsToEmit = hiddenPairs.map(p => `## ${p.name}\n\n${p.body}`);
|
|
@@ -191,7 +191,7 @@ export function listHiddenAgentNames() {
|
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* List hidden agent names matching a given kind ('retrieval' | 'maintenance').
|
|
194
|
-
* Consumed by collect.mjs to drive BP2 agent
|
|
194
|
+
* Consumed by collect.mjs to drive BP2 agent classification dynamically
|
|
195
195
|
* instead of hard-coding agent-name sets.
|
|
196
196
|
*/
|
|
197
197
|
export function listHiddenAgentsByKind(kind) {
|
|
@@ -24,18 +24,6 @@ const DEFAULT_MCP_STARTUP_TIMEOUT_MS = 10000;
|
|
|
24
24
|
// --- State ---
|
|
25
25
|
const servers = new Map();
|
|
26
26
|
let mcpSdkPromise = null;
|
|
27
|
-
// Agent-shard proxy mode (P2, docs/agent-shard-design.md): a shard child has
|
|
28
|
-
// no MCP connections of its own — the lead process owns the singleton server
|
|
29
|
-
// registry. When a proxy is installed, tool DEFINITIONS come from the
|
|
30
|
-
// lead-supplied snapshot and every call is forwarded over IPC; the local
|
|
31
|
-
// `servers` map stays empty and untouched.
|
|
32
|
-
let _mcpToolProxy = null;
|
|
33
|
-
export function setMcpToolProxy(proxy) {
|
|
34
|
-
_mcpToolProxy = proxy && typeof proxy.call === 'function'
|
|
35
|
-
? { tools: Array.isArray(proxy.tools) ? proxy.tools : [], call: proxy.call }
|
|
36
|
-
: null;
|
|
37
|
-
_invalidateMcpToolFieldMemo();
|
|
38
|
-
}
|
|
39
27
|
// Memo for mcpToolHasField(name, field) — keyed by `${toolName}|${field}`.
|
|
40
28
|
// The lookup (regex parse + servers Map get + tools.find + schema property
|
|
41
29
|
// inspection) runs on every MCP tool invocation but its result only changes
|
|
@@ -151,7 +139,6 @@ export async function connectMcpServers(config) {
|
|
|
151
139
|
* Tool names are prefixed: `mcp__{serverName}__{toolName}`
|
|
152
140
|
*/
|
|
153
141
|
export function getMcpTools() {
|
|
154
|
-
if (_mcpToolProxy) return _mcpToolProxy.tools;
|
|
155
142
|
const tools = [];
|
|
156
143
|
for (const server of servers.values()) {
|
|
157
144
|
tools.push(...server.tools);
|
|
@@ -195,9 +182,6 @@ export async function executeMcpTool(name, args) {
|
|
|
195
182
|
const match = name.match(/^mcp__(.+?)__(.+)$/);
|
|
196
183
|
if (!match)
|
|
197
184
|
throw new Error(`Not an MCP tool name: ${name}`);
|
|
198
|
-
// Proxy mode: the lead executes (including its own reconnect/timeout/cap
|
|
199
|
-
// handling) and returns the final model-visible result/envelope.
|
|
200
|
-
if (_mcpToolProxy) return _mcpToolProxy.call(name, args);
|
|
201
185
|
const [, serverName, toolName] = match;
|
|
202
186
|
const server = servers.get(serverName);
|
|
203
187
|
if (!server)
|
|
@@ -371,7 +355,6 @@ export function isMcpTool(name) {
|
|
|
371
355
|
/** True when the prefixed name exists on a connected MCP server. */
|
|
372
356
|
export function isRegisteredMcpTool(name) {
|
|
373
357
|
if (!isMcpTool(name)) return false;
|
|
374
|
-
if (_mcpToolProxy) return _mcpToolProxy.tools.some((t) => t?.name === name);
|
|
375
358
|
const match = name.match(/^mcp__(.+?)__(.+)$/);
|
|
376
359
|
if (!match) return false;
|
|
377
360
|
const [, serverName] = match;
|
|
@@ -389,13 +372,6 @@ export function mcpToolHasField(name, field) {
|
|
|
389
372
|
const memoKey = `${name}|${field}`;
|
|
390
373
|
const memoized = _mcpToolFieldMemo.get(memoKey);
|
|
391
374
|
if (memoized !== undefined) return memoized;
|
|
392
|
-
if (_mcpToolProxy) {
|
|
393
|
-
const proxyTool = _mcpToolProxy.tools.find((t) => t?.name === name);
|
|
394
|
-
const proxyProps = proxyTool?.inputSchema?.properties;
|
|
395
|
-
const proxyResult = Boolean(proxyProps && Object.prototype.hasOwnProperty.call(proxyProps, field));
|
|
396
|
-
_mcpToolFieldMemo.set(memoKey, proxyResult);
|
|
397
|
-
return proxyResult;
|
|
398
|
-
}
|
|
399
375
|
const match = name.match(/^mcp__(.+?)__(.+)$/);
|
|
400
376
|
if (!match) { _mcpToolFieldMemo.set(memoKey, false); return false; }
|
|
401
377
|
const [, serverName] = match;
|
|
@@ -123,9 +123,9 @@ export class ProviderAdmissionScheduler {
|
|
|
123
123
|
this.reportedRateLimits = new WeakSet();
|
|
124
124
|
this.closedReason = null;
|
|
125
125
|
this.context = currentAdmission;
|
|
126
|
-
//
|
|
127
|
-
//
|
|
128
|
-
//
|
|
126
|
+
// Cooldown listeners observe locally-recorded 429 cooldowns and
|
|
127
|
+
// credential-driven resets so every runtime using one account sees
|
|
128
|
+
// the same quota window.
|
|
129
129
|
this.cooldownListeners = new Set();
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
parseSSEStream,
|
|
60
60
|
_classifyMidstreamError,
|
|
61
61
|
_midstreamSleepWithAbort,
|
|
62
|
+
stampAnthropicStreamOutcome,
|
|
62
63
|
} from './anthropic-sse.mjs';
|
|
63
64
|
import { buildAnthropicBetaHeaders, supportsAnthropicFastMode } from './anthropic-betas.mjs';
|
|
64
65
|
import {
|
|
@@ -718,13 +719,19 @@ export class AnthropicOAuthProvider {
|
|
|
718
719
|
const quotaText = await result.response.text().catch(() => '');
|
|
719
720
|
cleanupCancelHandler(result.cancelHandler);
|
|
720
721
|
try { result.controller?.abort?.(); } catch {}
|
|
721
|
-
|
|
722
|
+
// Initial-response failure: nothing was sampled, so the
|
|
723
|
+
// typed retry rules upstream own the decision.
|
|
724
|
+
throw Object.assign(
|
|
725
|
+
anthropicQuotaError(status, result?.response?.headers, this.scrubTokens(quotaText)),
|
|
726
|
+
{ initialResponseError: true },
|
|
727
|
+
);
|
|
722
728
|
}
|
|
723
729
|
const err = new Error(`Anthropic OAuth API ${status}`);
|
|
724
730
|
err.httpStatus = status;
|
|
725
731
|
err.status = status;
|
|
726
732
|
err.headers = result?.response?.headers;
|
|
727
733
|
err.response = { status, headers: result?.response?.headers };
|
|
734
|
+
err.initialResponseError = true;
|
|
728
735
|
const retryAfterMs = retryAfterMsFromError(err);
|
|
729
736
|
if (transientStatus || retryAfterMs != null) {
|
|
730
737
|
try { await result.response.text(); } catch {}
|
|
@@ -972,6 +979,15 @@ export class AnthropicOAuthProvider {
|
|
|
972
979
|
} catch { /* ignore non-extensible result */ }
|
|
973
980
|
return result;
|
|
974
981
|
} catch (err) {
|
|
982
|
+
// Canonical stream-outcome contract: stamp before ANY safety
|
|
983
|
+
// decision below. The parser's stamped verdict (when present)
|
|
984
|
+
// is authoritative — coarse midState.partialToolCall must not
|
|
985
|
+
// overwrite an idempotent pending-input truncation — while
|
|
986
|
+
// genuinely new wrapper-observed exposure is still merged.
|
|
987
|
+
let _outcome = null;
|
|
988
|
+
try {
|
|
989
|
+
_outcome = stampAnthropicStreamOutcome(err, midState, { provider: 'anthropic-oauth' });
|
|
990
|
+
} catch { /* stamping is best-effort */ }
|
|
975
991
|
// Acknowledged reset semantics let the owner tombstone this
|
|
976
992
|
// attempt before the full request is restarted non-streaming.
|
|
977
993
|
// Without that acknowledgement, recoverNonStreaming stamps
|
|
@@ -979,16 +995,12 @@ export class AnthropicOAuthProvider {
|
|
|
979
995
|
if (midState.emittedText) {
|
|
980
996
|
return await recoverNonStreaming(midState, err, controller);
|
|
981
997
|
}
|
|
982
|
-
//
|
|
983
|
-
//
|
|
984
|
-
//
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
err.partialToolCall = !!midState.partialToolCall;
|
|
989
|
-
err.emittedThinking = !!midState.emittedThinking;
|
|
990
|
-
err.unsafeToRetry = true;
|
|
991
|
-
} catch {}
|
|
998
|
+
// Dispatched tools and exposed thinking are replay boundaries;
|
|
999
|
+
// the canonical merge above already recorded them and wrote the
|
|
1000
|
+
// aliases. Coarse midState flags are NOT written here: they
|
|
1001
|
+
// would downgrade the parser's authoritative verdict that an
|
|
1002
|
+
// incomplete, never dispatched tool input stays replay-safe.
|
|
1003
|
+
if (_outcome?.replayUnsafe === true) {
|
|
992
1004
|
try { controller?.abort?.(err); } catch {}
|
|
993
1005
|
throw err;
|
|
994
1006
|
}
|
|
@@ -1006,9 +1018,7 @@ export class AnthropicOAuthProvider {
|
|
|
1006
1018
|
}
|
|
1007
1019
|
if (classifyError(err) === 'transient'
|
|
1008
1020
|
&& !midState.sawMessageStart
|
|
1009
|
-
&&
|
|
1010
|
-
&& !midState.partialToolCall
|
|
1011
|
-
&& !midState.emittedThinking
|
|
1021
|
+
&& _outcome?.replayUnsafe !== true
|
|
1012
1022
|
&& attemptIndex < MAX_MIDSTREAM_RETRIES) {
|
|
1013
1023
|
firstAttemptError = err;
|
|
1014
1024
|
firstAttemptClassifier = err?.providerErrorType || 'sse_transient';
|
|
@@ -1032,9 +1042,7 @@ export class AnthropicOAuthProvider {
|
|
|
1032
1042
|
// yet; otherwise let the error surface.
|
|
1033
1043
|
if ((err?.truncatedStream === true || err?.code === 'TRUNCATED_STREAM')
|
|
1034
1044
|
&& classifyError(err) === 'transient'
|
|
1035
|
-
&&
|
|
1036
|
-
&& !midState.partialToolCall
|
|
1037
|
-
&& !midState.emittedThinking
|
|
1045
|
+
&& _outcome?.replayUnsafe !== true
|
|
1038
1046
|
&& attemptIndex < MAX_MIDSTREAM_RETRIES) {
|
|
1039
1047
|
firstAttemptError = err;
|
|
1040
1048
|
firstAttemptClassifier = 'truncated_stream';
|