@dreb/coding-agent 2.18.0 → 2.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +3 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +3 -2
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +6 -5
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/buddy/buddy-controller.d.ts.map +1 -1
- package/dist/core/buddy/buddy-controller.js +3 -2
- package/dist/core/buddy/buddy-controller.js.map +1 -1
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +2 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/logger.d.ts +29 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +54 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +3 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +25 -2
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/stderr-guard.d.ts +37 -0
- package/dist/core/stderr-guard.d.ts.map +1 -0
- package/dist/core/stderr-guard.js +90 -0
- package/dist/core/stderr-guard.js.map +1 -0
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +5 -4
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/subagent.d.ts +38 -1
- package/dist/core/tools/subagent.d.ts.map +1 -1
- package/dist/core/tools/subagent.js +192 -17
- package/dist/core/tools/subagent.js.map +1 -1
- package/dist/core/tools/terminal-render.d.ts.map +1 -1
- package/dist/core/tools/terminal-render.js +2 -1
- package/dist/core/tools/terminal-render.js.map +1 -1
- package/dist/core/tools/web-search-queue.d.ts.map +1 -1
- package/dist/core/tools/web-search-queue.js +2 -1
- package/dist/core/tools/web-search-queue.js.map +1 -1
- package/dist/core/tools/web.d.ts.map +1 -1
- package/dist/core/tools/web.js +6 -5
- package/dist/core/tools/web.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +25 -24
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +32 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +3 -2
- package/dist/modes/print-mode.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,11 +3,13 @@ import { randomBytes } from "node:crypto";
|
|
|
3
3
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { join, resolve } from "node:path";
|
|
6
|
+
import { complete } from "@dreb/ai";
|
|
6
7
|
import { Text } from "@dreb/tui";
|
|
7
8
|
import { Type } from "@sinclair/typebox";
|
|
8
9
|
import { CONFIG_DIR_NAME, getPackageDir, getSubagentSessionsDir } from "../../config.js";
|
|
9
10
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
|
10
11
|
import { attachJsonlLineReader } from "../../modes/rpc/jsonl.js";
|
|
12
|
+
import { log } from "../logger.js";
|
|
11
13
|
import { resolveCliModel } from "../model-resolver.js";
|
|
12
14
|
import { getTextOutput, invalidArgText, str } from "./render-utils.js";
|
|
13
15
|
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
@@ -86,20 +88,20 @@ function loadAgentsFromDir(dir, agents) {
|
|
|
86
88
|
const content = readFileSync(join(dir, file), "utf-8");
|
|
87
89
|
const parsed = parseAgentFrontmatter(content);
|
|
88
90
|
if (!parsed.ok) {
|
|
89
|
-
|
|
91
|
+
log.warn(`[subagent] Skipping agent file ${join(dir, file)}: ${parsed.error}`);
|
|
90
92
|
}
|
|
91
93
|
else {
|
|
92
94
|
agents.set(parsed.config.name, parsed.config);
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
catch (err) {
|
|
96
|
-
|
|
98
|
+
log.warn(`[subagent] Could not read agent file ${join(dir, file)}: ${err instanceof Error ? err.message : String(err)}`);
|
|
97
99
|
}
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
catch (err) {
|
|
101
103
|
if (err.code !== "ENOENT") {
|
|
102
|
-
|
|
104
|
+
log.warn(`[subagent] Could not read agents directory ${dir}: ${err instanceof Error ? err.message : String(err)}`);
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -140,7 +142,7 @@ function findDrebBinary() {
|
|
|
140
142
|
}
|
|
141
143
|
async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentProvider, sessionDir) {
|
|
142
144
|
const drebBin = findDrebBinary();
|
|
143
|
-
|
|
145
|
+
log.debug(`[subagent] spawn: agent=${agentConfig.name} cwd=${cwd}`);
|
|
144
146
|
// Validate cwd exists — spawn() throws a misleading ENOENT blaming the
|
|
145
147
|
// binary when the cwd is invalid, making the real cause hard to diagnose
|
|
146
148
|
if (!existsSync(cwd)) {
|
|
@@ -226,12 +228,12 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
226
228
|
}
|
|
227
229
|
});
|
|
228
230
|
proc.stderr?.on("error", (err) => {
|
|
229
|
-
|
|
231
|
+
log.warn(`[subagent] stderr stream error (agent=${agentConfig.name}): ${err.message}`);
|
|
230
232
|
});
|
|
231
233
|
// Parse JSONL events from stdout
|
|
232
234
|
if (proc.stdout) {
|
|
233
235
|
proc.stdout.on("error", (err) => {
|
|
234
|
-
|
|
236
|
+
log.warn(`[subagent] stdout stream error (agent=${agentConfig.name}): ${err.message}`);
|
|
235
237
|
});
|
|
236
238
|
attachJsonlLineReader(proc.stdout, (line) => {
|
|
237
239
|
if (!line.trim())
|
|
@@ -247,7 +249,7 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
247
249
|
// (e.g. startup errors printed before JSONL mode begins)
|
|
248
250
|
plainStdoutLines.push(line.trim());
|
|
249
251
|
if (line.trim().startsWith("{")) {
|
|
250
|
-
|
|
252
|
+
log.warn(`[subagent] Failed to parse JSONL event: ${line.slice(0, 200)}`);
|
|
251
253
|
}
|
|
252
254
|
return;
|
|
253
255
|
}
|
|
@@ -303,7 +305,7 @@ async function spawnSubagent(agentConfig, task, cwd, signal, onProgress, parentP
|
|
|
303
305
|
signal?.removeEventListener("abort", onAbort);
|
|
304
306
|
const exitCode = code ?? 1;
|
|
305
307
|
const stderr = stderrChunks.join("");
|
|
306
|
-
|
|
308
|
+
log.debug(`[subagent] close: agent=${agentConfig.name} exit=${exitCode} messages=${collectedMessages.length}${exitCode !== 0 ? ` stderr=${stderr.slice(0, 200)} stdout=${plainStdoutLines.join("|").slice(0, 200)}` : ""}`);
|
|
307
309
|
// Extract final text output from collected assistant messages
|
|
308
310
|
const outputParts = [];
|
|
309
311
|
for (const msg of collectedMessages) {
|
|
@@ -374,12 +376,12 @@ export function discoverSessionFile(sessionDir, agentName) {
|
|
|
374
376
|
}
|
|
375
377
|
}
|
|
376
378
|
if (best) {
|
|
377
|
-
|
|
379
|
+
log.debug(`[subagent] session file: ${best.path} (agent=${agentName})`);
|
|
378
380
|
return best.path;
|
|
379
381
|
}
|
|
380
382
|
}
|
|
381
383
|
catch (err) {
|
|
382
|
-
|
|
384
|
+
log.warn(`[subagent] failed to discover session file (agent=${agentName}): ${err instanceof Error ? err.message : String(err)}`);
|
|
383
385
|
}
|
|
384
386
|
return undefined;
|
|
385
387
|
}
|
|
@@ -458,6 +460,173 @@ export function resolveModelStringSingle(modelStr, parentProvider, registry) {
|
|
|
458
460
|
}
|
|
459
461
|
return { ok: true, modelId: resolved.model.id, provider: resolved.model.provider };
|
|
460
462
|
}
|
|
463
|
+
function compactErrorReason(reason) {
|
|
464
|
+
const singleLine = reason.replace(/\s+/g, " ").trim();
|
|
465
|
+
return singleLine.length > 180 ? `${singleLine.slice(0, 177)}...` : singleLine || "unknown error";
|
|
466
|
+
}
|
|
467
|
+
function reasonFromRuntimeError(value) {
|
|
468
|
+
if (value instanceof Error)
|
|
469
|
+
return value.message;
|
|
470
|
+
if (typeof value === "string")
|
|
471
|
+
return value;
|
|
472
|
+
if (value && typeof value === "object") {
|
|
473
|
+
const maybeMessage = value;
|
|
474
|
+
if (typeof maybeMessage.errorMessage === "string")
|
|
475
|
+
return maybeMessage.errorMessage;
|
|
476
|
+
if (typeof maybeMessage.message === "string")
|
|
477
|
+
return maybeMessage.message;
|
|
478
|
+
}
|
|
479
|
+
return String(value);
|
|
480
|
+
}
|
|
481
|
+
export function isRuntimeUnavailableError(value) {
|
|
482
|
+
if (value instanceof Error || typeof value === "string")
|
|
483
|
+
return true;
|
|
484
|
+
if (value && typeof value === "object") {
|
|
485
|
+
const maybeMessage = value;
|
|
486
|
+
return maybeMessage.stopReason === "error" || maybeMessage.stopReason === "aborted";
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
function makeProbeSignal(parentSignal, timeoutMs) {
|
|
491
|
+
const controller = new AbortController();
|
|
492
|
+
const timeoutError = new Error(`Model availability probe timed out after ${timeoutMs}ms`);
|
|
493
|
+
let timeout;
|
|
494
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
495
|
+
timeout = setTimeout(() => {
|
|
496
|
+
controller.abort(timeoutError);
|
|
497
|
+
reject(timeoutError);
|
|
498
|
+
}, timeoutMs);
|
|
499
|
+
});
|
|
500
|
+
const parentAbortHandler = () => controller.abort(parentSignal?.reason);
|
|
501
|
+
parentSignal?.addEventListener("abort", parentAbortHandler, { once: true });
|
|
502
|
+
if (parentSignal?.aborted)
|
|
503
|
+
controller.abort(parentSignal.reason);
|
|
504
|
+
return {
|
|
505
|
+
signal: controller.signal,
|
|
506
|
+
timeoutPromise,
|
|
507
|
+
cleanup: () => {
|
|
508
|
+
clearTimeout(timeout);
|
|
509
|
+
parentSignal?.removeEventListener("abort", parentAbortHandler);
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
export async function probeModelAvailability(model, options = {}) {
|
|
514
|
+
const { signal, registry, timeoutMs = 10_000 } = options;
|
|
515
|
+
if (signal?.aborted)
|
|
516
|
+
return { ok: false, reason: "Aborted before spawn", aborted: true };
|
|
517
|
+
const probeSignal = makeProbeSignal(signal, timeoutMs);
|
|
518
|
+
try {
|
|
519
|
+
const context = {
|
|
520
|
+
systemPrompt: "You are a model availability probe. Reply briefly.",
|
|
521
|
+
messages: [{ role: "user", content: "hi", timestamp: Date.now() }],
|
|
522
|
+
};
|
|
523
|
+
const apiKey = await Promise.race([
|
|
524
|
+
registry ? registry.getApiKey(model) : Promise.resolve(undefined),
|
|
525
|
+
probeSignal.timeoutPromise,
|
|
526
|
+
]);
|
|
527
|
+
if (signal?.aborted)
|
|
528
|
+
return { ok: false, reason: "Aborted before spawn", aborted: true };
|
|
529
|
+
const result = await Promise.race([
|
|
530
|
+
complete(model, context, {
|
|
531
|
+
apiKey,
|
|
532
|
+
maxRetryDelayMs: 0,
|
|
533
|
+
maxTokens: 1,
|
|
534
|
+
signal: probeSignal.signal,
|
|
535
|
+
}),
|
|
536
|
+
probeSignal.timeoutPromise,
|
|
537
|
+
]);
|
|
538
|
+
if (signal?.aborted)
|
|
539
|
+
return { ok: false, reason: "Aborted before spawn", aborted: true };
|
|
540
|
+
if (isRuntimeUnavailableError(result)) {
|
|
541
|
+
return { ok: false, reason: compactErrorReason(reasonFromRuntimeError(result)) };
|
|
542
|
+
}
|
|
543
|
+
return { ok: true };
|
|
544
|
+
}
|
|
545
|
+
catch (err) {
|
|
546
|
+
if (signal?.aborted)
|
|
547
|
+
return { ok: false, reason: "Aborted before spawn", aborted: true };
|
|
548
|
+
return { ok: false, reason: compactErrorReason(reasonFromRuntimeError(err)) };
|
|
549
|
+
}
|
|
550
|
+
finally {
|
|
551
|
+
probeSignal.cleanup();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
export async function resolveModelForSubagentSpawn(models, parentProvider, registry, parentModel, signal) {
|
|
555
|
+
if (signal?.aborted)
|
|
556
|
+
return { ok: false, error: "Aborted before spawn", skippedModels: [] };
|
|
557
|
+
// Runtime probing only applies to agent definition fallback lists. Single
|
|
558
|
+
// models, per-invocation overrides, and registry-less environments keep the
|
|
559
|
+
// existing spawn-time resolution behavior exactly.
|
|
560
|
+
if (!Array.isArray(models) || !registry) {
|
|
561
|
+
const resolved = resolveModelWithFallbacks(models, parentProvider, registry, parentModel);
|
|
562
|
+
return { ...resolved, skippedModels: [] };
|
|
563
|
+
}
|
|
564
|
+
const skippedModels = [];
|
|
565
|
+
let lastError = "";
|
|
566
|
+
for (const modelStr of models) {
|
|
567
|
+
if (signal?.aborted)
|
|
568
|
+
return { ok: false, error: "Aborted before spawn", skippedModels };
|
|
569
|
+
const resolved = resolveModelStringSingle(modelStr, parentProvider, registry);
|
|
570
|
+
if (!resolved.ok) {
|
|
571
|
+
lastError = resolved.error;
|
|
572
|
+
const reason = compactErrorReason(resolved.error);
|
|
573
|
+
skippedModels.push({ model: modelStr, reason });
|
|
574
|
+
log.warn(`[subagent] Model "${modelStr}" unavailable (${reason}). Trying next fallback...`);
|
|
575
|
+
continue;
|
|
576
|
+
}
|
|
577
|
+
const modelObj = resolved.provider ? registry.find(resolved.provider, resolved.modelId) : undefined;
|
|
578
|
+
if (modelObj) {
|
|
579
|
+
const probe = await probeModelAvailability(modelObj, { signal, registry });
|
|
580
|
+
if (!probe.ok && probe.aborted) {
|
|
581
|
+
return { ok: false, error: "Aborted before spawn", skippedModels };
|
|
582
|
+
}
|
|
583
|
+
if (signal?.aborted)
|
|
584
|
+
return { ok: false, error: "Aborted before spawn", skippedModels };
|
|
585
|
+
if (!probe.ok) {
|
|
586
|
+
lastError = probe.reason;
|
|
587
|
+
skippedModels.push({ model: modelStr, reason: probe.reason });
|
|
588
|
+
log.warn(`[subagent] Model "${modelStr}" failed probe (${probe.reason}). Trying next fallback...`);
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
log.debug(`[subagent] Using model "${resolved.modelId}" for subagent.`);
|
|
593
|
+
return { ...resolved, skippedModels };
|
|
594
|
+
}
|
|
595
|
+
if (signal?.aborted)
|
|
596
|
+
return { ok: false, error: "Aborted before spawn", skippedModels };
|
|
597
|
+
if (parentModel) {
|
|
598
|
+
const parentResolved = resolveModelStringSingle(parentModel, parentProvider, registry);
|
|
599
|
+
if (parentResolved.ok) {
|
|
600
|
+
const warning = `Agent preferred models were unavailable. Falling back to parent model "${parentResolved.modelId}".`;
|
|
601
|
+
log.warn(`[subagent] ${warning}`);
|
|
602
|
+
return { ...parentResolved, warning, skippedModels };
|
|
603
|
+
}
|
|
604
|
+
lastError = parentResolved.error;
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
ok: false,
|
|
608
|
+
skippedModels,
|
|
609
|
+
error: `None of the fallback models passed availability checks: ${[
|
|
610
|
+
...models,
|
|
611
|
+
...(parentModel ? [parentModel] : []),
|
|
612
|
+
].join(", ")}. Last error: ${lastError || "all probes failed"}`,
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
export function formatModelFallbackSummary(skippedModels, selectedModel) {
|
|
616
|
+
if (skippedModels.length === 0)
|
|
617
|
+
return undefined;
|
|
618
|
+
const skipped = skippedModels.map((s) => `- ${s.model}: ${s.reason}`).join("\n");
|
|
619
|
+
return `[MODEL FALLBACK: skipped ${skippedModels.length} unavailable model(s); using "${selectedModel ?? "unknown"}".]\n${skipped}`;
|
|
620
|
+
}
|
|
621
|
+
export function prependModelFallbackSummary(output, skippedModels, selectedModel) {
|
|
622
|
+
const fallbackSummary = formatModelFallbackSummary(skippedModels, selectedModel);
|
|
623
|
+
return fallbackSummary ? `${fallbackSummary}\n\n${output}` : output;
|
|
624
|
+
}
|
|
625
|
+
function formatSkippedModelFailureDetails(skippedModels) {
|
|
626
|
+
if (skippedModels.length === 0)
|
|
627
|
+
return undefined;
|
|
628
|
+
return `Skipped models:\n${skippedModels.map((s) => `- ${s.model}: ${s.reason}`).join("\n")}`;
|
|
629
|
+
}
|
|
461
630
|
const MAX_PARALLEL_TASKS = 8;
|
|
462
631
|
const MAX_CONCURRENCY = 4;
|
|
463
632
|
const MAX_TASK_LENGTH = 32_768; // 32 KB — prevent E2BIG from oversized argv
|
|
@@ -499,7 +668,7 @@ function clampCwd(defaultCwd, itemCwd) {
|
|
|
499
668
|
}
|
|
500
669
|
return { ok: true, cwd: resolved };
|
|
501
670
|
}
|
|
502
|
-
async function executeSingle(agents, agentName, task, cwd, signal, onProgress, modelOverride, parentProvider, registry, sessionDir, parentModel) {
|
|
671
|
+
export async function executeSingle(agents, agentName, task, cwd, signal, onProgress, modelOverride, parentProvider, registry, sessionDir, parentModel) {
|
|
503
672
|
const name = agentName || DEFAULT_AGENT;
|
|
504
673
|
const config = agents.get(name);
|
|
505
674
|
if (!config) {
|
|
@@ -529,20 +698,25 @@ async function executeSingle(agents, agentName, task, cwd, signal, onProgress, m
|
|
|
529
698
|
let effectiveConfig = modelOverride ? { ...config, model: modelOverride } : config;
|
|
530
699
|
let resolvedProvider = parentProvider;
|
|
531
700
|
let warning;
|
|
701
|
+
let skippedModels = [];
|
|
532
702
|
// Resolve and validate the model against the registry before spawning.
|
|
533
703
|
// This catches typos and invalid model names immediately instead of failing
|
|
534
704
|
// silently in the child process. Also passes the canonical model ID to the
|
|
535
|
-
// child, avoiding fuzzy matching entirely.
|
|
705
|
+
// child, avoiding fuzzy matching entirely. Agent definition fallback lists get
|
|
706
|
+
// an additional best-effort 1-token probe before spawn so runtime-unavailable
|
|
707
|
+
// models are skipped before committing to a child process.
|
|
536
708
|
if (modelSpec) {
|
|
537
|
-
const resolved =
|
|
709
|
+
const resolved = await resolveModelForSubagentSpawn(modelSpec, parentProvider, registry, parentModel, signal);
|
|
710
|
+
skippedModels = resolved.skippedModels;
|
|
538
711
|
if (!resolved.ok) {
|
|
712
|
+
const skippedDetails = formatSkippedModelFailureDetails(skippedModels);
|
|
539
713
|
return {
|
|
540
714
|
agent: name,
|
|
541
715
|
task,
|
|
542
716
|
exitCode: 1,
|
|
543
717
|
output: "",
|
|
544
718
|
stderr: "",
|
|
545
|
-
errorMessage: resolved.error,
|
|
719
|
+
errorMessage: skippedDetails ? `${resolved.error}\n\n${skippedDetails}` : resolved.error,
|
|
546
720
|
};
|
|
547
721
|
}
|
|
548
722
|
effectiveConfig = { ...effectiveConfig, model: resolved.modelId };
|
|
@@ -553,6 +727,7 @@ async function executeSingle(agents, agentName, task, cwd, signal, onProgress, m
|
|
|
553
727
|
}
|
|
554
728
|
onProgress?.(`Running ${name} agent...`);
|
|
555
729
|
const result = await spawnSubagent(effectiveConfig, task, cwd, signal, onProgress, resolvedProvider, sessionDir);
|
|
730
|
+
result.output = prependModelFallbackSummary(result.output, skippedModels, result.model ?? effectiveConfig.model?.toString());
|
|
556
731
|
if (warning) {
|
|
557
732
|
result.output = `[WARNING: ${warning}]\n\n${result.output}`;
|
|
558
733
|
}
|
|
@@ -845,7 +1020,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
845
1020
|
onBackgroundComplete(agentId, result, bgSignal.aborted);
|
|
846
1021
|
}
|
|
847
1022
|
catch (err) {
|
|
848
|
-
|
|
1023
|
+
log.warn(`[subagent] onBackgroundComplete threw for agent ${agentId}: ${err instanceof Error ? err.message : String(err)}. Background result lost.`);
|
|
849
1024
|
}
|
|
850
1025
|
};
|
|
851
1026
|
const run = async () => {
|
|
@@ -877,7 +1052,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
877
1052
|
}
|
|
878
1053
|
};
|
|
879
1054
|
run().catch((err) => {
|
|
880
|
-
|
|
1055
|
+
log.warn(`[subagent] Unhandled background error (${agentId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
881
1056
|
const entry = backgroundAgentRegistry.get(agentId);
|
|
882
1057
|
if (entry && entry.status === "running")
|
|
883
1058
|
entry.status = "failed";
|
|
@@ -893,7 +1068,7 @@ export function createSubagentToolDefinition(cwd, options) {
|
|
|
893
1068
|
}, bgSignal.aborted);
|
|
894
1069
|
}
|
|
895
1070
|
catch (notifyErr) {
|
|
896
|
-
|
|
1071
|
+
log.error(`[subagent] CRITICAL: Last-resort notification failed for ${agentId}: ${notifyErr instanceof Error ? notifyErr.message : String(notifyErr)}`);
|
|
897
1072
|
}
|
|
898
1073
|
});
|
|
899
1074
|
return agentId;
|