@wrongstack/core 0.285.0 → 0.286.0
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/dist/autophase/phase-orchestrator.d.ts.map +1 -1
- package/dist/boot.d.ts.map +1 -1
- package/dist/coordination/agent-monitor.d.ts +29 -0
- package/dist/coordination/agent-monitor.d.ts.map +1 -1
- package/dist/coordination/autonomous-brain.d.ts +0 -2
- package/dist/coordination/autonomous-brain.d.ts.map +1 -1
- package/dist/coordination/autonomous-coordinator.d.ts +4 -0
- package/dist/coordination/autonomous-coordinator.d.ts.map +1 -1
- package/dist/coordination/brain-ledger.d.ts +142 -0
- package/dist/coordination/brain-ledger.d.ts.map +1 -0
- package/dist/coordination/brain-monitor.d.ts +31 -2
- package/dist/coordination/brain-monitor.d.ts.map +1 -1
- package/dist/coordination/brain.d.ts +43 -0
- package/dist/coordination/brain.d.ts.map +1 -1
- package/dist/coordination/delegate-tool.d.ts.map +1 -1
- package/dist/coordination/director-construction.d.ts.map +1 -1
- package/dist/coordination/director.d.ts +1 -1
- package/dist/coordination/director.d.ts.map +1 -1
- package/dist/coordination/fleet.d.ts +4 -4
- package/dist/coordination/fleet.d.ts.map +1 -1
- package/dist/coordination/global-mailbox.d.ts +63 -2
- package/dist/coordination/global-mailbox.d.ts.map +1 -1
- package/dist/coordination/index.d.ts +2 -1
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +1566 -462
- package/dist/coordination/index.js.map +4 -4
- package/dist/coordination/knowledge-graph.d.ts +13 -1
- package/dist/coordination/knowledge-graph.d.ts.map +1 -1
- package/dist/coordination/mailbox-actions.d.ts +12 -13
- package/dist/coordination/mailbox-actions.d.ts.map +1 -1
- package/dist/coordination/mailbox-constants.d.ts +64 -0
- package/dist/coordination/mailbox-constants.d.ts.map +1 -0
- package/dist/coordination/mailbox-events.d.ts +32 -0
- package/dist/coordination/mailbox-events.d.ts.map +1 -0
- package/dist/coordination/mailbox-message-codec.d.ts +8 -0
- package/dist/coordination/mailbox-message-codec.d.ts.map +1 -0
- package/dist/coordination/mailbox-types.d.ts +88 -4
- package/dist/coordination/mailbox-types.d.ts.map +1 -1
- package/dist/coordination/mailbox.d.ts +21 -2
- package/dist/coordination/mailbox.d.ts.map +1 -1
- package/dist/coordination/package-author-tracker.d.ts.map +1 -1
- package/dist/coordination/subagent-result-tool.d.ts.map +1 -1
- package/dist/defaults/index.js +846 -523
- package/dist/defaults/index.js.map +4 -4
- package/dist/execution/autonomous-runner.d.ts +7 -1
- package/dist/execution/autonomous-runner.d.ts.map +1 -1
- package/dist/execution/autonomy-brain.d.ts +72 -11
- package/dist/execution/autonomy-brain.d.ts.map +1 -1
- package/dist/execution/brain-chain.d.ts +53 -0
- package/dist/execution/brain-chain.d.ts.map +1 -0
- package/dist/execution/brain-evaluation.d.ts +66 -0
- package/dist/execution/brain-evaluation.d.ts.map +1 -0
- package/dist/execution/brain-runtime.d.ts +171 -0
- package/dist/execution/brain-runtime.d.ts.map +1 -0
- package/dist/execution/council-brain.d.ts +79 -0
- package/dist/execution/council-brain.d.ts.map +1 -0
- package/dist/execution/eternal-autonomy.d.ts +12 -0
- package/dist/execution/eternal-autonomy.d.ts.map +1 -1
- package/dist/execution/index.d.ts +5 -1
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +1350 -377
- package/dist/execution/index.js.map +4 -4
- package/dist/execution/parallel-eternal-engine.d.ts +12 -0
- package/dist/execution/parallel-eternal-engine.d.ts.map +1 -1
- package/dist/execution/tool-executor.d.ts.map +1 -1
- package/dist/hq/auth-store.d.ts +21 -3
- package/dist/hq/auth-store.d.ts.map +1 -1
- package/dist/hq/factory.d.ts +3 -0
- package/dist/hq/factory.d.ts.map +1 -1
- package/dist/hq/index.js +628 -72
- package/dist/hq/index.js.map +4 -4
- package/dist/hq/protocol.d.ts +54 -2
- package/dist/hq/protocol.d.ts.map +1 -1
- package/dist/hq/publisher.d.ts +4 -0
- package/dist/hq/publisher.d.ts.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3482 -1495
- package/dist/index.js.map +4 -4
- package/dist/kernel/events.d.ts +22 -4
- package/dist/kernel/events.d.ts.map +1 -1
- package/dist/kernel/index.js.map +2 -2
- package/dist/mailbox-attach.d.ts.map +1 -1
- package/dist/observability/index.js +16 -15
- package/dist/observability/index.js.map +2 -2
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +253 -21
- package/dist/security/index.js.map +4 -4
- package/dist/security/permission-policy-schema.d.ts +77 -0
- package/dist/security/permission-policy-schema.d.ts.map +1 -0
- package/dist/security/permission-policy.d.ts +5 -0
- package/dist/security/permission-policy.d.ts.map +1 -1
- package/dist/security/secret-vault.d.ts +9 -0
- package/dist/security/secret-vault.d.ts.map +1 -1
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/storage/config-loader.d.ts +10 -0
- package/dist/storage/config-loader.d.ts.map +1 -1
- package/dist/storage/goal-store.d.ts +1 -1
- package/dist/storage/goal-store.d.ts.map +1 -1
- package/dist/storage/index.js +147 -162
- package/dist/storage/index.js.map +2 -2
- package/dist/storage/plan-store.d.ts +1 -1
- package/dist/storage/plan-store.d.ts.map +1 -1
- package/dist/storage/queue-store.d.ts +4 -0
- package/dist/storage/queue-store.d.ts.map +1 -1
- package/dist/storage/session-store.d.ts +9 -0
- package/dist/storage/session-store.d.ts.map +1 -1
- package/dist/storage/task-store.d.ts +1 -1
- package/dist/storage/task-store.d.ts.map +1 -1
- package/dist/storage/todos-checkpoint.d.ts +2 -2
- package/dist/storage/todos-checkpoint.d.ts.map +1 -1
- package/dist/types/config.d.ts +209 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +2 -2
- package/dist/types/multi-agent.d.ts +8 -0
- package/dist/types/multi-agent.d.ts.map +1 -1
- package/dist/types/observability.d.ts +5 -0
- package/dist/types/observability.d.ts.map +1 -1
- package/dist/types/tool.d.ts +7 -0
- package/dist/types/tool.d.ts.map +1 -1
- package/dist/utils/glob-expand.d.ts.map +1 -1
- package/dist/utils/incoming-images.d.ts +39 -0
- package/dist/utils/incoming-images.d.ts.map +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +81 -3
- package/dist/utils/index.js.map +3 -3
- package/dist/worktree/worktree-manager.d.ts +13 -4
- package/dist/worktree/worktree-manager.d.ts.map +1 -1
- package/instructions/agents/browser.md +25 -16
- package/instructions/coordination/subagent-baseline.md +16 -2
- package/instructions/leader-after-task.md +3 -1
- package/instructions/sections/tool/common-patterns.md +7 -1
- package/instructions/system.md +47 -0
- package/package.json +2 -2
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/wrongstack-mailbox/SKILL.md +51 -12
- package/dist/plugins/git-plugin.d.ts +0 -15
- package/dist/plugins/git-plugin.d.ts.map +0 -1
- package/dist/plugins/observability-plugin.d.ts +0 -19
- package/dist/plugins/observability-plugin.d.ts.map +0 -1
- package/dist/plugins/plan-plugin.d.ts +0 -16
- package/dist/plugins/plan-plugin.d.ts.map +0 -1
package/dist/execution/index.js
CHANGED
|
@@ -2069,19 +2069,27 @@ function adaptThresholdsForSignals(thresholds, signals) {
|
|
|
2069
2069
|
|
|
2070
2070
|
// src/execution/autonomous-runner.ts
|
|
2071
2071
|
var DoneConditionChecker = class {
|
|
2072
|
-
constructor(condition) {
|
|
2072
|
+
constructor(condition, opts) {
|
|
2073
2073
|
this.condition = condition;
|
|
2074
|
+
this.logger = opts?.logger;
|
|
2074
2075
|
if (condition.type === "output_match" && condition.pattern) {
|
|
2075
2076
|
const result = compileUserRegex(condition.pattern, "");
|
|
2076
2077
|
this.compiledRegex = result.ok ? result.regex : null;
|
|
2077
2078
|
if (!result.ok) {
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2079
|
+
if (this.logger) {
|
|
2080
|
+
this.logger.warn(
|
|
2081
|
+
"Done condition has invalid regex \u2014 will never match",
|
|
2082
|
+
{ event: "autonomous.done_condition_invalid_regex", pattern: condition.pattern, reason: result.reason }
|
|
2083
|
+
);
|
|
2084
|
+
} else {
|
|
2085
|
+
console.warn(JSON.stringify({
|
|
2086
|
+
level: "warn",
|
|
2087
|
+
event: "autonomous.done_condition_invalid_regex",
|
|
2088
|
+
pattern: condition.pattern,
|
|
2089
|
+
reason: result.reason,
|
|
2090
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2091
|
+
}));
|
|
2092
|
+
}
|
|
2085
2093
|
}
|
|
2086
2094
|
} else {
|
|
2087
2095
|
this.compiledRegex = null;
|
|
@@ -2089,6 +2097,7 @@ var DoneConditionChecker = class {
|
|
|
2089
2097
|
}
|
|
2090
2098
|
condition;
|
|
2091
2099
|
compiledRegex;
|
|
2100
|
+
logger;
|
|
2092
2101
|
check(state) {
|
|
2093
2102
|
switch (this.condition.type) {
|
|
2094
2103
|
case "iterations":
|
|
@@ -2139,7 +2148,7 @@ var DoneConditionChecker = class {
|
|
|
2139
2148
|
var AutonomousRunner = class {
|
|
2140
2149
|
constructor(opts) {
|
|
2141
2150
|
this.opts = opts;
|
|
2142
|
-
this.doneChecker = new DoneConditionChecker(opts.doneCondition);
|
|
2151
|
+
this.doneChecker = new DoneConditionChecker(opts.doneCondition, { logger: opts.logger });
|
|
2143
2152
|
}
|
|
2144
2153
|
opts;
|
|
2145
2154
|
iterations = 0;
|
|
@@ -2300,12 +2309,25 @@ function createTieredBrainArbiter(opts) {
|
|
|
2300
2309
|
return {
|
|
2301
2310
|
async decide(request) {
|
|
2302
2311
|
const policyDecision = await opts.policy.decide(request);
|
|
2303
|
-
|
|
2312
|
+
const provisionalContinue = policyDecision.type === "answer" && policyDecision.optionId === void 0 && request.fallback === "continue";
|
|
2313
|
+
if (policyDecision.type !== "ask_human" && !provisionalContinue) return policyDecision;
|
|
2304
2314
|
const ceiling = opts.getMaxAutoRisk?.() ?? "medium";
|
|
2305
|
-
if (
|
|
2315
|
+
if (ceiling === "off") return policyDecision;
|
|
2306
2316
|
const ceilingLevel = ceiling === "all" ? 3 : RISK_LEVELS[ceiling] ?? 1;
|
|
2307
2317
|
const requestLevel = RISK_LEVELS[request.risk] ?? 2;
|
|
2308
2318
|
if (requestLevel > ceilingLevel) return policyDecision;
|
|
2319
|
+
if (opts.council) {
|
|
2320
|
+
const floor = opts.getCouncilMinRisk?.() ?? "high";
|
|
2321
|
+
const floorLevel = RISK_LEVELS[floor] ?? 2;
|
|
2322
|
+
if (requestLevel >= floorLevel) {
|
|
2323
|
+
try {
|
|
2324
|
+
const councilDecision = await opts.council.decide(request);
|
|
2325
|
+
if (councilDecision.type !== "ask_human") return councilDecision;
|
|
2326
|
+
} catch {
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
if (!opts.autonomous) return policyDecision;
|
|
2309
2331
|
try {
|
|
2310
2332
|
const llmDecision = await opts.autonomous.decide(request);
|
|
2311
2333
|
if (llmDecision.type === "answer") return llmDecision;
|
|
@@ -2319,6 +2341,12 @@ function createAutonomyBrain(opts) {
|
|
|
2319
2341
|
const maxRisk = opts.maxAutoRisk ?? "high";
|
|
2320
2342
|
const maxRiskLevel = RISK_LEVELS[maxRisk] ?? 2;
|
|
2321
2343
|
const timeoutMs = opts.decisionTimeoutMs ?? 15e3;
|
|
2344
|
+
const targets = opts.targets && opts.targets.length > 0 ? opts.targets : opts.provider && opts.model ? [{ provider: opts.provider, model: opts.model }] : [];
|
|
2345
|
+
if (targets.length === 0) {
|
|
2346
|
+
throw new Error("createAutonomyBrain: provide `targets` or `provider` + `model`.");
|
|
2347
|
+
}
|
|
2348
|
+
const strategy = opts.strategy ?? "fallback";
|
|
2349
|
+
let rrCursor = 0;
|
|
2322
2350
|
return {
|
|
2323
2351
|
async decide(request) {
|
|
2324
2352
|
const requestLevel = RISK_LEVELS[request.risk] ?? 2;
|
|
@@ -2337,7 +2365,10 @@ function createAutonomyBrain(opts) {
|
|
|
2337
2365
|
opts.onDecision?.(formatDecisionSummary(heuristic, request), heuristic, request);
|
|
2338
2366
|
return heuristic;
|
|
2339
2367
|
}
|
|
2340
|
-
const
|
|
2368
|
+
const start = strategy === "round-robin" ? rrCursor++ % targets.length : 0;
|
|
2369
|
+
const ordered = [...targets.slice(start), ...targets.slice(0, start)];
|
|
2370
|
+
const digest = opts.getDecisionDigest?.(request);
|
|
2371
|
+
const llmDecision = await llmDecide(request, ordered, timeoutMs, digest);
|
|
2341
2372
|
opts.onDecision?.(formatDecisionSummary(llmDecision, request), llmDecision, request);
|
|
2342
2373
|
return llmDecision;
|
|
2343
2374
|
}
|
|
@@ -2355,6 +2386,7 @@ function formatDecisionSummary(decision, request) {
|
|
|
2355
2386
|
return `\u{1F9E0} Brain: ASKED HUMAN \u2014 "${question}"`;
|
|
2356
2387
|
}
|
|
2357
2388
|
function quickDecide(request) {
|
|
2389
|
+
if (request.options?.length) return null;
|
|
2358
2390
|
const q = request.question.toLowerCase();
|
|
2359
2391
|
const ctx = request.context?.toLowerCase() ?? "";
|
|
2360
2392
|
if (q.includes("deadlock") && ctx.includes("failed")) {
|
|
@@ -2364,7 +2396,7 @@ function quickDecide(request) {
|
|
|
2364
2396
|
rationale: "Heuristic: deadlocked tasks blocked by failed dependencies \u2014 skipping unblocks remaining work."
|
|
2365
2397
|
};
|
|
2366
2398
|
}
|
|
2367
|
-
if ((q.includes("failed") || q.includes("retry")) && (ctx
|
|
2399
|
+
if ((q.includes("failed") || q.includes("retry")) && (/\bexhausted\b/.test(ctx) || /\b(?:[3-9]|\d{2,})\s+(?:consecutive\s+)?(?:times|attempts|retries|failures)\b/.test(ctx) || /\b(?:attempt|retr(?:y|ies)|failure)s?\W{0,3}(?:[3-9]|\d{2,})\b/.test(ctx))) {
|
|
2368
2400
|
return {
|
|
2369
2401
|
type: "answer",
|
|
2370
2402
|
text: "Mark as failed and move on. Note the failure for the final report.",
|
|
@@ -2374,7 +2406,7 @@ function quickDecide(request) {
|
|
|
2374
2406
|
if (q.includes("goal complete") || q.includes("mission complete")) {
|
|
2375
2407
|
return null;
|
|
2376
2408
|
}
|
|
2377
|
-
if (
|
|
2409
|
+
if (request.fallback === "continue" && /\b(?:continue|proceed)\b/.test(q) && !/\b(?:stop|abort|halt|cancel|pause|rollback)\b/.test(q) && !/\bor\b/.test(q)) {
|
|
2378
2410
|
return {
|
|
2379
2411
|
type: "answer",
|
|
2380
2412
|
text: "Continue execution. Do not stop.",
|
|
@@ -2383,94 +2415,1160 @@ function quickDecide(request) {
|
|
|
2383
2415
|
}
|
|
2384
2416
|
return null;
|
|
2385
2417
|
}
|
|
2386
|
-
|
|
2418
|
+
function buildBrainUserMessage(request) {
|
|
2387
2419
|
const optionsText = request.options?.length ? "\nOptions:\n" + request.options.map(
|
|
2388
2420
|
(o) => ` [${o.id}] ${o.label}${o.consequence ? ` \u2014 ${o.consequence}` : ""}${o.recommended ? " \u2605 recommended" : ""}`
|
|
2389
2421
|
).join("\n") : "";
|
|
2390
|
-
|
|
2391
|
-
const userMessage = [
|
|
2422
|
+
return [
|
|
2392
2423
|
`Question: ${request.question}`,
|
|
2393
2424
|
request.context ? `
|
|
2394
2425
|
Context:
|
|
2395
2426
|
${request.context}` : "",
|
|
2396
2427
|
optionsText
|
|
2397
2428
|
].filter(Boolean).join("\n");
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2429
|
+
}
|
|
2430
|
+
function withDecisionDigest(user, digest) {
|
|
2431
|
+
if (!digest?.trim()) return user;
|
|
2432
|
+
return `${user}
|
|
2433
|
+
|
|
2434
|
+
Outcome history of similar past decisions (learn from it):
|
|
2435
|
+
${digest}`;
|
|
2436
|
+
}
|
|
2437
|
+
async function completeBrainLlm(target, input) {
|
|
2438
|
+
const signal = AbortSignal.timeout(input.timeoutMs);
|
|
2439
|
+
const response = await target.provider.complete(
|
|
2440
|
+
{
|
|
2441
|
+
model: target.model,
|
|
2442
|
+
system: [{ type: "text", text: input.system }],
|
|
2443
|
+
messages: [{ role: "user", content: input.user || "Decide." }],
|
|
2444
|
+
maxTokens: input.maxTokens ?? 200
|
|
2445
|
+
},
|
|
2446
|
+
{ signal }
|
|
2447
|
+
);
|
|
2448
|
+
return extractText(response).trim();
|
|
2449
|
+
}
|
|
2450
|
+
async function llmDecide(request, targets, timeoutMs, digest) {
|
|
2451
|
+
const systemPrompt = readBundledInstructionText("llm/autonomy-brain.md");
|
|
2452
|
+
const userMessage = withDecisionDigest(buildBrainUserMessage(request), digest);
|
|
2453
|
+
let text = null;
|
|
2454
|
+
for (const target of targets) {
|
|
2455
|
+
try {
|
|
2456
|
+
text = await completeBrainLlm(target, {
|
|
2457
|
+
system: systemPrompt,
|
|
2458
|
+
user: userMessage,
|
|
2459
|
+
timeoutMs
|
|
2460
|
+
});
|
|
2461
|
+
break;
|
|
2462
|
+
} catch {
|
|
2463
|
+
}
|
|
2464
|
+
}
|
|
2465
|
+
if (text === null) {
|
|
2466
|
+
if (request.fallback === "continue") {
|
|
2467
|
+
return {
|
|
2468
|
+
type: "answer",
|
|
2469
|
+
text: "Continue (Autonomy Brain LLM unavailable \u2014 using fallback)."
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
return { type: "deny", reason: "Autonomy Brain LLM unavailable for decision." };
|
|
2473
|
+
}
|
|
2474
|
+
if (request.options?.length) {
|
|
2475
|
+
const parsed = parseOptionDecision(text, request.options);
|
|
2476
|
+
if (parsed) {
|
|
2477
|
+
return parsed;
|
|
2478
|
+
}
|
|
2479
|
+
return {
|
|
2480
|
+
type: "deny",
|
|
2481
|
+
reason: "Autonomy Brain returned no exact valid option id."
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
return {
|
|
2485
|
+
type: "answer",
|
|
2486
|
+
text: text || (request.fallback === "continue" ? "Continue execution." : "Denied by autonomy policy."),
|
|
2487
|
+
rationale: text || void 0
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
function parseOptionDecision(rawText, options) {
|
|
2491
|
+
const text = rawText.trim();
|
|
2492
|
+
const byId = new Map(options.map((option2) => [option2.id, option2]));
|
|
2493
|
+
const wholeFence = /^```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```$/i.exec(text);
|
|
2494
|
+
const jsonCandidate = wholeFence ? (wholeFence[1] ?? "").trim() : text;
|
|
2495
|
+
const parsed = safeParse(jsonCandidate, 16384);
|
|
2496
|
+
if (parsed.ok && parsed.value && typeof parsed.value.optionId === "string") {
|
|
2497
|
+
const option2 = byId.get(parsed.value.optionId);
|
|
2498
|
+
if (!option2) return null;
|
|
2499
|
+
return {
|
|
2500
|
+
type: "answer",
|
|
2501
|
+
optionId: option2.id,
|
|
2502
|
+
text: option2.label,
|
|
2503
|
+
rationale: typeof parsed.value.rationale === "string" && parsed.value.rationale.trim() ? parsed.value.rationale.trim() : void 0
|
|
2504
|
+
};
|
|
2505
|
+
}
|
|
2506
|
+
const legacy = /^\s*\[([^\]\r\n]+)\](?:\s*(?:—|-|:)\s*)?([\s\S]*)$/.exec(text);
|
|
2507
|
+
if (!legacy) return null;
|
|
2508
|
+
const option = byId.get((legacy[1] ?? "").trim());
|
|
2509
|
+
if (!option) return null;
|
|
2510
|
+
const rationale = (legacy[2] ?? "").trim();
|
|
2511
|
+
return {
|
|
2512
|
+
type: "answer",
|
|
2513
|
+
optionId: option.id,
|
|
2514
|
+
text: option.label,
|
|
2515
|
+
rationale: rationale || void 0
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2518
|
+
function extractText(result) {
|
|
2519
|
+
if (!result || typeof result !== "object") return "";
|
|
2520
|
+
const r = result;
|
|
2521
|
+
if (Array.isArray(r.content)) {
|
|
2522
|
+
return r.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
2523
|
+
}
|
|
2524
|
+
if (Array.isArray(r.choices)) {
|
|
2525
|
+
return r.choices[0]?.message?.content ?? "";
|
|
2526
|
+
}
|
|
2527
|
+
return typeof r.text === "string" ? r.text : "";
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
// src/types/provider.ts
|
|
2531
|
+
var CONTEXT_OVERFLOW_RE = /context.length|context.window|max.*tokens?.*exceeded|prompt is too long|too long|exceeds the context|\btokens\b.*exceed|context_length_exceeded/i;
|
|
2532
|
+
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
2533
|
+
function classifyProviderError(status, body, message) {
|
|
2534
|
+
const type = body?.type;
|
|
2535
|
+
if (status === 0) return "network";
|
|
2536
|
+
if (status === 408) return "timeout";
|
|
2537
|
+
if (status === 599) return "stream_hang";
|
|
2538
|
+
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
2539
|
+
if (type === "overloaded_error" || status === 529) return "overloaded";
|
|
2540
|
+
if (status >= 500) return "server";
|
|
2541
|
+
if (type === "authentication_error" || type === "permission_error" || status === 401 || status === 403) {
|
|
2542
|
+
return "auth";
|
|
2543
|
+
}
|
|
2544
|
+
const text = [message, body?.message, type, body?.raw].filter(Boolean).join("\n");
|
|
2545
|
+
if (type === "content_filter" || CONTENT_FILTER_RE.test(text)) return "content_filter";
|
|
2546
|
+
if (status === 413 || status >= 400 && CONTEXT_OVERFLOW_RE.test(text)) {
|
|
2547
|
+
return "context_overflow";
|
|
2548
|
+
}
|
|
2549
|
+
if (status >= 400) return "invalid_request";
|
|
2550
|
+
return "unknown";
|
|
2551
|
+
}
|
|
2552
|
+
function isRetryableKind(kind) {
|
|
2553
|
+
return RETRYABLE_BY_KIND[kind];
|
|
2554
|
+
}
|
|
2555
|
+
var RETRYABLE_BY_KIND = {
|
|
2556
|
+
rate_limit: true,
|
|
2557
|
+
overloaded: true,
|
|
2558
|
+
server: true,
|
|
2559
|
+
timeout: true,
|
|
2560
|
+
network: true,
|
|
2561
|
+
stream_hang: true,
|
|
2562
|
+
auth: false,
|
|
2563
|
+
context_overflow: false,
|
|
2564
|
+
content_filter: false,
|
|
2565
|
+
invalid_request: false,
|
|
2566
|
+
unknown: false
|
|
2567
|
+
};
|
|
2568
|
+
var ProviderError = class extends WrongStackError {
|
|
2569
|
+
status;
|
|
2570
|
+
retryable;
|
|
2571
|
+
providerId;
|
|
2572
|
+
/** Canonical failure classification — see {@link ProviderErrorKind}. */
|
|
2573
|
+
kind;
|
|
2574
|
+
body;
|
|
2575
|
+
constructor(message, status, retryable, providerId, opts = {}) {
|
|
2576
|
+
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
2577
|
+
super({
|
|
2578
|
+
message,
|
|
2579
|
+
code: kindToCode(kind),
|
|
2580
|
+
subsystem: "provider",
|
|
2581
|
+
severity: status >= 500 ? "error" : "warning",
|
|
2582
|
+
recoverable: retryable,
|
|
2583
|
+
context: { providerId, status },
|
|
2584
|
+
cause: opts.cause
|
|
2585
|
+
});
|
|
2586
|
+
this.name = "ProviderError";
|
|
2587
|
+
this.status = status;
|
|
2588
|
+
this.retryable = retryable;
|
|
2589
|
+
this.providerId = providerId;
|
|
2590
|
+
this.kind = kind;
|
|
2591
|
+
this.body = opts.body;
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Render a one-line, user-facing description. Designed for the CLI/TUI
|
|
2595
|
+
* status line and the agent's retry warning. Avoids dumping raw JSON
|
|
2596
|
+
* (which is what users see today when a 529 lands and the log message
|
|
2597
|
+
* includes the full `{"type":"error",...}` body).
|
|
2598
|
+
*
|
|
2599
|
+
* Examples:
|
|
2600
|
+
* "minimax-coding-plan overloaded (529): High traffic detected. Upgrade for highspeed model. [req 06534785201de9c0…]"
|
|
2601
|
+
* "openai rate limited (429): Retry after 12s"
|
|
2602
|
+
* "anthropic invalid request (400): messages.0.role must be one of 'user'|'assistant'"
|
|
2603
|
+
* "groq HTTP 500 (server error)"
|
|
2604
|
+
*/
|
|
2605
|
+
describe() {
|
|
2606
|
+
const kind = describeStatus(this.status, this.body?.type);
|
|
2607
|
+
const head = `${this.providerId} ${kind}`;
|
|
2608
|
+
const detail = this.body?.message?.trim();
|
|
2609
|
+
const reqId = this.body?.requestId ? ` [req ${this.body.requestId.slice(0, 16)}${this.body.requestId.length > 16 ? "\u2026" : ""}]` : "";
|
|
2610
|
+
if (detail && detail.length > 0) {
|
|
2611
|
+
return `${head}: ${truncate(detail, 240)}${reqId}`;
|
|
2612
|
+
}
|
|
2613
|
+
return `${head}${reqId}`;
|
|
2614
|
+
}
|
|
2615
|
+
};
|
|
2616
|
+
function describeStatus(status, type) {
|
|
2617
|
+
if (status === 0) return "network error";
|
|
2618
|
+
if (status === 599) return `stream hang (${status})`;
|
|
2619
|
+
if (type === "overloaded_error" || status === 529) return `overloaded (${status})`;
|
|
2620
|
+
if (type === "rate_limit_error" || status === 429) return `rate limited (${status})`;
|
|
2621
|
+
if (type === "authentication_error" || status === 401) return `auth failed (${status})`;
|
|
2622
|
+
if (type === "permission_error" || status === 403) return `forbidden (${status})`;
|
|
2623
|
+
if (type === "not_found_error" || status === 404) return `not found (${status})`;
|
|
2624
|
+
if (type === "content_filter") return `content filtered (${status})`;
|
|
2625
|
+
if (type === "invalid_request_error" || status === 400) return `invalid request (${status})`;
|
|
2626
|
+
if (status === 408) return `timeout (${status})`;
|
|
2627
|
+
if (status >= 500 && status < 600) return `HTTP ${status} (server error)`;
|
|
2628
|
+
if (type) return `${type} (${status})`;
|
|
2629
|
+
return `HTTP ${status}`;
|
|
2630
|
+
}
|
|
2631
|
+
var KIND_TO_CODE = {
|
|
2632
|
+
network: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
2633
|
+
timeout: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
2634
|
+
rate_limit: ERROR_CODES.PROVIDER_RATE_LIMITED,
|
|
2635
|
+
auth: ERROR_CODES.PROVIDER_AUTH_FAILED,
|
|
2636
|
+
overloaded: ERROR_CODES.PROVIDER_OVERLOADED,
|
|
2637
|
+
context_overflow: ERROR_CODES.PROVIDER_CONTEXT_OVERFLOW,
|
|
2638
|
+
server: ERROR_CODES.PROVIDER_SERVER_ERROR,
|
|
2639
|
+
stream_hang: ERROR_CODES.PROVIDER_SERVER_ERROR,
|
|
2640
|
+
content_filter: ERROR_CODES.PROVIDER_INVALID_REQUEST,
|
|
2641
|
+
invalid_request: ERROR_CODES.PROVIDER_INVALID_REQUEST,
|
|
2642
|
+
unknown: ERROR_CODES.PROVIDER_INVALID_REQUEST
|
|
2643
|
+
};
|
|
2644
|
+
function kindToCode(kind) {
|
|
2645
|
+
return KIND_TO_CODE[kind];
|
|
2646
|
+
}
|
|
2647
|
+
|
|
2648
|
+
// src/core/fallback-model.ts
|
|
2649
|
+
function parseModelRef(ref) {
|
|
2650
|
+
const trimmed = ref.trim();
|
|
2651
|
+
const slash = trimmed.indexOf("/");
|
|
2652
|
+
if (slash !== -1) {
|
|
2653
|
+
return {
|
|
2654
|
+
provider: trimmed.slice(0, slash) || void 0,
|
|
2655
|
+
model: trimmed.slice(slash + 1).trim()
|
|
2656
|
+
};
|
|
2657
|
+
}
|
|
2658
|
+
const parts = trimmed.split(/\s+/);
|
|
2659
|
+
if (parts.length >= 2) {
|
|
2660
|
+
return { provider: parts[0], model: parts.slice(1).join(" ") };
|
|
2661
|
+
}
|
|
2662
|
+
return { model: trimmed };
|
|
2663
|
+
}
|
|
2664
|
+
var DEFAULT_PRIMARY_COOLDOWN_MAX_MS = 10 * 6e4;
|
|
2665
|
+
|
|
2666
|
+
// src/execution/council-brain.ts
|
|
2667
|
+
var COUNCIL_REFUSE_OPTION_ID = "council_refuse";
|
|
2668
|
+
var BUILTIN_PERSONAS = {
|
|
2669
|
+
executor: "PERSONA: You hold the EXECUTOR seat. You are biased toward forward progress \u2014 stalled work is the failure mode you exist to prevent. Favor the option that keeps the system moving, unless doing so is clearly reckless.",
|
|
2670
|
+
skeptic: "PERSONA: You hold the SKEPTIC seat. Your job is to find the concrete reason the proposed action is wrong, unsafe, or based on a false premise. If you find one, vote against it or refuse. Do not oppose for its own sake \u2014 a sound proposal deserves your vote.",
|
|
2671
|
+
auditor: "PERSONA: You hold the AUDITOR seat. You judge through the cost/waste lens: budget already burned, expected cost of each option, and the odds of throwing resources at a dead end. Prefer the option with the best expected value per token spent."
|
|
2672
|
+
};
|
|
2673
|
+
function personaText(persona) {
|
|
2674
|
+
if (!persona) return BUILTIN_PERSONAS["executor"] ?? "";
|
|
2675
|
+
return BUILTIN_PERSONAS[persona] ?? `PERSONA: ${persona}`;
|
|
2676
|
+
}
|
|
2677
|
+
function voterLabel(voter, index) {
|
|
2678
|
+
return voter.label ?? `${voter.persona ?? "voter"}#${index + 1} (${voter.model})`;
|
|
2679
|
+
}
|
|
2680
|
+
function ballotOptions(request) {
|
|
2681
|
+
const options = request.options ?? [];
|
|
2682
|
+
if (options.some((o) => o.id === COUNCIL_REFUSE_OPTION_ID)) return options;
|
|
2683
|
+
return [
|
|
2684
|
+
...options,
|
|
2685
|
+
{
|
|
2686
|
+
id: COUNCIL_REFUSE_OPTION_ID,
|
|
2687
|
+
label: "Refuse \u2014 none of these options should be taken",
|
|
2688
|
+
consequence: "The proposed action is not taken at all."
|
|
2689
|
+
}
|
|
2690
|
+
];
|
|
2691
|
+
}
|
|
2692
|
+
function createCouncilBrainArbiter(opts) {
|
|
2693
|
+
if (opts.voters.length === 0) {
|
|
2694
|
+
throw new Error("createCouncilBrainArbiter: at least one voter is required.");
|
|
2695
|
+
}
|
|
2696
|
+
const quorumFraction = opts.quorumFraction ?? 0.5;
|
|
2697
|
+
const approvalFraction = opts.approvalFraction ?? 0.5;
|
|
2698
|
+
const timeoutMs = opts.decisionTimeoutMs ?? 15e3;
|
|
2699
|
+
const baseSystem = readBundledInstructionText("llm/autonomy-brain.md");
|
|
2700
|
+
const abstain = (request, why) => ({
|
|
2701
|
+
type: "ask_human",
|
|
2702
|
+
prompt: `Council abstained (${why}): ${request.question}`,
|
|
2703
|
+
options: request.options,
|
|
2704
|
+
rationale: `Council could not reach a decision: ${why}.`
|
|
2705
|
+
});
|
|
2706
|
+
const finish = (request, decision) => {
|
|
2707
|
+
opts.onDecision?.(formatDecisionSummary(decision, request), decision, request);
|
|
2708
|
+
return decision;
|
|
2709
|
+
};
|
|
2710
|
+
async function collectVotes(request, ballot, digest) {
|
|
2711
|
+
const ballotRequest = { ...request, options: ballot };
|
|
2712
|
+
const user = withDecisionDigest(buildBrainUserMessage(ballotRequest), digest);
|
|
2713
|
+
const settled = await Promise.allSettled(
|
|
2714
|
+
opts.voters.map(
|
|
2715
|
+
(voter) => completeBrainLlm(voter, {
|
|
2716
|
+
system: `${baseSystem}
|
|
2717
|
+
|
|
2718
|
+
${personaText(voter.persona)}`,
|
|
2719
|
+
user,
|
|
2720
|
+
timeoutMs
|
|
2721
|
+
})
|
|
2722
|
+
)
|
|
2723
|
+
);
|
|
2724
|
+
const votes = [];
|
|
2725
|
+
settled.forEach((result, i) => {
|
|
2726
|
+
const voter = opts.voters[i];
|
|
2727
|
+
if (!voter || result.status !== "fulfilled") return;
|
|
2728
|
+
const parsed = parseOptionDecision(result.value, ballot);
|
|
2729
|
+
if (parsed?.type !== "answer" || !parsed.optionId) return;
|
|
2730
|
+
votes.push({ voter, optionId: parsed.optionId, rationale: parsed.rationale });
|
|
2731
|
+
});
|
|
2732
|
+
return votes;
|
|
2733
|
+
}
|
|
2734
|
+
async function judgeRound(request, ballot, votes, reason) {
|
|
2735
|
+
if (!opts.judge) return null;
|
|
2736
|
+
const voteLines = votes.map(
|
|
2737
|
+
(v, i) => `- ${voterLabel(v.voter, i)} voted [${v.optionId}]` + (v.rationale ? ` \u2014 ${v.rationale}` : "")
|
|
2738
|
+
).join("\n");
|
|
2739
|
+
const judgeRequest = {
|
|
2740
|
+
...request,
|
|
2741
|
+
options: ballot,
|
|
2742
|
+
context: [
|
|
2743
|
+
request.context ?? "",
|
|
2744
|
+
"",
|
|
2745
|
+
`Council votes (${reason} \u2014 you are the judge; issue the final decision):`,
|
|
2746
|
+
voteLines
|
|
2747
|
+
].join("\n").trim()
|
|
2748
|
+
};
|
|
2749
|
+
try {
|
|
2750
|
+
const text = await completeBrainLlm(opts.judge, {
|
|
2751
|
+
system: `${baseSystem}
|
|
2752
|
+
|
|
2753
|
+
PERSONA: You are the council JUDGE. The voting seats disagreed or were too close to call. Weigh their rationales and issue the single final decision.`,
|
|
2754
|
+
user: buildBrainUserMessage(judgeRequest),
|
|
2755
|
+
timeoutMs,
|
|
2756
|
+
maxTokens: 300
|
|
2757
|
+
});
|
|
2758
|
+
const parsed = parseOptionDecision(text, ballot);
|
|
2759
|
+
if (parsed?.type === "answer" && parsed.optionId) return parsed;
|
|
2760
|
+
return null;
|
|
2761
|
+
} catch {
|
|
2762
|
+
return null;
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
return {
|
|
2766
|
+
async decide(request) {
|
|
2767
|
+
const digest = opts.getDecisionDigest?.(request);
|
|
2768
|
+
if (!request.options?.length) {
|
|
2769
|
+
const user = withDecisionDigest(buildBrainUserMessage(request), digest);
|
|
2770
|
+
const settled = await Promise.allSettled(
|
|
2771
|
+
opts.voters.map(
|
|
2772
|
+
(voter) => completeBrainLlm(voter, {
|
|
2773
|
+
system: `${baseSystem}
|
|
2774
|
+
|
|
2775
|
+
${personaText(voter.persona)}`,
|
|
2776
|
+
user,
|
|
2777
|
+
timeoutMs
|
|
2778
|
+
})
|
|
2779
|
+
)
|
|
2780
|
+
);
|
|
2781
|
+
const stances = [];
|
|
2782
|
+
settled.forEach((result, i) => {
|
|
2783
|
+
const voter = opts.voters[i];
|
|
2784
|
+
if (!voter || result.status !== "fulfilled" || !result.value.trim()) return;
|
|
2785
|
+
stances.push({ voter, text: result.value.trim() });
|
|
2786
|
+
});
|
|
2787
|
+
if (stances.length / opts.voters.length < quorumFraction) {
|
|
2788
|
+
return finish(request, abstain(request, "quorum not met"));
|
|
2789
|
+
}
|
|
2790
|
+
if (opts.judge) {
|
|
2791
|
+
const stanceLines = stances.map((s, i) => `- ${voterLabel(s.voter, i)}: ${s.text}`).join("\n");
|
|
2792
|
+
try {
|
|
2793
|
+
const text = await completeBrainLlm(opts.judge, {
|
|
2794
|
+
system: `${baseSystem}
|
|
2795
|
+
|
|
2796
|
+
PERSONA: You are the council JUDGE. Synthesize the seats\u2019 independent stances below into the single final decision (1-2 sentences).`,
|
|
2797
|
+
user: `${buildBrainUserMessage(request)}
|
|
2798
|
+
|
|
2799
|
+
Council stances:
|
|
2800
|
+
${stanceLines}`,
|
|
2801
|
+
timeoutMs,
|
|
2802
|
+
maxTokens: 300
|
|
2803
|
+
});
|
|
2804
|
+
if (text) {
|
|
2805
|
+
return finish(request, {
|
|
2806
|
+
type: "answer",
|
|
2807
|
+
text,
|
|
2808
|
+
rationale: `Council synthesis of ${stances.length}/${opts.voters.length} stances.`
|
|
2809
|
+
});
|
|
2810
|
+
}
|
|
2811
|
+
} catch {
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
const first = stances[0];
|
|
2815
|
+
if (first) {
|
|
2816
|
+
return finish(request, {
|
|
2817
|
+
type: "answer",
|
|
2818
|
+
text: first.text,
|
|
2819
|
+
rationale: `Council (no judge): stance of ${voterLabel(first.voter, 0)}.`
|
|
2820
|
+
});
|
|
2821
|
+
}
|
|
2822
|
+
return finish(request, abstain(request, "no usable stance"));
|
|
2823
|
+
}
|
|
2824
|
+
const ballot = ballotOptions(request);
|
|
2825
|
+
const votes = await collectVotes(request, ballot, digest);
|
|
2826
|
+
if (votes.length / opts.voters.length < quorumFraction) {
|
|
2827
|
+
return finish(request, abstain(request, `quorum not met (${votes.length}/${opts.voters.length} votes)`));
|
|
2828
|
+
}
|
|
2829
|
+
const vetoRefusal = votes.find(
|
|
2830
|
+
(v) => v.voter.veto === true && v.optionId === COUNCIL_REFUSE_OPTION_ID
|
|
2831
|
+
);
|
|
2832
|
+
if (vetoRefusal) {
|
|
2833
|
+
return finish(request, {
|
|
2834
|
+
type: "deny",
|
|
2835
|
+
reason: `Council veto by ${voterLabel(vetoRefusal.voter, 0)}` + (vetoRefusal.rationale ? `: ${vetoRefusal.rationale}` : ".")
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
const weightByOption = /* @__PURE__ */ new Map();
|
|
2839
|
+
let castWeight = 0;
|
|
2840
|
+
for (const v of votes) {
|
|
2841
|
+
const w = v.voter.weight ?? 1;
|
|
2842
|
+
castWeight += w;
|
|
2843
|
+
weightByOption.set(v.optionId, (weightByOption.get(v.optionId) ?? 0) + w);
|
|
2844
|
+
}
|
|
2845
|
+
let winner = null;
|
|
2846
|
+
let contested = false;
|
|
2847
|
+
for (const [optionId, weight] of weightByOption) {
|
|
2848
|
+
if (!winner || weight > winner.weight) {
|
|
2849
|
+
winner = { optionId, weight };
|
|
2850
|
+
contested = false;
|
|
2851
|
+
} else if (weight === winner.weight) {
|
|
2852
|
+
contested = true;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
const decisive = winner !== null && !contested && winner.weight > approvalFraction * castWeight;
|
|
2856
|
+
let finalOptionId = decisive && winner ? winner.optionId : null;
|
|
2857
|
+
let viaJudge = false;
|
|
2858
|
+
if (!finalOptionId) {
|
|
2859
|
+
const judged = await judgeRound(
|
|
2860
|
+
request,
|
|
2861
|
+
ballot,
|
|
2862
|
+
votes,
|
|
2863
|
+
contested ? "tie" : "no option cleared the approval threshold"
|
|
2864
|
+
);
|
|
2865
|
+
if (judged?.type === "answer" && judged.optionId) {
|
|
2866
|
+
finalOptionId = judged.optionId;
|
|
2867
|
+
viaJudge = true;
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
if (!finalOptionId) {
|
|
2871
|
+
return finish(request, abstain(request, "split vote and no judge verdict"));
|
|
2872
|
+
}
|
|
2873
|
+
const supporting = votes.filter((v) => v.optionId === finalOptionId);
|
|
2874
|
+
const voteSummary = votes.map((v, i) => `${voterLabel(v.voter, i)}\u2192[${v.optionId}]`).join(", ");
|
|
2875
|
+
const rationale = (supporting.find((v) => v.rationale)?.rationale ?? "") + ` (council${viaJudge ? " via judge" : ""}: ${voteSummary})`;
|
|
2876
|
+
if (finalOptionId === COUNCIL_REFUSE_OPTION_ID) {
|
|
2877
|
+
return finish(request, {
|
|
2878
|
+
type: "deny",
|
|
2879
|
+
reason: `Council refused the proposed action.${rationale}`
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
const option = request.options.find((o) => o.id === finalOptionId);
|
|
2883
|
+
return finish(request, {
|
|
2884
|
+
type: "answer",
|
|
2885
|
+
optionId: finalOptionId,
|
|
2886
|
+
text: option?.label ?? finalOptionId,
|
|
2887
|
+
rationale: rationale.trim()
|
|
2888
|
+
});
|
|
2889
|
+
}
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
|
|
2893
|
+
// src/execution/brain-chain.ts
|
|
2894
|
+
var DEFAULT_SEATS = [
|
|
2895
|
+
{ persona: "executor" },
|
|
2896
|
+
{ persona: "skeptic", veto: true },
|
|
2897
|
+
{ persona: "auditor" }
|
|
2898
|
+
];
|
|
2899
|
+
function assembleBrainTiers(opts) {
|
|
2900
|
+
const brainCfg = opts.brainConfig;
|
|
2901
|
+
const toEntry = (entry) => typeof entry === "string" ? parseModelRef(entry) : entry;
|
|
2902
|
+
const buildTarget = (entry) => {
|
|
2903
|
+
if (!entry.model) return null;
|
|
2904
|
+
const providerId = entry.provider ?? opts.defaultProviderId;
|
|
2905
|
+
try {
|
|
2906
|
+
const provider = providerId === opts.defaultProviderId ? opts.sessionProvider() : opts.resolveProvider(providerId, entry.model);
|
|
2907
|
+
if (!provider) return null;
|
|
2908
|
+
return { provider, model: entry.model, label: `${providerId}/${entry.model}` };
|
|
2909
|
+
} catch {
|
|
2910
|
+
return null;
|
|
2911
|
+
}
|
|
2912
|
+
};
|
|
2913
|
+
const poolTargets = (brainCfg?.models ?? []).map(toEntry).map(buildTarget).filter((t) => t !== null);
|
|
2914
|
+
const autonomous = poolTargets.length > 0 ? createAutonomyBrain({
|
|
2915
|
+
targets: poolTargets,
|
|
2916
|
+
strategy: brainCfg?.strategy ?? "fallback",
|
|
2917
|
+
maxAutoRisk: "all",
|
|
2918
|
+
// the tiered ceiling gates risk — keep inner permissive
|
|
2919
|
+
decisionTimeoutMs: brainCfg?.decisionTimeoutMs,
|
|
2920
|
+
getDecisionDigest: opts.getDecisionDigest
|
|
2921
|
+
}) : {
|
|
2922
|
+
decide: (request) => createAutonomyBrain({
|
|
2923
|
+
provider: opts.sessionProvider(),
|
|
2924
|
+
model: opts.sessionModel(),
|
|
2925
|
+
maxAutoRisk: "all",
|
|
2926
|
+
decisionTimeoutMs: brainCfg?.decisionTimeoutMs,
|
|
2927
|
+
getDecisionDigest: opts.getDecisionDigest
|
|
2928
|
+
}).decide(request)
|
|
2929
|
+
};
|
|
2930
|
+
const councilCfg = brainCfg?.council;
|
|
2931
|
+
const explicitVoters = (councilCfg?.voters ?? []).map(
|
|
2932
|
+
(v) => typeof v === "string" ? { entry: toEntry(v), cfg: {} } : { entry: v, cfg: v }
|
|
2933
|
+
);
|
|
2934
|
+
const voters = (explicitVoters.length > 0 ? explicitVoters.map(({ entry, cfg }, i) => {
|
|
2935
|
+
const target = buildTarget(entry);
|
|
2936
|
+
if (!target) return null;
|
|
2937
|
+
const seat = DEFAULT_SEATS[i % DEFAULT_SEATS.length];
|
|
2938
|
+
return {
|
|
2939
|
+
...target,
|
|
2940
|
+
persona: cfg.persona ?? seat?.persona,
|
|
2941
|
+
weight: cfg.weight,
|
|
2942
|
+
veto: cfg.veto ?? seat?.veto
|
|
2943
|
+
};
|
|
2944
|
+
}) : poolTargets.slice(0, DEFAULT_SEATS.length).map((target, i) => ({
|
|
2945
|
+
...target,
|
|
2946
|
+
...DEFAULT_SEATS[i % DEFAULT_SEATS.length]
|
|
2947
|
+
}))).filter((v) => v !== null);
|
|
2948
|
+
const judgeEntry = councilCfg?.judge ? typeof councilCfg.judge === "string" ? toEntry(councilCfg.judge) : councilCfg.judge : void 0;
|
|
2949
|
+
const judge = (judgeEntry ? buildTarget(judgeEntry) : null) ?? poolTargets[0] ?? voters[0];
|
|
2950
|
+
const councilEnabled = councilCfg?.enabled ?? voters.length >= 2;
|
|
2951
|
+
const council = councilEnabled && voters.length >= 2 ? createCouncilBrainArbiter({
|
|
2952
|
+
voters,
|
|
2953
|
+
judge,
|
|
2954
|
+
quorumFraction: councilCfg?.quorum,
|
|
2955
|
+
approvalFraction: councilCfg?.approval,
|
|
2956
|
+
decisionTimeoutMs: brainCfg?.decisionTimeoutMs,
|
|
2957
|
+
getDecisionDigest: opts.getDecisionDigest
|
|
2958
|
+
}) : void 0;
|
|
2959
|
+
return {
|
|
2960
|
+
autonomous,
|
|
2961
|
+
council,
|
|
2962
|
+
getCouncilMinRisk: () => councilCfg?.minRisk ?? "high",
|
|
2963
|
+
poolLabels: poolTargets.map((t) => t.label ?? t.model),
|
|
2964
|
+
councilLabels: council ? voters.map((v) => `${v.label ?? v.model} (${v.persona ?? "voter"}${v.veto ? ", veto" : ""})`) : []
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
|
|
2968
|
+
// src/execution/brain-evaluation.ts
|
|
2969
|
+
var BRAIN_EVALUATION_CASE_VERSION = 1;
|
|
2970
|
+
function isRecord3(value) {
|
|
2971
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2972
|
+
}
|
|
2973
|
+
function isStringArray(value) {
|
|
2974
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
2975
|
+
}
|
|
2976
|
+
function validateBrainEvaluationCase(value) {
|
|
2977
|
+
const diagnostics = [];
|
|
2978
|
+
if (!isRecord3(value)) return { ok: false, diagnostics: ["case must be an object"] };
|
|
2979
|
+
if (value["version"] !== BRAIN_EVALUATION_CASE_VERSION) {
|
|
2980
|
+
diagnostics.push(`version must be ${BRAIN_EVALUATION_CASE_VERSION}`);
|
|
2981
|
+
}
|
|
2982
|
+
if (typeof value["id"] !== "string" || value["id"].trim().length === 0) {
|
|
2983
|
+
diagnostics.push("id must be a non-empty string");
|
|
2984
|
+
}
|
|
2985
|
+
const request = value["request"];
|
|
2986
|
+
if (!isRecord3(request)) {
|
|
2987
|
+
diagnostics.push("request must be an object");
|
|
2988
|
+
} else {
|
|
2989
|
+
if (typeof request["id"] !== "string" || request["id"].trim().length === 0) {
|
|
2990
|
+
diagnostics.push("request.id must be a non-empty string");
|
|
2991
|
+
}
|
|
2992
|
+
if (!["autophase", "director", "tool", "user", "system"].includes(String(request["source"]))) {
|
|
2993
|
+
diagnostics.push("request.source is invalid");
|
|
2994
|
+
}
|
|
2995
|
+
if (typeof request["question"] !== "string" || request["question"].trim().length === 0) {
|
|
2996
|
+
diagnostics.push("request.question must be a non-empty string");
|
|
2997
|
+
}
|
|
2998
|
+
if (!["low", "medium", "high", "critical"].includes(String(request["risk"]))) {
|
|
2999
|
+
diagnostics.push("request.risk is invalid");
|
|
3000
|
+
}
|
|
3001
|
+
if (!["ask_human", "deny", "continue"].includes(String(request["fallback"]))) {
|
|
3002
|
+
diagnostics.push("request.fallback is invalid");
|
|
3003
|
+
}
|
|
3004
|
+
if (request["options"] !== void 0) {
|
|
3005
|
+
if (!Array.isArray(request["options"])) {
|
|
3006
|
+
diagnostics.push("request.options must be an array");
|
|
3007
|
+
} else {
|
|
3008
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3009
|
+
for (const [index, option] of request["options"].entries()) {
|
|
3010
|
+
if (!isRecord3(option)) {
|
|
3011
|
+
diagnostics.push(`request.options[${index}] must be an object`);
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
const id = option["id"];
|
|
3015
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
3016
|
+
diagnostics.push(`request.options[${index}].id must be a non-empty string`);
|
|
3017
|
+
} else if (ids.has(id)) {
|
|
3018
|
+
diagnostics.push(`request option id "${id}" is duplicated`);
|
|
3019
|
+
} else {
|
|
3020
|
+
ids.add(id);
|
|
3021
|
+
}
|
|
3022
|
+
if (typeof option["label"] !== "string" || option["label"].trim().length === 0) {
|
|
3023
|
+
diagnostics.push(`request.options[${index}].label must be a non-empty string`);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
}
|
|
3029
|
+
const expectations = value["expectations"];
|
|
3030
|
+
if (expectations !== void 0 && !isRecord3(expectations)) {
|
|
3031
|
+
diagnostics.push("expectations must be an object");
|
|
3032
|
+
} else if (isRecord3(expectations)) {
|
|
3033
|
+
const allowedTypes = expectations["allowedDecisionTypes"];
|
|
3034
|
+
if (allowedTypes !== void 0 && (!isStringArray(allowedTypes) || allowedTypes.length === 0 || allowedTypes.some((entry) => !["answer", "ask_human", "deny"].includes(entry)))) {
|
|
3035
|
+
diagnostics.push("expectations.allowedDecisionTypes is invalid");
|
|
3036
|
+
}
|
|
3037
|
+
for (const field of ["allowedOptionIds", "unsafeOptionIds"]) {
|
|
3038
|
+
const entries = expectations[field];
|
|
3039
|
+
if (entries !== void 0 && !isStringArray(entries)) {
|
|
3040
|
+
diagnostics.push(`expectations.${field} must be a string array`);
|
|
3041
|
+
}
|
|
3042
|
+
}
|
|
3043
|
+
if (isRecord3(request) && Array.isArray(request["options"])) {
|
|
3044
|
+
const requestIds = new Set(
|
|
3045
|
+
request["options"].filter(isRecord3).map((option) => option["id"]).filter((id) => typeof id === "string")
|
|
3046
|
+
);
|
|
3047
|
+
for (const field of ["allowedOptionIds", "unsafeOptionIds"]) {
|
|
3048
|
+
const entries = expectations[field];
|
|
3049
|
+
if (!isStringArray(entries)) continue;
|
|
3050
|
+
for (const id of entries) {
|
|
3051
|
+
if (!requestIds.has(id))
|
|
3052
|
+
diagnostics.push(`expectations.${field} contains unknown id "${id}"`);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
for (const field of ["requireOptionId", "allowEscalation"]) {
|
|
3057
|
+
const setting = expectations[field];
|
|
3058
|
+
if (setting !== void 0 && typeof setting !== "boolean") {
|
|
3059
|
+
diagnostics.push(`expectations.${field} must be a boolean`);
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
return diagnostics.length > 0 ? { ok: false, diagnostics } : { ok: true, value };
|
|
3064
|
+
}
|
|
3065
|
+
function assessDecision(fixture, decision) {
|
|
3066
|
+
const failures = [];
|
|
3067
|
+
const expectations = fixture.expectations;
|
|
3068
|
+
if (expectations?.allowedDecisionTypes && !expectations.allowedDecisionTypes.includes(decision.type)) {
|
|
3069
|
+
failures.push({
|
|
3070
|
+
code: "unexpected_decision_type",
|
|
3071
|
+
message: `decision type "${decision.type}" is not allowed`
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
if (decision.type === "ask_human" && expectations?.allowEscalation !== true) {
|
|
3075
|
+
failures.push({
|
|
3076
|
+
code: "unexpected_escalation",
|
|
3077
|
+
message: "case escalated to a human without allowEscalation"
|
|
3078
|
+
});
|
|
3079
|
+
}
|
|
3080
|
+
if (decision.type !== "answer") return failures;
|
|
3081
|
+
const requestOptionIds = new Set(fixture.request.options?.map((option) => option.id) ?? []);
|
|
3082
|
+
if (decision.optionId === void 0) {
|
|
3083
|
+
if (expectations?.requireOptionId || expectations?.allowedOptionIds) {
|
|
3084
|
+
failures.push({
|
|
3085
|
+
code: "missing_option_id",
|
|
3086
|
+
message: "answer did not return the required exact option id"
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
return failures;
|
|
3090
|
+
}
|
|
3091
|
+
if (!requestOptionIds.has(decision.optionId)) {
|
|
3092
|
+
failures.push({
|
|
3093
|
+
code: "invalid_option_id",
|
|
3094
|
+
message: `answer returned unknown option id "${decision.optionId}"`
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
if (expectations?.allowedOptionIds && !expectations.allowedOptionIds.includes(decision.optionId)) {
|
|
3098
|
+
failures.push({
|
|
3099
|
+
code: "disallowed_option_id",
|
|
3100
|
+
message: `option id "${decision.optionId}" is not allowed for this case`
|
|
3101
|
+
});
|
|
3102
|
+
}
|
|
3103
|
+
if (expectations?.unsafeOptionIds?.includes(decision.optionId)) {
|
|
3104
|
+
failures.push({
|
|
3105
|
+
code: "unsafe_option_id",
|
|
3106
|
+
message: `unsafe option id "${decision.optionId}" was selected`
|
|
3107
|
+
});
|
|
3108
|
+
}
|
|
3109
|
+
return failures;
|
|
3110
|
+
}
|
|
3111
|
+
async function runBrainEvaluation(arbiter, cases) {
|
|
3112
|
+
const results = [];
|
|
3113
|
+
const seenCaseIds = /* @__PURE__ */ new Set();
|
|
3114
|
+
for (const rawCase of cases) {
|
|
3115
|
+
const validation = validateBrainEvaluationCase(rawCase);
|
|
3116
|
+
if (!validation.ok) {
|
|
3117
|
+
const caseId = isRecord3(rawCase) && typeof rawCase["id"] === "string" ? rawCase["id"] : "<invalid>";
|
|
3118
|
+
results.push({
|
|
3119
|
+
caseId,
|
|
3120
|
+
passed: false,
|
|
3121
|
+
failures: validation.diagnostics.map((message) => ({ code: "invalid_fixture", message })),
|
|
3122
|
+
latencyMs: 0
|
|
3123
|
+
});
|
|
3124
|
+
continue;
|
|
3125
|
+
}
|
|
3126
|
+
const fixture = validation.value;
|
|
3127
|
+
if (seenCaseIds.has(fixture.id)) {
|
|
3128
|
+
results.push({
|
|
3129
|
+
caseId: fixture.id,
|
|
3130
|
+
passed: false,
|
|
3131
|
+
failures: [{ code: "invalid_fixture", message: `case id "${fixture.id}" is duplicated` }],
|
|
3132
|
+
latencyMs: 0
|
|
3133
|
+
});
|
|
3134
|
+
continue;
|
|
3135
|
+
}
|
|
3136
|
+
seenCaseIds.add(fixture.id);
|
|
3137
|
+
const startedAt = performance.now();
|
|
3138
|
+
try {
|
|
3139
|
+
const decision = await arbiter.decide(structuredClone(fixture.request));
|
|
3140
|
+
const failures = assessDecision(fixture, decision);
|
|
3141
|
+
results.push({
|
|
3142
|
+
caseId: fixture.id,
|
|
3143
|
+
passed: failures.length === 0,
|
|
3144
|
+
decision,
|
|
3145
|
+
failures,
|
|
3146
|
+
latencyMs: Math.max(0, performance.now() - startedAt)
|
|
3147
|
+
});
|
|
3148
|
+
} catch (error) {
|
|
3149
|
+
results.push({
|
|
3150
|
+
caseId: fixture.id,
|
|
3151
|
+
passed: false,
|
|
3152
|
+
failures: [
|
|
3153
|
+
{
|
|
3154
|
+
code: "arbiter_error",
|
|
3155
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3156
|
+
}
|
|
3157
|
+
],
|
|
3158
|
+
latencyMs: Math.max(0, performance.now() - startedAt)
|
|
3159
|
+
});
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
const optionResults = results.filter(
|
|
3163
|
+
(result) => result.decision?.type === "answer" && result.decision.optionId !== void 0
|
|
3164
|
+
);
|
|
3165
|
+
const invalidOptionCount = results.reduce(
|
|
3166
|
+
(count, result) => count + result.failures.filter((failure) => failure.code === "invalid_option_id").length,
|
|
3167
|
+
0
|
|
3168
|
+
);
|
|
3169
|
+
const totalLatency = results.reduce((sum, result) => sum + result.latencyMs, 0);
|
|
3170
|
+
const metrics = {
|
|
3171
|
+
totalCases: results.length,
|
|
3172
|
+
passedCases: results.filter((result) => result.passed).length,
|
|
3173
|
+
failedCases: results.filter((result) => !result.passed).length,
|
|
3174
|
+
optionDecisionCount: optionResults.length,
|
|
3175
|
+
invalidOptionCount,
|
|
3176
|
+
validOptionRate: optionResults.length === 0 ? 1 : (optionResults.length - invalidOptionCount) / optionResults.length,
|
|
3177
|
+
unsafeAllowanceCount: results.reduce(
|
|
3178
|
+
(count, result) => count + result.failures.filter((failure) => failure.code === "unsafe_option_id").length,
|
|
3179
|
+
0
|
|
3180
|
+
),
|
|
3181
|
+
unnecessaryEscalationCount: results.reduce(
|
|
3182
|
+
(count, result) => count + result.failures.filter((failure) => failure.code === "unexpected_escalation").length,
|
|
3183
|
+
0
|
|
3184
|
+
),
|
|
3185
|
+
averageLatencyMs: results.length === 0 ? 0 : totalLatency / results.length
|
|
3186
|
+
};
|
|
3187
|
+
return { version: BRAIN_EVALUATION_CASE_VERSION, results, metrics };
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
// src/coordination/brain.ts
|
|
3191
|
+
var DefaultBrainArbiter = class {
|
|
3192
|
+
allowLowRiskAutoAnswer;
|
|
3193
|
+
constructor(opts = {}) {
|
|
3194
|
+
this.allowLowRiskAutoAnswer = opts.allowLowRiskAutoAnswer ?? true;
|
|
3195
|
+
}
|
|
3196
|
+
async decide(request) {
|
|
3197
|
+
const recommended = request.options?.find((option) => option.recommended);
|
|
3198
|
+
if (this.allowLowRiskAutoAnswer && request.risk === "low" && recommended) {
|
|
3199
|
+
return {
|
|
3200
|
+
type: "answer",
|
|
3201
|
+
optionId: recommended.id,
|
|
3202
|
+
text: recommended.label,
|
|
3203
|
+
rationale: "Low-risk request with an explicit recommended option."
|
|
3204
|
+
};
|
|
3205
|
+
}
|
|
3206
|
+
switch (request.fallback) {
|
|
3207
|
+
case "deny":
|
|
3208
|
+
return {
|
|
3209
|
+
type: "deny",
|
|
3210
|
+
reason: `Brain could not safely decide: ${request.question}`
|
|
3211
|
+
};
|
|
3212
|
+
case "continue":
|
|
3213
|
+
return {
|
|
3214
|
+
type: "answer",
|
|
3215
|
+
text: "Continue with the caller default.",
|
|
3216
|
+
rationale: "No safe Brain decision was available; request fallback is continue."
|
|
3217
|
+
};
|
|
3218
|
+
case "ask_human":
|
|
3219
|
+
return {
|
|
3220
|
+
type: "ask_human",
|
|
3221
|
+
prompt: formatHumanPrompt(request),
|
|
3222
|
+
options: request.options,
|
|
3223
|
+
rationale: "Decision requires human authority or lacks a safe automatic option."
|
|
3224
|
+
};
|
|
3225
|
+
default:
|
|
3226
|
+
return {
|
|
3227
|
+
type: "deny",
|
|
3228
|
+
reason: `Brain could not safely decide (unknown fallback: ${String(request.fallback)}): ${request.question}`
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
};
|
|
3233
|
+
function formatHumanPrompt(request) {
|
|
3234
|
+
const lines = [
|
|
3235
|
+
`Brain requires human decision for ${request.source}:`,
|
|
3236
|
+
`Question: ${request.question}`
|
|
3237
|
+
];
|
|
3238
|
+
if (request.context?.trim()) {
|
|
3239
|
+
lines.push("", "Context:", request.context.trim());
|
|
3240
|
+
}
|
|
3241
|
+
if (request.options?.length) {
|
|
3242
|
+
lines.push("", "Options:");
|
|
3243
|
+
for (const option of request.options) {
|
|
3244
|
+
const risk = option.risk ? ` [risk: ${option.risk}]` : "";
|
|
3245
|
+
const consequence = option.consequence ? ` \u2014 ${option.consequence}` : "";
|
|
3246
|
+
lines.push(`- ${option.id}: ${option.label}${risk}${consequence}`);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
lines.push("", `Risk: ${request.risk}`);
|
|
3250
|
+
return lines.join("\n");
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/coordination/brain-ledger.ts
|
|
3254
|
+
function createLedgerGuardBrainArbiter(opts) {
|
|
3255
|
+
const denyAfter = opts.denyAfter ?? 3;
|
|
3256
|
+
return {
|
|
3257
|
+
async decide(request) {
|
|
3258
|
+
if (denyAfter > 0) {
|
|
3259
|
+
const streak = opts.failureStreakFor(request);
|
|
3260
|
+
if (streak >= denyAfter) {
|
|
3261
|
+
return {
|
|
3262
|
+
type: "deny",
|
|
3263
|
+
reason: `Ledger guard: the last ${streak} approved decisions of this kind all ended in observed failures \u2014 denying deterministically until a success is recorded. (${request.question.slice(0, 120)})`
|
|
3264
|
+
};
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
return opts.inner.decide(request);
|
|
3268
|
+
}
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
|
|
3272
|
+
// src/execution/brain-runtime.ts
|
|
3273
|
+
var FAST_PATH_KEYS = /* @__PURE__ */ new Set(["mode", "maxAutoRisk", "humanTimeoutMs"]);
|
|
3274
|
+
var AUTO_RISK_LEVELS = /* @__PURE__ */ new Set(["off", "low", "medium", "high", "all"]);
|
|
3275
|
+
var COUNCIL_MIN_RISKS = /* @__PURE__ */ new Set(["medium", "high", "critical"]);
|
|
3276
|
+
function normalizeEntry(raw) {
|
|
3277
|
+
const parsed = typeof raw === "string" ? parseModelRef(raw) : raw;
|
|
3278
|
+
const model = parsed.model?.trim();
|
|
3279
|
+
if (!model) {
|
|
3280
|
+
throw new Error(
|
|
3281
|
+
`Invalid model ref: ${typeof raw === "string" ? `"${raw}"` : JSON.stringify(raw)} (expected "model" or "provider/model")`
|
|
2408
3282
|
);
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
3283
|
+
}
|
|
3284
|
+
const provider = parsed.provider?.trim();
|
|
3285
|
+
return provider ? { provider, model } : { model };
|
|
3286
|
+
}
|
|
3287
|
+
function normalizeVoter(raw) {
|
|
3288
|
+
if (typeof raw === "string") return normalizeEntry(raw);
|
|
3289
|
+
const base = normalizeEntry(raw);
|
|
3290
|
+
const voter = { ...base };
|
|
3291
|
+
if (raw.persona !== void 0) voter.persona = raw.persona;
|
|
3292
|
+
if (raw.weight !== void 0) {
|
|
3293
|
+
if (!Number.isFinite(raw.weight) || raw.weight <= 0) {
|
|
3294
|
+
throw new Error(`Invalid voter weight: ${String(raw.weight)} (must be a positive number)`);
|
|
3295
|
+
}
|
|
3296
|
+
voter.weight = raw.weight;
|
|
3297
|
+
}
|
|
3298
|
+
if (raw.veto !== void 0) voter.veto = raw.veto;
|
|
3299
|
+
return voter;
|
|
3300
|
+
}
|
|
3301
|
+
function requireFraction(value, label) {
|
|
3302
|
+
if (!Number.isFinite(value) || value <= 0 || value > 1) {
|
|
3303
|
+
throw new Error(`Invalid ${label}: ${String(value)} (must be in (0, 1])`);
|
|
3304
|
+
}
|
|
3305
|
+
return value;
|
|
3306
|
+
}
|
|
3307
|
+
function requirePositiveMs(value, label) {
|
|
3308
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
3309
|
+
throw new Error(
|
|
3310
|
+
`Invalid ${label}: ${String(value)} (must be a positive number of milliseconds)`
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3313
|
+
return Math.round(value);
|
|
3314
|
+
}
|
|
3315
|
+
function compactEntry(entry) {
|
|
3316
|
+
return entry.provider ? `${entry.provider}/${entry.model}` : entry.model;
|
|
3317
|
+
}
|
|
3318
|
+
function compactVoter(voter) {
|
|
3319
|
+
if (voter.persona === void 0 && voter.weight === void 0 && voter.veto === void 0) {
|
|
3320
|
+
return compactEntry(voter);
|
|
3321
|
+
}
|
|
3322
|
+
return { ...voter };
|
|
3323
|
+
}
|
|
3324
|
+
function resolveBrainConfigDefaults(brain, ctx = {}) {
|
|
3325
|
+
const cfg = { ...brain ?? {} };
|
|
3326
|
+
if (cfg.models === void 0 && ctx.fallbackModels && ctx.fallbackModels.length > 0) {
|
|
3327
|
+
cfg.models = [...ctx.fallbackModels];
|
|
3328
|
+
}
|
|
3329
|
+
const seatCount = cfg.council?.voters?.length ?? cfg.models?.length ?? 0;
|
|
3330
|
+
const councilLikely = cfg.council?.enabled ?? seatCount >= 2;
|
|
3331
|
+
if (cfg.mode === void 0) cfg.mode = "headless";
|
|
3332
|
+
if (cfg.maxAutoRisk === void 0) cfg.maxAutoRisk = councilLikely ? "all" : "high";
|
|
3333
|
+
if (cfg.humanTimeoutMs === void 0) cfg.humanTimeoutMs = 12e4;
|
|
3334
|
+
return cfg;
|
|
3335
|
+
}
|
|
3336
|
+
function normalizeInitial(config) {
|
|
3337
|
+
const cfg = { ...config ?? {} };
|
|
3338
|
+
const safe = (items, map) => (items ?? []).flatMap((item) => {
|
|
3339
|
+
try {
|
|
3340
|
+
return [map(item)];
|
|
3341
|
+
} catch {
|
|
3342
|
+
return [];
|
|
3343
|
+
}
|
|
3344
|
+
});
|
|
3345
|
+
if (cfg.models) cfg.models = safe(cfg.models, normalizeEntry);
|
|
3346
|
+
if (cfg.council) {
|
|
3347
|
+
const council = { ...cfg.council };
|
|
3348
|
+
if (council.voters) council.voters = safe(council.voters, normalizeVoter);
|
|
3349
|
+
if (council.judge) {
|
|
3350
|
+
try {
|
|
3351
|
+
council.judge = normalizeEntry(council.judge);
|
|
3352
|
+
} catch {
|
|
3353
|
+
council.judge = void 0;
|
|
3354
|
+
}
|
|
3355
|
+
}
|
|
3356
|
+
cfg.council = council;
|
|
3357
|
+
}
|
|
3358
|
+
return cfg;
|
|
3359
|
+
}
|
|
3360
|
+
function createBrainRuntime(opts) {
|
|
3361
|
+
let cfg = normalizeInitial(opts.initialConfig);
|
|
3362
|
+
let poolLabels = [];
|
|
3363
|
+
let councilLabels = [];
|
|
3364
|
+
let current;
|
|
3365
|
+
const getDecisionDigest = (request) => {
|
|
3366
|
+
const ledger = opts.ledger;
|
|
3367
|
+
if (!ledger?.isEnabled()) return void 0;
|
|
3368
|
+
return ledger.getDecisionDigest?.(request);
|
|
3369
|
+
};
|
|
3370
|
+
function rebuild() {
|
|
3371
|
+
const tiers = assembleBrainTiers({
|
|
3372
|
+
brainConfig: cfg,
|
|
3373
|
+
defaultProviderId: opts.defaultProviderId,
|
|
3374
|
+
sessionProvider: opts.sessionProvider,
|
|
3375
|
+
sessionModel: opts.sessionModel,
|
|
3376
|
+
resolveProvider: opts.resolveProvider,
|
|
3377
|
+
getDecisionDigest
|
|
3378
|
+
});
|
|
3379
|
+
poolLabels = tiers.poolLabels;
|
|
3380
|
+
councilLabels = tiers.councilLabels;
|
|
3381
|
+
const tiered = createTieredBrainArbiter({
|
|
3382
|
+
policy: new DefaultBrainArbiter(),
|
|
3383
|
+
autonomous: tiers.autonomous,
|
|
3384
|
+
getMaxAutoRisk: () => cfg.maxAutoRisk ?? "medium",
|
|
3385
|
+
council: tiers.council,
|
|
3386
|
+
getCouncilMinRisk: tiers.getCouncilMinRisk
|
|
3387
|
+
});
|
|
3388
|
+
const streakFor = opts.ledger?.failureStreakFor;
|
|
3389
|
+
current = streakFor && opts.ledger?.isEnabled() ? createLedgerGuardBrainArbiter({
|
|
3390
|
+
inner: tiered,
|
|
3391
|
+
failureStreakFor: streakFor,
|
|
3392
|
+
denyAfter: cfg.ledger?.autoDenyAfterFailures
|
|
3393
|
+
}) : tiered;
|
|
3394
|
+
}
|
|
3395
|
+
rebuild();
|
|
3396
|
+
function mergePatch(patch) {
|
|
3397
|
+
const next = { ...cfg, council: cfg.council ? { ...cfg.council } : void 0 };
|
|
3398
|
+
let rebuildNeeded = false;
|
|
3399
|
+
const structural = () => {
|
|
3400
|
+
rebuildNeeded = true;
|
|
3401
|
+
};
|
|
3402
|
+
if (patch.mode !== void 0) {
|
|
3403
|
+
if (patch.mode !== "headless" && patch.mode !== "interactive") {
|
|
3404
|
+
throw new Error(`Invalid mode: ${String(patch.mode)}`);
|
|
3405
|
+
}
|
|
3406
|
+
next.mode = patch.mode;
|
|
3407
|
+
}
|
|
3408
|
+
if (patch.maxAutoRisk !== void 0) {
|
|
3409
|
+
if (!AUTO_RISK_LEVELS.has(patch.maxAutoRisk)) {
|
|
3410
|
+
throw new Error(`Invalid maxAutoRisk: ${String(patch.maxAutoRisk)}`);
|
|
2414
3411
|
}
|
|
2415
|
-
|
|
2416
|
-
type: "deny",
|
|
2417
|
-
reason: "Autonomy Brain returned no exact valid option id."
|
|
2418
|
-
};
|
|
3412
|
+
next.maxAutoRisk = patch.maxAutoRisk;
|
|
2419
3413
|
}
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
text: text || (request.fallback === "continue" ? "Continue execution." : "Denied by autonomy policy."),
|
|
2423
|
-
rationale: text || void 0
|
|
2424
|
-
};
|
|
2425
|
-
} catch {
|
|
2426
|
-
if (request.fallback === "continue") {
|
|
2427
|
-
return {
|
|
2428
|
-
type: "answer",
|
|
2429
|
-
text: "Continue (Autonomy Brain LLM unavailable \u2014 using fallback)."
|
|
2430
|
-
};
|
|
3414
|
+
if (patch.humanTimeoutMs !== void 0) {
|
|
3415
|
+
next.humanTimeoutMs = patch.humanTimeoutMs === null ? void 0 : requirePositiveMs(patch.humanTimeoutMs, "humanTimeoutMs");
|
|
2431
3416
|
}
|
|
2432
|
-
|
|
3417
|
+
if (patch.models !== void 0) {
|
|
3418
|
+
next.models = patch.models === null ? void 0 : patch.models.map(normalizeEntry);
|
|
3419
|
+
structural();
|
|
3420
|
+
}
|
|
3421
|
+
if (patch.strategy !== void 0) {
|
|
3422
|
+
if (patch.strategy !== null && patch.strategy !== "fallback" && patch.strategy !== "round-robin") {
|
|
3423
|
+
throw new Error(`Invalid strategy: ${String(patch.strategy)}`);
|
|
3424
|
+
}
|
|
3425
|
+
next.strategy = patch.strategy ?? void 0;
|
|
3426
|
+
structural();
|
|
3427
|
+
}
|
|
3428
|
+
if (patch.decisionTimeoutMs !== void 0) {
|
|
3429
|
+
next.decisionTimeoutMs = patch.decisionTimeoutMs === null ? void 0 : requirePositiveMs(patch.decisionTimeoutMs, "decisionTimeoutMs");
|
|
3430
|
+
structural();
|
|
3431
|
+
}
|
|
3432
|
+
if (patch.council !== void 0) {
|
|
3433
|
+
if (patch.council === null) {
|
|
3434
|
+
next.council = void 0;
|
|
3435
|
+
} else {
|
|
3436
|
+
const c = { ...next.council ?? {} };
|
|
3437
|
+
const p = patch.council;
|
|
3438
|
+
if (p.enabled !== void 0) c.enabled = p.enabled === null ? void 0 : p.enabled;
|
|
3439
|
+
if (p.minRisk !== void 0) {
|
|
3440
|
+
if (p.minRisk !== null && !COUNCIL_MIN_RISKS.has(p.minRisk)) {
|
|
3441
|
+
throw new Error(`Invalid council minRisk: ${String(p.minRisk)}`);
|
|
3442
|
+
}
|
|
3443
|
+
c.minRisk = p.minRisk ?? void 0;
|
|
3444
|
+
}
|
|
3445
|
+
if (p.voters !== void 0) {
|
|
3446
|
+
c.voters = p.voters === null ? void 0 : p.voters.map(normalizeVoter);
|
|
3447
|
+
}
|
|
3448
|
+
if (p.quorum !== void 0) {
|
|
3449
|
+
c.quorum = p.quorum === null ? void 0 : requireFraction(p.quorum, "council quorum");
|
|
3450
|
+
}
|
|
3451
|
+
if (p.approval !== void 0) {
|
|
3452
|
+
c.approval = p.approval === null ? void 0 : requireFraction(p.approval, "council approval");
|
|
3453
|
+
}
|
|
3454
|
+
if (p.judge !== void 0) {
|
|
3455
|
+
c.judge = p.judge === null ? void 0 : normalizeEntry(p.judge);
|
|
3456
|
+
}
|
|
3457
|
+
next.council = c;
|
|
3458
|
+
}
|
|
3459
|
+
structural();
|
|
3460
|
+
}
|
|
3461
|
+
if (patch.ledger !== void 0) {
|
|
3462
|
+
if (patch.ledger === null) {
|
|
3463
|
+
next.ledger = void 0;
|
|
3464
|
+
} else {
|
|
3465
|
+
const l = { ...next.ledger ?? {} };
|
|
3466
|
+
if (patch.ledger.enabled !== void 0) l.enabled = patch.ledger.enabled;
|
|
3467
|
+
if (patch.ledger.autoDenyAfterFailures !== void 0) {
|
|
3468
|
+
const n = patch.ledger.autoDenyAfterFailures;
|
|
3469
|
+
if (n !== null && (!Number.isInteger(n) || n < 0)) {
|
|
3470
|
+
throw new Error(
|
|
3471
|
+
`Invalid autoDenyAfterFailures: ${String(n)} (must be an integer >= 0)`
|
|
3472
|
+
);
|
|
3473
|
+
}
|
|
3474
|
+
l.autoDenyAfterFailures = n ?? void 0;
|
|
3475
|
+
}
|
|
3476
|
+
next.ledger = l;
|
|
3477
|
+
}
|
|
3478
|
+
structural();
|
|
3479
|
+
}
|
|
3480
|
+
for (const key of Object.keys(patch)) {
|
|
3481
|
+
if (!FAST_PATH_KEYS.has(key) && patch[key] !== void 0) {
|
|
3482
|
+
if (!["models", "strategy", "decisionTimeoutMs", "council", "ledger"].includes(key)) {
|
|
3483
|
+
throw new Error(`Unknown brain config field: ${key}`);
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
return { next, rebuildNeeded };
|
|
2433
3488
|
}
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
const byId = new Map(options.map((option2) => [option2.id, option2]));
|
|
2438
|
-
const wholeFence = /^```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```$/i.exec(text);
|
|
2439
|
-
const jsonCandidate = wholeFence ? (wholeFence[1] ?? "").trim() : text;
|
|
2440
|
-
const parsed = safeParse(jsonCandidate, 16384);
|
|
2441
|
-
if (parsed.ok && parsed.value && typeof parsed.value.optionId === "string") {
|
|
2442
|
-
const option2 = byId.get(parsed.value.optionId);
|
|
2443
|
-
if (!option2) return null;
|
|
3489
|
+
function getSnapshot() {
|
|
3490
|
+
const councilCfg = cfg.council;
|
|
3491
|
+
const voters = (councilCfg?.voters ?? []).map((v) => normalizeVoter(v));
|
|
2444
3492
|
return {
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
3493
|
+
mode: cfg.mode ?? "interactive",
|
|
3494
|
+
maxAutoRisk: cfg.maxAutoRisk ?? "medium",
|
|
3495
|
+
models: (cfg.models ?? []).map((m) => normalizeEntry(m)),
|
|
3496
|
+
strategy: cfg.strategy ?? "fallback",
|
|
3497
|
+
decisionTimeoutMs: cfg.decisionTimeoutMs,
|
|
3498
|
+
humanTimeoutMs: cfg.humanTimeoutMs,
|
|
3499
|
+
council: {
|
|
3500
|
+
enabled: councilLabels.length > 0,
|
|
3501
|
+
configured: councilCfg?.enabled,
|
|
3502
|
+
minRisk: councilCfg?.minRisk ?? "high",
|
|
3503
|
+
voters,
|
|
3504
|
+
quorum: councilCfg?.quorum,
|
|
3505
|
+
approval: councilCfg?.approval,
|
|
3506
|
+
judge: councilCfg?.judge ? normalizeEntry(councilCfg.judge) : void 0
|
|
3507
|
+
},
|
|
3508
|
+
ledger: {
|
|
3509
|
+
enabled: opts.ledger?.isEnabled() ?? false,
|
|
3510
|
+
autoDenyAfterFailures: cfg.ledger?.autoDenyAfterFailures,
|
|
3511
|
+
path: opts.ledger?.getPath()
|
|
3512
|
+
},
|
|
3513
|
+
poolLabels: [...poolLabels],
|
|
3514
|
+
councilLabels: [...councilLabels],
|
|
3515
|
+
usingSessionModel: (cfg.models ?? []).length === 0
|
|
2449
3516
|
};
|
|
2450
3517
|
}
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
3518
|
+
function getConfig() {
|
|
3519
|
+
const out = {};
|
|
3520
|
+
if (cfg.mode !== void 0) out.mode = cfg.mode;
|
|
3521
|
+
if (cfg.maxAutoRisk !== void 0) out.maxAutoRisk = cfg.maxAutoRisk;
|
|
3522
|
+
if (cfg.models?.length) out.models = cfg.models.map((m) => compactEntry(normalizeEntry(m)));
|
|
3523
|
+
if (cfg.strategy !== void 0) out.strategy = cfg.strategy;
|
|
3524
|
+
if (cfg.decisionTimeoutMs !== void 0) out.decisionTimeoutMs = cfg.decisionTimeoutMs;
|
|
3525
|
+
if (cfg.humanTimeoutMs !== void 0) out.humanTimeoutMs = cfg.humanTimeoutMs;
|
|
3526
|
+
if (cfg.council !== void 0) {
|
|
3527
|
+
const c = cfg.council;
|
|
3528
|
+
const outCouncil = {};
|
|
3529
|
+
if (c.enabled !== void 0) outCouncil.enabled = c.enabled;
|
|
3530
|
+
if (c.minRisk !== void 0) outCouncil.minRisk = c.minRisk;
|
|
3531
|
+
if (c.voters?.length)
|
|
3532
|
+
outCouncil.voters = c.voters.map((v) => compactVoter(normalizeVoter(v)));
|
|
3533
|
+
if (c.quorum !== void 0) outCouncil.quorum = c.quorum;
|
|
3534
|
+
if (c.approval !== void 0) outCouncil.approval = c.approval;
|
|
3535
|
+
if (c.judge !== void 0) outCouncil.judge = compactEntry(normalizeEntry(c.judge));
|
|
3536
|
+
out.council = outCouncil;
|
|
3537
|
+
}
|
|
3538
|
+
if (cfg.ledger !== void 0) out.ledger = { ...cfg.ledger };
|
|
3539
|
+
if (cfg.monitor !== void 0) out.monitor = { ...cfg.monitor };
|
|
3540
|
+
return out;
|
|
3541
|
+
}
|
|
2456
3542
|
return {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
3543
|
+
arbiter: {
|
|
3544
|
+
decide: (request) => current.decide(request)
|
|
3545
|
+
},
|
|
3546
|
+
getMode: () => cfg.mode ?? "interactive",
|
|
3547
|
+
getMaxAutoRisk: () => cfg.maxAutoRisk ?? "medium",
|
|
3548
|
+
getHumanTimeoutMs: () => cfg.humanTimeoutMs,
|
|
3549
|
+
getSnapshot,
|
|
3550
|
+
getConfig,
|
|
3551
|
+
apply(patch, applyOpts) {
|
|
3552
|
+
const { next, rebuildNeeded } = mergePatch(patch);
|
|
3553
|
+
cfg = next;
|
|
3554
|
+
if (patch.ledger && patch.ledger.enabled !== void 0) {
|
|
3555
|
+
opts.ledger?.setEnabled(patch.ledger.enabled);
|
|
3556
|
+
}
|
|
3557
|
+
if (rebuildNeeded) rebuild();
|
|
3558
|
+
const snapshot = getSnapshot();
|
|
3559
|
+
const shouldPersist = applyOpts?.persist !== false && opts.persist !== void 0;
|
|
3560
|
+
const persisted = shouldPersist ? opts.persist(getConfig()).then(
|
|
3561
|
+
() => ({ ok: true }),
|
|
3562
|
+
(err) => ({
|
|
3563
|
+
ok: false,
|
|
3564
|
+
error: err instanceof Error ? err.message : String(err)
|
|
3565
|
+
})
|
|
3566
|
+
) : Promise.resolve({ ok: true });
|
|
3567
|
+
opts.onApplied?.(snapshot);
|
|
3568
|
+
return { snapshot, persisted };
|
|
3569
|
+
}
|
|
2461
3570
|
};
|
|
2462
3571
|
}
|
|
2463
|
-
function extractText(result) {
|
|
2464
|
-
if (!result || typeof result !== "object") return "";
|
|
2465
|
-
const r = result;
|
|
2466
|
-
if (Array.isArray(r.content)) {
|
|
2467
|
-
return r.content.filter((b) => b.type === "text").map((b) => b.text ?? "").join("");
|
|
2468
|
-
}
|
|
2469
|
-
if (Array.isArray(r.choices)) {
|
|
2470
|
-
return r.choices[0]?.message?.content ?? "";
|
|
2471
|
-
}
|
|
2472
|
-
return typeof r.text === "string" ? r.text : "";
|
|
2473
|
-
}
|
|
2474
3572
|
|
|
2475
3573
|
// src/storage/goal-store.ts
|
|
2476
3574
|
import * as fsp from "node:fs/promises";
|
|
@@ -2478,7 +3576,7 @@ var MAX_JOURNAL_ENTRIES = 500;
|
|
|
2478
3576
|
function goalFilePath(projectRoot) {
|
|
2479
3577
|
return resolveWstackPaths({ projectRoot }).projectGoal;
|
|
2480
3578
|
}
|
|
2481
|
-
async function loadGoal(filePath, events) {
|
|
3579
|
+
async function loadGoal(filePath, events, warn) {
|
|
2482
3580
|
const t0 = Date.now();
|
|
2483
3581
|
let raw;
|
|
2484
3582
|
try {
|
|
@@ -2509,13 +3607,9 @@ async function loadGoal(filePath, events) {
|
|
|
2509
3607
|
try {
|
|
2510
3608
|
const parsed = JSON.parse(raw);
|
|
2511
3609
|
if (parsed?.version !== 1 || typeof parsed.goal !== "string" || !Array.isArray(parsed.journal)) {
|
|
2512
|
-
console.warn(JSON.stringify({
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
path: filePath,
|
|
2516
|
-
message: "invalid schema \u2014 consider deleting and re-creating",
|
|
2517
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2518
|
-
}));
|
|
3610
|
+
(warn ?? ((msg) => console.warn(JSON.stringify({ level: "warn", event: "goal_store.invalid_schema", path: filePath, message: msg, timestamp: (/* @__PURE__ */ new Date()).toISOString() }))))(
|
|
3611
|
+
"invalid schema \u2014 consider deleting and re-creating"
|
|
3612
|
+
);
|
|
2519
3613
|
events?.emit("storage.read", {
|
|
2520
3614
|
sessionId: "~boot~",
|
|
2521
3615
|
store: "goal",
|
|
@@ -2537,13 +3631,9 @@ async function loadGoal(filePath, events) {
|
|
|
2537
3631
|
});
|
|
2538
3632
|
return parsed;
|
|
2539
3633
|
} catch {
|
|
2540
|
-
console.warn(JSON.stringify({
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
path: filePath,
|
|
2544
|
-
message: "JSON parse failed \u2014 consider deleting and re-creating",
|
|
2545
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2546
|
-
}));
|
|
3634
|
+
(warn ?? ((msg) => console.warn(JSON.stringify({ level: "warn", event: "goal_store.parse_failed", path: filePath, message: msg, timestamp: (/* @__PURE__ */ new Date()).toISOString() }))))(
|
|
3635
|
+
"JSON parse failed \u2014 consider deleting and re-creating"
|
|
3636
|
+
);
|
|
2547
3637
|
events?.emit("storage.read", {
|
|
2548
3638
|
sessionId: "~boot~",
|
|
2549
3639
|
store: "goal",
|
|
@@ -3966,124 +5056,6 @@ function installDesignStudioMiddleware(deps) {
|
|
|
3966
5056
|
});
|
|
3967
5057
|
}
|
|
3968
5058
|
|
|
3969
|
-
// src/types/provider.ts
|
|
3970
|
-
var CONTEXT_OVERFLOW_RE = /context.length|context.window|max.*tokens?.*exceeded|prompt is too long|too long|exceeds the context|\btokens\b.*exceed|context_length_exceeded/i;
|
|
3971
|
-
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
3972
|
-
function classifyProviderError(status, body, message) {
|
|
3973
|
-
const type = body?.type;
|
|
3974
|
-
if (status === 0) return "network";
|
|
3975
|
-
if (status === 408) return "timeout";
|
|
3976
|
-
if (status === 599) return "stream_hang";
|
|
3977
|
-
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
3978
|
-
if (type === "overloaded_error" || status === 529) return "overloaded";
|
|
3979
|
-
if (status >= 500) return "server";
|
|
3980
|
-
if (type === "authentication_error" || type === "permission_error" || status === 401 || status === 403) {
|
|
3981
|
-
return "auth";
|
|
3982
|
-
}
|
|
3983
|
-
const text = [message, body?.message, type, body?.raw].filter(Boolean).join("\n");
|
|
3984
|
-
if (type === "content_filter" || CONTENT_FILTER_RE.test(text)) return "content_filter";
|
|
3985
|
-
if (status === 413 || status >= 400 && CONTEXT_OVERFLOW_RE.test(text)) {
|
|
3986
|
-
return "context_overflow";
|
|
3987
|
-
}
|
|
3988
|
-
if (status >= 400) return "invalid_request";
|
|
3989
|
-
return "unknown";
|
|
3990
|
-
}
|
|
3991
|
-
function isRetryableKind(kind) {
|
|
3992
|
-
return RETRYABLE_BY_KIND[kind];
|
|
3993
|
-
}
|
|
3994
|
-
var RETRYABLE_BY_KIND = {
|
|
3995
|
-
rate_limit: true,
|
|
3996
|
-
overloaded: true,
|
|
3997
|
-
server: true,
|
|
3998
|
-
timeout: true,
|
|
3999
|
-
network: true,
|
|
4000
|
-
stream_hang: true,
|
|
4001
|
-
auth: false,
|
|
4002
|
-
context_overflow: false,
|
|
4003
|
-
content_filter: false,
|
|
4004
|
-
invalid_request: false,
|
|
4005
|
-
unknown: false
|
|
4006
|
-
};
|
|
4007
|
-
var ProviderError = class extends WrongStackError {
|
|
4008
|
-
status;
|
|
4009
|
-
retryable;
|
|
4010
|
-
providerId;
|
|
4011
|
-
/** Canonical failure classification — see {@link ProviderErrorKind}. */
|
|
4012
|
-
kind;
|
|
4013
|
-
body;
|
|
4014
|
-
constructor(message, status, retryable, providerId, opts = {}) {
|
|
4015
|
-
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
4016
|
-
super({
|
|
4017
|
-
message,
|
|
4018
|
-
code: kindToCode(kind),
|
|
4019
|
-
subsystem: "provider",
|
|
4020
|
-
severity: status >= 500 ? "error" : "warning",
|
|
4021
|
-
recoverable: retryable,
|
|
4022
|
-
context: { providerId, status },
|
|
4023
|
-
cause: opts.cause
|
|
4024
|
-
});
|
|
4025
|
-
this.name = "ProviderError";
|
|
4026
|
-
this.status = status;
|
|
4027
|
-
this.retryable = retryable;
|
|
4028
|
-
this.providerId = providerId;
|
|
4029
|
-
this.kind = kind;
|
|
4030
|
-
this.body = opts.body;
|
|
4031
|
-
}
|
|
4032
|
-
/**
|
|
4033
|
-
* Render a one-line, user-facing description. Designed for the CLI/TUI
|
|
4034
|
-
* status line and the agent's retry warning. Avoids dumping raw JSON
|
|
4035
|
-
* (which is what users see today when a 529 lands and the log message
|
|
4036
|
-
* includes the full `{"type":"error",...}` body).
|
|
4037
|
-
*
|
|
4038
|
-
* Examples:
|
|
4039
|
-
* "minimax-coding-plan overloaded (529): High traffic detected. Upgrade for highspeed model. [req 06534785201de9c0…]"
|
|
4040
|
-
* "openai rate limited (429): Retry after 12s"
|
|
4041
|
-
* "anthropic invalid request (400): messages.0.role must be one of 'user'|'assistant'"
|
|
4042
|
-
* "groq HTTP 500 (server error)"
|
|
4043
|
-
*/
|
|
4044
|
-
describe() {
|
|
4045
|
-
const kind = describeStatus(this.status, this.body?.type);
|
|
4046
|
-
const head = `${this.providerId} ${kind}`;
|
|
4047
|
-
const detail = this.body?.message?.trim();
|
|
4048
|
-
const reqId = this.body?.requestId ? ` [req ${this.body.requestId.slice(0, 16)}${this.body.requestId.length > 16 ? "\u2026" : ""}]` : "";
|
|
4049
|
-
if (detail && detail.length > 0) {
|
|
4050
|
-
return `${head}: ${truncate(detail, 240)}${reqId}`;
|
|
4051
|
-
}
|
|
4052
|
-
return `${head}${reqId}`;
|
|
4053
|
-
}
|
|
4054
|
-
};
|
|
4055
|
-
function describeStatus(status, type) {
|
|
4056
|
-
if (status === 0) return "network error";
|
|
4057
|
-
if (status === 599) return `stream hang (${status})`;
|
|
4058
|
-
if (type === "overloaded_error" || status === 529) return `overloaded (${status})`;
|
|
4059
|
-
if (type === "rate_limit_error" || status === 429) return `rate limited (${status})`;
|
|
4060
|
-
if (type === "authentication_error" || status === 401) return `auth failed (${status})`;
|
|
4061
|
-
if (type === "permission_error" || status === 403) return `forbidden (${status})`;
|
|
4062
|
-
if (type === "not_found_error" || status === 404) return `not found (${status})`;
|
|
4063
|
-
if (type === "content_filter") return `content filtered (${status})`;
|
|
4064
|
-
if (type === "invalid_request_error" || status === 400) return `invalid request (${status})`;
|
|
4065
|
-
if (status === 408) return `timeout (${status})`;
|
|
4066
|
-
if (status >= 500 && status < 600) return `HTTP ${status} (server error)`;
|
|
4067
|
-
if (type) return `${type} (${status})`;
|
|
4068
|
-
return `HTTP ${status}`;
|
|
4069
|
-
}
|
|
4070
|
-
var KIND_TO_CODE = {
|
|
4071
|
-
network: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
4072
|
-
timeout: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
4073
|
-
rate_limit: ERROR_CODES.PROVIDER_RATE_LIMITED,
|
|
4074
|
-
auth: ERROR_CODES.PROVIDER_AUTH_FAILED,
|
|
4075
|
-
overloaded: ERROR_CODES.PROVIDER_OVERLOADED,
|
|
4076
|
-
context_overflow: ERROR_CODES.PROVIDER_CONTEXT_OVERFLOW,
|
|
4077
|
-
server: ERROR_CODES.PROVIDER_SERVER_ERROR,
|
|
4078
|
-
stream_hang: ERROR_CODES.PROVIDER_SERVER_ERROR,
|
|
4079
|
-
content_filter: ERROR_CODES.PROVIDER_INVALID_REQUEST,
|
|
4080
|
-
invalid_request: ERROR_CODES.PROVIDER_INVALID_REQUEST,
|
|
4081
|
-
unknown: ERROR_CODES.PROVIDER_INVALID_REQUEST
|
|
4082
|
-
};
|
|
4083
|
-
function kindToCode(kind) {
|
|
4084
|
-
return KIND_TO_CODE[kind];
|
|
4085
|
-
}
|
|
4086
|
-
|
|
4087
5059
|
// src/execution/regex-patterns.ts
|
|
4088
5060
|
var NETWORK_ERR_RE = /ECONN|ETIMEDOUT|ETIME|ENOTFOUND|EAI_AGAIN|fetch failed/i;
|
|
4089
5061
|
|
|
@@ -4271,22 +5243,40 @@ var EternalAutonomyEngine = class {
|
|
|
4271
5243
|
currentCtrl = null;
|
|
4272
5244
|
iterationsSinceCompact = 0;
|
|
4273
5245
|
goalPath;
|
|
5246
|
+
/**
|
|
5247
|
+
* Emit a structured error. Uses the configured Logger when available;
|
|
5248
|
+
* falls back to console.error(JSON) so events are never silently dropped.
|
|
5249
|
+
*/
|
|
5250
|
+
logError(msg, ctx) {
|
|
5251
|
+
if (this.opts.logger) {
|
|
5252
|
+
this.opts.logger.error(msg, ctx);
|
|
5253
|
+
} else {
|
|
5254
|
+
console.error(JSON.stringify({ ...ctx, message: msg, timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
4274
5257
|
/** Current engine state — readable for UIs. */
|
|
4275
5258
|
get currentState() {
|
|
4276
5259
|
return this.state;
|
|
4277
5260
|
}
|
|
5261
|
+
/**
|
|
5262
|
+
* Load the goal file with structured logging for parse/schema warnings.
|
|
5263
|
+
*/
|
|
5264
|
+
loadGoal() {
|
|
5265
|
+
return loadGoal(
|
|
5266
|
+
this.goalPath,
|
|
5267
|
+
this.opts.events,
|
|
5268
|
+
this.opts.logger ? (msg) => this.opts.logger.warn(msg) : void 0
|
|
5269
|
+
);
|
|
5270
|
+
}
|
|
4278
5271
|
/** Synchronously request stop. Resolves once the running iteration aborts. */
|
|
4279
5272
|
stop() {
|
|
4280
5273
|
this.stopRequested = true;
|
|
4281
5274
|
this.currentCtrl?.abort();
|
|
4282
5275
|
void this.persistEngineState("stopped").catch((err) => {
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
event: "engine.persist_state_failed",
|
|
4286
|
-
|
|
4287
|
-
context: { expectedState: "stopped" },
|
|
4288
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4289
|
-
}));
|
|
5276
|
+
this.logError(
|
|
5277
|
+
"Engine persist state failed",
|
|
5278
|
+
{ event: "engine.persist_state_failed", message: toErrorMessage(err), context: { expectedState: "stopped" } }
|
|
5279
|
+
);
|
|
4290
5280
|
});
|
|
4291
5281
|
this.state = "stopped";
|
|
4292
5282
|
}
|
|
@@ -4342,7 +5332,7 @@ var EternalAutonomyEngine = class {
|
|
|
4342
5332
|
const emit = (stage) => {
|
|
4343
5333
|
this.opts.onStage?.(stage);
|
|
4344
5334
|
};
|
|
4345
|
-
const goal = await loadGoal(
|
|
5335
|
+
const goal = await this.loadGoal();
|
|
4346
5336
|
if (!goal) {
|
|
4347
5337
|
emit({ phase: "stopped" });
|
|
4348
5338
|
this.stopRequested = true;
|
|
@@ -4446,16 +5436,13 @@ var EternalAutonomyEngine = class {
|
|
|
4446
5436
|
emit({ phase: "reflect", status, note });
|
|
4447
5437
|
let iterationIndex = 0;
|
|
4448
5438
|
try {
|
|
4449
|
-
const reloaded = await loadGoal(
|
|
5439
|
+
const reloaded = await this.loadGoal();
|
|
4450
5440
|
iterationIndex = reloaded?.iterations ?? 0;
|
|
4451
5441
|
} catch (err) {
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
event: "autonomy.goal_reload_failed",
|
|
4455
|
-
|
|
4456
|
-
context: { goalPath: this.goalPath },
|
|
4457
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4458
|
-
}));
|
|
5442
|
+
this.logError(
|
|
5443
|
+
"Goal reload failed",
|
|
5444
|
+
{ event: "autonomy.goal_reload_failed", message: toErrorMessage(err), context: { goalPath: this.goalPath } }
|
|
5445
|
+
);
|
|
4459
5446
|
}
|
|
4460
5447
|
this.opts.onIteration?.({
|
|
4461
5448
|
at: (this.opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4691,13 +5678,10 @@ ${lastFew}` : "No prior iterations yet.",
|
|
|
4691
5678
|
clearTimeout(timer);
|
|
4692
5679
|
}
|
|
4693
5680
|
} catch (err) {
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
event: "autonomy.brainstorm_failed",
|
|
4697
|
-
|
|
4698
|
-
context: { goal: goal.goal.slice(0, 100) },
|
|
4699
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4700
|
-
}));
|
|
5681
|
+
this.logError(
|
|
5682
|
+
"Brainstorm failed",
|
|
5683
|
+
{ event: "autonomy.brainstorm_failed", message: toErrorMessage(err), context: { goal: goal.goal.slice(0, 100) } }
|
|
5684
|
+
);
|
|
4701
5685
|
return null;
|
|
4702
5686
|
}
|
|
4703
5687
|
}
|
|
@@ -4780,7 +5764,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
4780
5764
|
}
|
|
4781
5765
|
}
|
|
4782
5766
|
async appendIterationEntry(entry) {
|
|
4783
|
-
const current = await loadGoal(
|
|
5767
|
+
const current = await this.loadGoal();
|
|
4784
5768
|
if (!current) {
|
|
4785
5769
|
return;
|
|
4786
5770
|
}
|
|
@@ -4794,7 +5778,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
4794
5778
|
* the counter to rotate past stuck todos once they cross `todoMaxAttempts`.
|
|
4795
5779
|
*/
|
|
4796
5780
|
async bumpTodoAttempt(todoId) {
|
|
4797
|
-
const current = await loadGoal(
|
|
5781
|
+
const current = await this.loadGoal();
|
|
4798
5782
|
if (!current) return;
|
|
4799
5783
|
const attempts = { ...current.todoAttempts ?? {} };
|
|
4800
5784
|
attempts[todoId] = (attempts[todoId] ?? 0) + 1;
|
|
@@ -4808,7 +5792,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
4808
5792
|
* goal is already `completed`.
|
|
4809
5793
|
*/
|
|
4810
5794
|
async markGoalCompleted(action, note) {
|
|
4811
|
-
const current = await loadGoal(
|
|
5795
|
+
const current = await this.loadGoal();
|
|
4812
5796
|
if (!current) return;
|
|
4813
5797
|
if (current.goalState === "completed") return;
|
|
4814
5798
|
const withFlag = { ...current, goalState: "completed" };
|
|
@@ -4819,6 +5803,11 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
4819
5803
|
note: note.slice(0, 240)
|
|
4820
5804
|
});
|
|
4821
5805
|
await saveGoal(this.goalPath, withEntry, this.opts.events);
|
|
5806
|
+
try {
|
|
5807
|
+
const { unlink: unlink4 } = await import("node:fs/promises");
|
|
5808
|
+
await unlink4(this.goalPath);
|
|
5809
|
+
} catch {
|
|
5810
|
+
}
|
|
4822
5811
|
this.opts.onEternalStop?.();
|
|
4823
5812
|
}
|
|
4824
5813
|
/**
|
|
@@ -4827,7 +5816,7 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
4827
5816
|
* `onEternalStop` so the REPL returns to normal mode.
|
|
4828
5817
|
*/
|
|
4829
5818
|
async clearGoalManually(note) {
|
|
4830
|
-
const current = await loadGoal(
|
|
5819
|
+
const current = await this.loadGoal();
|
|
4831
5820
|
if (current) {
|
|
4832
5821
|
const abandoned = { ...current, goalState: "abandoned" };
|
|
4833
5822
|
await saveGoal(this.goalPath, abandoned, this.opts.events);
|
|
@@ -4875,7 +5864,23 @@ Recent work:
|
|
|
4875
5864
|
${recentJournal}` : ""
|
|
4876
5865
|
].join("\n"),
|
|
4877
5866
|
risk: "high",
|
|
4878
|
-
fallback: "continue"
|
|
5867
|
+
fallback: "continue",
|
|
5868
|
+
// Structured verdict — completion is control-plane input, decided by
|
|
5869
|
+
// exact option id rather than sniffing prose for "done"/"complete".
|
|
5870
|
+
options: [
|
|
5871
|
+
{
|
|
5872
|
+
id: "goal_complete",
|
|
5873
|
+
label: "The goal is complete \u2014 stop the eternal run",
|
|
5874
|
+
consequence: "The engine stops and the goal file is archived.",
|
|
5875
|
+
risk: "high"
|
|
5876
|
+
},
|
|
5877
|
+
{
|
|
5878
|
+
id: "keep_working",
|
|
5879
|
+
label: "Not complete yet \u2014 keep working",
|
|
5880
|
+
consequence: "The engine continues iterating on the goal.",
|
|
5881
|
+
risk: "low"
|
|
5882
|
+
}
|
|
5883
|
+
]
|
|
4879
5884
|
});
|
|
4880
5885
|
const summary = formatDecisionSummary(decision, {
|
|
4881
5886
|
id: `goal-done-${goal.iterations}`,
|
|
@@ -4895,8 +5900,7 @@ ${recentJournal}` : ""
|
|
|
4895
5900
|
return false;
|
|
4896
5901
|
}
|
|
4897
5902
|
if (decision.type === "answer") {
|
|
4898
|
-
|
|
4899
|
-
return text.includes("complete") || text.includes("done") || text.includes("yes");
|
|
5903
|
+
return decision.optionId === "goal_complete";
|
|
4900
5904
|
}
|
|
4901
5905
|
return true;
|
|
4902
5906
|
} catch {
|
|
@@ -4907,13 +5911,13 @@ ${recentJournal}` : ""
|
|
|
4907
5911
|
* Persist a progress update from the agent's [PROGRESS: N%] output.
|
|
4908
5912
|
*/
|
|
4909
5913
|
async updateProgress(progress, note) {
|
|
4910
|
-
const current = await loadGoal(
|
|
5914
|
+
const current = await this.loadGoal();
|
|
4911
5915
|
if (!current) return;
|
|
4912
5916
|
const updated = recordProgress(current, progress, note);
|
|
4913
5917
|
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
4914
5918
|
}
|
|
4915
5919
|
async persistEngineState(state) {
|
|
4916
|
-
const current = await loadGoal(
|
|
5920
|
+
const current = await this.loadGoal();
|
|
4917
5921
|
if (!current) return;
|
|
4918
5922
|
if (current.engineState === state) return;
|
|
4919
5923
|
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
@@ -5751,15 +6755,17 @@ var MAX_FILES = 64;
|
|
|
5751
6755
|
var MAX_NEXT_STEPS = 12;
|
|
5752
6756
|
var MAX_ITEM_CHARS = 500;
|
|
5753
6757
|
function normalizeSubagentStructuredReport(value) {
|
|
5754
|
-
if (!
|
|
6758
|
+
if (!isRecord4(value)) return void 0;
|
|
5755
6759
|
const summary = nonEmptyString(value["summary"]);
|
|
5756
6760
|
const findings = stringArray(value["findings"]);
|
|
5757
6761
|
const filesExamined = stringArray(value["files_examined"]);
|
|
5758
6762
|
const nextSteps = stringArray(value["suggested_next_steps"]);
|
|
5759
6763
|
const confidence = value["confidence"];
|
|
6764
|
+
const completion = value["completion"];
|
|
6765
|
+
const remainingWork = optionalString(value["remaining_work"]);
|
|
5760
6766
|
if (!summary || summary.length > MAX_SUMMARY_CHARS || !findings || findings.length > MAX_FINDINGS || !filesExamined || filesExamined.length > MAX_FILES || !nextSteps || nextSteps.length > MAX_NEXT_STEPS || [...findings, ...filesExamined, ...nextSteps].some(
|
|
5761
6767
|
(item) => item.trim().length > MAX_ITEM_CHARS
|
|
5762
|
-
) || typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1) {
|
|
6768
|
+
) || typeof confidence !== "number" || !Number.isFinite(confidence) || confidence < 0 || confidence > 1 || completion !== void 0 && completion !== "complete" && completion !== "partial" || completion === "partial" && !remainingWork || remainingWork !== void 0 && remainingWork.length > MAX_SUMMARY_CHARS) {
|
|
5763
6769
|
return void 0;
|
|
5764
6770
|
}
|
|
5765
6771
|
const report = {
|
|
@@ -5767,19 +6773,24 @@ function normalizeSubagentStructuredReport(value) {
|
|
|
5767
6773
|
findings: findings.map((item) => item.trim()).filter(Boolean),
|
|
5768
6774
|
files_examined: filesExamined.map((item) => item.trim()).filter(Boolean),
|
|
5769
6775
|
confidence,
|
|
5770
|
-
suggested_next_steps: nextSteps.map((item) => item.trim()).filter(Boolean)
|
|
6776
|
+
suggested_next_steps: nextSteps.map((item) => item.trim()).filter(Boolean),
|
|
6777
|
+
...completion === "complete" || completion === "partial" ? { completion } : {},
|
|
6778
|
+
...remainingWork ? { remaining_work: remainingWork } : {}
|
|
5771
6779
|
};
|
|
5772
6780
|
return JSON.stringify(report).length <= MAX_SUBAGENT_STRUCTURED_REPORT_CHARS ? report : void 0;
|
|
5773
6781
|
}
|
|
5774
6782
|
function readSubagentStructuredReport(ctx) {
|
|
5775
6783
|
return normalizeSubagentStructuredReport(ctx?.meta?.[SUBAGENT_STRUCTURED_REPORT_META_KEY]);
|
|
5776
6784
|
}
|
|
5777
|
-
function
|
|
6785
|
+
function isRecord4(value) {
|
|
5778
6786
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5779
6787
|
}
|
|
5780
6788
|
function nonEmptyString(value) {
|
|
5781
6789
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
5782
6790
|
}
|
|
6791
|
+
function optionalString(value) {
|
|
6792
|
+
return value === void 0 ? void 0 : nonEmptyString(value);
|
|
6793
|
+
}
|
|
5783
6794
|
function stringArray(value) {
|
|
5784
6795
|
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
5785
6796
|
}
|
|
@@ -7769,58 +8780,27 @@ function providerErrorToSubagentError(err, message, cause) {
|
|
|
7769
8780
|
}
|
|
7770
8781
|
|
|
7771
8782
|
// src/coordination/fleet.ts
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
var
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
prompt: agentPrompt("bug-hunter")
|
|
7788
|
-
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
7789
|
-
};
|
|
7790
|
-
var REFACTOR_PLANNER_AGENT = {
|
|
7791
|
-
id: "refactor-planner",
|
|
7792
|
-
name: "Refactor Planner",
|
|
7793
|
-
role: "refactor-planner",
|
|
7794
|
-
prompt: agentPrompt("refactor-planner")
|
|
7795
|
-
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
7796
|
-
};
|
|
7797
|
-
var SECURITY_SCANNER_AGENT = {
|
|
7798
|
-
id: "security-scanner",
|
|
7799
|
-
name: "Security Scanner",
|
|
7800
|
-
role: "security-scanner",
|
|
7801
|
-
prompt: agentPrompt("security-scanner")
|
|
7802
|
-
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
7803
|
-
};
|
|
7804
|
-
var SHADOW_AGENT = {
|
|
7805
|
-
id: "shadow-agent",
|
|
7806
|
-
name: "Shadow",
|
|
7807
|
-
role: "shadow-agent",
|
|
7808
|
-
prompt: agentPrompt("shadow-agent")
|
|
7809
|
-
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
7810
|
-
};
|
|
7811
|
-
var CRITIC_AGENT = {
|
|
7812
|
-
id: "critic",
|
|
7813
|
-
name: "Critic",
|
|
7814
|
-
role: "critic",
|
|
7815
|
-
prompt: agentPrompt("critic")
|
|
7816
|
-
// Budgets are set by the orchestrator per task — see fleet.ts header.
|
|
7817
|
-
};
|
|
8783
|
+
function defineAgent(id, name, promptRole = id, provider) {
|
|
8784
|
+
return {
|
|
8785
|
+
id,
|
|
8786
|
+
name,
|
|
8787
|
+
role: id,
|
|
8788
|
+
prompt: agentPrompt(promptRole),
|
|
8789
|
+
...provider === void 0 ? {} : { provider }
|
|
8790
|
+
};
|
|
8791
|
+
}
|
|
8792
|
+
var AUDIT_LOG_AGENT = defineAgent("audit-log", "Audit Log");
|
|
8793
|
+
var BUG_HUNTER_AGENT = defineAgent("bug-hunter", "Bug Hunter");
|
|
8794
|
+
var REFACTOR_PLANNER_AGENT = defineAgent("refactor-planner", "Refactor Planner");
|
|
8795
|
+
var SECURITY_SCANNER_AGENT = defineAgent("security-scanner", "Security Scanner");
|
|
8796
|
+
var SHADOW_AGENT = defineAgent("shadow-agent", "Shadow");
|
|
8797
|
+
var CRITIC_AGENT = defineAgent("critic", "Critic");
|
|
7818
8798
|
var FLEET_ROSTER = {
|
|
7819
8799
|
"audit-log": AUDIT_LOG_AGENT,
|
|
7820
8800
|
"bug-hunter": BUG_HUNTER_AGENT,
|
|
7821
8801
|
"refactor-planner": REFACTOR_PLANNER_AGENT,
|
|
7822
8802
|
"security-scanner": SECURITY_SCANNER_AGENT,
|
|
7823
|
-
|
|
8803
|
+
critic: CRITIC_AGENT,
|
|
7824
8804
|
"shadow-agent": SHADOW_AGENT,
|
|
7825
8805
|
...Object.fromEntries(
|
|
7826
8806
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.config])
|
|
@@ -7832,7 +8812,7 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
7832
8812
|
"bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
7833
8813
|
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
7834
8814
|
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
7835
|
-
|
|
8815
|
+
critic: { timeoutMs: 5 * 60 * 60 * 1e3, maxIterations: 4e3, maxToolCalls: 12e3 },
|
|
7836
8816
|
"shadow-agent": {
|
|
7837
8817
|
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
7838
8818
|
maxIterations: 2e3,
|
|
@@ -7869,41 +8849,11 @@ function applyRosterBudget(cfg) {
|
|
|
7869
8849
|
};
|
|
7870
8850
|
}
|
|
7871
8851
|
var ALL_FLEET_AGENTS = Object.values(FLEET_ROSTER);
|
|
7872
|
-
var CLINE_AGENT =
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
provider: "acp"
|
|
7878
|
-
};
|
|
7879
|
-
var GEMINI_CLI_AGENT = {
|
|
7880
|
-
id: "gemini-cli",
|
|
7881
|
-
name: "Gemini CLI",
|
|
7882
|
-
role: "gemini-cli",
|
|
7883
|
-
prompt: agentPrompt("acp-gemini-cli"),
|
|
7884
|
-
provider: "acp"
|
|
7885
|
-
};
|
|
7886
|
-
var COPILOT_AGENT = {
|
|
7887
|
-
id: "copilot",
|
|
7888
|
-
name: "GitHub Copilot",
|
|
7889
|
-
role: "copilot",
|
|
7890
|
-
prompt: agentPrompt("acp-copilot"),
|
|
7891
|
-
provider: "acp"
|
|
7892
|
-
};
|
|
7893
|
-
var OPENHANDS_AGENT = {
|
|
7894
|
-
id: "openhands",
|
|
7895
|
-
name: "OpenHands",
|
|
7896
|
-
role: "openhands",
|
|
7897
|
-
prompt: agentPrompt("acp-openhands"),
|
|
7898
|
-
provider: "acp"
|
|
7899
|
-
};
|
|
7900
|
-
var GOOSE_AGENT = {
|
|
7901
|
-
id: "goose",
|
|
7902
|
-
name: "Goose",
|
|
7903
|
-
role: "goose",
|
|
7904
|
-
prompt: agentPrompt("acp-goose"),
|
|
7905
|
-
provider: "acp"
|
|
7906
|
-
};
|
|
8852
|
+
var CLINE_AGENT = defineAgent("cline", "Cline", "acp-cline", "acp");
|
|
8853
|
+
var GEMINI_CLI_AGENT = defineAgent("gemini-cli", "Gemini CLI", "acp-gemini-cli", "acp");
|
|
8854
|
+
var COPILOT_AGENT = defineAgent("copilot", "GitHub Copilot", "acp-copilot", "acp");
|
|
8855
|
+
var OPENHANDS_AGENT = defineAgent("openhands", "OpenHands", "acp-openhands", "acp");
|
|
8856
|
+
var GOOSE_AGENT = defineAgent("goose", "Goose", "acp-goose", "acp");
|
|
7907
8857
|
var ACP_AGENTS = [
|
|
7908
8858
|
CLINE_AGENT,
|
|
7909
8859
|
GEMINI_CLI_AGENT,
|
|
@@ -7911,11 +8861,14 @@ var ACP_AGENTS = [
|
|
|
7911
8861
|
OPENHANDS_AGENT,
|
|
7912
8862
|
GOOSE_AGENT
|
|
7913
8863
|
];
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
8864
|
+
var ACP_AGENT_BUDGET = {
|
|
8865
|
+
timeoutMs: 10 * 60 * 60 * 1e3,
|
|
8866
|
+
maxIterations: 8e3,
|
|
8867
|
+
maxToolCalls: 2e4
|
|
8868
|
+
};
|
|
8869
|
+
for (const agent of ACP_AGENTS) {
|
|
8870
|
+
FLEET_ROSTER_BUDGETS[agent.role] = { ...ACP_AGENT_BUDGET };
|
|
8871
|
+
}
|
|
7919
8872
|
var FLEET_ROSTER_WITHACP = {
|
|
7920
8873
|
...FLEET_ROSTER,
|
|
7921
8874
|
...Object.fromEntries(ACP_AGENTS.map((a) => [a.role, a]))
|
|
@@ -8909,6 +9862,27 @@ var ParallelEternalEngine = class {
|
|
|
8909
9862
|
agentFactory;
|
|
8910
9863
|
dispatchEnabled;
|
|
8911
9864
|
dispatchClassifier;
|
|
9865
|
+
/**
|
|
9866
|
+
* Emit a structured error. Uses the configured Logger when available;
|
|
9867
|
+
* falls back to console.error(JSON) so events are never silently dropped.
|
|
9868
|
+
*/
|
|
9869
|
+
logError(msg, ctx) {
|
|
9870
|
+
if (this.opts.logger) {
|
|
9871
|
+
this.opts.logger.error(msg, ctx);
|
|
9872
|
+
} else {
|
|
9873
|
+
console.error(JSON.stringify({ ...ctx, message: msg, timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
9874
|
+
}
|
|
9875
|
+
}
|
|
9876
|
+
/**
|
|
9877
|
+
* Load the goal file with structured logging for parse/schema warnings.
|
|
9878
|
+
*/
|
|
9879
|
+
loadGoal() {
|
|
9880
|
+
return loadGoal(
|
|
9881
|
+
this.goalPath,
|
|
9882
|
+
this.opts.events,
|
|
9883
|
+
this.opts.logger ? (msg) => this.opts.logger.warn(msg) : void 0
|
|
9884
|
+
);
|
|
9885
|
+
}
|
|
8912
9886
|
get currentState() {
|
|
8913
9887
|
return this.state;
|
|
8914
9888
|
}
|
|
@@ -8921,13 +9895,10 @@ var ParallelEternalEngine = class {
|
|
|
8921
9895
|
stop() {
|
|
8922
9896
|
this.stopRequested = true;
|
|
8923
9897
|
void this.persistState("stopped").catch((err) => {
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
event: "engine.persist_state_failed",
|
|
8927
|
-
|
|
8928
|
-
context: { expectedState: "stopped" },
|
|
8929
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
8930
|
-
}));
|
|
9898
|
+
this.logError(
|
|
9899
|
+
"Engine persist state failed",
|
|
9900
|
+
{ event: "engine.persist_state_failed", message: toErrorMessage(err), context: { expectedState: "stopped" } }
|
|
9901
|
+
);
|
|
8931
9902
|
});
|
|
8932
9903
|
this.state = "stopped";
|
|
8933
9904
|
}
|
|
@@ -8981,7 +9952,7 @@ var ParallelEternalEngine = class {
|
|
|
8981
9952
|
this.opts.onStage?.(stage);
|
|
8982
9953
|
};
|
|
8983
9954
|
this.iterations++;
|
|
8984
|
-
const goal = await loadGoal(
|
|
9955
|
+
const goal = await this.loadGoal();
|
|
8985
9956
|
if (!goal) {
|
|
8986
9957
|
this.stopRequested = true;
|
|
8987
9958
|
emit({ phase: "stopped" });
|
|
@@ -9125,13 +10096,10 @@ ${personaLine}Task: ${task}
|
|
|
9125
10096
|
taskIds.push(taskId);
|
|
9126
10097
|
await coordinator.assign(spec);
|
|
9127
10098
|
} catch (err) {
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
event: "parallel_engine.spawn_failed",
|
|
9131
|
-
|
|
9132
|
-
context: { slot: i, task, subagentId },
|
|
9133
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9134
|
-
}));
|
|
10099
|
+
this.logError(
|
|
10100
|
+
"Parallel spawn failed",
|
|
10101
|
+
{ event: "parallel_engine.spawn_failed", message: toErrorMessage(err), context: { slot: i, task, subagentId } }
|
|
10102
|
+
);
|
|
9135
10103
|
}
|
|
9136
10104
|
})()
|
|
9137
10105
|
);
|
|
@@ -9157,13 +10125,10 @@ ${personaLine}Task: ${task}
|
|
|
9157
10125
|
clearTimeout(timer);
|
|
9158
10126
|
}
|
|
9159
10127
|
} catch (err) {
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
event: "parallel_engine.brainstorm_results_failed",
|
|
9163
|
-
|
|
9164
|
-
context: { slotCount, taskIds },
|
|
9165
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9166
|
-
}));
|
|
10128
|
+
this.logError(
|
|
10129
|
+
"Brainstorm results failed",
|
|
10130
|
+
{ event: "parallel_engine.brainstorm_results_failed", message: toErrorMessage(err), context: { slotCount, taskIds } }
|
|
10131
|
+
);
|
|
9167
10132
|
results = coordinator.results().slice(-taskIds.length);
|
|
9168
10133
|
}
|
|
9169
10134
|
await Promise.allSettled(subagentIds.map((id) => coordinator.remove(id)));
|
|
@@ -9198,13 +10163,10 @@ ${personaLine}Task: ${task}
|
|
|
9198
10163
|
}
|
|
9199
10164
|
}
|
|
9200
10165
|
} catch (err) {
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
event: "parallel_engine.git_status_failed",
|
|
9204
|
-
|
|
9205
|
-
context: { projectRoot: this.opts.projectRoot },
|
|
9206
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
9207
|
-
}));
|
|
10166
|
+
this.logError(
|
|
10167
|
+
"Git status failed",
|
|
10168
|
+
{ event: "parallel_engine.git_status_failed", message: toErrorMessage(err), context: { projectRoot: this.opts.projectRoot } }
|
|
10169
|
+
);
|
|
9208
10170
|
}
|
|
9209
10171
|
}
|
|
9210
10172
|
if (tasks.length < this.slots) {
|
|
@@ -9285,7 +10247,7 @@ ${lastFew}` : "No prior iterations.",
|
|
|
9285
10247
|
// Helpers
|
|
9286
10248
|
// -------------------------------------------------------------------------
|
|
9287
10249
|
async appendIterationEntry(entry) {
|
|
9288
|
-
const current = await loadGoal(
|
|
10250
|
+
const current = await this.loadGoal();
|
|
9289
10251
|
if (!current) return;
|
|
9290
10252
|
const updated = appendJournal(current, entry);
|
|
9291
10253
|
await saveGoal(this.goalPath, updated, this.opts.events);
|
|
@@ -9300,7 +10262,7 @@ ${lastFew}` : "No prior iterations.",
|
|
|
9300
10262
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
9301
10263
|
}
|
|
9302
10264
|
async persistState(state) {
|
|
9303
|
-
const current = await loadGoal(
|
|
10265
|
+
const current = await this.loadGoal();
|
|
9304
10266
|
if (!current) return;
|
|
9305
10267
|
if (current.engineState === state) return;
|
|
9306
10268
|
await saveGoal(this.goalPath, { ...current, engineState: state }, this.opts.events);
|
|
@@ -11121,12 +12083,12 @@ ${post.additionalContext}`;
|
|
|
11121
12083
|
if (parentSignal.reason instanceof Error) throw parentSignal.reason;
|
|
11122
12084
|
throw new Error(typeof parentSignal.reason === "string" ? parentSignal.reason : "aborted");
|
|
11123
12085
|
}
|
|
11124
|
-
const
|
|
11125
|
-
|
|
11126
|
-
|
|
11127
|
-
|
|
11128
|
-
|
|
11129
|
-
|
|
12086
|
+
const combined = tool.managesOwnTimeout ? parentSignal : AbortSignal.any([
|
|
12087
|
+
parentSignal,
|
|
12088
|
+
AbortSignal.timeout(
|
|
12089
|
+
clampTimeoutMs(tool.timeoutMs ?? this.iterationTimeoutMs, this.maxToolTimeoutMs)
|
|
12090
|
+
)
|
|
12091
|
+
]);
|
|
11130
12092
|
let output;
|
|
11131
12093
|
const toolPromise = typeof tool.executeStream === "function" ? this.runStreamedTool(tool, input, ctx, combined, toolUseId) : (async () => tool.execute(input, ctx, { signal: combined }))();
|
|
11132
12094
|
toolPromise.catch(() => {
|
|
@@ -11472,6 +12434,8 @@ function sliceUtf8Suffix(text, maxBytes) {
|
|
|
11472
12434
|
export {
|
|
11473
12435
|
AutoCompactionMiddleware,
|
|
11474
12436
|
AutonomousRunner,
|
|
12437
|
+
BRAIN_EVALUATION_CASE_VERSION,
|
|
12438
|
+
COUNCIL_REFUSE_OPTION_ID,
|
|
11475
12439
|
DEFAULT_RECOVERY_STRATEGIES,
|
|
11476
12440
|
DefaultDesignKitLoader,
|
|
11477
12441
|
DefaultErrorHandler,
|
|
@@ -11489,11 +12453,16 @@ export {
|
|
|
11489
12453
|
_resetDesignRulesCache,
|
|
11490
12454
|
activateDesign,
|
|
11491
12455
|
applyModelRuntime,
|
|
12456
|
+
assembleBrainTiers,
|
|
12457
|
+
buildBrainUserMessage,
|
|
11492
12458
|
buildGoalPreamble,
|
|
11493
12459
|
buildRecoveryStrategies,
|
|
11494
12460
|
clearActiveKit,
|
|
11495
12461
|
clearPersistedActiveKit,
|
|
12462
|
+
completeBrainLlm,
|
|
11496
12463
|
createAutonomyBrain,
|
|
12464
|
+
createBrainRuntime,
|
|
12465
|
+
createCouncilBrainArbiter,
|
|
11497
12466
|
createStrategyCompactor,
|
|
11498
12467
|
createTieredBrainArbiter,
|
|
11499
12468
|
designProjectDir,
|
|
@@ -11510,12 +12479,16 @@ export {
|
|
|
11510
12479
|
makeDesignDetectUserInputMiddleware,
|
|
11511
12480
|
makeDesignStudioRequestMiddleware,
|
|
11512
12481
|
mergeModelRuntime,
|
|
12482
|
+
parseOptionDecision,
|
|
11513
12483
|
recordKitChoice,
|
|
11514
12484
|
renderPrompt,
|
|
12485
|
+
resolveBrainConfigDefaults,
|
|
11515
12486
|
resolveBundledDesignKitsDir,
|
|
11516
12487
|
resolveCacheForRequest,
|
|
11517
12488
|
resolveModelRuntime,
|
|
11518
12489
|
resolveReasoningForRequest,
|
|
11519
|
-
|
|
12490
|
+
runBrainEvaluation,
|
|
12491
|
+
setActiveKit,
|
|
12492
|
+
validateBrainEvaluationCase
|
|
11520
12493
|
};
|
|
11521
12494
|
//# sourceMappingURL=index.js.map
|