@wrongstack/core 0.298.3 → 0.300.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/chronicle/index.js +4 -1
- package/dist/coordination/agents/index.js +4 -1
- package/dist/coordination/director.d.ts +8 -0
- package/dist/coordination/fleet-manager.d.ts +48 -3
- package/dist/coordination/ifleet-manager.d.ts +2 -0
- package/dist/coordination/index.js +127 -24
- package/dist/coordination/multi-agent-coordinator.d.ts +1 -0
- package/dist/core/fallback-model.d.ts +48 -0
- package/dist/core/index.d.ts +3 -2
- package/dist/core/index.js +288 -34
- package/dist/core/instruction-template.d.ts +80 -0
- package/dist/core/system-prompt-blocks.d.ts +10 -1
- package/dist/core/system-prompt-builder.d.ts +35 -1
- package/dist/defaults/index.js +358 -117
- package/dist/design/index.js +4 -1
- package/dist/execution/autonomy-brain.d.ts +7 -0
- package/dist/execution/council-brain.d.ts +17 -2
- package/dist/execution/council-orchestrator.d.ts +23 -4
- package/dist/execution/council-personas.d.ts +10 -0
- package/dist/execution/council-prompts.d.ts +12 -1
- package/dist/execution/index.d.ts +1 -1
- package/dist/execution/index.js +412 -145
- package/dist/fleet-notifier.d.ts +9 -2
- package/dist/goal/index.js +4 -1
- package/dist/hooks/index.js +140 -10
- package/dist/hq/exposure.d.ts +0 -11
- package/dist/hq/index.js +34 -8
- package/dist/hq/protocol/client.d.ts +14 -1
- package/dist/hq/protocol/fleet.d.ts +22 -0
- package/dist/hq/protocol.js +12 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1718 -753
- package/dist/infrastructure/index.js +50 -2
- package/dist/infrastructure/mcp-servers.d.ts +35 -0
- package/dist/kernel/events/brain-events.d.ts +9 -0
- package/dist/kernel/events/provider-events.d.ts +49 -2
- package/dist/kernel/events/sdd-events.d.ts +2 -0
- package/dist/models/index.js +1 -1
- package/dist/plugin/api.d.ts +6 -0
- package/dist/plugin/config.d.ts +55 -0
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.js +138 -22
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +157 -42
- package/dist/security/permission-helpers.d.ts +23 -6
- package/dist/security/permission-policy.d.ts +16 -0
- package/dist/security/totp.d.ts +14 -0
- package/dist/storage/director-state.d.ts +7 -0
- package/dist/storage/index.js +46 -9
- package/dist/tools/council-tool.d.ts +1 -1
- package/dist/tools/fallback-system-config-view-tool.d.ts +1 -1
- package/dist/tools/index.js +449 -112
- package/dist/types/config/skills-fleet-brain.d.ts +4 -2
- package/dist/types/config/tools.d.ts +99 -0
- package/dist/types/council.d.ts +11 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.js +3 -3
- package/dist/types/multi-agent.d.ts +10 -0
- package/dist/types/one-shot-llm.d.ts +31 -3
- package/dist/types/plugin.d.ts +28 -0
- package/dist/types/session.d.ts +5 -1
- package/dist/utils/index.js +4 -1
- package/dist/utils/wstack-paths.d.ts +2 -0
- package/dist/worktree/index.js +47 -25
- package/dist/worktree/worktree-manager.d.ts +16 -10
- package/instructions/coordination/subagent-baseline.md +8 -0
- package/instructions/system-lite.md +83 -3
- package/instructions/system-pro.md +286 -97
- package/instructions/system.md +236 -85
- package/package.json +3 -3
package/dist/execution/index.js
CHANGED
|
@@ -3979,7 +3979,11 @@ async function completeBrainLlm(target, input) {
|
|
|
3979
3979
|
return (await completeBrainLlmDetailed(target, input)).text;
|
|
3980
3980
|
}
|
|
3981
3981
|
async function completeBrainLlmDetailed(target, input) {
|
|
3982
|
-
|
|
3982
|
+
if (input.signal?.aborted) {
|
|
3983
|
+
throw new DOMException("Brain call aborted before it started.", "AbortError");
|
|
3984
|
+
}
|
|
3985
|
+
const timeoutSignal = AbortSignal.timeout(input.timeoutMs);
|
|
3986
|
+
const signal = input.signal ? AbortSignal.any([input.signal, timeoutSignal]) : timeoutSignal;
|
|
3983
3987
|
const response = await target.provider.complete(
|
|
3984
3988
|
{
|
|
3985
3989
|
model: target.model,
|
|
@@ -4193,7 +4197,7 @@ function truncate(s, max) {
|
|
|
4193
4197
|
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
|
|
4194
4198
|
|
|
4195
4199
|
// src/types/provider.ts
|
|
4196
|
-
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|prompt
|
|
4200
|
+
var CONTEXT_OVERFLOW_RE = /context.length|context.window|maximum context|max.*tokens?.*exceeded|(?:prompt|request|input|messages?).{0,12}too (?:large|long)|exceeds the context|\btokens\b.*exceed|too many tokens|reduce the length|resulted in \d+ tokens|context_length_exceeded/i;
|
|
4197
4201
|
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
4198
4202
|
var RATE_LIMIT_EXCEEDED_RE = /rate[-_\s]*limit[-_\s]*exceeded/i;
|
|
4199
4203
|
function classifyProviderError(status, body, message) {
|
|
@@ -4203,7 +4207,7 @@ function classifyProviderError(status, body, message) {
|
|
|
4203
4207
|
if (status === 408) return "timeout";
|
|
4204
4208
|
if (status === 599) return "stream_hang";
|
|
4205
4209
|
if (status === 402 || QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
4206
|
-
if (status === 429 && body?.message && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
4210
|
+
if (status === 429 && body?.message && body.type !== "rate_limit_exceeded" && RATE_LIMIT_EXCEEDED_RE.test(body.message)) {
|
|
4207
4211
|
return "quota_exhausted";
|
|
4208
4212
|
}
|
|
4209
4213
|
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
@@ -4280,7 +4284,7 @@ var ProviderError = class extends WrongStackError {
|
|
|
4280
4284
|
const e = err;
|
|
4281
4285
|
const name = e.name;
|
|
4282
4286
|
if (typeof name !== "string" || !name.endsWith("Error")) return false;
|
|
4283
|
-
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string";
|
|
4287
|
+
return typeof e.status === "number" && typeof e.retryable === "boolean" && typeof e.kind === "string" && typeof e.describe === "function";
|
|
4284
4288
|
}
|
|
4285
4289
|
constructor(message, status, retryable, providerId, opts = {}) {
|
|
4286
4290
|
const kind = opts.kind ?? classifyProviderError(status, opts.body, message);
|
|
@@ -4785,6 +4789,9 @@ var BUILTIN_COUNCIL_PERSONAS = Object.freeze([
|
|
|
4785
4789
|
tags: ["users", "usability", "accessibility"]
|
|
4786
4790
|
})
|
|
4787
4791
|
]);
|
|
4792
|
+
var BUILTIN_COUNCIL_PERSONA_IDS = Object.freeze(
|
|
4793
|
+
BUILTIN_COUNCIL_PERSONAS.map((persona) => persona.id)
|
|
4794
|
+
);
|
|
4788
4795
|
var CouncilPersonaRegistry = class _CouncilPersonaRegistry {
|
|
4789
4796
|
byId;
|
|
4790
4797
|
constructor(personas = []) {
|
|
@@ -5171,22 +5178,29 @@ function buildCouncilJudgeUserPrompt(question, votes, opts = {}) {
|
|
|
5171
5178
|
"</council-ballots>"
|
|
5172
5179
|
].filter(Boolean).join("\n\n");
|
|
5173
5180
|
}
|
|
5174
|
-
function
|
|
5175
|
-
|
|
5181
|
+
function validateCouncilOptions(options) {
|
|
5182
|
+
const errors = [];
|
|
5176
5183
|
const seen = /* @__PURE__ */ new Set();
|
|
5177
|
-
|
|
5184
|
+
for (const option of options ?? []) {
|
|
5178
5185
|
const id = option.id.trim();
|
|
5179
|
-
|
|
5180
|
-
if (!
|
|
5181
|
-
if (
|
|
5182
|
-
if (seen.has(id)) throw new Error(`buildCouncilQuestionPrompt: duplicate option id "${id}".`);
|
|
5186
|
+
if (!id) errors.push("Every option must have a non-empty `id`.");
|
|
5187
|
+
if (!option.label.trim()) errors.push(`Option "${id || "<empty>"}" must have a label.`);
|
|
5188
|
+
if (seen.has(id)) errors.push(`Duplicate option id "${id}".`);
|
|
5183
5189
|
seen.add(id);
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
+
}
|
|
5191
|
+
return errors;
|
|
5192
|
+
}
|
|
5193
|
+
function normalizeOptions(options) {
|
|
5194
|
+
if (!options) return [];
|
|
5195
|
+
const errors = validateCouncilOptions(options);
|
|
5196
|
+
if (errors.length > 0) {
|
|
5197
|
+
throw new Error(`buildCouncilQuestionPrompt: ${errors.join("; ")}`);
|
|
5198
|
+
}
|
|
5199
|
+
return options.map((option) => ({
|
|
5200
|
+
id: option.id.trim(),
|
|
5201
|
+
label: option.label.trim(),
|
|
5202
|
+
...option.consequence?.trim() ? { consequence: option.consequence.trim() } : {}
|
|
5203
|
+
}));
|
|
5190
5204
|
}
|
|
5191
5205
|
function requiredInstruction(path17) {
|
|
5192
5206
|
const text = readBundledInstructionText(path17);
|
|
@@ -5294,6 +5308,8 @@ function requireFraction(value, label) {
|
|
|
5294
5308
|
var COUNCIL_REFUSAL_OPTION_ID = "council_refuse";
|
|
5295
5309
|
var DEFAULT_COUNCIL_MAX_CONCURRENCY = 3;
|
|
5296
5310
|
var MAX_COUNCIL_CONCURRENCY = 8;
|
|
5311
|
+
var OVERALL_TIMEOUT_REASON = "Council overall timeout exceeded.";
|
|
5312
|
+
var CALL_CANCELLED_REASON = "Cancelled.";
|
|
5297
5313
|
var CouncilOrchestrator = class {
|
|
5298
5314
|
caller;
|
|
5299
5315
|
personas;
|
|
@@ -5304,6 +5320,17 @@ var CouncilOrchestrator = class {
|
|
|
5304
5320
|
fallbackProfileManager;
|
|
5305
5321
|
seatCaller;
|
|
5306
5322
|
judgeCaller;
|
|
5323
|
+
/**
|
|
5324
|
+
* Normalized ad-hoc profiles keyed by the caller's config object identity.
|
|
5325
|
+
* The Brain adapter reuses ONE profile object for every decision, so this
|
|
5326
|
+
* avoids re-validating + re-freezing it on every ask() without caching
|
|
5327
|
+
* string-keyed registry lookups (those are already O(1)).
|
|
5328
|
+
*
|
|
5329
|
+
* Hosts must treat ad-hoc profile configs as IMMUTABLE once passed to
|
|
5330
|
+
* ask(): the cache is keyed by object identity and never invalidated, so
|
|
5331
|
+
* mutating a cached profile would silently serve the first snapshot.
|
|
5332
|
+
*/
|
|
5333
|
+
profileCache = /* @__PURE__ */ new WeakMap();
|
|
5307
5334
|
constructor(opts) {
|
|
5308
5335
|
if (!opts.caller && !opts.seatCaller && !opts.judgeCaller) {
|
|
5309
5336
|
throw new Error(
|
|
@@ -5322,13 +5349,33 @@ var CouncilOrchestrator = class {
|
|
|
5322
5349
|
this.seatCaller = opts.seatCaller;
|
|
5323
5350
|
this.judgeCaller = opts.judgeCaller;
|
|
5324
5351
|
}
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5352
|
+
/**
|
|
5353
|
+
* Resolve the effective profile for a question. String ids and the default
|
|
5354
|
+
* go through the registry (already O(1)); ad-hoc config objects are
|
|
5355
|
+
* normalized once per stable object identity and cached, because hosts such
|
|
5356
|
+
* as the Brain adapter pass the same profile object on every ask().
|
|
5357
|
+
*/
|
|
5358
|
+
resolveProfile(profile) {
|
|
5359
|
+
if (typeof profile === "string" || profile === void 0) {
|
|
5360
|
+
return resolveCouncilProfile(profile, {
|
|
5361
|
+
registry: this.profiles,
|
|
5362
|
+
personas: this.personas,
|
|
5363
|
+
defaultProfile: this.defaultProfile
|
|
5364
|
+
});
|
|
5365
|
+
}
|
|
5366
|
+
const cached = this.profileCache.get(profile);
|
|
5367
|
+
if (cached) return cached;
|
|
5368
|
+
const resolved = resolveCouncilProfile(profile, {
|
|
5328
5369
|
registry: this.profiles,
|
|
5329
5370
|
personas: this.personas,
|
|
5330
5371
|
defaultProfile: this.defaultProfile
|
|
5331
5372
|
});
|
|
5373
|
+
this.profileCache.set(profile, resolved);
|
|
5374
|
+
return resolved;
|
|
5375
|
+
}
|
|
5376
|
+
async ask(question) {
|
|
5377
|
+
const startedAt = Date.now();
|
|
5378
|
+
const profile = this.resolveProfile(question.profile);
|
|
5332
5379
|
validateRefusalCollision(question, this.refusalOptionId);
|
|
5333
5380
|
const timeoutSignal = AbortSignal.timeout(profile.overallTimeoutMs);
|
|
5334
5381
|
const signal = question.signal ? AbortSignal.any([question.signal, timeoutSignal]) : timeoutSignal;
|
|
@@ -5345,11 +5392,16 @@ var CouncilOrchestrator = class {
|
|
|
5345
5392
|
try {
|
|
5346
5393
|
return await this.callSeat(question, profile, seat, i, signal, usage);
|
|
5347
5394
|
} catch (error) {
|
|
5395
|
+
const timedOut = signal.aborted && !question.signal?.aborted;
|
|
5348
5396
|
return {
|
|
5349
5397
|
seatId: seat.id,
|
|
5350
5398
|
persona: seat.persona,
|
|
5351
|
-
|
|
5352
|
-
|
|
5399
|
+
// Only the caller's own cancel is a "cancelled" vote; the overall
|
|
5400
|
+
// budget expiring is a failure (timeout), matching the envelope.
|
|
5401
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
5402
|
+
// Canonical text for aborted-by-budget or cancelled seats, so one
|
|
5403
|
+
// event does not surface a different string per code path.
|
|
5404
|
+
error: question.signal?.aborted ? CALL_CANCELLED_REASON : timedOut ? OVERALL_TIMEOUT_REASON : errorMessage(error)
|
|
5353
5405
|
};
|
|
5354
5406
|
}
|
|
5355
5407
|
}
|
|
@@ -5359,7 +5411,7 @@ var CouncilOrchestrator = class {
|
|
|
5359
5411
|
if (question.signal?.aborted) {
|
|
5360
5412
|
return resultEnvelope({
|
|
5361
5413
|
status: "cancelled",
|
|
5362
|
-
reason:
|
|
5414
|
+
reason: CALL_CANCELLED_REASON,
|
|
5363
5415
|
resolution: "none",
|
|
5364
5416
|
votes,
|
|
5365
5417
|
profile,
|
|
@@ -5372,14 +5424,17 @@ var CouncilOrchestrator = class {
|
|
|
5372
5424
|
if (timeoutSignal.aborted) {
|
|
5373
5425
|
return resultEnvelope({
|
|
5374
5426
|
status: "failed",
|
|
5375
|
-
reason:
|
|
5427
|
+
reason: OVERALL_TIMEOUT_REASON,
|
|
5376
5428
|
resolution: "none",
|
|
5377
5429
|
votes,
|
|
5378
5430
|
profile,
|
|
5379
5431
|
usage,
|
|
5380
5432
|
startedAt,
|
|
5381
5433
|
warnings,
|
|
5382
|
-
|
|
5434
|
+
// Seat-prefixed errors normally carry the canonical timeout text, but
|
|
5435
|
+
// a signal-blind caller can resolve valid votes even after the budget
|
|
5436
|
+
// expired — append the standalone entry only when nothing carries it.
|
|
5437
|
+
errors: errors.some((entry) => entry.includes(OVERALL_TIMEOUT_REASON)) ? errors : [...errors, OVERALL_TIMEOUT_REASON]
|
|
5383
5438
|
});
|
|
5384
5439
|
}
|
|
5385
5440
|
if (!question.options || question.options.length === 0) {
|
|
@@ -5406,7 +5461,17 @@ var CouncilOrchestrator = class {
|
|
|
5406
5461
|
);
|
|
5407
5462
|
}
|
|
5408
5463
|
async callSeat(question, profile, seat, seatIndex, signal, usage) {
|
|
5409
|
-
if (signal.aborted)
|
|
5464
|
+
if (signal.aborted) {
|
|
5465
|
+
return question.signal?.aborted ? cancelledVote(seat) : {
|
|
5466
|
+
seatId: seat.id,
|
|
5467
|
+
persona: seat.persona,
|
|
5468
|
+
status: "failed",
|
|
5469
|
+
...seat.target?.providerId ? { provider: seat.target.providerId } : {},
|
|
5470
|
+
...seat.target?.model ? { model: seat.target.model } : {},
|
|
5471
|
+
durationMs: 0,
|
|
5472
|
+
error: OVERALL_TIMEOUT_REASON
|
|
5473
|
+
};
|
|
5474
|
+
}
|
|
5410
5475
|
let persona;
|
|
5411
5476
|
try {
|
|
5412
5477
|
persona = this.personas.require(seat.persona);
|
|
@@ -5432,12 +5497,15 @@ var CouncilOrchestrator = class {
|
|
|
5432
5497
|
});
|
|
5433
5498
|
const metadata = callMetadata(result);
|
|
5434
5499
|
if (result.error) {
|
|
5500
|
+
const timedOut = signal.aborted && !question.signal?.aborted;
|
|
5435
5501
|
return {
|
|
5436
5502
|
seatId: seat.id,
|
|
5437
5503
|
persona: seat.persona,
|
|
5438
|
-
status: signal
|
|
5504
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
5439
5505
|
...metadata,
|
|
5440
|
-
|
|
5506
|
+
// Canonical text for cancelled or aborted-by-budget seats, so one
|
|
5507
|
+
// cancel/timeout event does not surface a raw provider string.
|
|
5508
|
+
error: question.signal?.aborted ? CALL_CANCELLED_REASON : timedOut ? OVERALL_TIMEOUT_REASON : result.error
|
|
5441
5509
|
};
|
|
5442
5510
|
}
|
|
5443
5511
|
const parsed = parseVote(result.text, question, this.refusalOptionId);
|
|
@@ -5536,9 +5604,27 @@ var CouncilOrchestrator = class {
|
|
|
5536
5604
|
signal,
|
|
5537
5605
|
usage
|
|
5538
5606
|
);
|
|
5607
|
+
if (signal.aborted) {
|
|
5608
|
+
const cancelled = question.signal?.aborted === true;
|
|
5609
|
+
const reason = cancelled ? CALL_CANCELLED_REASON : OVERALL_TIMEOUT_REASON;
|
|
5610
|
+
return resultEnvelope({
|
|
5611
|
+
status: cancelled ? "cancelled" : "failed",
|
|
5612
|
+
reason,
|
|
5613
|
+
resolution: "none",
|
|
5614
|
+
votes,
|
|
5615
|
+
profile,
|
|
5616
|
+
usage,
|
|
5617
|
+
startedAt,
|
|
5618
|
+
warnings,
|
|
5619
|
+
errors: errors.some((entry) => entry.includes(reason)) ? errors : [...errors, reason],
|
|
5620
|
+
judgeUsed: true
|
|
5621
|
+
});
|
|
5622
|
+
}
|
|
5539
5623
|
if (!judged.ok) {
|
|
5540
5624
|
return resultEnvelope({
|
|
5541
|
-
|
|
5625
|
+
// User cancel -> cancelled; overall budget expired mid-judge -> failed;
|
|
5626
|
+
// otherwise the judge simply failed -> abstained (can't decide).
|
|
5627
|
+
status: question.signal?.aborted ? "cancelled" : signal.aborted ? "failed" : "abstained",
|
|
5542
5628
|
reason: judged.error,
|
|
5543
5629
|
resolution: "none",
|
|
5544
5630
|
votes,
|
|
@@ -5598,6 +5684,19 @@ var CouncilOrchestrator = class {
|
|
|
5598
5684
|
});
|
|
5599
5685
|
}
|
|
5600
5686
|
if (!profile.judge) {
|
|
5687
|
+
if (divergentStances(valid)) {
|
|
5688
|
+
return resultEnvelope({
|
|
5689
|
+
status: "abstained",
|
|
5690
|
+
reason: "Council produced multiple distinct stances and has no judge to reconcile them.",
|
|
5691
|
+
resolution: "none",
|
|
5692
|
+
votes,
|
|
5693
|
+
profile,
|
|
5694
|
+
usage,
|
|
5695
|
+
startedAt,
|
|
5696
|
+
warnings,
|
|
5697
|
+
errors
|
|
5698
|
+
});
|
|
5699
|
+
}
|
|
5601
5700
|
const first = valid[0];
|
|
5602
5701
|
if (!first) {
|
|
5603
5702
|
return resultEnvelope({
|
|
@@ -5634,9 +5733,30 @@ var CouncilOrchestrator = class {
|
|
|
5634
5733
|
signal,
|
|
5635
5734
|
usage
|
|
5636
5735
|
);
|
|
5736
|
+
if (signal.aborted) {
|
|
5737
|
+
const cancelled = question.signal?.aborted === true;
|
|
5738
|
+
const reason = cancelled ? CALL_CANCELLED_REASON : OVERALL_TIMEOUT_REASON;
|
|
5739
|
+
return resultEnvelope({
|
|
5740
|
+
status: cancelled ? "cancelled" : "failed",
|
|
5741
|
+
reason,
|
|
5742
|
+
resolution: "none",
|
|
5743
|
+
votes,
|
|
5744
|
+
profile,
|
|
5745
|
+
usage,
|
|
5746
|
+
startedAt,
|
|
5747
|
+
warnings,
|
|
5748
|
+
errors: errors.some((entry) => entry.includes(reason)) ? errors : [...errors, reason],
|
|
5749
|
+
judgeUsed: true
|
|
5750
|
+
});
|
|
5751
|
+
}
|
|
5637
5752
|
if (!judged.ok) {
|
|
5638
5753
|
return resultEnvelope({
|
|
5639
|
-
|
|
5754
|
+
// User cancel -> cancelled; overall budget expired mid-judge -> failed;
|
|
5755
|
+
// otherwise the judge simply failed -> failed (open questions cannot
|
|
5756
|
+
// abstain for a judge failure — 'abstained' is reserved for quorum
|
|
5757
|
+
// failure and stance divergence; the option path maps this same
|
|
5758
|
+
// plain-judge-failure case to 'abstained' instead).
|
|
5759
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
5640
5760
|
reason: judged.error,
|
|
5641
5761
|
resolution: "none",
|
|
5642
5762
|
votes,
|
|
@@ -5675,18 +5795,28 @@ var CouncilOrchestrator = class {
|
|
|
5675
5795
|
signal,
|
|
5676
5796
|
usage
|
|
5677
5797
|
});
|
|
5678
|
-
if (result.error)
|
|
5798
|
+
if (result.error) {
|
|
5799
|
+
if (signal.aborted && !question.signal?.aborted) {
|
|
5800
|
+
return { ok: false, error: OVERALL_TIMEOUT_REASON };
|
|
5801
|
+
}
|
|
5802
|
+
if (question.signal?.aborted) {
|
|
5803
|
+
return { ok: false, error: CALL_CANCELLED_REASON };
|
|
5804
|
+
}
|
|
5805
|
+
return { ok: false, error: result.error };
|
|
5806
|
+
}
|
|
5679
5807
|
return parseJudge(result.text, question, this.refusalOptionId);
|
|
5680
5808
|
}
|
|
5681
5809
|
/**
|
|
5682
|
-
* Resolve the effective LLM caller for a call. Voter seats
|
|
5683
|
-
* `seatCaller(seatIndex)` when wired
|
|
5684
|
-
*
|
|
5685
|
-
*
|
|
5810
|
+
* Resolve the effective LLM caller for a call. Voter seats (defined
|
|
5811
|
+
* seatIndex) use `seatCaller(seatIndex)` when wired, otherwise the shared
|
|
5812
|
+
* `caller` — a seat never falls through to the judge caller. Judge seats
|
|
5813
|
+
* (seatIndex undefined) use `judgeCaller` if set, otherwise `seatCaller(0)`
|
|
5814
|
+
* if set, otherwise the shared `caller`.
|
|
5686
5815
|
*/
|
|
5687
5816
|
resolveCaller(seatIndex) {
|
|
5688
|
-
if (seatIndex !== void 0
|
|
5689
|
-
return this.seatCaller(seatIndex);
|
|
5817
|
+
if (seatIndex !== void 0) {
|
|
5818
|
+
if (this.seatCaller) return this.seatCaller(seatIndex);
|
|
5819
|
+
return this.caller ?? this.judgeCaller;
|
|
5690
5820
|
}
|
|
5691
5821
|
if (this.judgeCaller) return this.judgeCaller;
|
|
5692
5822
|
if (this.seatCaller) return this.seatCaller(0);
|
|
@@ -5695,6 +5825,7 @@ var CouncilOrchestrator = class {
|
|
|
5695
5825
|
async safeCall(input) {
|
|
5696
5826
|
const effectiveCaller = this.resolveCaller(input.seatIndex);
|
|
5697
5827
|
const resolvedTarget = this.resolveCouncilTarget(input.target);
|
|
5828
|
+
const startedAt = Date.now();
|
|
5698
5829
|
try {
|
|
5699
5830
|
const result = await effectiveCaller.call({
|
|
5700
5831
|
system: input.system,
|
|
@@ -5711,8 +5842,17 @@ var CouncilOrchestrator = class {
|
|
|
5711
5842
|
addUsage(input.usage, result);
|
|
5712
5843
|
return result;
|
|
5713
5844
|
} catch (error) {
|
|
5714
|
-
|
|
5715
|
-
|
|
5845
|
+
const failed = {
|
|
5846
|
+
text: "",
|
|
5847
|
+
model: resolvedTarget?.model ?? "",
|
|
5848
|
+
provider: resolvedTarget?.providerId ?? "",
|
|
5849
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
5850
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
5851
|
+
fromFallback: false,
|
|
5852
|
+
error: errorMessage(error)
|
|
5853
|
+
};
|
|
5854
|
+
addUsage(input.usage, failed);
|
|
5855
|
+
return failed;
|
|
5716
5856
|
}
|
|
5717
5857
|
}
|
|
5718
5858
|
/**
|
|
@@ -5744,47 +5884,77 @@ var CouncilOrchestrator = class {
|
|
|
5744
5884
|
};
|
|
5745
5885
|
}
|
|
5746
5886
|
};
|
|
5747
|
-
function
|
|
5887
|
+
function parseCouncilResponse(text, question, refusalOptionId, opts) {
|
|
5888
|
+
const roleLabel = opts.role === "judge" ? "Judge" : "Voter";
|
|
5748
5889
|
const parsed = parseObject(text);
|
|
5749
5890
|
if (!parsed.ok && (!question.options || question.options.length === 0)) {
|
|
5750
5891
|
const fallback = text.trim();
|
|
5751
|
-
if (fallback) return { ok: true,
|
|
5752
|
-
return { ok: false, error:
|
|
5892
|
+
if (fallback) return { ok: true, value: { [opts.freeTextField]: fallback } };
|
|
5893
|
+
return { ok: false, error: `${roleLabel} returned an empty response.` };
|
|
5753
5894
|
}
|
|
5754
5895
|
if (!parsed.ok) return parsed;
|
|
5755
5896
|
const rationale = optionalString(parsed.value["rationale"]);
|
|
5756
5897
|
if (question.options && question.options.length > 0) {
|
|
5757
5898
|
const optionId = optionalString(parsed.value["optionId"]);
|
|
5758
|
-
const allowed = /* @__PURE__ */ new Set([
|
|
5899
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
5900
|
+
...question.options.map((option) => option.id.trim()),
|
|
5901
|
+
refusalOptionId
|
|
5902
|
+
]);
|
|
5759
5903
|
if (!optionId || !allowed.has(optionId)) {
|
|
5760
|
-
return { ok: false, error:
|
|
5904
|
+
return { ok: false, error: `${roleLabel} returned an unknown or missing optionId.` };
|
|
5761
5905
|
}
|
|
5762
|
-
return { ok: true,
|
|
5906
|
+
return { ok: true, value: { optionId, ...rationale ? { rationale } : {} } };
|
|
5763
5907
|
}
|
|
5764
|
-
const
|
|
5765
|
-
if (!
|
|
5766
|
-
|
|
5908
|
+
const freeText = optionalString(parsed.value[opts.freeTextField]);
|
|
5909
|
+
if (!freeText) {
|
|
5910
|
+
return {
|
|
5911
|
+
ok: false,
|
|
5912
|
+
error: `${roleLabel} returned an empty or missing ${opts.freeTextField}.`
|
|
5913
|
+
};
|
|
5914
|
+
}
|
|
5915
|
+
return { ok: true, value: { [opts.freeTextField]: freeText, ...rationale ? { rationale } : {} } };
|
|
5767
5916
|
}
|
|
5768
|
-
function
|
|
5769
|
-
const
|
|
5770
|
-
|
|
5771
|
-
const
|
|
5772
|
-
if (
|
|
5773
|
-
|
|
5917
|
+
function divergentStances(valid) {
|
|
5918
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5919
|
+
for (const vote of valid) {
|
|
5920
|
+
const normalized = vote.stance.trim().replace(/^["'`]+|["'`]+$/g, "").toLowerCase().replace(/[.!?;:,]+$/g, "").replace(/\s+/g, " ").trim();
|
|
5921
|
+
if (!normalized) return true;
|
|
5922
|
+
seen.add(normalized);
|
|
5923
|
+
if (seen.size > 1) return true;
|
|
5774
5924
|
}
|
|
5925
|
+
return false;
|
|
5926
|
+
}
|
|
5927
|
+
function parseVote(text, question, refusalOptionId) {
|
|
5928
|
+
const parsed = parseCouncilResponse(text, question, refusalOptionId, {
|
|
5929
|
+
role: "voter",
|
|
5930
|
+
freeTextField: "stance"
|
|
5931
|
+
});
|
|
5775
5932
|
if (!parsed.ok) return parsed;
|
|
5776
|
-
const rationale =
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5933
|
+
const { optionId, stance, rationale } = parsed.value;
|
|
5934
|
+
return {
|
|
5935
|
+
ok: true,
|
|
5936
|
+
vote: {
|
|
5937
|
+
...optionId ? { optionId } : {},
|
|
5938
|
+
...stance ? { stance } : {},
|
|
5939
|
+
...rationale ? { rationale } : {}
|
|
5782
5940
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
function parseJudge(text, question, refusalOptionId) {
|
|
5944
|
+
const parsed = parseCouncilResponse(text, question, refusalOptionId, {
|
|
5945
|
+
role: "judge",
|
|
5946
|
+
freeTextField: "answer"
|
|
5947
|
+
});
|
|
5948
|
+
if (!parsed.ok) return parsed;
|
|
5949
|
+
const { optionId, answer, rationale } = parsed.value;
|
|
5950
|
+
return {
|
|
5951
|
+
ok: true,
|
|
5952
|
+
value: {
|
|
5953
|
+
...optionId ? { optionId } : {},
|
|
5954
|
+
...answer ? { answer } : {},
|
|
5955
|
+
...rationale ? { rationale } : {}
|
|
5956
|
+
}
|
|
5957
|
+
};
|
|
5788
5958
|
}
|
|
5789
5959
|
function parseObject(text) {
|
|
5790
5960
|
const trimmed = text.trim();
|
|
@@ -5828,7 +5998,7 @@ function callMetadata(result) {
|
|
|
5828
5998
|
};
|
|
5829
5999
|
}
|
|
5830
6000
|
function addUsage(usage, result) {
|
|
5831
|
-
usage.calls += 1;
|
|
6001
|
+
usage.calls += Math.max(1, result.attempts ?? 1);
|
|
5832
6002
|
usage.inputTokens += result.tokens.input;
|
|
5833
6003
|
usage.outputTokens += result.tokens.output;
|
|
5834
6004
|
usage.totalTokens += result.tokens.total;
|
|
@@ -5837,21 +6007,40 @@ function usageResult(usage, startedAt) {
|
|
|
5837
6007
|
return Object.freeze({ ...usage, durationMs: Math.max(0, Date.now() - startedAt) });
|
|
5838
6008
|
}
|
|
5839
6009
|
function cancelledVote(seat) {
|
|
5840
|
-
return {
|
|
6010
|
+
return {
|
|
6011
|
+
seatId: seat.id,
|
|
6012
|
+
persona: seat.persona,
|
|
6013
|
+
status: "cancelled",
|
|
6014
|
+
...seat.target?.providerId ? { provider: seat.target.providerId } : {},
|
|
6015
|
+
...seat.target?.model ? { model: seat.target.model } : {},
|
|
6016
|
+
durationMs: 0,
|
|
6017
|
+
error: CALL_CANCELLED_REASON
|
|
6018
|
+
};
|
|
5841
6019
|
}
|
|
5842
6020
|
function distinctTargetCount(votes, profile) {
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
6021
|
+
return new Set(distinctTargetKeys(votes, profile)).size;
|
|
6022
|
+
}
|
|
6023
|
+
function distinctTargetKeys(votes, profile) {
|
|
6024
|
+
const keys = [];
|
|
6025
|
+
for (const vote of votes) {
|
|
6026
|
+
if (vote.status !== "valid") continue;
|
|
6027
|
+
const provider = vote.provider?.trim() ?? "";
|
|
6028
|
+
const model = vote.model?.trim() ?? "";
|
|
6029
|
+
if (profile.distinctness === "provider") {
|
|
6030
|
+
if (provider) keys.push(provider);
|
|
6031
|
+
} else if (provider || model) {
|
|
6032
|
+
keys.push(`${provider}/${model}`);
|
|
6033
|
+
}
|
|
6034
|
+
}
|
|
6035
|
+
return keys;
|
|
5847
6036
|
}
|
|
5848
6037
|
function distinctnessWarnings(votes, profile) {
|
|
5849
6038
|
if (profile.distinctness === "none") return [];
|
|
5850
|
-
const
|
|
5851
|
-
const distinct =
|
|
5852
|
-
if (
|
|
6039
|
+
const keys = distinctTargetKeys(votes, profile);
|
|
6040
|
+
const distinct = new Set(keys).size;
|
|
6041
|
+
if (keys.length > 1 && distinct < keys.length) {
|
|
5853
6042
|
return [
|
|
5854
|
-
`Council distinctness policy "${profile.distinctness}" was not met: ${distinct} distinct target(s) served ${
|
|
6043
|
+
`Council distinctness policy "${profile.distinctness}" was not met: ${distinct} distinct target(s) served ${keys.length} valid vote(s).`
|
|
5855
6044
|
];
|
|
5856
6045
|
}
|
|
5857
6046
|
return [];
|
|
@@ -5887,17 +6076,6 @@ async function mapConcurrent(items, concurrency, worker) {
|
|
|
5887
6076
|
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
|
|
5888
6077
|
return results;
|
|
5889
6078
|
}
|
|
5890
|
-
function emptyCallResult(error) {
|
|
5891
|
-
return {
|
|
5892
|
-
text: "",
|
|
5893
|
-
model: "",
|
|
5894
|
-
provider: "",
|
|
5895
|
-
tokens: { input: 0, output: 0, total: 0 },
|
|
5896
|
-
durationMs: 0,
|
|
5897
|
-
fromFallback: false,
|
|
5898
|
-
error
|
|
5899
|
-
};
|
|
5900
|
-
}
|
|
5901
6079
|
function optionalString(value) {
|
|
5902
6080
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
5903
6081
|
}
|
|
@@ -5906,52 +6084,96 @@ function errorMessage(error) {
|
|
|
5906
6084
|
}
|
|
5907
6085
|
|
|
5908
6086
|
// src/execution/council-brain.ts
|
|
6087
|
+
var MAX_CUSTOM_PERSONA_ID_CHARS = 48;
|
|
6088
|
+
function customPersonaId(raw, index) {
|
|
6089
|
+
const slug = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_CUSTOM_PERSONA_ID_CHARS).replace(/-+$/g, "");
|
|
6090
|
+
return slug ? `custom-${slug}` : `custom-${index + 1}`;
|
|
6091
|
+
}
|
|
6092
|
+
function resolvePersonaRegistry(voters) {
|
|
6093
|
+
let registry = DEFAULT_COUNCIL_PERSONA_REGISTRY;
|
|
6094
|
+
const personaIds = [];
|
|
6095
|
+
const byRawText = /* @__PURE__ */ new Map();
|
|
6096
|
+
voters.forEach((voter, index) => {
|
|
6097
|
+
const raw = voter.persona?.trim();
|
|
6098
|
+
if (!raw) {
|
|
6099
|
+
personaIds.push("executor");
|
|
6100
|
+
return;
|
|
6101
|
+
}
|
|
6102
|
+
if (registry.has(raw)) {
|
|
6103
|
+
personaIds.push(raw);
|
|
6104
|
+
return;
|
|
6105
|
+
}
|
|
6106
|
+
const existing = byRawText.get(raw);
|
|
6107
|
+
if (existing) {
|
|
6108
|
+
personaIds.push(existing);
|
|
6109
|
+
return;
|
|
6110
|
+
}
|
|
6111
|
+
let id = customPersonaId(raw, index);
|
|
6112
|
+
let suffix = 2;
|
|
6113
|
+
while (registry.has(id)) id = `${customPersonaId(raw, index)}-${suffix++}`;
|
|
6114
|
+
registry = registry.with({
|
|
6115
|
+
id,
|
|
6116
|
+
name: raw.length <= 40 ? raw : `${raw.slice(0, 39)}\u2026`,
|
|
6117
|
+
description: "Custom decision lens supplied by the host configuration.",
|
|
6118
|
+
instruction: raw
|
|
6119
|
+
});
|
|
6120
|
+
byRawText.set(raw, id);
|
|
6121
|
+
personaIds.push(id);
|
|
6122
|
+
});
|
|
6123
|
+
return { registry, personaIds };
|
|
6124
|
+
}
|
|
6125
|
+
function makeSeatCallerForVoter(target) {
|
|
6126
|
+
return {
|
|
6127
|
+
async call(input) {
|
|
6128
|
+
const startedAt = Date.now();
|
|
6129
|
+
try {
|
|
6130
|
+
const result = await completeBrainLlmDetailed(
|
|
6131
|
+
{ provider: target.provider, model: input.model ?? target.model },
|
|
6132
|
+
{
|
|
6133
|
+
system: typeof input.system === "string" ? input.system : Array.isArray(input.system) ? input.system.map((b) => b.text).join("\n") : "",
|
|
6134
|
+
user: input.userPrompt ?? "",
|
|
6135
|
+
timeoutMs: input.timeoutMs ?? 15e3,
|
|
6136
|
+
maxTokens: input.maxTokens,
|
|
6137
|
+
// Forward the orchestrator's signal (overall budget + caller
|
|
6138
|
+
// cancellation) so an in-flight seat call is interrupted when
|
|
6139
|
+
// the council times out or is cancelled instead of running to
|
|
6140
|
+
// its per-call timeout. Previously this was dropped and a
|
|
6141
|
+
// cancelled council waited for every seat to finish.
|
|
6142
|
+
signal: input.signal
|
|
6143
|
+
}
|
|
6144
|
+
);
|
|
6145
|
+
const inputTokens = result.usage?.input ?? 0;
|
|
6146
|
+
const outputTokens = result.usage?.output ?? 0;
|
|
6147
|
+
return {
|
|
6148
|
+
text: result.text,
|
|
6149
|
+
model: target.model,
|
|
6150
|
+
provider: target.provider.id,
|
|
6151
|
+
tokens: {
|
|
6152
|
+
input: inputTokens,
|
|
6153
|
+
output: outputTokens,
|
|
6154
|
+
total: inputTokens + outputTokens
|
|
6155
|
+
},
|
|
6156
|
+
durationMs: Date.now() - startedAt,
|
|
6157
|
+
fromFallback: false
|
|
6158
|
+
};
|
|
6159
|
+
} catch (error) {
|
|
6160
|
+
return {
|
|
6161
|
+
text: "",
|
|
6162
|
+
model: target.model,
|
|
6163
|
+
provider: target.provider.id,
|
|
6164
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
6165
|
+
durationMs: 0,
|
|
6166
|
+
fromFallback: false,
|
|
6167
|
+
error: error instanceof Error ? error.message : String(error)
|
|
6168
|
+
};
|
|
6169
|
+
}
|
|
6170
|
+
}
|
|
6171
|
+
};
|
|
6172
|
+
}
|
|
5909
6173
|
function createCouncilBrainArbiter(opts) {
|
|
5910
6174
|
if (opts.voters.length === 0) {
|
|
5911
6175
|
throw new Error("createCouncilBrainArbiter: at least one voter is required.");
|
|
5912
6176
|
}
|
|
5913
|
-
function makeSeatCallerForVoter(target) {
|
|
5914
|
-
return {
|
|
5915
|
-
async call(input) {
|
|
5916
|
-
const startedAt = Date.now();
|
|
5917
|
-
try {
|
|
5918
|
-
const result = await completeBrainLlmDetailed(
|
|
5919
|
-
{ provider: target.provider, model: input.model ?? target.model },
|
|
5920
|
-
{
|
|
5921
|
-
system: typeof input.system === "string" ? input.system : Array.isArray(input.system) ? input.system.map((b) => b.text).join("\n") : "",
|
|
5922
|
-
user: input.userPrompt ?? "",
|
|
5923
|
-
timeoutMs: input.timeoutMs ?? 15e3,
|
|
5924
|
-
maxTokens: input.maxTokens
|
|
5925
|
-
}
|
|
5926
|
-
);
|
|
5927
|
-
const inputTokens = result.usage?.input ?? 0;
|
|
5928
|
-
const outputTokens = result.usage?.output ?? 0;
|
|
5929
|
-
return {
|
|
5930
|
-
text: result.text,
|
|
5931
|
-
model: target.model,
|
|
5932
|
-
provider: target.provider.id,
|
|
5933
|
-
tokens: {
|
|
5934
|
-
input: inputTokens,
|
|
5935
|
-
output: outputTokens,
|
|
5936
|
-
total: inputTokens + outputTokens
|
|
5937
|
-
},
|
|
5938
|
-
durationMs: Date.now() - startedAt,
|
|
5939
|
-
fromFallback: false
|
|
5940
|
-
};
|
|
5941
|
-
} catch (error) {
|
|
5942
|
-
return {
|
|
5943
|
-
text: "",
|
|
5944
|
-
model: target.model,
|
|
5945
|
-
provider: target.provider.id,
|
|
5946
|
-
tokens: { input: 0, output: 0, total: 0 },
|
|
5947
|
-
durationMs: 0,
|
|
5948
|
-
fromFallback: false,
|
|
5949
|
-
error: error instanceof Error ? error.message : String(error)
|
|
5950
|
-
};
|
|
5951
|
-
}
|
|
5952
|
-
}
|
|
5953
|
-
};
|
|
5954
|
-
}
|
|
5955
6177
|
const seatCaller = (seatIndex) => {
|
|
5956
6178
|
const voter = opts.voters[seatIndex];
|
|
5957
6179
|
if (!voter) {
|
|
@@ -5971,14 +6193,15 @@ function createCouncilBrainArbiter(opts) {
|
|
|
5971
6193
|
}
|
|
5972
6194
|
return makeSeatCallerForVoter(voter);
|
|
5973
6195
|
};
|
|
6196
|
+
const { registry: personas, personaIds } = resolvePersonaRegistry(opts.voters);
|
|
5974
6197
|
const seats = opts.voters.map((voter, i) => ({
|
|
5975
6198
|
id: `voter-${i}`,
|
|
5976
6199
|
label: voter.label ?? voter.model,
|
|
5977
|
-
persona:
|
|
6200
|
+
persona: personaIds[i] ?? "executor",
|
|
5978
6201
|
target: {
|
|
5979
6202
|
providerId: voter.provider.id,
|
|
5980
6203
|
model: voter.model,
|
|
5981
|
-
role:
|
|
6204
|
+
role: personaIds[i] ?? "executor"
|
|
5982
6205
|
},
|
|
5983
6206
|
weight: voter.weight,
|
|
5984
6207
|
veto: voter.veto
|
|
@@ -5988,18 +6211,31 @@ function createCouncilBrainArbiter(opts) {
|
|
|
5988
6211
|
model: opts.judge.model,
|
|
5989
6212
|
role: "judge"
|
|
5990
6213
|
} : false;
|
|
6214
|
+
const perCallTimeoutMs = opts.decisionTimeoutMs ?? 15e3;
|
|
6215
|
+
const effectiveConcurrency = Math.max(
|
|
6216
|
+
1,
|
|
6217
|
+
Math.min(opts.maxConcurrency ?? DEFAULT_COUNCIL_MAX_CONCURRENCY, seats.length)
|
|
6218
|
+
);
|
|
6219
|
+
const overallTimeoutMs = Math.max(
|
|
6220
|
+
DEFAULT_COUNCIL_OVERALL_TIMEOUT_MS,
|
|
6221
|
+
perCallTimeoutMs * (Math.ceil(seats.length / effectiveConcurrency) + 1)
|
|
6222
|
+
);
|
|
5991
6223
|
const profile = {
|
|
5992
6224
|
id: "brain-council-adapter",
|
|
5993
6225
|
seats,
|
|
5994
6226
|
judge: judgeTarget,
|
|
5995
6227
|
quorumFraction: opts.quorumFraction ?? 0.5,
|
|
5996
6228
|
approvalFraction: opts.approvalFraction ?? 0.5,
|
|
5997
|
-
perCallTimeoutMs
|
|
6229
|
+
perCallTimeoutMs,
|
|
6230
|
+
overallTimeoutMs,
|
|
5998
6231
|
...opts.judgeMaxTokens !== void 0 ? { judgeMaxTokens: opts.judgeMaxTokens } : {},
|
|
5999
6232
|
distinctness: opts.distinctness ?? "none"
|
|
6000
6233
|
};
|
|
6001
6234
|
const orchestrator = new CouncilOrchestrator({
|
|
6002
6235
|
defaultProfile: "brain-council-adapter",
|
|
6236
|
+
// Carries the ad-hoc lenses registered above; without it the orchestrator
|
|
6237
|
+
// falls back to the built-in registry and rejects every custom persona.
|
|
6238
|
+
personas,
|
|
6003
6239
|
// Previously never passed, so the panel was pinned at the orchestrator's
|
|
6004
6240
|
// default of 3 concurrent seats no matter how many voters were configured.
|
|
6005
6241
|
...opts.maxConcurrency !== void 0 ? { maxConcurrency: opts.maxConcurrency } : {},
|
|
@@ -7820,7 +8056,9 @@ function safeProfileName(name) {
|
|
|
7820
8056
|
function activeProfileName(globalRoot) {
|
|
7821
8057
|
try {
|
|
7822
8058
|
const parsed = JSON.parse(fs.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
|
|
7823
|
-
return safeProfileName(
|
|
8059
|
+
return safeProfileName(
|
|
8060
|
+
typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0
|
|
8061
|
+
);
|
|
7824
8062
|
} catch {
|
|
7825
8063
|
return "default";
|
|
7826
8064
|
}
|
|
@@ -7900,6 +8138,7 @@ function resolveWstackPaths(opts) {
|
|
|
7900
8138
|
projectPlan: path4.join(projectDir, "plan.json"),
|
|
7901
8139
|
projectAutophase: path4.join(projectDir, "autophase"),
|
|
7902
8140
|
projectSddBoards: path4.join(projectDir, "sdd-boards"),
|
|
8141
|
+
projectRequirementIntakes: path4.join(projectDir, "requirement-intakes"),
|
|
7903
8142
|
syncConfig: path4.join(profileDir, "sync.json"),
|
|
7904
8143
|
configHistoryDir: path4.join(globalRoot, "config-history"),
|
|
7905
8144
|
projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
|
|
@@ -15917,8 +16156,17 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
15917
16156
|
durationMs: 0
|
|
15918
16157
|
};
|
|
15919
16158
|
this.completedResults.push(synthetic);
|
|
16159
|
+
this.trimCompletedResults();
|
|
15920
16160
|
this.emit("task.completed", { task, result: synthetic });
|
|
15921
16161
|
}
|
|
16162
|
+
trimCompletedResults() {
|
|
16163
|
+
if (this.completedResults.length > _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS) {
|
|
16164
|
+
this.completedResults.splice(
|
|
16165
|
+
0,
|
|
16166
|
+
this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
|
|
16167
|
+
);
|
|
16168
|
+
}
|
|
16169
|
+
}
|
|
15922
16170
|
async runDispatched(subagentId, task) {
|
|
15923
16171
|
const subagent = this.subagents.get(subagentId);
|
|
15924
16172
|
if (!subagent) return;
|
|
@@ -16052,12 +16300,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
16052
16300
|
}
|
|
16053
16301
|
recordCompletion(result) {
|
|
16054
16302
|
this.completedResults.push(result);
|
|
16055
|
-
|
|
16056
|
-
this.completedResults.splice(
|
|
16057
|
-
0,
|
|
16058
|
-
this.completedResults.length - _DefaultMultiAgentCoordinator.MAX_COMPLETED_RESULTS
|
|
16059
|
-
);
|
|
16060
|
-
}
|
|
16303
|
+
this.trimCompletedResults();
|
|
16061
16304
|
this.totalIterations += result.iterations;
|
|
16062
16305
|
if (this.inFlight > 0) {
|
|
16063
16306
|
this.inFlight--;
|
|
@@ -20363,6 +20606,7 @@ var OneShotOrchestrator = class {
|
|
|
20363
20606
|
tokens: { input: 0, output: 0, total: 0 },
|
|
20364
20607
|
durationMs: Math.round(performance.now() - startedAt),
|
|
20365
20608
|
fromFallback: false,
|
|
20609
|
+
attempts: 0,
|
|
20366
20610
|
error: "No provider or model could be resolved. Check your config."
|
|
20367
20611
|
};
|
|
20368
20612
|
}
|
|
@@ -20377,6 +20621,7 @@ var OneShotOrchestrator = class {
|
|
|
20377
20621
|
tokens: { input: 0, output: 0, total: 0 },
|
|
20378
20622
|
durationMs: Math.round(performance.now() - startedAt),
|
|
20379
20623
|
fromFallback: false,
|
|
20624
|
+
attempts: 0,
|
|
20380
20625
|
error: `Cannot build provider "${target.providerId}": ${err instanceof Error ? err.message : String(err)}`
|
|
20381
20626
|
};
|
|
20382
20627
|
}
|
|
@@ -20389,11 +20634,13 @@ var OneShotOrchestrator = class {
|
|
|
20389
20634
|
let fromFallback = false;
|
|
20390
20635
|
let lastError;
|
|
20391
20636
|
let fallbackEligible = false;
|
|
20637
|
+
let attempts = 0;
|
|
20392
20638
|
if (tracker && !tracker.isAvailable(target.providerId, target.model) || !evaluateModelCalendar(config.modelAvailabilitySchedule, target.providerId, target.model).allowed) {
|
|
20393
20639
|
this.opts.logger?.debug(
|
|
20394
20640
|
`one-shot: primary "${target.providerId}/${target.model}" is blocked \u2014 trying fallback`
|
|
20395
20641
|
);
|
|
20396
20642
|
} else {
|
|
20643
|
+
attempts += 1;
|
|
20397
20644
|
const primaryAttempt = await this.tryCall(
|
|
20398
20645
|
provider,
|
|
20399
20646
|
request,
|
|
@@ -20408,10 +20655,17 @@ var OneShotOrchestrator = class {
|
|
|
20408
20655
|
tracker?.recordSuccess(target.providerId, target.model);
|
|
20409
20656
|
servingProviderId = provider.id;
|
|
20410
20657
|
servingModel = target.model;
|
|
20411
|
-
return this.buildResult(result, servingProviderId, servingModel, false, startedAt);
|
|
20658
|
+
return this.buildResult(result, servingProviderId, servingModel, false, startedAt, attempts);
|
|
20412
20659
|
}
|
|
20413
20660
|
if (!fallbackEligible || chain.length === 0) {
|
|
20414
|
-
return this.buildErrorResult(
|
|
20661
|
+
return this.buildErrorResult(
|
|
20662
|
+
lastError,
|
|
20663
|
+
target.providerId,
|
|
20664
|
+
target.model,
|
|
20665
|
+
false,
|
|
20666
|
+
startedAt,
|
|
20667
|
+
attempts
|
|
20668
|
+
);
|
|
20415
20669
|
}
|
|
20416
20670
|
}
|
|
20417
20671
|
const estimatedTokens = estimateRequestTokens(request.messages, request.system, []).total;
|
|
@@ -20439,6 +20693,7 @@ var OneShotOrchestrator = class {
|
|
|
20439
20693
|
}
|
|
20440
20694
|
servingProviderId = fbProvider.id;
|
|
20441
20695
|
servingModel = entry.model;
|
|
20696
|
+
attempts += 1;
|
|
20442
20697
|
const attempt = await this.tryCall(
|
|
20443
20698
|
fbProvider,
|
|
20444
20699
|
this.buildRequest(input, entry.model),
|
|
@@ -20449,7 +20704,14 @@ var OneShotOrchestrator = class {
|
|
|
20449
20704
|
if (attempt.response) {
|
|
20450
20705
|
tracker?.recordSuccess(entry.providerId, entry.model);
|
|
20451
20706
|
fromFallback = true;
|
|
20452
|
-
return this.buildResult(
|
|
20707
|
+
return this.buildResult(
|
|
20708
|
+
attempt.response,
|
|
20709
|
+
servingProviderId,
|
|
20710
|
+
servingModel,
|
|
20711
|
+
true,
|
|
20712
|
+
startedAt,
|
|
20713
|
+
attempts
|
|
20714
|
+
);
|
|
20453
20715
|
}
|
|
20454
20716
|
lastError = attempt.error;
|
|
20455
20717
|
}
|
|
@@ -20458,7 +20720,8 @@ var OneShotOrchestrator = class {
|
|
|
20458
20720
|
servingProviderId,
|
|
20459
20721
|
servingModel,
|
|
20460
20722
|
fromFallback,
|
|
20461
|
-
startedAt
|
|
20723
|
+
startedAt,
|
|
20724
|
+
attempts
|
|
20462
20725
|
);
|
|
20463
20726
|
}
|
|
20464
20727
|
// ── Private helpers ─────────────────────────────────────────────
|
|
@@ -20469,7 +20732,8 @@ var OneShotOrchestrator = class {
|
|
|
20469
20732
|
resolveTarget(input, config) {
|
|
20470
20733
|
if (input.role && this.opts.modelRouter) {
|
|
20471
20734
|
const pick = this.opts.modelRouter.pickForTask(input.role, "");
|
|
20472
|
-
|
|
20735
|
+
const hasExplicitTarget = Boolean(input.providerId || input.model);
|
|
20736
|
+
if (pick && (pick.fromMatrix === true || !hasExplicitTarget)) {
|
|
20473
20737
|
return { providerId: pick.provider, model: pick.model };
|
|
20474
20738
|
}
|
|
20475
20739
|
}
|
|
@@ -20581,7 +20845,7 @@ var OneShotOrchestrator = class {
|
|
|
20581
20845
|
}
|
|
20582
20846
|
}
|
|
20583
20847
|
/** Build a success result from a provider Response. */
|
|
20584
|
-
buildResult(response, servingProviderId, servingModel, fromFallback, startedAt) {
|
|
20848
|
+
buildResult(response, servingProviderId, servingModel, fromFallback, startedAt, attempts) {
|
|
20585
20849
|
const textBlocks = response.content.filter(isTextBlock);
|
|
20586
20850
|
const text = textBlocks.map((b) => b.text).join("\n").trim();
|
|
20587
20851
|
return {
|
|
@@ -20595,11 +20859,12 @@ var OneShotOrchestrator = class {
|
|
|
20595
20859
|
},
|
|
20596
20860
|
durationMs: Math.round(performance.now() - startedAt),
|
|
20597
20861
|
fromFallback,
|
|
20862
|
+
attempts,
|
|
20598
20863
|
stopReason: response.stopReason
|
|
20599
20864
|
};
|
|
20600
20865
|
}
|
|
20601
20866
|
/** Build a total-failure error result. */
|
|
20602
|
-
buildErrorResult(error, servingProviderId, servingModel, fromFallback, startedAt) {
|
|
20867
|
+
buildErrorResult(error, servingProviderId, servingModel, fromFallback, startedAt, attempts) {
|
|
20603
20868
|
return {
|
|
20604
20869
|
text: "",
|
|
20605
20870
|
model: servingModel,
|
|
@@ -20607,6 +20872,7 @@ var OneShotOrchestrator = class {
|
|
|
20607
20872
|
tokens: { input: 0, output: 0, total: 0 },
|
|
20608
20873
|
durationMs: Math.round(performance.now() - startedAt),
|
|
20609
20874
|
fromFallback,
|
|
20875
|
+
attempts,
|
|
20610
20876
|
error: error instanceof Error ? error.message : String(error ?? "Unknown error")
|
|
20611
20877
|
};
|
|
20612
20878
|
}
|
|
@@ -21142,6 +21408,7 @@ export {
|
|
|
21142
21408
|
AutonomousRunner,
|
|
21143
21409
|
BRAIN_EVALUATION_CASE_VERSION,
|
|
21144
21410
|
BUILTIN_COUNCIL_PERSONAS,
|
|
21411
|
+
BUILTIN_COUNCIL_PERSONA_IDS,
|
|
21145
21412
|
BUILTIN_COUNCIL_PROFILES,
|
|
21146
21413
|
COUNCIL_JUDGE_PROMPT_PATH,
|
|
21147
21414
|
COUNCIL_REFUSAL_OPTION_ID,
|