@wrongstack/core 0.299.0 → 0.301.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.d.ts +2 -0
- package/dist/chronicle/index.js +297 -10
- package/dist/chronicle/project-server.js +253 -15
- package/dist/chronicle/sqlite-compaction.d.ts +20 -0
- package/dist/chronicle/sqlite-journal.d.ts +57 -0
- package/dist/coordination/agents/capability-manifest.d.ts +7 -0
- package/dist/coordination/agents/index.d.ts +3 -2
- package/dist/coordination/agents/index.js +818 -570
- package/dist/coordination/agents/project-agent-identity.d.ts +6 -6
- package/dist/coordination/agents/role-skills.d.ts +1 -1
- package/dist/coordination/agents/types.d.ts +6 -0
- package/dist/coordination/director.d.ts +10 -2
- package/dist/coordination/fleet-manager.d.ts +48 -3
- package/dist/coordination/ifleet-manager.d.ts +2 -0
- package/dist/coordination/index.js +6880 -6440
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-project-server.js +3 -4
- package/dist/coordination/mailbox-types.d.ts +6 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +1 -0
- package/dist/core/agent-response.d.ts +5 -1
- package/dist/core/agent-tools.d.ts +3 -0
- package/dist/core/context.d.ts +20 -0
- package/dist/core/fallback-model.d.ts +48 -0
- package/dist/core/index.d.ts +11 -9
- package/dist/core/index.js +1119 -310
- package/dist/core/instruction-template.d.ts +83 -0
- package/dist/core/next-steps-slot.d.ts +88 -0
- package/dist/core/system-prompt-blocks.d.ts +10 -1
- package/dist/core/system-prompt-builder.d.ts +40 -4
- package/dist/core/system-prompt-memory-skills.d.ts +1 -0
- package/dist/core/system-prompt-skill-bodies.d.ts +3 -3
- package/dist/defaults/index.js +9805 -8767
- package/dist/design/index.js +11 -3
- package/dist/execution/auto-compaction-middleware.d.ts +17 -0
- package/dist/execution/autonomy-brain.d.ts +15 -2
- package/dist/execution/brain-runtime.d.ts +3 -1
- package/dist/execution/compaction-core.d.ts +41 -2
- package/dist/execution/council-brain.d.ts +47 -1
- package/dist/execution/council-orchestrator.d.ts +25 -6
- package/dist/execution/council-prompts.d.ts +12 -1
- package/dist/execution/index.d.ts +11 -10
- package/dist/execution/index.js +10507 -9189
- package/dist/execution/tool-executor.d.ts +4 -1
- package/dist/execution/topic-shift-advisor.d.ts +53 -0
- package/dist/extension/index.js +8 -2
- package/dist/extension/registry.d.ts +8 -1
- package/dist/fleet-notifier.d.ts +9 -2
- package/dist/goal/index.js +11 -3
- package/dist/hooks/index.js +50 -13
- package/dist/hooks/runner.d.ts +12 -1
- package/dist/hq/index.js +59 -21
- package/dist/hq/protocol/fleet.d.ts +20 -0
- package/dist/hq/protocol.js +10 -0
- package/dist/hq/publisher.d.ts +21 -3
- package/dist/index.d.ts +22 -20
- package/dist/index.js +12211 -8572
- package/dist/infrastructure/index.js +108 -21
- package/dist/kernel/events/brain-events.d.ts +9 -0
- package/dist/kernel/events/provider-events.d.ts +42 -1
- package/dist/kernel/events/session-events.d.ts +13 -0
- package/dist/kernel/events/tool-events.d.ts +3 -3
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +18 -1
- package/dist/models/provider-credentials.d.ts +54 -0
- package/dist/plugin/api.d.ts +6 -0
- package/dist/plugin/config.d.ts +55 -0
- package/dist/plugin/index.d.ts +2 -1
- package/dist/plugin/index.js +2179 -544
- package/dist/plugins/auto-review-plugin.d.ts +3 -0
- package/dist/plugins/cloud-config-sync-plugin.d.ts +22 -0
- package/dist/plugins/review-claim-registry.d.ts +23 -8
- package/dist/plugins/review-types.d.ts +2 -0
- package/dist/registry/index.js +109 -74
- package/dist/registry/tool-registry.d.ts +15 -0
- package/dist/security/capabilities.d.ts +2 -0
- package/dist/security/index.d.ts +2 -2
- package/dist/security/index.js +166 -44
- package/dist/security/permission-helpers.d.ts +23 -6
- package/dist/security/permission-policy.d.ts +16 -0
- package/dist/security/readonly-permission-policy.d.ts +20 -0
- package/dist/security/totp.d.ts +14 -0
- package/dist/session-registry-atomic-file.d.ts +32 -5
- package/dist/skills/frontmatter.d.ts +6 -0
- package/dist/skills/index.js +22 -5
- package/dist/storage/cloud-config-sync/sanitize.d.ts +54 -0
- package/dist/storage/cloud-config-sync.d.ts +87 -0
- package/dist/storage/director-state.d.ts +7 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +887 -74
- package/dist/tools/fallback-system-config-view-tool.d.ts +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +898 -335
- package/dist/tools/one-shot-llm-tool.d.ts +1 -0
- package/dist/tools/plugin-manager.d.ts +27 -0
- package/dist/types/config/mcp-features.d.ts +18 -11
- package/dist/types/config/root.d.ts +8 -1
- package/dist/types/config/runtime.d.ts +20 -6
- package/dist/types/config/skills-fleet-brain.d.ts +12 -4
- package/dist/types/config/tools.d.ts +16 -0
- package/dist/types/context-window.d.ts +1 -0
- package/dist/types/council.d.ts +11 -0
- package/dist/types/hooks.d.ts +14 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +1 -0
- package/dist/types/multi-agent.d.ts +12 -0
- package/dist/types/one-shot-llm.d.ts +25 -0
- package/dist/types/plugin.d.ts +28 -0
- package/dist/types/provider.d.ts +16 -0
- package/dist/types/runtime-capability-manifest.d.ts +168 -0
- package/dist/types/skill.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +3 -1
- package/dist/types/tool-executor.d.ts +8 -1
- package/dist/utils/context-breakdown.d.ts +8 -2
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +141 -31
- package/dist/utils/regex-guard.d.ts +16 -2
- package/dist/utils/sage-output-block.d.ts +7 -10
- package/dist/utils/wstack-paths.d.ts +11 -3
- package/dist/worktree/index.js +4 -4
- package/instructions/agents/browser.md +1 -4
- package/instructions/agents/e2e.md +17 -15
- package/instructions/agents/ios.md +3 -3
- package/instructions/agents/search.md +1 -1
- package/instructions/autonomy/goal-preamble.md +4 -4
- package/instructions/coordination/director-preamble.md +2 -3
- package/instructions/coordination/subagent-baseline.md +3 -1
- package/instructions/leader-after-task.md +12 -0
- package/instructions/llm/chimera-review.md +1 -1
- package/instructions/modes/audit-lite.md +1 -1
- package/instructions/sections/tool/common-patterns.md +18 -2
- package/instructions/sections/tool/mailbox-compact.md +1 -1
- package/instructions/sections/tool/mailbox-full.md +1 -1
- package/instructions/system-lite.md +91 -3
- package/instructions/system-pro.md +296 -102
- package/instructions/system.md +248 -92
- package/package.json +3 -3
- package/skills/api-design/SKILL.md +3 -0
- package/skills/audit-log/SKILL.md +2 -0
- package/skills/auto-review/SKILL.md +6 -1
- package/skills/bug-hunter/SKILL.md +4 -1
- package/skills/chimera/SKILL.md +3 -0
- package/skills/data-governance/SKILL.md +3 -0
- package/skills/design-system/SKILL.md +5 -2
- package/skills/docker-deploy/SKILL.md +2 -0
- package/skills/git-flow/SKILL.md +2 -0
- package/skills/mailbox-bridge/SKILL.md +3 -0
- package/skills/mnemosyne/SKILL.md +2 -0
- package/skills/multi-agent/SKILL.md +4 -1
- package/skills/node-modern/SKILL.md +4 -1
- package/skills/observability/SKILL.md +3 -0
- package/skills/output-standards/SKILL.md +2 -0
- package/skills/plugin-author/SKILL.md +4 -1
- package/skills/prompt-engineering/SKILL.md +3 -1
- package/skills/react-modern/SKILL.md +6 -3
- package/skills/refactor-planner/SKILL.md +2 -0
- package/skills/research-web/SKILL.md +3 -0
- package/skills/sdd/SKILL.md +3 -1
- package/skills/security-scanner/SKILL.md +3 -0
- package/skills/skill-creator/SKILL.md +2 -0
- package/skills/tech-stack/SKILL.md +3 -0
- package/skills/testing/SKILL.md +4 -1
- package/skills/typescript-strict/SKILL.md +4 -1
- package/skills/wrongstack-kanban/SKILL.md +6 -3
- package/skills/wrongstack-mailbox/SKILL.md +4 -1
- package/skills/wrongstack-mailbox-mcp/SKILL.md +10 -8
package/dist/tools/index.js
CHANGED
|
@@ -472,22 +472,29 @@ function buildCouncilJudgeUserPrompt(question, votes, opts = {}) {
|
|
|
472
472
|
"</council-ballots>"
|
|
473
473
|
].filter(Boolean).join("\n\n");
|
|
474
474
|
}
|
|
475
|
-
function
|
|
476
|
-
|
|
475
|
+
function validateCouncilOptions(options) {
|
|
476
|
+
const errors = [];
|
|
477
477
|
const seen = /* @__PURE__ */ new Set();
|
|
478
|
-
|
|
478
|
+
for (const option of options ?? []) {
|
|
479
479
|
const id = option.id.trim();
|
|
480
|
-
|
|
481
|
-
if (!
|
|
482
|
-
if (
|
|
483
|
-
if (seen.has(id)) throw new Error(`buildCouncilQuestionPrompt: duplicate option id "${id}".`);
|
|
480
|
+
if (!id) errors.push("Every option must have a non-empty `id`.");
|
|
481
|
+
if (!option.label.trim()) errors.push(`Option "${id || "<empty>"}" must have a label.`);
|
|
482
|
+
if (seen.has(id)) errors.push(`Duplicate option id "${id}".`);
|
|
484
483
|
seen.add(id);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
484
|
+
}
|
|
485
|
+
return errors;
|
|
486
|
+
}
|
|
487
|
+
function normalizeOptions(options) {
|
|
488
|
+
if (!options) return [];
|
|
489
|
+
const errors = validateCouncilOptions(options);
|
|
490
|
+
if (errors.length > 0) {
|
|
491
|
+
throw new Error(`buildCouncilQuestionPrompt: ${errors.join("; ")}`);
|
|
492
|
+
}
|
|
493
|
+
return options.map((option) => ({
|
|
494
|
+
id: option.id.trim(),
|
|
495
|
+
label: option.label.trim(),
|
|
496
|
+
...option.consequence?.trim() ? { consequence: option.consequence.trim() } : {}
|
|
497
|
+
}));
|
|
491
498
|
}
|
|
492
499
|
function requiredInstruction(path7) {
|
|
493
500
|
const text = readBundledInstructionText(path7);
|
|
@@ -595,6 +602,8 @@ function requireFraction(value, label) {
|
|
|
595
602
|
var COUNCIL_REFUSAL_OPTION_ID = "council_refuse";
|
|
596
603
|
var DEFAULT_COUNCIL_MAX_CONCURRENCY = 3;
|
|
597
604
|
var MAX_COUNCIL_CONCURRENCY = 8;
|
|
605
|
+
var OVERALL_TIMEOUT_REASON = "Council overall timeout exceeded.";
|
|
606
|
+
var CALL_CANCELLED_REASON = "Cancelled.";
|
|
598
607
|
var CouncilOrchestrator = class {
|
|
599
608
|
caller;
|
|
600
609
|
personas;
|
|
@@ -605,11 +614,20 @@ var CouncilOrchestrator = class {
|
|
|
605
614
|
fallbackProfileManager;
|
|
606
615
|
seatCaller;
|
|
607
616
|
judgeCaller;
|
|
617
|
+
/**
|
|
618
|
+
* Normalized ad-hoc profiles keyed by the caller's config object identity.
|
|
619
|
+
* The Brain adapter reuses ONE profile object for every decision, so this
|
|
620
|
+
* avoids re-validating + re-freezing it on every ask() without caching
|
|
621
|
+
* string-keyed registry lookups (those are already O(1)).
|
|
622
|
+
*
|
|
623
|
+
* Hosts must treat ad-hoc profile configs as IMMUTABLE once passed to
|
|
624
|
+
* ask(): the cache is keyed by object identity and never invalidated, so
|
|
625
|
+
* mutating a cached profile would silently serve the first snapshot.
|
|
626
|
+
*/
|
|
627
|
+
profileCache = /* @__PURE__ */ new WeakMap();
|
|
608
628
|
constructor(opts) {
|
|
609
629
|
if (!opts.caller && !opts.seatCaller && !opts.judgeCaller) {
|
|
610
|
-
throw new Error(
|
|
611
|
-
"CouncilOrchestrator: provide `caller`, `seatCaller`, or `judgeCaller`."
|
|
612
|
-
);
|
|
630
|
+
throw new Error("CouncilOrchestrator: provide `caller`, `seatCaller`, or `judgeCaller`.");
|
|
613
631
|
}
|
|
614
632
|
this.caller = opts.caller;
|
|
615
633
|
this.personas = opts.personas ?? DEFAULT_COUNCIL_PERSONA_REGISTRY;
|
|
@@ -623,13 +641,33 @@ var CouncilOrchestrator = class {
|
|
|
623
641
|
this.seatCaller = opts.seatCaller;
|
|
624
642
|
this.judgeCaller = opts.judgeCaller;
|
|
625
643
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
644
|
+
/**
|
|
645
|
+
* Resolve the effective profile for a question. String ids and the default
|
|
646
|
+
* go through the registry (already O(1)); ad-hoc config objects are
|
|
647
|
+
* normalized once per stable object identity and cached, because hosts such
|
|
648
|
+
* as the Brain adapter pass the same profile object on every ask().
|
|
649
|
+
*/
|
|
650
|
+
resolveProfile(profile) {
|
|
651
|
+
if (typeof profile === "string" || profile === void 0) {
|
|
652
|
+
return resolveCouncilProfile(profile, {
|
|
653
|
+
registry: this.profiles,
|
|
654
|
+
personas: this.personas,
|
|
655
|
+
defaultProfile: this.defaultProfile
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
const cached = this.profileCache.get(profile);
|
|
659
|
+
if (cached) return cached;
|
|
660
|
+
const resolved = resolveCouncilProfile(profile, {
|
|
629
661
|
registry: this.profiles,
|
|
630
662
|
personas: this.personas,
|
|
631
663
|
defaultProfile: this.defaultProfile
|
|
632
664
|
});
|
|
665
|
+
this.profileCache.set(profile, resolved);
|
|
666
|
+
return resolved;
|
|
667
|
+
}
|
|
668
|
+
async ask(question) {
|
|
669
|
+
const startedAt = Date.now();
|
|
670
|
+
const profile = this.resolveProfile(question.profile);
|
|
633
671
|
validateRefusalCollision(question, this.refusalOptionId);
|
|
634
672
|
const timeoutSignal = AbortSignal.timeout(profile.overallTimeoutMs);
|
|
635
673
|
const signal = question.signal ? AbortSignal.any([question.signal, timeoutSignal]) : timeoutSignal;
|
|
@@ -646,11 +684,16 @@ var CouncilOrchestrator = class {
|
|
|
646
684
|
try {
|
|
647
685
|
return await this.callSeat(question, profile, seat, i, signal, usage);
|
|
648
686
|
} catch (error) {
|
|
687
|
+
const timedOut = signal.aborted && !question.signal?.aborted;
|
|
649
688
|
return {
|
|
650
689
|
seatId: seat.id,
|
|
651
690
|
persona: seat.persona,
|
|
652
|
-
|
|
653
|
-
|
|
691
|
+
// Only the caller's own cancel is a "cancelled" vote; the overall
|
|
692
|
+
// budget expiring is a failure (timeout), matching the envelope.
|
|
693
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
694
|
+
// Canonical text for aborted-by-budget or cancelled seats, so one
|
|
695
|
+
// event does not surface a different string per code path.
|
|
696
|
+
error: question.signal?.aborted ? CALL_CANCELLED_REASON : timedOut ? OVERALL_TIMEOUT_REASON : errorMessage(error)
|
|
654
697
|
};
|
|
655
698
|
}
|
|
656
699
|
}
|
|
@@ -660,7 +703,7 @@ var CouncilOrchestrator = class {
|
|
|
660
703
|
if (question.signal?.aborted) {
|
|
661
704
|
return resultEnvelope({
|
|
662
705
|
status: "cancelled",
|
|
663
|
-
reason:
|
|
706
|
+
reason: CALL_CANCELLED_REASON,
|
|
664
707
|
resolution: "none",
|
|
665
708
|
votes,
|
|
666
709
|
profile,
|
|
@@ -673,14 +716,17 @@ var CouncilOrchestrator = class {
|
|
|
673
716
|
if (timeoutSignal.aborted) {
|
|
674
717
|
return resultEnvelope({
|
|
675
718
|
status: "failed",
|
|
676
|
-
reason:
|
|
719
|
+
reason: OVERALL_TIMEOUT_REASON,
|
|
677
720
|
resolution: "none",
|
|
678
721
|
votes,
|
|
679
722
|
profile,
|
|
680
723
|
usage,
|
|
681
724
|
startedAt,
|
|
682
725
|
warnings,
|
|
683
|
-
|
|
726
|
+
// Seat-prefixed errors normally carry the canonical timeout text, but
|
|
727
|
+
// a signal-blind caller can resolve valid votes even after the budget
|
|
728
|
+
// expired — append the standalone entry only when nothing carries it.
|
|
729
|
+
errors: errors.some((entry) => entry.includes(OVERALL_TIMEOUT_REASON)) ? errors : [...errors, OVERALL_TIMEOUT_REASON]
|
|
684
730
|
});
|
|
685
731
|
}
|
|
686
732
|
if (!question.options || question.options.length === 0) {
|
|
@@ -707,7 +753,17 @@ var CouncilOrchestrator = class {
|
|
|
707
753
|
);
|
|
708
754
|
}
|
|
709
755
|
async callSeat(question, profile, seat, seatIndex, signal, usage) {
|
|
710
|
-
if (signal.aborted)
|
|
756
|
+
if (signal.aborted) {
|
|
757
|
+
return question.signal?.aborted ? cancelledVote(seat) : {
|
|
758
|
+
seatId: seat.id,
|
|
759
|
+
persona: seat.persona,
|
|
760
|
+
status: "failed",
|
|
761
|
+
...seat.target?.providerId ? { provider: seat.target.providerId } : {},
|
|
762
|
+
...seat.target?.model ? { model: seat.target.model } : {},
|
|
763
|
+
durationMs: 0,
|
|
764
|
+
error: OVERALL_TIMEOUT_REASON
|
|
765
|
+
};
|
|
766
|
+
}
|
|
711
767
|
let persona;
|
|
712
768
|
try {
|
|
713
769
|
persona = this.personas.require(seat.persona);
|
|
@@ -733,12 +789,15 @@ var CouncilOrchestrator = class {
|
|
|
733
789
|
});
|
|
734
790
|
const metadata = callMetadata(result);
|
|
735
791
|
if (result.error) {
|
|
792
|
+
const timedOut = signal.aborted && !question.signal?.aborted;
|
|
736
793
|
return {
|
|
737
794
|
seatId: seat.id,
|
|
738
795
|
persona: seat.persona,
|
|
739
|
-
status: signal
|
|
796
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
740
797
|
...metadata,
|
|
741
|
-
|
|
798
|
+
// Canonical text for cancelled or aborted-by-budget seats, so one
|
|
799
|
+
// cancel/timeout event does not surface a raw provider string.
|
|
800
|
+
error: question.signal?.aborted ? CALL_CANCELLED_REASON : timedOut ? OVERALL_TIMEOUT_REASON : result.error
|
|
742
801
|
};
|
|
743
802
|
}
|
|
744
803
|
const parsed = parseVote(result.text, question, this.refusalOptionId);
|
|
@@ -748,7 +807,7 @@ var CouncilOrchestrator = class {
|
|
|
748
807
|
persona: seat.persona,
|
|
749
808
|
status: "invalid",
|
|
750
809
|
...metadata,
|
|
751
|
-
error: parsed.error
|
|
810
|
+
error: withTruncationNote(parsed.error, result, profile.voterMaxTokens)
|
|
752
811
|
};
|
|
753
812
|
}
|
|
754
813
|
return {
|
|
@@ -837,9 +896,27 @@ var CouncilOrchestrator = class {
|
|
|
837
896
|
signal,
|
|
838
897
|
usage
|
|
839
898
|
);
|
|
899
|
+
if (signal.aborted) {
|
|
900
|
+
const cancelled = question.signal?.aborted === true;
|
|
901
|
+
const reason = cancelled ? CALL_CANCELLED_REASON : OVERALL_TIMEOUT_REASON;
|
|
902
|
+
return resultEnvelope({
|
|
903
|
+
status: cancelled ? "cancelled" : "failed",
|
|
904
|
+
reason,
|
|
905
|
+
resolution: "none",
|
|
906
|
+
votes,
|
|
907
|
+
profile,
|
|
908
|
+
usage,
|
|
909
|
+
startedAt,
|
|
910
|
+
warnings,
|
|
911
|
+
errors: errors.some((entry) => entry.includes(reason)) ? errors : [...errors, reason],
|
|
912
|
+
judgeUsed: true
|
|
913
|
+
});
|
|
914
|
+
}
|
|
840
915
|
if (!judged.ok) {
|
|
841
916
|
return resultEnvelope({
|
|
842
|
-
|
|
917
|
+
// User cancel -> cancelled; overall budget expired mid-judge -> failed;
|
|
918
|
+
// otherwise the judge simply failed -> abstained (can't decide).
|
|
919
|
+
status: question.signal?.aborted ? "cancelled" : signal.aborted ? "failed" : "abstained",
|
|
843
920
|
reason: judged.error,
|
|
844
921
|
resolution: "none",
|
|
845
922
|
votes,
|
|
@@ -899,6 +976,19 @@ var CouncilOrchestrator = class {
|
|
|
899
976
|
});
|
|
900
977
|
}
|
|
901
978
|
if (!profile.judge) {
|
|
979
|
+
if (divergentStances(valid)) {
|
|
980
|
+
return resultEnvelope({
|
|
981
|
+
status: "abstained",
|
|
982
|
+
reason: "Council produced multiple distinct stances and has no judge to reconcile them.",
|
|
983
|
+
resolution: "none",
|
|
984
|
+
votes,
|
|
985
|
+
profile,
|
|
986
|
+
usage,
|
|
987
|
+
startedAt,
|
|
988
|
+
warnings,
|
|
989
|
+
errors
|
|
990
|
+
});
|
|
991
|
+
}
|
|
902
992
|
const first = valid[0];
|
|
903
993
|
if (!first) {
|
|
904
994
|
return resultEnvelope({
|
|
@@ -935,9 +1025,30 @@ var CouncilOrchestrator = class {
|
|
|
935
1025
|
signal,
|
|
936
1026
|
usage
|
|
937
1027
|
);
|
|
1028
|
+
if (signal.aborted) {
|
|
1029
|
+
const cancelled = question.signal?.aborted === true;
|
|
1030
|
+
const reason = cancelled ? CALL_CANCELLED_REASON : OVERALL_TIMEOUT_REASON;
|
|
1031
|
+
return resultEnvelope({
|
|
1032
|
+
status: cancelled ? "cancelled" : "failed",
|
|
1033
|
+
reason,
|
|
1034
|
+
resolution: "none",
|
|
1035
|
+
votes,
|
|
1036
|
+
profile,
|
|
1037
|
+
usage,
|
|
1038
|
+
startedAt,
|
|
1039
|
+
warnings,
|
|
1040
|
+
errors: errors.some((entry) => entry.includes(reason)) ? errors : [...errors, reason],
|
|
1041
|
+
judgeUsed: true
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
938
1044
|
if (!judged.ok) {
|
|
939
1045
|
return resultEnvelope({
|
|
940
|
-
|
|
1046
|
+
// User cancel -> cancelled; overall budget expired mid-judge -> failed;
|
|
1047
|
+
// otherwise the judge simply failed -> failed (open questions cannot
|
|
1048
|
+
// abstain for a judge failure — 'abstained' is reserved for quorum
|
|
1049
|
+
// failure and stance divergence; the option path maps this same
|
|
1050
|
+
// plain-judge-failure case to 'abstained' instead).
|
|
1051
|
+
status: question.signal?.aborted ? "cancelled" : "failed",
|
|
941
1052
|
reason: judged.error,
|
|
942
1053
|
resolution: "none",
|
|
943
1054
|
votes,
|
|
@@ -976,18 +1087,35 @@ var CouncilOrchestrator = class {
|
|
|
976
1087
|
signal,
|
|
977
1088
|
usage
|
|
978
1089
|
});
|
|
979
|
-
if (result.error)
|
|
980
|
-
|
|
1090
|
+
if (result.error) {
|
|
1091
|
+
if (signal.aborted && !question.signal?.aborted) {
|
|
1092
|
+
return { ok: false, error: OVERALL_TIMEOUT_REASON };
|
|
1093
|
+
}
|
|
1094
|
+
if (question.signal?.aborted) {
|
|
1095
|
+
return { ok: false, error: CALL_CANCELLED_REASON };
|
|
1096
|
+
}
|
|
1097
|
+
return { ok: false, error: result.error };
|
|
1098
|
+
}
|
|
1099
|
+
const judged = parseJudge(result.text, question, this.refusalOptionId);
|
|
1100
|
+
if (!judged.ok) {
|
|
1101
|
+
return {
|
|
1102
|
+
ok: false,
|
|
1103
|
+
error: withTruncationNote(judged.error, result, profile.judgeMaxTokens)
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
return judged;
|
|
981
1107
|
}
|
|
982
1108
|
/**
|
|
983
|
-
* Resolve the effective LLM caller for a call. Voter seats
|
|
984
|
-
* `seatCaller(seatIndex)` when wired
|
|
985
|
-
*
|
|
986
|
-
*
|
|
1109
|
+
* Resolve the effective LLM caller for a call. Voter seats (defined
|
|
1110
|
+
* seatIndex) use `seatCaller(seatIndex)` when wired, otherwise the shared
|
|
1111
|
+
* `caller` — a seat never falls through to the judge caller. Judge seats
|
|
1112
|
+
* (seatIndex undefined) use `judgeCaller` if set, otherwise `seatCaller(0)`
|
|
1113
|
+
* if set, otherwise the shared `caller`.
|
|
987
1114
|
*/
|
|
988
1115
|
resolveCaller(seatIndex) {
|
|
989
|
-
if (seatIndex !== void 0
|
|
990
|
-
return this.seatCaller(seatIndex);
|
|
1116
|
+
if (seatIndex !== void 0) {
|
|
1117
|
+
if (this.seatCaller) return this.seatCaller(seatIndex);
|
|
1118
|
+
return this.caller ?? this.judgeCaller;
|
|
991
1119
|
}
|
|
992
1120
|
if (this.judgeCaller) return this.judgeCaller;
|
|
993
1121
|
if (this.seatCaller) return this.seatCaller(0);
|
|
@@ -996,6 +1124,7 @@ var CouncilOrchestrator = class {
|
|
|
996
1124
|
async safeCall(input) {
|
|
997
1125
|
const effectiveCaller = this.resolveCaller(input.seatIndex);
|
|
998
1126
|
const resolvedTarget = this.resolveCouncilTarget(input.target);
|
|
1127
|
+
const startedAt = Date.now();
|
|
999
1128
|
try {
|
|
1000
1129
|
const result = await effectiveCaller.call({
|
|
1001
1130
|
system: input.system,
|
|
@@ -1012,8 +1141,17 @@ var CouncilOrchestrator = class {
|
|
|
1012
1141
|
addUsage(input.usage, result);
|
|
1013
1142
|
return result;
|
|
1014
1143
|
} catch (error) {
|
|
1015
|
-
|
|
1016
|
-
|
|
1144
|
+
const failed = {
|
|
1145
|
+
text: "",
|
|
1146
|
+
model: resolvedTarget?.model ?? "",
|
|
1147
|
+
provider: resolvedTarget?.providerId ?? "",
|
|
1148
|
+
tokens: { input: 0, output: 0, total: 0 },
|
|
1149
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
1150
|
+
fromFallback: false,
|
|
1151
|
+
error: errorMessage(error)
|
|
1152
|
+
};
|
|
1153
|
+
addUsage(input.usage, failed);
|
|
1154
|
+
return failed;
|
|
1017
1155
|
}
|
|
1018
1156
|
}
|
|
1019
1157
|
/**
|
|
@@ -1045,47 +1183,84 @@ var CouncilOrchestrator = class {
|
|
|
1045
1183
|
};
|
|
1046
1184
|
}
|
|
1047
1185
|
};
|
|
1048
|
-
function
|
|
1186
|
+
function parseCouncilResponse(text, question, refusalOptionId, opts) {
|
|
1187
|
+
const roleLabel = opts.role === "judge" ? "Judge" : "Voter";
|
|
1049
1188
|
const parsed = parseObject(text);
|
|
1050
1189
|
if (!parsed.ok && (!question.options || question.options.length === 0)) {
|
|
1051
1190
|
const fallback = text.trim();
|
|
1052
|
-
if (fallback) return { ok: true,
|
|
1053
|
-
return { ok: false, error:
|
|
1191
|
+
if (fallback) return { ok: true, value: { [opts.freeTextField]: fallback } };
|
|
1192
|
+
return { ok: false, error: `${roleLabel} returned an empty response.` };
|
|
1054
1193
|
}
|
|
1055
1194
|
if (!parsed.ok) return parsed;
|
|
1056
1195
|
const rationale = optionalString(parsed.value["rationale"]);
|
|
1057
1196
|
if (question.options && question.options.length > 0) {
|
|
1058
1197
|
const optionId = optionalString(parsed.value["optionId"]);
|
|
1059
|
-
const allowed = /* @__PURE__ */ new Set([
|
|
1198
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
1199
|
+
...question.options.map((option) => option.id.trim()),
|
|
1200
|
+
refusalOptionId
|
|
1201
|
+
]);
|
|
1060
1202
|
if (!optionId || !allowed.has(optionId)) {
|
|
1061
|
-
return { ok: false, error:
|
|
1203
|
+
return { ok: false, error: `${roleLabel} returned an unknown or missing optionId.` };
|
|
1062
1204
|
}
|
|
1063
|
-
return { ok: true,
|
|
1205
|
+
return { ok: true, value: { optionId, ...rationale ? { rationale } : {} } };
|
|
1206
|
+
}
|
|
1207
|
+
const freeText = optionalString(parsed.value[opts.freeTextField]);
|
|
1208
|
+
if (!freeText) {
|
|
1209
|
+
return {
|
|
1210
|
+
ok: false,
|
|
1211
|
+
error: `${roleLabel} returned an empty or missing ${opts.freeTextField}.`
|
|
1212
|
+
};
|
|
1064
1213
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1214
|
+
return {
|
|
1215
|
+
ok: true,
|
|
1216
|
+
value: { [opts.freeTextField]: freeText, ...rationale ? { rationale } : {} }
|
|
1217
|
+
};
|
|
1068
1218
|
}
|
|
1069
|
-
function
|
|
1070
|
-
const
|
|
1071
|
-
|
|
1072
|
-
const
|
|
1073
|
-
if (
|
|
1074
|
-
|
|
1219
|
+
function divergentStances(valid) {
|
|
1220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1221
|
+
for (const vote of valid) {
|
|
1222
|
+
const normalized = vote.stance.trim().replace(/^["'`]+|["'`]+$/g, "").toLowerCase().replace(/[.!?;:,]+$/g, "").replace(/\s+/g, " ").trim();
|
|
1223
|
+
if (!normalized) return true;
|
|
1224
|
+
seen.add(normalized);
|
|
1225
|
+
if (seen.size > 1) return true;
|
|
1075
1226
|
}
|
|
1227
|
+
return false;
|
|
1228
|
+
}
|
|
1229
|
+
function parseVote(text, question, refusalOptionId) {
|
|
1230
|
+
const parsed = parseCouncilResponse(text, question, refusalOptionId, {
|
|
1231
|
+
role: "voter",
|
|
1232
|
+
freeTextField: "stance"
|
|
1233
|
+
});
|
|
1076
1234
|
if (!parsed.ok) return parsed;
|
|
1077
|
-
const rationale =
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1235
|
+
const { optionId, stance, rationale } = parsed.value;
|
|
1236
|
+
return {
|
|
1237
|
+
ok: true,
|
|
1238
|
+
vote: {
|
|
1239
|
+
...optionId ? { optionId } : {},
|
|
1240
|
+
...stance ? { stance } : {},
|
|
1241
|
+
...rationale ? { rationale } : {}
|
|
1083
1242
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function parseJudge(text, question, refusalOptionId) {
|
|
1246
|
+
const parsed = parseCouncilResponse(text, question, refusalOptionId, {
|
|
1247
|
+
role: "judge",
|
|
1248
|
+
freeTextField: "answer"
|
|
1249
|
+
});
|
|
1250
|
+
if (!parsed.ok) return parsed;
|
|
1251
|
+
const { optionId, answer, rationale } = parsed.value;
|
|
1252
|
+
return {
|
|
1253
|
+
ok: true,
|
|
1254
|
+
value: {
|
|
1255
|
+
...optionId ? { optionId } : {},
|
|
1256
|
+
...answer ? { answer } : {},
|
|
1257
|
+
...rationale ? { rationale } : {}
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
}
|
|
1261
|
+
function withTruncationNote(error, result, maxTokens) {
|
|
1262
|
+
if (result.stopReason !== "max_tokens") return error;
|
|
1263
|
+
return `${error} (response truncated at maxTokens=${maxTokens} \u2014 reasoning models may need a larger budget)`;
|
|
1089
1264
|
}
|
|
1090
1265
|
function parseObject(text) {
|
|
1091
1266
|
const trimmed = text.trim();
|
|
@@ -1129,7 +1304,7 @@ function callMetadata(result) {
|
|
|
1129
1304
|
};
|
|
1130
1305
|
}
|
|
1131
1306
|
function addUsage(usage, result) {
|
|
1132
|
-
usage.calls += 1;
|
|
1307
|
+
usage.calls += Math.max(1, result.attempts ?? 1);
|
|
1133
1308
|
usage.inputTokens += result.tokens.input;
|
|
1134
1309
|
usage.outputTokens += result.tokens.output;
|
|
1135
1310
|
usage.totalTokens += result.tokens.total;
|
|
@@ -1138,21 +1313,40 @@ function usageResult(usage, startedAt) {
|
|
|
1138
1313
|
return Object.freeze({ ...usage, durationMs: Math.max(0, Date.now() - startedAt) });
|
|
1139
1314
|
}
|
|
1140
1315
|
function cancelledVote(seat) {
|
|
1141
|
-
return {
|
|
1316
|
+
return {
|
|
1317
|
+
seatId: seat.id,
|
|
1318
|
+
persona: seat.persona,
|
|
1319
|
+
status: "cancelled",
|
|
1320
|
+
...seat.target?.providerId ? { provider: seat.target.providerId } : {},
|
|
1321
|
+
...seat.target?.model ? { model: seat.target.model } : {},
|
|
1322
|
+
durationMs: 0,
|
|
1323
|
+
error: CALL_CANCELLED_REASON
|
|
1324
|
+
};
|
|
1142
1325
|
}
|
|
1143
1326
|
function distinctTargetCount(votes, profile) {
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1327
|
+
return new Set(distinctTargetKeys(votes, profile)).size;
|
|
1328
|
+
}
|
|
1329
|
+
function distinctTargetKeys(votes, profile) {
|
|
1330
|
+
const keys = [];
|
|
1331
|
+
for (const vote of votes) {
|
|
1332
|
+
if (vote.status !== "valid") continue;
|
|
1333
|
+
const provider = vote.provider?.trim() ?? "";
|
|
1334
|
+
const model = vote.model?.trim() ?? "";
|
|
1335
|
+
if (profile.distinctness === "provider") {
|
|
1336
|
+
if (provider) keys.push(provider);
|
|
1337
|
+
} else if (provider || model) {
|
|
1338
|
+
keys.push(`${provider}/${model}`);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return keys;
|
|
1148
1342
|
}
|
|
1149
1343
|
function distinctnessWarnings(votes, profile) {
|
|
1150
1344
|
if (profile.distinctness === "none") return [];
|
|
1151
|
-
const
|
|
1152
|
-
const distinct =
|
|
1153
|
-
if (
|
|
1345
|
+
const keys = distinctTargetKeys(votes, profile);
|
|
1346
|
+
const distinct = new Set(keys).size;
|
|
1347
|
+
if (keys.length > 1 && distinct < keys.length) {
|
|
1154
1348
|
return [
|
|
1155
|
-
`Council distinctness policy "${profile.distinctness}" was not met: ${distinct} distinct target(s) served ${
|
|
1349
|
+
`Council distinctness policy "${profile.distinctness}" was not met: ${distinct} distinct target(s) served ${keys.length} valid vote(s).`
|
|
1156
1350
|
];
|
|
1157
1351
|
}
|
|
1158
1352
|
return [];
|
|
@@ -1188,17 +1382,6 @@ async function mapConcurrent(items, concurrency, worker) {
|
|
|
1188
1382
|
await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => run()));
|
|
1189
1383
|
return results;
|
|
1190
1384
|
}
|
|
1191
|
-
function emptyCallResult(error) {
|
|
1192
|
-
return {
|
|
1193
|
-
text: "",
|
|
1194
|
-
model: "",
|
|
1195
|
-
provider: "",
|
|
1196
|
-
tokens: { input: 0, output: 0, total: 0 },
|
|
1197
|
-
durationMs: 0,
|
|
1198
|
-
fromFallback: false,
|
|
1199
|
-
error
|
|
1200
|
-
};
|
|
1201
|
-
}
|
|
1202
1385
|
function optionalString(value) {
|
|
1203
1386
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
1204
1387
|
}
|
|
@@ -1297,14 +1480,7 @@ function validateCouncilToolInput(input, profileIds) {
|
|
|
1297
1480
|
if ((input.options?.length ?? 0) > MAX_COUNCIL_TOOL_OPTIONS) {
|
|
1298
1481
|
errors.push(`\`options\` must not contain more than ${MAX_COUNCIL_TOOL_OPTIONS} items.`);
|
|
1299
1482
|
}
|
|
1300
|
-
|
|
1301
|
-
for (const option of input.options ?? []) {
|
|
1302
|
-
const id = option.id.trim();
|
|
1303
|
-
if (!id) errors.push("Every option must have a non-empty `id`.");
|
|
1304
|
-
if (!option.label.trim()) errors.push(`Option "${id || "<empty>"}" must have a label.`);
|
|
1305
|
-
if (ids.has(id)) errors.push(`Duplicate option id "${id}".`);
|
|
1306
|
-
ids.add(id);
|
|
1307
|
-
}
|
|
1483
|
+
errors.push(...validateCouncilOptions(input.options));
|
|
1308
1484
|
return errors;
|
|
1309
1485
|
}
|
|
1310
1486
|
|
|
@@ -2127,59 +2303,6 @@ function fallbackProfileChain(config, profileName) {
|
|
|
2127
2303
|
}
|
|
2128
2304
|
var DEFAULT_PRIMARY_COOLDOWN_MAX_MS = 10 * 6e4;
|
|
2129
2305
|
|
|
2130
|
-
// src/coordination/agents/types.ts
|
|
2131
|
-
var HOUR = 60 * 60 * 1e3;
|
|
2132
|
-
var LIGHT_BUDGET = {
|
|
2133
|
-
timeoutMs: 3 * HOUR,
|
|
2134
|
-
maxIterations: 3e3,
|
|
2135
|
-
maxToolCalls: 8e3
|
|
2136
|
-
};
|
|
2137
|
-
var MEDIUM_BUDGET = {
|
|
2138
|
-
timeoutMs: 5 * HOUR,
|
|
2139
|
-
maxIterations: 5e3,
|
|
2140
|
-
maxToolCalls: 14e3
|
|
2141
|
-
};
|
|
2142
|
-
var HEAVY_BUDGET = {
|
|
2143
|
-
timeoutMs: 10 * HOUR,
|
|
2144
|
-
maxIterations: 8e3,
|
|
2145
|
-
maxToolCalls: 2e4
|
|
2146
|
-
};
|
|
2147
|
-
var TOOLS = {
|
|
2148
|
-
/** Pure read/inspect — safe for analysis and review agents. */
|
|
2149
|
-
read: ["read", "grep", "glob", "search", "tree", "mailbox"],
|
|
2150
|
-
/** Read + structured inspection (logs, diffs, json, dependency audit). */
|
|
2151
|
-
inspect: ["read", "grep", "glob", "search", "tree", "json", "diff", "logs", "audit", "mailbox"],
|
|
2152
|
-
/** Read + edit (no shell). For agents that write code/docs but don't run it. */
|
|
2153
|
-
write: ["read", "grep", "glob", "search", "tree", "write", "edit", "replace", "patch", "mailbox"],
|
|
2154
|
-
/** Full build loop: edit + run (lint/format/typecheck/test/bash). */
|
|
2155
|
-
build: [
|
|
2156
|
-
"read",
|
|
2157
|
-
"grep",
|
|
2158
|
-
"glob",
|
|
2159
|
-
"search",
|
|
2160
|
-
"tree",
|
|
2161
|
-
"write",
|
|
2162
|
-
"edit",
|
|
2163
|
-
"replace",
|
|
2164
|
-
"patch",
|
|
2165
|
-
"bash",
|
|
2166
|
-
"exec",
|
|
2167
|
-
"lint",
|
|
2168
|
-
"format",
|
|
2169
|
-
"typecheck",
|
|
2170
|
-
"test",
|
|
2171
|
-
"mailbox"
|
|
2172
|
-
],
|
|
2173
|
-
/** Version control. */
|
|
2174
|
-
vcs: ["read", "grep", "glob", "git", "diff"],
|
|
2175
|
-
/** Dependency management + CVE audit. */
|
|
2176
|
-
deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
|
|
2177
|
-
/** Documentation authoring. */
|
|
2178
|
-
docs: ["read", "grep", "glob", "search", "tree", "write", "edit", "document", "mailbox"],
|
|
2179
|
-
/** Web research. */
|
|
2180
|
-
research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
|
|
2181
|
-
};
|
|
2182
|
-
|
|
2183
2306
|
// src/coordination/agents/agent-prompts.ts
|
|
2184
2307
|
import { readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
|
|
2185
2308
|
import * as path6 from "node:path";
|
|
@@ -2202,6 +2325,9 @@ function canonicalProjectRoot(absRoot) {
|
|
|
2202
2325
|
const commonDirFile = path2.join(gitDir, "commondir");
|
|
2203
2326
|
if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
|
|
2204
2327
|
const commonDir = path2.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
|
|
2328
|
+
const worktreesDir = path2.dirname(gitDir);
|
|
2329
|
+
if (path2.basename(worktreesDir).toLowerCase() !== "worktrees") return checkoutRoot;
|
|
2330
|
+
if (path2.resolve(worktreesDir, "..") !== commonDir) return checkoutRoot;
|
|
2205
2331
|
if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
|
|
2206
2332
|
return path2.dirname(commonDir);
|
|
2207
2333
|
} catch {
|
|
@@ -2224,7 +2350,7 @@ function safeProfileName(name) {
|
|
|
2224
2350
|
const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
|
|
2225
2351
|
return safe || "default";
|
|
2226
2352
|
}
|
|
2227
|
-
function
|
|
2353
|
+
function bootstrapProfileName(globalRoot) {
|
|
2228
2354
|
try {
|
|
2229
2355
|
const parsed = JSON.parse(fs.readFileSync(path2.join(globalRoot, "config.json"), "utf8"));
|
|
2230
2356
|
return safeProfileName(
|
|
@@ -2242,7 +2368,7 @@ function wstackGlobalRoot() {
|
|
|
2242
2368
|
function resolveWstackPaths(opts) {
|
|
2243
2369
|
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
2244
2370
|
const homeDir = opts.userHome ?? os.homedir();
|
|
2245
|
-
const profileName = safeProfileName(opts.profileName ??
|
|
2371
|
+
const profileName = safeProfileName(opts.profileName ?? bootstrapProfileName(globalRoot));
|
|
2246
2372
|
const profileDir = path2.join(globalRoot, "profiles", profileName);
|
|
2247
2373
|
const hash = projectHash(opts.projectRoot);
|
|
2248
2374
|
const slug = projectSlug(opts.projectRoot);
|
|
@@ -2312,7 +2438,12 @@ function resolveWstackPaths(opts) {
|
|
|
2312
2438
|
projectRequirementIntakes: path2.join(projectDir, "requirement-intakes"),
|
|
2313
2439
|
syncConfig: path2.join(profileDir, "sync.json"),
|
|
2314
2440
|
configHistoryDir: path2.join(globalRoot, "config-history"),
|
|
2315
|
-
projectStatus: (
|
|
2441
|
+
projectStatus: (statusProjectSlug) => {
|
|
2442
|
+
if (!/^[a-z0-9](?:[a-z0-9-]{0,39})-[a-f0-9]{6}$/.test(statusProjectSlug)) {
|
|
2443
|
+
throw new Error(`Invalid project slug: ${statusProjectSlug}`);
|
|
2444
|
+
}
|
|
2445
|
+
return path2.join(globalRoot, "projects", statusProjectSlug, "status.json");
|
|
2446
|
+
}
|
|
2316
2447
|
};
|
|
2317
2448
|
}
|
|
2318
2449
|
|
|
@@ -2357,6 +2488,8 @@ var ToolCapabilities = {
|
|
|
2357
2488
|
NET_OUTBOUND: "net.outbound",
|
|
2358
2489
|
/** Can mutate in-memory session todos only. */
|
|
2359
2490
|
SESSION_TODO: "session.todo",
|
|
2491
|
+
/** Can park after-task `<nextsteps>` suggestions for the current turn. */
|
|
2492
|
+
SESSION_NEXTSTEPS: "session.nextsteps",
|
|
2360
2493
|
/** Can mutate in-memory session mode only. */
|
|
2361
2494
|
SESSION_MODE: "session.mode",
|
|
2362
2495
|
/** Can inspect registered tool metadata. */
|
|
@@ -2418,6 +2551,334 @@ var WIDE_SUBAGENT_CAPABILITIES = [
|
|
|
2418
2551
|
ToolCapabilities.COORDINATION_FLEET_READ,
|
|
2419
2552
|
ToolCapabilities.COORDINATION_RESULT_SUBMIT
|
|
2420
2553
|
];
|
|
2554
|
+
function getDangerousCapabilities(toolOrCaps) {
|
|
2555
|
+
if (!toolOrCaps) return [];
|
|
2556
|
+
const input = toolOrCaps;
|
|
2557
|
+
const caps = Array.isArray(toolOrCaps) ? toolOrCaps : input.capabilities ?? [];
|
|
2558
|
+
return caps.filter(
|
|
2559
|
+
(c) => DANGEROUS_FOR_SUBAGENTS.includes(c)
|
|
2560
|
+
);
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// src/types/runtime-capability-manifest.ts
|
|
2564
|
+
var PLAYWRIGHT_ALIASES = {
|
|
2565
|
+
playwright_navigate: "browser_navigate",
|
|
2566
|
+
playwright_screenshot: "browser_screenshot",
|
|
2567
|
+
playwright_click: "browser_click",
|
|
2568
|
+
playwright_type: "browser_type",
|
|
2569
|
+
playwright_evaluate: "browser_evaluate",
|
|
2570
|
+
playwright_select_option: "browser_select",
|
|
2571
|
+
playwright_hover: "browser_hover",
|
|
2572
|
+
playwright_fill_form: "browser_type",
|
|
2573
|
+
playwright_wait_for: "browser_wait",
|
|
2574
|
+
playwright_press_key: "browser_press",
|
|
2575
|
+
playwright_drag: "browser_drag"
|
|
2576
|
+
};
|
|
2577
|
+
var RUNTIME_CAPABILITY_MANIFEST = [
|
|
2578
|
+
{
|
|
2579
|
+
id: "filesystem.read",
|
|
2580
|
+
pack: "core",
|
|
2581
|
+
exposure: "direct",
|
|
2582
|
+
tools: ["read", "grep", "glob", "tree"]
|
|
2583
|
+
},
|
|
2584
|
+
{
|
|
2585
|
+
id: "code.inspect",
|
|
2586
|
+
pack: "core",
|
|
2587
|
+
exposure: "direct",
|
|
2588
|
+
tools: [
|
|
2589
|
+
"codebase-stats",
|
|
2590
|
+
"codebase-search",
|
|
2591
|
+
"codebase-incoming-calls",
|
|
2592
|
+
"codebase-outgoing-calls",
|
|
2593
|
+
"codebase-index",
|
|
2594
|
+
"dead-code-scan",
|
|
2595
|
+
"diff",
|
|
2596
|
+
"json",
|
|
2597
|
+
"logs"
|
|
2598
|
+
]
|
|
2599
|
+
},
|
|
2600
|
+
{
|
|
2601
|
+
id: "filesystem.write",
|
|
2602
|
+
pack: "development",
|
|
2603
|
+
exposure: "direct",
|
|
2604
|
+
tools: ["write", "edit", "replace", "patch"]
|
|
2605
|
+
},
|
|
2606
|
+
{
|
|
2607
|
+
id: "execution.shell",
|
|
2608
|
+
pack: "development",
|
|
2609
|
+
exposure: "direct",
|
|
2610
|
+
tools: ["bash", "exec", "language", "language_info", "language_package"]
|
|
2611
|
+
},
|
|
2612
|
+
{
|
|
2613
|
+
id: "verification.run",
|
|
2614
|
+
pack: "development",
|
|
2615
|
+
exposure: "direct",
|
|
2616
|
+
tools: ["lint", "format", "typecheck", "test", "e2e_plan"]
|
|
2617
|
+
},
|
|
2618
|
+
{
|
|
2619
|
+
id: "version-control.manage",
|
|
2620
|
+
pack: "development",
|
|
2621
|
+
exposure: "direct",
|
|
2622
|
+
tools: ["git"]
|
|
2623
|
+
},
|
|
2624
|
+
{
|
|
2625
|
+
id: "dependencies.manage",
|
|
2626
|
+
pack: "development",
|
|
2627
|
+
exposure: "on-demand",
|
|
2628
|
+
tools: ["install", "outdated", "audit"]
|
|
2629
|
+
},
|
|
2630
|
+
{
|
|
2631
|
+
id: "documentation.author",
|
|
2632
|
+
pack: "development",
|
|
2633
|
+
exposure: "on-demand",
|
|
2634
|
+
tools: ["document", "design", "scaffold"]
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
id: "web.research",
|
|
2638
|
+
pack: "core",
|
|
2639
|
+
exposure: "direct",
|
|
2640
|
+
tools: ["search", "fetch"]
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
id: "work.plan",
|
|
2644
|
+
pack: "core",
|
|
2645
|
+
exposure: "direct",
|
|
2646
|
+
tools: ["todo", "plan", "task", "kanban", "nextsteps"]
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
id: "coordination.mailbox",
|
|
2650
|
+
pack: "fleet",
|
|
2651
|
+
exposure: "direct",
|
|
2652
|
+
tools: ["mailbox", "mail_send", "mail_inbox", "fleet_status"]
|
|
2653
|
+
},
|
|
2654
|
+
{
|
|
2655
|
+
id: "fleet.delegate",
|
|
2656
|
+
pack: "fleet",
|
|
2657
|
+
exposure: "on-demand",
|
|
2658
|
+
tools: [
|
|
2659
|
+
"delegate",
|
|
2660
|
+
"spawn_subagent",
|
|
2661
|
+
"assign_task",
|
|
2662
|
+
"kanban_queue",
|
|
2663
|
+
"await_tasks",
|
|
2664
|
+
"ask_subagent",
|
|
2665
|
+
"ask_result",
|
|
2666
|
+
"roll_up",
|
|
2667
|
+
"quality_gate",
|
|
2668
|
+
"terminate_subagent",
|
|
2669
|
+
"terminate_all",
|
|
2670
|
+
"work_complete",
|
|
2671
|
+
"collab_debug",
|
|
2672
|
+
"fleet_emit",
|
|
2673
|
+
"fleet"
|
|
2674
|
+
]
|
|
2675
|
+
},
|
|
2676
|
+
{
|
|
2677
|
+
id: "reasoning.oneshot",
|
|
2678
|
+
pack: "core",
|
|
2679
|
+
exposure: "on-demand",
|
|
2680
|
+
tools: ["llm", "council"]
|
|
2681
|
+
},
|
|
2682
|
+
{
|
|
2683
|
+
id: "memory.manage",
|
|
2684
|
+
pack: "memory",
|
|
2685
|
+
exposure: "on-demand",
|
|
2686
|
+
tools: ["remember", "search_memory", "find_related_memories", "forget"]
|
|
2687
|
+
},
|
|
2688
|
+
{
|
|
2689
|
+
id: "memory.curate",
|
|
2690
|
+
pack: "memory",
|
|
2691
|
+
exposure: "on-demand",
|
|
2692
|
+
tools: [
|
|
2693
|
+
"memory_candidates",
|
|
2694
|
+
"memory_update",
|
|
2695
|
+
"memory_delete",
|
|
2696
|
+
"memory_recover",
|
|
2697
|
+
"memory_backfill_recoverable",
|
|
2698
|
+
"memory_for_file",
|
|
2699
|
+
"memory_for_path",
|
|
2700
|
+
"memory_search",
|
|
2701
|
+
"memory_graph",
|
|
2702
|
+
"memory_gather_batch",
|
|
2703
|
+
"memory_verify",
|
|
2704
|
+
"memory_hygiene"
|
|
2705
|
+
]
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
id: "browser.interact",
|
|
2709
|
+
pack: "browser",
|
|
2710
|
+
exposure: "on-demand",
|
|
2711
|
+
tools: [
|
|
2712
|
+
"browser_open",
|
|
2713
|
+
"browser_status",
|
|
2714
|
+
"browser_list",
|
|
2715
|
+
"browser_navigate",
|
|
2716
|
+
"browser_snapshot",
|
|
2717
|
+
"browser_screenshot",
|
|
2718
|
+
"browser_click",
|
|
2719
|
+
"browser_type",
|
|
2720
|
+
"browser_select",
|
|
2721
|
+
"browser_press",
|
|
2722
|
+
"browser_hover",
|
|
2723
|
+
"browser_drag",
|
|
2724
|
+
"browser_wait",
|
|
2725
|
+
"browser_evaluate",
|
|
2726
|
+
"browser_upload",
|
|
2727
|
+
"browser_close"
|
|
2728
|
+
],
|
|
2729
|
+
aliases: PLAYWRIGHT_ALIASES
|
|
2730
|
+
},
|
|
2731
|
+
{
|
|
2732
|
+
id: "mcp.dynamic",
|
|
2733
|
+
pack: "mcp",
|
|
2734
|
+
exposure: "on-demand",
|
|
2735
|
+
tools: ["mcp_use"]
|
|
2736
|
+
},
|
|
2737
|
+
{
|
|
2738
|
+
id: "automation.manage",
|
|
2739
|
+
pack: "admin",
|
|
2740
|
+
exposure: "on-demand",
|
|
2741
|
+
tools: ["cron_schedule", "cron_list", "cron_cancel", "watch_start", "watch_list", "watch_stop"]
|
|
2742
|
+
},
|
|
2743
|
+
{
|
|
2744
|
+
id: "context.pin",
|
|
2745
|
+
pack: "admin",
|
|
2746
|
+
exposure: "on-demand",
|
|
2747
|
+
tools: ["pin_add", "pin_list", "pin_remove"]
|
|
2748
|
+
},
|
|
2749
|
+
{
|
|
2750
|
+
id: "quality.analyze",
|
|
2751
|
+
pack: "development",
|
|
2752
|
+
exposure: "on-demand",
|
|
2753
|
+
tools: ["dead_code_scan", "detect_duplicate_code", "secret_scanner_test", "error_lens_history"]
|
|
2754
|
+
},
|
|
2755
|
+
{
|
|
2756
|
+
id: "release.manage",
|
|
2757
|
+
pack: "development",
|
|
2758
|
+
exposure: "on-demand",
|
|
2759
|
+
tools: ["git_autocommit", "semver_current", "semver_bump", "semver_changelog"]
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
id: "messaging.telegram",
|
|
2763
|
+
pack: "fleet",
|
|
2764
|
+
exposure: "on-demand",
|
|
2765
|
+
tools: ["telegram_send", "telegram_read", "telegram_approve"]
|
|
2766
|
+
},
|
|
2767
|
+
{
|
|
2768
|
+
id: "tools.discover",
|
|
2769
|
+
pack: "admin",
|
|
2770
|
+
exposure: "direct",
|
|
2771
|
+
tools: ["tool_search", "tool_use", "tool_help", "batch_tool_use"]
|
|
2772
|
+
},
|
|
2773
|
+
{
|
|
2774
|
+
id: "runtime.admin",
|
|
2775
|
+
pack: "admin",
|
|
2776
|
+
exposure: "internal",
|
|
2777
|
+
tools: ["set_working_dir", "context_manager", "mode", "skill", "mcp_control"]
|
|
2778
|
+
}
|
|
2779
|
+
];
|
|
2780
|
+
var CAPABILITY_BY_ID = new Map(
|
|
2781
|
+
RUNTIME_CAPABILITY_MANIFEST.map((definition) => [definition.id, definition])
|
|
2782
|
+
);
|
|
2783
|
+
var CAPABILITY_BY_TOOL = /* @__PURE__ */ new Map();
|
|
2784
|
+
var TOOL_ALIASES = /* @__PURE__ */ new Map();
|
|
2785
|
+
for (const definition of RUNTIME_CAPABILITY_MANIFEST) {
|
|
2786
|
+
for (const tool of definition.tools) {
|
|
2787
|
+
CAPABILITY_BY_TOOL.set(tool, definition.id);
|
|
2788
|
+
}
|
|
2789
|
+
for (const [alias, tool] of Object.entries(definition.aliases ?? {})) {
|
|
2790
|
+
TOOL_ALIASES.set(alias, tool);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
function toolsForRuntimeCapabilities(capabilityIds) {
|
|
2794
|
+
return [
|
|
2795
|
+
...new Set(
|
|
2796
|
+
capabilityIds.flatMap((id) => {
|
|
2797
|
+
const definition = CAPABILITY_BY_ID.get(id);
|
|
2798
|
+
if (!definition) throw new Error(`Unknown runtime capability: "${id}"`);
|
|
2799
|
+
return definition.tools;
|
|
2800
|
+
})
|
|
2801
|
+
)
|
|
2802
|
+
];
|
|
2803
|
+
}
|
|
2804
|
+
function normalizeRuntimeToolName(name) {
|
|
2805
|
+
return TOOL_ALIASES.get(name) ?? name;
|
|
2806
|
+
}
|
|
2807
|
+
function runtimeCapabilityForTool(name) {
|
|
2808
|
+
return CAPABILITY_BY_TOOL.get(normalizeRuntimeToolName(name));
|
|
2809
|
+
}
|
|
2810
|
+
function inferRuntimeCapabilities(toolNames) {
|
|
2811
|
+
const ids = /* @__PURE__ */ new Set();
|
|
2812
|
+
for (const rawName of toolNames) {
|
|
2813
|
+
const id = runtimeCapabilityForTool(rawName);
|
|
2814
|
+
if (id) ids.add(id);
|
|
2815
|
+
}
|
|
2816
|
+
return [...ids];
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2819
|
+
// src/coordination/agents/project-agent-consolidation.ts
|
|
2820
|
+
import {
|
|
2821
|
+
existsSync,
|
|
2822
|
+
mkdirSync,
|
|
2823
|
+
readFileSync as readFileSync3,
|
|
2824
|
+
rmSync
|
|
2825
|
+
} from "node:fs";
|
|
2826
|
+
import * as path4 from "node:path";
|
|
2827
|
+
|
|
2828
|
+
// src/coordination/agents/project-agent-learning-entries.ts
|
|
2829
|
+
function splitLearnedEntries(body) {
|
|
2830
|
+
return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
|
|
2831
|
+
(entry) => entry.replace(/^# Learned wisdom for .+$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
|
|
2832
|
+
).filter(Boolean);
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
// src/coordination/agents/project-agent-paths.ts
|
|
2836
|
+
import * as path3 from "node:path";
|
|
2837
|
+
var AGENT_ROLE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,95}$/i;
|
|
2838
|
+
function assertProjectAgentRole(role) {
|
|
2839
|
+
const normalized = role.trim();
|
|
2840
|
+
if (!AGENT_ROLE_PATTERN.test(normalized) || normalized === "." || normalized === ".." || normalized.includes("/") || normalized.includes("\\")) {
|
|
2841
|
+
throw new Error(`Invalid project agent role: "${role}".`);
|
|
2842
|
+
}
|
|
2843
|
+
return normalized;
|
|
2844
|
+
}
|
|
2845
|
+
function agentsDir(projectRoot) {
|
|
2846
|
+
const root = projectRoot ?? process.cwd();
|
|
2847
|
+
return path3.join(root, ".wrongstack", "agents");
|
|
2848
|
+
}
|
|
2849
|
+
function roleDir(role, projectRoot) {
|
|
2850
|
+
return path3.join(agentsDir(projectRoot), assertProjectAgentRole(role));
|
|
2851
|
+
}
|
|
2852
|
+
function learningPolicyPath(role, projectRoot) {
|
|
2853
|
+
return path3.join(roleDir(role, projectRoot), "learning.json");
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
// src/coordination/agents/project-agent-consolidation.ts
|
|
2857
|
+
function consolidationPath(role, projectRoot) {
|
|
2858
|
+
return path4.join(roleDir(role, projectRoot), "consolidated.md");
|
|
2859
|
+
}
|
|
2860
|
+
function consolidationMetaPath(role, projectRoot) {
|
|
2861
|
+
return path4.join(roleDir(role, projectRoot), "consolidation.json");
|
|
2862
|
+
}
|
|
2863
|
+
function loadProjectAgentConsolidated(role, projectRoot) {
|
|
2864
|
+
try {
|
|
2865
|
+
return readFileSync3(consolidationPath(role, projectRoot), "utf8").trim();
|
|
2866
|
+
} catch {
|
|
2867
|
+
return "";
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
function loadConsolidationMetadata(role, projectRoot) {
|
|
2871
|
+
try {
|
|
2872
|
+
const raw = readFileSync3(consolidationMetaPath(role, projectRoot), "utf8");
|
|
2873
|
+
const parsed = JSON.parse(raw);
|
|
2874
|
+
if (typeof parsed === "object" && parsed !== null && typeof parsed.consolidatedAt === "string" && typeof parsed.sourceEntryCount === "number") {
|
|
2875
|
+
return parsed;
|
|
2876
|
+
}
|
|
2877
|
+
return void 0;
|
|
2878
|
+
} catch {
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2421
2882
|
|
|
2422
2883
|
// src/coordination/agents/project-agent-knowledge-manifests.ts
|
|
2423
2884
|
var BUILT_IN_KNOWLEDGE_MANIFESTS = {
|
|
@@ -2485,30 +2946,7 @@ var BUILT_IN_KNOWLEDGE_MANIFESTS = {
|
|
|
2485
2946
|
};
|
|
2486
2947
|
|
|
2487
2948
|
// src/coordination/agents/project-agent-learning-policy.ts
|
|
2488
|
-
import { readFileSync as
|
|
2489
|
-
|
|
2490
|
-
// src/coordination/agents/project-agent-paths.ts
|
|
2491
|
-
import * as path3 from "node:path";
|
|
2492
|
-
var AGENT_ROLE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,95}$/i;
|
|
2493
|
-
function assertProjectAgentRole(role) {
|
|
2494
|
-
const normalized = role.trim();
|
|
2495
|
-
if (!AGENT_ROLE_PATTERN.test(normalized) || normalized === "." || normalized === ".." || normalized.includes("/") || normalized.includes("\\")) {
|
|
2496
|
-
throw new Error(`Invalid project agent role: "${role}".`);
|
|
2497
|
-
}
|
|
2498
|
-
return normalized;
|
|
2499
|
-
}
|
|
2500
|
-
function agentsDir(projectRoot) {
|
|
2501
|
-
const root = projectRoot ?? process.cwd();
|
|
2502
|
-
return path3.join(root, ".wrongstack", "agents");
|
|
2503
|
-
}
|
|
2504
|
-
function roleDir(role, projectRoot) {
|
|
2505
|
-
return path3.join(agentsDir(projectRoot), assertProjectAgentRole(role));
|
|
2506
|
-
}
|
|
2507
|
-
function learningPolicyPath(role, projectRoot) {
|
|
2508
|
-
return path3.join(roleDir(role, projectRoot), "learning.json");
|
|
2509
|
-
}
|
|
2510
|
-
|
|
2511
|
-
// src/coordination/agents/project-agent-learning-policy.ts
|
|
2949
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
2512
2950
|
var DEFAULT_LEARNING_POLICY = {
|
|
2513
2951
|
enabled: true,
|
|
2514
2952
|
lifetimeCaptureCount: 0
|
|
@@ -2516,7 +2954,7 @@ var DEFAULT_LEARNING_POLICY = {
|
|
|
2516
2954
|
function loadProjectAgentLearningPolicy(role, projectRoot) {
|
|
2517
2955
|
try {
|
|
2518
2956
|
const parsed = JSON.parse(
|
|
2519
|
-
|
|
2957
|
+
readFileSync4(learningPolicyPath(role, projectRoot), "utf8")
|
|
2520
2958
|
);
|
|
2521
2959
|
return {
|
|
2522
2960
|
enabled: parsed.enabled !== false,
|
|
@@ -2529,47 +2967,6 @@ function loadProjectAgentLearningPolicy(role, projectRoot) {
|
|
|
2529
2967
|
}
|
|
2530
2968
|
}
|
|
2531
2969
|
|
|
2532
|
-
// src/coordination/agents/project-agent-learning-entries.ts
|
|
2533
|
-
function splitLearnedEntries(body) {
|
|
2534
|
-
return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
|
|
2535
|
-
(entry) => entry.replace(/^# Learned wisdom for .+$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
|
|
2536
|
-
).filter(Boolean);
|
|
2537
|
-
}
|
|
2538
|
-
|
|
2539
|
-
// src/coordination/agents/project-agent-consolidation.ts
|
|
2540
|
-
import {
|
|
2541
|
-
existsSync,
|
|
2542
|
-
mkdirSync,
|
|
2543
|
-
readFileSync as readFileSync4,
|
|
2544
|
-
rmSync
|
|
2545
|
-
} from "node:fs";
|
|
2546
|
-
import * as path4 from "node:path";
|
|
2547
|
-
function consolidationPath(role, projectRoot) {
|
|
2548
|
-
return path4.join(roleDir(role, projectRoot), "consolidated.md");
|
|
2549
|
-
}
|
|
2550
|
-
function consolidationMetaPath(role, projectRoot) {
|
|
2551
|
-
return path4.join(roleDir(role, projectRoot), "consolidation.json");
|
|
2552
|
-
}
|
|
2553
|
-
function loadProjectAgentConsolidated(role, projectRoot) {
|
|
2554
|
-
try {
|
|
2555
|
-
return readFileSync4(consolidationPath(role, projectRoot), "utf8").trim();
|
|
2556
|
-
} catch {
|
|
2557
|
-
return "";
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2560
|
-
function loadConsolidationMetadata(role, projectRoot) {
|
|
2561
|
-
try {
|
|
2562
|
-
const raw = readFileSync4(consolidationMetaPath(role, projectRoot), "utf8");
|
|
2563
|
-
const parsed = JSON.parse(raw);
|
|
2564
|
-
if (typeof parsed === "object" && parsed !== null && typeof parsed.consolidatedAt === "string" && typeof parsed.sourceEntryCount === "number") {
|
|
2565
|
-
return parsed;
|
|
2566
|
-
}
|
|
2567
|
-
return void 0;
|
|
2568
|
-
} catch {
|
|
2569
|
-
return void 0;
|
|
2570
|
-
}
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
2970
|
// src/coordination/agents/project-agent-identity.ts
|
|
2574
2971
|
function loadProjectAgentIdentity(role, projectRoot) {
|
|
2575
2972
|
const identityPath = path5.join(roleDir(role, projectRoot), "identity.md");
|
|
@@ -2898,6 +3295,64 @@ function isDirectory2(candidate) {
|
|
|
2898
3295
|
}
|
|
2899
3296
|
}
|
|
2900
3297
|
|
|
3298
|
+
// src/coordination/agents/types.ts
|
|
3299
|
+
var HOUR = 60 * 60 * 1e3;
|
|
3300
|
+
var LIGHT_BUDGET = {
|
|
3301
|
+
timeoutMs: 3 * HOUR,
|
|
3302
|
+
maxIterations: 3e3,
|
|
3303
|
+
maxToolCalls: 8e3
|
|
3304
|
+
};
|
|
3305
|
+
var MEDIUM_BUDGET = {
|
|
3306
|
+
timeoutMs: 5 * HOUR,
|
|
3307
|
+
maxIterations: 5e3,
|
|
3308
|
+
maxToolCalls: 14e3
|
|
3309
|
+
};
|
|
3310
|
+
var HEAVY_BUDGET = {
|
|
3311
|
+
timeoutMs: 10 * HOUR,
|
|
3312
|
+
maxIterations: 8e3,
|
|
3313
|
+
maxToolCalls: 2e4
|
|
3314
|
+
};
|
|
3315
|
+
var TOOLS = {
|
|
3316
|
+
/** Pure read/inspect — safe for analysis and review agents. */
|
|
3317
|
+
read: ["read", "grep", "glob", "search", "tree", "mailbox"],
|
|
3318
|
+
/** Read + structured inspection (logs, diffs, json, dependency audit). */
|
|
3319
|
+
inspect: ["read", "grep", "glob", "search", "tree", "json", "diff", "logs", "audit", "mailbox"],
|
|
3320
|
+
/** Read + edit (no shell). For agents that write code/docs but don't run it. */
|
|
3321
|
+
write: ["read", "grep", "glob", "search", "tree", "write", "edit", "replace", "patch", "mailbox"],
|
|
3322
|
+
/** Full build loop: edit + run (lint/format/typecheck/test/bash). */
|
|
3323
|
+
build: [
|
|
3324
|
+
"read",
|
|
3325
|
+
"grep",
|
|
3326
|
+
"glob",
|
|
3327
|
+
"search",
|
|
3328
|
+
"tree",
|
|
3329
|
+
"write",
|
|
3330
|
+
"edit",
|
|
3331
|
+
"replace",
|
|
3332
|
+
"patch",
|
|
3333
|
+
"bash",
|
|
3334
|
+
"exec",
|
|
3335
|
+
"lint",
|
|
3336
|
+
"format",
|
|
3337
|
+
"typecheck",
|
|
3338
|
+
"test",
|
|
3339
|
+
"mailbox"
|
|
3340
|
+
],
|
|
3341
|
+
/** Version control. */
|
|
3342
|
+
vcs: ["read", "grep", "glob", "git", "diff"],
|
|
3343
|
+
/** Dependency management + CVE audit. */
|
|
3344
|
+
deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
|
|
3345
|
+
/** Documentation authoring. */
|
|
3346
|
+
docs: ["read", "grep", "glob", "search", "tree", "write", "edit", "document", "mailbox"],
|
|
3347
|
+
/** Web research. */
|
|
3348
|
+
research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
|
|
3349
|
+
};
|
|
3350
|
+
var SPECIALIST_TOOLS = {
|
|
3351
|
+
browser: toolsForRuntimeCapabilities(["browser.interact"]),
|
|
3352
|
+
memory: toolsForRuntimeCapabilities(["memory.manage"]),
|
|
3353
|
+
mcp: toolsForRuntimeCapabilities(["mcp.dynamic"])
|
|
3354
|
+
};
|
|
3355
|
+
|
|
2901
3356
|
// src/coordination/agents/phase1-discovery.ts
|
|
2902
3357
|
var DISCOVERY_AGENTS = [
|
|
2903
3358
|
{
|
|
@@ -2933,7 +3388,7 @@ var DISCOVERY_AGENTS = [
|
|
|
2933
3388
|
id: "search",
|
|
2934
3389
|
name: "Search",
|
|
2935
3390
|
role: "search",
|
|
2936
|
-
tools: [...TOOLS.read],
|
|
3391
|
+
tools: [...TOOLS.read, "codebase-search"],
|
|
2937
3392
|
prompt: agentPrompt("search")
|
|
2938
3393
|
},
|
|
2939
3394
|
budget: MEDIUM_BUDGET,
|
|
@@ -3395,14 +3850,7 @@ var WAVE1_AGENTS = [
|
|
|
3395
3850
|
capability: {
|
|
3396
3851
|
phase: "domain",
|
|
3397
3852
|
summary: "Android platform specialist: Kotlin, Jetpack Compose, Android SDK, lifecycle and Play Store delivery.",
|
|
3398
|
-
keywords: [
|
|
3399
|
-
...ANDROID_META.signals,
|
|
3400
|
-
"android",
|
|
3401
|
-
"kotlin",
|
|
3402
|
-
"compose",
|
|
3403
|
-
"gradle",
|
|
3404
|
-
"playstore"
|
|
3405
|
-
]
|
|
3853
|
+
keywords: [...ANDROID_META.signals, "android", "kotlin", "compose", "gradle", "playstore"]
|
|
3406
3854
|
}
|
|
3407
3855
|
},
|
|
3408
3856
|
{
|
|
@@ -3417,14 +3865,7 @@ var WAVE1_AGENTS = [
|
|
|
3417
3865
|
capability: {
|
|
3418
3866
|
phase: "domain",
|
|
3419
3867
|
summary: "Desktop application specialist: Electron or Tauri, native packaging, IPC and OS integration.",
|
|
3420
|
-
keywords: [
|
|
3421
|
-
...DESKTOP_META.signals,
|
|
3422
|
-
"desktop",
|
|
3423
|
-
"electron",
|
|
3424
|
-
"tauri",
|
|
3425
|
-
"ipc",
|
|
3426
|
-
"tray"
|
|
3427
|
-
]
|
|
3868
|
+
keywords: [...DESKTOP_META.signals, "desktop", "electron", "tauri", "ipc", "tray"]
|
|
3428
3869
|
}
|
|
3429
3870
|
},
|
|
3430
3871
|
{
|
|
@@ -3500,7 +3941,7 @@ var WAVE1_AGENTS = [
|
|
|
3500
3941
|
id: "platform-engineer",
|
|
3501
3942
|
name: "Platform Engineer",
|
|
3502
3943
|
role: "platform-engineer",
|
|
3503
|
-
tools: [...TOOLS.build, "install", "git"
|
|
3944
|
+
tools: [...TOOLS.build, "install", "git"],
|
|
3504
3945
|
prompt: agentPrompt("platform-engineer")
|
|
3505
3946
|
},
|
|
3506
3947
|
budget: MEDIUM_BUDGET,
|
|
@@ -3744,8 +4185,7 @@ var WAVE2_AGENTS = [
|
|
|
3744
4185
|
id: "resilience-engineer",
|
|
3745
4186
|
name: "Resilience Engineer",
|
|
3746
4187
|
role: "resilience-engineer",
|
|
3747
|
-
tools: [...TOOLS.build, "logs"
|
|
3748
|
-
// uniqueness guaranteed by 'observability' vs chaos-engineer's 'test'
|
|
4188
|
+
tools: [...TOOLS.build, "logs"],
|
|
3749
4189
|
prompt: agentPrompt("resilience-engineer")
|
|
3750
4190
|
},
|
|
3751
4191
|
budget: MEDIUM_BUDGET,
|
|
@@ -3768,8 +4208,7 @@ var WAVE2_AGENTS = [
|
|
|
3768
4208
|
id: "chaos-engineer",
|
|
3769
4209
|
name: "Chaos Engineer",
|
|
3770
4210
|
role: "chaos-engineer",
|
|
3771
|
-
tools: [...TOOLS.build, "logs"
|
|
3772
|
-
// uniqueness guaranteed by 'observability' vs chaos-engineer's 'test'
|
|
4211
|
+
tools: [...TOOLS.build, "logs"],
|
|
3773
4212
|
prompt: agentPrompt("chaos-engineer")
|
|
3774
4213
|
},
|
|
3775
4214
|
budget: MEDIUM_BUDGET,
|
|
@@ -3910,21 +4349,7 @@ var VERIFY_AGENTS = [
|
|
|
3910
4349
|
id: "e2e",
|
|
3911
4350
|
name: "E2E",
|
|
3912
4351
|
role: "e2e",
|
|
3913
|
-
tools: [
|
|
3914
|
-
...TOOLS.build,
|
|
3915
|
-
"fetch",
|
|
3916
|
-
"playwright_navigate",
|
|
3917
|
-
"playwright_screenshot",
|
|
3918
|
-
"playwright_click",
|
|
3919
|
-
"playwright_type",
|
|
3920
|
-
"playwright_evaluate",
|
|
3921
|
-
"playwright_select_option",
|
|
3922
|
-
"playwright_hover",
|
|
3923
|
-
"playwright_fill_form",
|
|
3924
|
-
"playwright_wait_for",
|
|
3925
|
-
"playwright_press_key",
|
|
3926
|
-
"playwright_drag"
|
|
3927
|
-
],
|
|
4352
|
+
tools: [...TOOLS.build, "fetch", ...SPECIALIST_TOOLS.browser],
|
|
3928
4353
|
prompt: agentPrompt("e2e")
|
|
3929
4354
|
},
|
|
3930
4355
|
budget: HEAVY_BUDGET,
|
|
@@ -3959,21 +4384,7 @@ var VERIFY_AGENTS = [
|
|
|
3959
4384
|
id: "browser",
|
|
3960
4385
|
name: "Browser",
|
|
3961
4386
|
role: "browser",
|
|
3962
|
-
tools: [
|
|
3963
|
-
...TOOLS.read,
|
|
3964
|
-
"fetch",
|
|
3965
|
-
"playwright_navigate",
|
|
3966
|
-
"playwright_screenshot",
|
|
3967
|
-
"playwright_click",
|
|
3968
|
-
"playwright_type",
|
|
3969
|
-
"playwright_evaluate",
|
|
3970
|
-
"playwright_select_option",
|
|
3971
|
-
"playwright_hover",
|
|
3972
|
-
"playwright_fill_form",
|
|
3973
|
-
"playwright_wait_for",
|
|
3974
|
-
"playwright_press_key",
|
|
3975
|
-
"playwright_drag"
|
|
3976
|
-
],
|
|
4387
|
+
tools: [...TOOLS.read, "fetch", ...SPECIALIST_TOOLS.browser],
|
|
3977
4388
|
prompt: agentPrompt("browser")
|
|
3978
4389
|
},
|
|
3979
4390
|
budget: MEDIUM_BUDGET,
|
|
@@ -4674,27 +5085,7 @@ var DELIVERY_AGENTS = [
|
|
|
4674
5085
|
id: "devops",
|
|
4675
5086
|
name: "DevOps",
|
|
4676
5087
|
role: "devops",
|
|
4677
|
-
tools: [
|
|
4678
|
-
...TOOLS.build,
|
|
4679
|
-
"mcp__ssh__ssh_list_servers",
|
|
4680
|
-
"mcp__ssh__ssh_connection_status",
|
|
4681
|
-
"mcp__ssh__ssh_execute",
|
|
4682
|
-
"mcp__ssh__ssh_execute_sudo",
|
|
4683
|
-
"mcp__ssh__ssh_upload",
|
|
4684
|
-
"mcp__ssh__ssh_download",
|
|
4685
|
-
"mcp__ssh__ssh_sync",
|
|
4686
|
-
"mcp__ssh__ssh_deploy",
|
|
4687
|
-
"mcp__ssh__ssh_health_check",
|
|
4688
|
-
"mcp__ssh__ssh_service_status",
|
|
4689
|
-
"mcp__ssh__ssh_process_manager",
|
|
4690
|
-
"mcp__ssh__ssh_tunnel",
|
|
4691
|
-
"mcp__ssh__ssh_backup_create",
|
|
4692
|
-
"mcp__ssh__ssh_backup_list",
|
|
4693
|
-
"mcp__ssh__ssh_backup_restore",
|
|
4694
|
-
"mcp__ssh__ssh_db_list",
|
|
4695
|
-
"mcp__ssh__ssh_db_query",
|
|
4696
|
-
"mcp__ssh__ssh_profile"
|
|
4697
|
-
],
|
|
5088
|
+
tools: [...TOOLS.build, ...SPECIALIST_TOOLS.mcp],
|
|
4698
5089
|
prompt: agentPrompt("devops")
|
|
4699
5090
|
},
|
|
4700
5091
|
budget: MEDIUM_BUDGET,
|
|
@@ -4895,7 +5286,7 @@ var WAVE3_AGENTS = [
|
|
|
4895
5286
|
id: "messaging",
|
|
4896
5287
|
name: "Messaging",
|
|
4897
5288
|
role: "messaging",
|
|
4898
|
-
tools: [...TOOLS.build, "logs"
|
|
5289
|
+
tools: [...TOOLS.build, "logs"],
|
|
4899
5290
|
prompt: agentPrompt("messaging")
|
|
4900
5291
|
},
|
|
4901
5292
|
budget: HEAVY_BUDGET,
|
|
@@ -5336,7 +5727,7 @@ var WAVE4_AGENTS = [
|
|
|
5336
5727
|
id: "memory-curator",
|
|
5337
5728
|
name: "Memory Curator",
|
|
5338
5729
|
role: "memory-curator",
|
|
5339
|
-
tools: [...TOOLS.read,
|
|
5730
|
+
tools: [...TOOLS.read, ...SPECIALIST_TOOLS.memory],
|
|
5340
5731
|
prompt: agentPrompt("memory-curator")
|
|
5341
5732
|
},
|
|
5342
5733
|
budget: LIGHT_BUDGET,
|
|
@@ -5358,7 +5749,7 @@ var WAVE4_AGENTS = [
|
|
|
5358
5749
|
id: "fleet-coordinator",
|
|
5359
5750
|
name: "Fleet Coordinator",
|
|
5360
5751
|
role: "fleet-coordinator",
|
|
5361
|
-
tools: [...TOOLS.read, "plan"
|
|
5752
|
+
tools: [...TOOLS.read, "plan"],
|
|
5362
5753
|
prompt: agentPrompt("fleet-coordinator")
|
|
5363
5754
|
},
|
|
5364
5755
|
budget: MEDIUM_BUDGET,
|
|
@@ -5458,8 +5849,20 @@ var ROLE_SKILL_SETS = {
|
|
|
5458
5849
|
cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
|
|
5459
5850
|
"tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
|
|
5460
5851
|
// ── Wave 1: Platform and systems engineering ──────────────────────────
|
|
5461
|
-
android: skillSet(
|
|
5462
|
-
|
|
5852
|
+
android: skillSet(
|
|
5853
|
+
"react-modern",
|
|
5854
|
+
"typescript-strict",
|
|
5855
|
+
"testing",
|
|
5856
|
+
"security-scanner",
|
|
5857
|
+
"tech-stack"
|
|
5858
|
+
),
|
|
5859
|
+
desktop: skillSet(
|
|
5860
|
+
"node-modern",
|
|
5861
|
+
"typescript-strict",
|
|
5862
|
+
"testing",
|
|
5863
|
+
"security-scanner",
|
|
5864
|
+
"tech-stack"
|
|
5865
|
+
),
|
|
5463
5866
|
realtime: skillSet(
|
|
5464
5867
|
"api-design",
|
|
5465
5868
|
"observability",
|
|
@@ -5563,26 +5966,9 @@ var ROLE_SKILL_SETS = {
|
|
|
5563
5966
|
"security-scanner",
|
|
5564
5967
|
"tech-stack"
|
|
5565
5968
|
),
|
|
5566
|
-
"benchmark-engineer": skillSet(
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
"output-standards",
|
|
5570
|
-
"tech-stack"
|
|
5571
|
-
),
|
|
5572
|
-
"memory-curator": skillSet(
|
|
5573
|
-
"mnemosyne",
|
|
5574
|
-
"audit-log",
|
|
5575
|
-
"testing",
|
|
5576
|
-
"output-standards",
|
|
5577
|
-
"tech-stack"
|
|
5578
|
-
),
|
|
5579
|
-
"fleet-coordinator": skillSet(
|
|
5580
|
-
"multi-agent",
|
|
5581
|
-
"sdd",
|
|
5582
|
-
"output-standards",
|
|
5583
|
-
"testing",
|
|
5584
|
-
"tech-stack"
|
|
5585
|
-
),
|
|
5969
|
+
"benchmark-engineer": skillSet("observability", "testing", "output-standards", "tech-stack"),
|
|
5970
|
+
"memory-curator": skillSet("mnemosyne", "audit-log", "testing", "output-standards", "tech-stack"),
|
|
5971
|
+
"fleet-coordinator": skillSet("multi-agent", "sdd", "output-standards", "testing", "tech-stack"),
|
|
5586
5972
|
// ── Wave 2: Security, resilience, governance and agent platform ────────
|
|
5587
5973
|
"threat-modeler": skillSet(
|
|
5588
5974
|
"security-scanner",
|
|
@@ -5634,6 +6020,7 @@ var SHADOW_AGENT_SKILLS = skillSet(
|
|
|
5634
6020
|
"observability",
|
|
5635
6021
|
"security-scanner"
|
|
5636
6022
|
);
|
|
6023
|
+
var MAX_EAGER_ROSTER_SKILLS = 3;
|
|
5637
6024
|
function assignSkillsToAgents(definitions) {
|
|
5638
6025
|
return definitions.map((definition) => {
|
|
5639
6026
|
const role = definition.config.role;
|
|
@@ -5647,7 +6034,8 @@ function assignSkillsToAgents(definitions) {
|
|
|
5647
6034
|
...definition,
|
|
5648
6035
|
config: {
|
|
5649
6036
|
...definition.config,
|
|
5650
|
-
|
|
6037
|
+
capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
|
|
6038
|
+
skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS)
|
|
5651
6039
|
}
|
|
5652
6040
|
};
|
|
5653
6041
|
});
|
|
@@ -6199,8 +6587,18 @@ var SYSTEM_CONFIG_VIEW_SCHEMA = {
|
|
|
6199
6587
|
properties: {
|
|
6200
6588
|
section: {
|
|
6201
6589
|
type: "string",
|
|
6202
|
-
enum: [
|
|
6203
|
-
|
|
6590
|
+
enum: [
|
|
6591
|
+
"all",
|
|
6592
|
+
"providers",
|
|
6593
|
+
"models",
|
|
6594
|
+
"fallbacks",
|
|
6595
|
+
"matrix",
|
|
6596
|
+
"agents",
|
|
6597
|
+
"fleet",
|
|
6598
|
+
"refiner",
|
|
6599
|
+
"doctor"
|
|
6600
|
+
],
|
|
6601
|
+
description: "Which section to show: all (everything), providers (configured providers + keys), models (favorites + leader), fallbacks (chain + profiles + toggles), matrix (per-role assignments), agents (every catalog agent with resolved model), fleet (concurrency + lifetime spawn/token/cost budgets), refiner (goal refinement config), doctor (validate config and show issues/warnings). Default: all."
|
|
6204
6602
|
}
|
|
6205
6603
|
},
|
|
6206
6604
|
additionalProperties: false
|
|
@@ -6302,6 +6700,7 @@ ${content}`);
|
|
|
6302
6700
|
const profiles = config.fallbackProfiles ?? {};
|
|
6303
6701
|
const chain = config.fallbackModels ?? [];
|
|
6304
6702
|
const matrix = config.modelMatrix ?? {};
|
|
6703
|
+
const fleetBudget = config.fleet?.budget;
|
|
6305
6704
|
const bridge = config.fallbackBridge?.trim();
|
|
6306
6705
|
if (bridge) {
|
|
6307
6706
|
const parsed = parseModelRef(bridge);
|
|
@@ -6371,6 +6770,26 @@ ${content}`);
|
|
|
6371
6770
|
}
|
|
6372
6771
|
}
|
|
6373
6772
|
}
|
|
6773
|
+
if (typeof config.maxConcurrent === "number") {
|
|
6774
|
+
if (!Number.isFinite(config.maxConcurrent) || config.maxConcurrent < 0) {
|
|
6775
|
+
issues.push(`maxConcurrent must be a non-negative number (got ${config.maxConcurrent})`);
|
|
6776
|
+
} else if (config.maxConcurrent === 0) {
|
|
6777
|
+
warnings.push("maxConcurrent is 0 \u2014 subagent concurrency effectively disabled");
|
|
6778
|
+
} else {
|
|
6779
|
+
ok.push(`maxConcurrent ${config.maxConcurrent}`);
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
if (fleetBudget && typeof fleetBudget === "object") {
|
|
6783
|
+
for (const key of ["maxSpawns", "maxTokens", "maxCostUsd"]) {
|
|
6784
|
+
const v = fleetBudget[key];
|
|
6785
|
+
if (v === void 0) continue;
|
|
6786
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0) {
|
|
6787
|
+
issues.push(`fleet.budget.${key} must be a non-negative number (got ${JSON.stringify(v)})`);
|
|
6788
|
+
} else {
|
|
6789
|
+
ok.push(`fleet.budget.${key} ${v}`);
|
|
6790
|
+
}
|
|
6791
|
+
}
|
|
6792
|
+
}
|
|
6374
6793
|
for (const [key, entry] of Object.entries(matrix)) {
|
|
6375
6794
|
const eProvider = entry.provider ?? config.provider;
|
|
6376
6795
|
const eModel = entry.model;
|
|
@@ -6411,6 +6830,27 @@ ${content}`);
|
|
|
6411
6830
|
}
|
|
6412
6831
|
addSection("Configuration Doctor", lines.join("\n"));
|
|
6413
6832
|
}
|
|
6833
|
+
if (section === "all" || section === "fleet") {
|
|
6834
|
+
const fleet = config.fleet;
|
|
6835
|
+
const budget = fleet?.budget;
|
|
6836
|
+
const lifecycle = fleet?.lifecycle;
|
|
6837
|
+
const fmt = (n, unit = "") => typeof n === "number" && Number.isFinite(n) ? `${n}${unit}` : "(default)";
|
|
6838
|
+
addSection(
|
|
6839
|
+
"Fleet Budgets (configured ceilings)",
|
|
6840
|
+
[
|
|
6841
|
+
` maxConcurrent: ${typeof config.maxConcurrent === "number" ? config.maxConcurrent : "(default 4)"}`,
|
|
6842
|
+
` fleet.budget.maxSpawns: ${fmt(budget?.maxSpawns)} ${typeof budget?.maxSpawns !== "number" ? "\u2192 default 64" : ""}`,
|
|
6843
|
+
` fleet.budget.maxTokens: ${fmt(budget?.maxTokens)}`,
|
|
6844
|
+
` fleet.budget.maxCostUsd: ${fmt(budget?.maxCostUsd)}`,
|
|
6845
|
+
` fleet.lifecycle.idleTimeoutMs: ${fmt(lifecycle?.idleTimeoutMs, "ms")}`,
|
|
6846
|
+
` fleet.lifecycle.retireOnTaskComplete: ${lifecycle?.retireOnTaskComplete === void 0 ? "(default true)" : String(lifecycle.retireOnTaskComplete)}`,
|
|
6847
|
+
"",
|
|
6848
|
+
" Live used/remaining spawns are on /fleet status (not static config).",
|
|
6849
|
+
" Override ceilings: --max-concurrent / WRONGSTACK_MAX_CONCURRENT,",
|
|
6850
|
+
" --max-spawns / WRONGSTACK_MAX_SPAWNS, or fleet.budget.maxSpawns in profile."
|
|
6851
|
+
].join("\n")
|
|
6852
|
+
);
|
|
6853
|
+
}
|
|
6414
6854
|
if (section === "all" || section === "refiner") {
|
|
6415
6855
|
const ref = config.autonomy;
|
|
6416
6856
|
addSection(
|
|
@@ -7732,6 +8172,7 @@ var OneShotOrchestrator = class {
|
|
|
7732
8172
|
tokens: { input: 0, output: 0, total: 0 },
|
|
7733
8173
|
durationMs: Math.round(performance.now() - startedAt),
|
|
7734
8174
|
fromFallback: false,
|
|
8175
|
+
attempts: 0,
|
|
7735
8176
|
error: "No provider or model could be resolved. Check your config."
|
|
7736
8177
|
};
|
|
7737
8178
|
}
|
|
@@ -7746,6 +8187,7 @@ var OneShotOrchestrator = class {
|
|
|
7746
8187
|
tokens: { input: 0, output: 0, total: 0 },
|
|
7747
8188
|
durationMs: Math.round(performance.now() - startedAt),
|
|
7748
8189
|
fromFallback: false,
|
|
8190
|
+
attempts: 0,
|
|
7749
8191
|
error: `Cannot build provider "${target.providerId}": ${err instanceof Error ? err.message : String(err)}`
|
|
7750
8192
|
};
|
|
7751
8193
|
}
|
|
@@ -7758,11 +8200,13 @@ var OneShotOrchestrator = class {
|
|
|
7758
8200
|
let fromFallback = false;
|
|
7759
8201
|
let lastError;
|
|
7760
8202
|
let fallbackEligible = false;
|
|
8203
|
+
let attempts = 0;
|
|
7761
8204
|
if (tracker && !tracker.isAvailable(target.providerId, target.model) || !evaluateModelCalendar(config.modelAvailabilitySchedule, target.providerId, target.model).allowed) {
|
|
7762
8205
|
this.opts.logger?.debug(
|
|
7763
8206
|
`one-shot: primary "${target.providerId}/${target.model}" is blocked \u2014 trying fallback`
|
|
7764
8207
|
);
|
|
7765
8208
|
} else {
|
|
8209
|
+
attempts += 1;
|
|
7766
8210
|
const primaryAttempt = await this.tryCall(
|
|
7767
8211
|
provider,
|
|
7768
8212
|
request,
|
|
@@ -7777,10 +8221,17 @@ var OneShotOrchestrator = class {
|
|
|
7777
8221
|
tracker?.recordSuccess(target.providerId, target.model);
|
|
7778
8222
|
servingProviderId = provider.id;
|
|
7779
8223
|
servingModel = target.model;
|
|
7780
|
-
return this.buildResult(result, servingProviderId, servingModel, false, startedAt);
|
|
8224
|
+
return this.buildResult(result, servingProviderId, servingModel, false, startedAt, attempts);
|
|
7781
8225
|
}
|
|
7782
8226
|
if (!fallbackEligible || chain.length === 0) {
|
|
7783
|
-
return this.buildErrorResult(
|
|
8227
|
+
return this.buildErrorResult(
|
|
8228
|
+
lastError,
|
|
8229
|
+
target.providerId,
|
|
8230
|
+
target.model,
|
|
8231
|
+
false,
|
|
8232
|
+
startedAt,
|
|
8233
|
+
attempts
|
|
8234
|
+
);
|
|
7784
8235
|
}
|
|
7785
8236
|
}
|
|
7786
8237
|
const estimatedTokens = estimateRequestTokens(request.messages, request.system, []).total;
|
|
@@ -7808,6 +8259,7 @@ var OneShotOrchestrator = class {
|
|
|
7808
8259
|
}
|
|
7809
8260
|
servingProviderId = fbProvider.id;
|
|
7810
8261
|
servingModel = entry.model;
|
|
8262
|
+
attempts += 1;
|
|
7811
8263
|
const attempt = await this.tryCall(
|
|
7812
8264
|
fbProvider,
|
|
7813
8265
|
this.buildRequest(input, entry.model),
|
|
@@ -7818,7 +8270,14 @@ var OneShotOrchestrator = class {
|
|
|
7818
8270
|
if (attempt.response) {
|
|
7819
8271
|
tracker?.recordSuccess(entry.providerId, entry.model);
|
|
7820
8272
|
fromFallback = true;
|
|
7821
|
-
return this.buildResult(
|
|
8273
|
+
return this.buildResult(
|
|
8274
|
+
attempt.response,
|
|
8275
|
+
servingProviderId,
|
|
8276
|
+
servingModel,
|
|
8277
|
+
true,
|
|
8278
|
+
startedAt,
|
|
8279
|
+
attempts
|
|
8280
|
+
);
|
|
7822
8281
|
}
|
|
7823
8282
|
lastError = attempt.error;
|
|
7824
8283
|
}
|
|
@@ -7827,7 +8286,8 @@ var OneShotOrchestrator = class {
|
|
|
7827
8286
|
servingProviderId,
|
|
7828
8287
|
servingModel,
|
|
7829
8288
|
fromFallback,
|
|
7830
|
-
startedAt
|
|
8289
|
+
startedAt,
|
|
8290
|
+
attempts
|
|
7831
8291
|
);
|
|
7832
8292
|
}
|
|
7833
8293
|
// ── Private helpers ─────────────────────────────────────────────
|
|
@@ -7929,10 +8389,9 @@ var OneShotOrchestrator = class {
|
|
|
7929
8389
|
/** Attempt a provider call while preserving the actual failure for callers. */
|
|
7930
8390
|
async tryCall(provider, request, signal, providerId, model) {
|
|
7931
8391
|
try {
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
};
|
|
8392
|
+
const direct = (req) => provider.complete(req, { signal });
|
|
8393
|
+
const response = this.opts.wrapProviderCall ? await this.opts.wrapProviderCall(request, direct) : await direct(request);
|
|
8394
|
+
return { response, fallbackEligible: false };
|
|
7936
8395
|
} catch (err) {
|
|
7937
8396
|
if (err instanceof ProviderError && providerId && model) {
|
|
7938
8397
|
this.opts.statusTracker?.recordFailure(
|
|
@@ -7946,12 +8405,17 @@ var OneShotOrchestrator = class {
|
|
|
7946
8405
|
}
|
|
7947
8406
|
return {
|
|
7948
8407
|
error: err,
|
|
7949
|
-
|
|
8408
|
+
// Provider failures retain their normal fallback policy. A plain error
|
|
8409
|
+
// thrown by the host wrapper is a local refusal (for example,
|
|
8410
|
+
// prompt-firewall block mode), not evidence that another provider may
|
|
8411
|
+
// succeed; retrying would only replay the same blocked request and
|
|
8412
|
+
// misattribute the final error to the last fallback.
|
|
8413
|
+
fallbackEligible: !signal.aborted && (err instanceof ProviderError ? isFallbackWorthy(err.kind) : this.opts.wrapProviderCall === void 0)
|
|
7950
8414
|
};
|
|
7951
8415
|
}
|
|
7952
8416
|
}
|
|
7953
8417
|
/** Build a success result from a provider Response. */
|
|
7954
|
-
buildResult(response, servingProviderId, servingModel, fromFallback, startedAt) {
|
|
8418
|
+
buildResult(response, servingProviderId, servingModel, fromFallback, startedAt, attempts) {
|
|
7955
8419
|
const textBlocks = response.content.filter(isTextBlock);
|
|
7956
8420
|
const text = textBlocks.map((b) => b.text).join("\n").trim();
|
|
7957
8421
|
return {
|
|
@@ -7965,11 +8429,12 @@ var OneShotOrchestrator = class {
|
|
|
7965
8429
|
},
|
|
7966
8430
|
durationMs: Math.round(performance.now() - startedAt),
|
|
7967
8431
|
fromFallback,
|
|
8432
|
+
attempts,
|
|
7968
8433
|
stopReason: response.stopReason
|
|
7969
8434
|
};
|
|
7970
8435
|
}
|
|
7971
8436
|
/** Build a total-failure error result. */
|
|
7972
|
-
buildErrorResult(error, servingProviderId, servingModel, fromFallback, startedAt) {
|
|
8437
|
+
buildErrorResult(error, servingProviderId, servingModel, fromFallback, startedAt, attempts) {
|
|
7973
8438
|
return {
|
|
7974
8439
|
text: "",
|
|
7975
8440
|
model: servingModel,
|
|
@@ -7977,6 +8442,7 @@ var OneShotOrchestrator = class {
|
|
|
7977
8442
|
tokens: { input: 0, output: 0, total: 0 },
|
|
7978
8443
|
durationMs: Math.round(performance.now() - startedAt),
|
|
7979
8444
|
fromFallback,
|
|
8445
|
+
attempts,
|
|
7980
8446
|
error: error instanceof Error ? error.message : String(error ?? "Unknown error")
|
|
7981
8447
|
};
|
|
7982
8448
|
}
|
|
@@ -8075,7 +8541,8 @@ function createOneShotLLMTool(opts) {
|
|
|
8075
8541
|
getConfig: opts.getConfig,
|
|
8076
8542
|
fallbackProfileManager: opts.fallbackProfileManager,
|
|
8077
8543
|
modelRouter: opts.modelRouter,
|
|
8078
|
-
logger: opts.logger
|
|
8544
|
+
logger: opts.logger,
|
|
8545
|
+
wrapProviderCall: opts.wrapProviderCall
|
|
8079
8546
|
});
|
|
8080
8547
|
return {
|
|
8081
8548
|
name: ONE_SHOT_LLM_TOOL_NAME,
|
|
@@ -8107,6 +8574,63 @@ function createOneShotLLMTool(opts) {
|
|
|
8107
8574
|
};
|
|
8108
8575
|
}
|
|
8109
8576
|
|
|
8577
|
+
// src/plugin/config.ts
|
|
8578
|
+
function pluginEntryMatchesName(configuredName, name, aliases = []) {
|
|
8579
|
+
for (const candidate of [name, ...aliases]) {
|
|
8580
|
+
if (configuredName === candidate) return true;
|
|
8581
|
+
if (configuredName === `@wrongstack/plugins/${candidate}`) return true;
|
|
8582
|
+
}
|
|
8583
|
+
return false;
|
|
8584
|
+
}
|
|
8585
|
+
function resolvePluginEnablement(input) {
|
|
8586
|
+
if (input.config?.features?.plugins === false) {
|
|
8587
|
+
return { enabled: false, source: "feature-flag" };
|
|
8588
|
+
}
|
|
8589
|
+
const names = [.../* @__PURE__ */ new Set([input.name, ...input.aliases ?? []])];
|
|
8590
|
+
const matches = input.matches ?? ((configuredName) => pluginEntryMatchesName(configuredName, input.name, input.aliases ?? []));
|
|
8591
|
+
const plugins = input.config?.plugins;
|
|
8592
|
+
if (Array.isArray(plugins)) {
|
|
8593
|
+
for (const candidate of plugins) {
|
|
8594
|
+
if (typeof candidate === "string") {
|
|
8595
|
+
if (matches(candidate)) return { enabled: true, source: "plugin-entry" };
|
|
8596
|
+
continue;
|
|
8597
|
+
}
|
|
8598
|
+
if (!isPluginEntry(candidate) || !matches(candidate.name)) continue;
|
|
8599
|
+
return { enabled: candidate.enabled !== false, source: "plugin-entry" };
|
|
8600
|
+
}
|
|
8601
|
+
}
|
|
8602
|
+
for (const name of names) {
|
|
8603
|
+
const enabled = input.config?.extensions?.[name]?.["enabled"];
|
|
8604
|
+
if (typeof enabled === "boolean") return { enabled, source: "extension" };
|
|
8605
|
+
}
|
|
8606
|
+
return { enabled: input.defaultState === "active", source: "default" };
|
|
8607
|
+
}
|
|
8608
|
+
function isPluginEntry(value) {
|
|
8609
|
+
return isRecord2(value) && typeof value["name"] === "string";
|
|
8610
|
+
}
|
|
8611
|
+
function isRecord2(value) {
|
|
8612
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8613
|
+
}
|
|
8614
|
+
|
|
8615
|
+
// src/security/readonly-permission-policy.ts
|
|
8616
|
+
var MUTATION_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
8617
|
+
ToolCapabilities.FS_WRITE,
|
|
8618
|
+
ToolCapabilities.FS_WRITE_OUTSIDE_PROJECT,
|
|
8619
|
+
ToolCapabilities.MEMORY_WRITE,
|
|
8620
|
+
ToolCapabilities.MEMORY_DELETE,
|
|
8621
|
+
ToolCapabilities.SHELL_ARBITRARY,
|
|
8622
|
+
ToolCapabilities.SHELL_RESTRICTED,
|
|
8623
|
+
ToolCapabilities.SHELL_EXEC,
|
|
8624
|
+
ToolCapabilities.TOOL_MUTATE_ANY,
|
|
8625
|
+
ToolCapabilities.CONFIG_MUTATE,
|
|
8626
|
+
ToolCapabilities.PACKAGE_INSTALL,
|
|
8627
|
+
ToolCapabilities.SUBAGENT_SPAWN
|
|
8628
|
+
]);
|
|
8629
|
+
function toolMutates(tool) {
|
|
8630
|
+
if (tool.mutating === true) return true;
|
|
8631
|
+
return (tool.capabilities ?? []).some((c) => MUTATION_CAPABILITIES.has(c));
|
|
8632
|
+
}
|
|
8633
|
+
|
|
8110
8634
|
// src/utils/json-schema-validate.ts
|
|
8111
8635
|
function validateAgainstSchema(value, schema) {
|
|
8112
8636
|
const errors = [];
|
|
@@ -8230,6 +8754,12 @@ function deepEqual(a, b) {
|
|
|
8230
8754
|
|
|
8231
8755
|
// src/tools/plugin-manager.ts
|
|
8232
8756
|
var PLUGIN_MANAGER_TOOL_NAME = "plugin_manager";
|
|
8757
|
+
var PLUGIN_VIEW_STATE_SOURCE = {
|
|
8758
|
+
"feature-flag": "feature_flag",
|
|
8759
|
+
"plugin-entry": "config",
|
|
8760
|
+
extension: "extension",
|
|
8761
|
+
default: "default"
|
|
8762
|
+
};
|
|
8233
8763
|
var INPUT_SCHEMA3 = {
|
|
8234
8764
|
type: "object",
|
|
8235
8765
|
properties: {
|
|
@@ -8416,7 +8946,36 @@ function createPluginManagerTool(opts) {
|
|
|
8416
8946
|
directCall: { tool: tool.name, input: input.input ?? {}, inputSchema: tool.inputSchema }
|
|
8417
8947
|
};
|
|
8418
8948
|
}
|
|
8419
|
-
const
|
|
8949
|
+
const dangerousCaps = getDangerousCapabilities(tool);
|
|
8950
|
+
if (dangerousCaps.length > 0) {
|
|
8951
|
+
return {
|
|
8952
|
+
status: "needs_direct_call",
|
|
8953
|
+
message: `Tool "${tool.name}" declares dangerous capabilities (${dangerousCaps.join(", ")}); the permission pipeline must rule on it. Call it directly.`,
|
|
8954
|
+
directCall: { tool: tool.name, input: input.input ?? {}, inputSchema: tool.inputSchema }
|
|
8955
|
+
};
|
|
8956
|
+
}
|
|
8957
|
+
if (ctx.meta["readOnly"] === true && toolMutates(tool)) {
|
|
8958
|
+
return {
|
|
8959
|
+
status: "needs_direct_call",
|
|
8960
|
+
message: `Session is in read-only mode and "${tool.name}" mutates. Call it directly so read-only mode can rule on it.`,
|
|
8961
|
+
directCall: { tool: tool.name, input: input.input ?? {}, inputSchema: tool.inputSchema }
|
|
8962
|
+
};
|
|
8963
|
+
}
|
|
8964
|
+
let nestedInput = input.input ?? {};
|
|
8965
|
+
const hooks = opts.getHookRunner?.();
|
|
8966
|
+
if (hooks) {
|
|
8967
|
+
const pre = await hooks.preToolUse(tool.name, nestedInput, ctx, {
|
|
8968
|
+
capabilities: tool.capabilities,
|
|
8969
|
+
mutating: tool.mutating
|
|
8970
|
+
});
|
|
8971
|
+
if (pre.block) {
|
|
8972
|
+
return {
|
|
8973
|
+
status: "error",
|
|
8974
|
+
message: `A policy hook blocked "${tool.name}"` + (pre.reason ? `: ${pre.reason}` : ".")
|
|
8975
|
+
};
|
|
8976
|
+
}
|
|
8977
|
+
if (pre.input) nestedInput = pre.input;
|
|
8978
|
+
}
|
|
8420
8979
|
const validation = validateAgainstSchema(nestedInput, tool.inputSchema);
|
|
8421
8980
|
if (!validation.ok) {
|
|
8422
8981
|
return {
|
|
@@ -8461,10 +9020,14 @@ function buildPluginViews(opts) {
|
|
|
8461
9020
|
return catalog.map((entry) => {
|
|
8462
9021
|
const aliases = [...entry.aliases ?? []];
|
|
8463
9022
|
const names = /* @__PURE__ */ new Set([entry.name, ...aliases]);
|
|
8464
|
-
const
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
9023
|
+
const { enabled, source } = resolvePluginEnablement({
|
|
9024
|
+
name: entry.name,
|
|
9025
|
+
aliases,
|
|
9026
|
+
defaultState: entry.defaultState,
|
|
9027
|
+
config,
|
|
9028
|
+
matches: (spec) => names.has(spec)
|
|
9029
|
+
});
|
|
9030
|
+
const stateSource = PLUGIN_VIEW_STATE_SOURCE[source];
|
|
8468
9031
|
const tools = pluginTools(opts.toolRegistry, entry.name, aliases);
|
|
8469
9032
|
const managerControl = isManagerLocked(config, entry.name, aliases) ? "locked" : "allowed";
|
|
8470
9033
|
return {
|