@wrongstack/core 0.4.1 → 0.5.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.
Files changed (60) hide show
  1. package/dist/{agent-bridge-DaOnnHNW.d.ts → agent-bridge-BkjOkSkD.d.ts} +1 -1
  2. package/dist/{compactor-CFky6JKM.d.ts → compactor-CTHjZAwf.d.ts} +1 -1
  3. package/dist/{config-RlhKLjme.d.ts → config-DO2eCKgq.d.ts} +1 -1
  4. package/dist/{context-B1kBj1lY.d.ts → context-P207fTpo.d.ts} +11 -0
  5. package/dist/coordination/index.d.ts +10 -891
  6. package/dist/coordination/index.js +407 -236
  7. package/dist/coordination/index.js.map +1 -1
  8. package/dist/defaults/index.d.ts +19 -18
  9. package/dist/defaults/index.js +2139 -284
  10. package/dist/defaults/index.js.map +1 -1
  11. package/dist/{events-BBaKeMfb.d.ts → events-DYAbU84e.d.ts} +41 -1
  12. package/dist/execution/index.d.ts +19 -13
  13. package/dist/execution/index.js +15 -6
  14. package/dist/execution/index.js.map +1 -1
  15. package/dist/extension/index.d.ts +6 -6
  16. package/dist/index-BHL8QDCL.d.ts +899 -0
  17. package/dist/{index-meJRuQtc.d.ts → index-BNzUUDVR.d.ts} +8 -6
  18. package/dist/index.d.ts +30 -27
  19. package/dist/index.js +3764 -1446
  20. package/dist/index.js.map +1 -1
  21. package/dist/infrastructure/index.d.ts +6 -6
  22. package/dist/infrastructure/index.js +67 -2
  23. package/dist/infrastructure/index.js.map +1 -1
  24. package/dist/kernel/index.d.ts +10 -9
  25. package/dist/kernel/index.js.map +1 -1
  26. package/dist/{mcp-servers-Cf4-bJnd.d.ts → mcp-servers-CLkhKdnF.d.ts} +18 -3
  27. package/dist/models/index.d.ts +2 -2
  28. package/dist/models/index.js +106 -0
  29. package/dist/models/index.js.map +1 -1
  30. package/dist/{multi-agent-D5m66hzB.d.ts → multi-agent-B1Nn9eC0.d.ts} +71 -16
  31. package/dist/observability/index.d.ts +2 -2
  32. package/dist/{path-resolver-Bg4OP5fi.d.ts → path-resolver-mPccVA0l.d.ts} +9 -2
  33. package/dist/{provider-runner-CU9gnU2E.d.ts → provider-runner-D7lHu07L.d.ts} +3 -3
  34. package/dist/{skill-DayhFUBb.d.ts → retry-policy-CnYVKGPv.d.ts} +2 -28
  35. package/dist/sdd/index.d.ts +419 -5
  36. package/dist/sdd/index.js +1580 -1
  37. package/dist/sdd/index.js.map +1 -1
  38. package/dist/{secret-scrubber-Dyh5LVYL.d.ts → secret-scrubber-BSgec8CU.d.ts} +1 -1
  39. package/dist/{secret-scrubber-DyUAWS09.d.ts → secret-scrubber-Buo9zLGs.d.ts} +1 -1
  40. package/dist/security/index.d.ts +8 -4
  41. package/dist/security/index.js +8 -0
  42. package/dist/security/index.js.map +1 -1
  43. package/dist/{selector-DBEiwXBk.d.ts → selector-B-CGOshv.d.ts} +1 -1
  44. package/dist/{session-reader-D-z0AgHs.d.ts → session-reader-DmOEmSkq.d.ts} +1 -1
  45. package/dist/skill-CxuWrsKK.d.ts +29 -0
  46. package/dist/skills/index.d.ts +136 -0
  47. package/dist/skills/index.js +478 -0
  48. package/dist/skills/index.js.map +1 -0
  49. package/dist/storage/index.d.ts +5 -5
  50. package/dist/{system-prompt-DNetCecu.d.ts → system-prompt-CzY1zrbC.d.ts} +1 -1
  51. package/dist/{tool-executor-B5bgmEgE.d.ts → tool-executor-CYe5cp5U.d.ts} +4 -4
  52. package/dist/types/index.d.ts +16 -15
  53. package/dist/types/index.js +116 -0
  54. package/dist/types/index.js.map +1 -1
  55. package/dist/utils/index.d.ts +35 -2
  56. package/dist/utils/index.js +49 -1
  57. package/dist/utils/index.js.map +1 -1
  58. package/package.json +5 -1
  59. package/skills/sdd/SKILL.md +93 -6
  60. package/skills/skill-creator/SKILL.md +98 -0
@@ -450,7 +450,9 @@ var FleetBus = class {
450
450
  "session.damaged",
451
451
  "compaction.fired",
452
452
  "compaction.failed",
453
- "token.threshold"
453
+ "token.threshold",
454
+ // Subagent hit a soft budget limit — coordinator can extend or stop.
455
+ "budget.threshold_reached"
454
456
  ];
455
457
  const offs = [];
456
458
  for (const t of FORWARDED_TYPES) {
@@ -713,6 +715,21 @@ var BudgetExceededError = class extends Error {
713
715
  this.observed = observed;
714
716
  }
715
717
  };
718
+ var BudgetThresholdSignal = class extends Error {
719
+ kind;
720
+ limit;
721
+ used;
722
+ /** Resolves to 'extend' (with optional new limits) or 'stop' */
723
+ decision;
724
+ constructor(kind, limit, used, decision) {
725
+ super(`Budget soft limit: ${kind} (limit=${limit}, used=${used})`);
726
+ this.name = "BudgetThresholdSignal";
727
+ this.kind = kind;
728
+ this.limit = limit;
729
+ this.used = used;
730
+ this.decision = decision;
731
+ }
732
+ };
716
733
  var SubagentBudget = class {
717
734
  limits;
718
735
  iterations = 0;
@@ -721,22 +738,111 @@ var SubagentBudget = class {
721
738
  tokenOutput = 0;
722
739
  costUsd = 0;
723
740
  startTime = null;
741
+ _onThreshold;
742
+ /**
743
+ * Injected by the runner when wiring the budget to its EventBus.
744
+ * Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
745
+ */
746
+ _events;
747
+ /**
748
+ * Optional callback for soft-limit handling. When set, the budget will
749
+ * call this instead of throwing when a limit is first reached. The
750
+ * handler decides whether to throw, continue, or ask the coordinator.
751
+ */
752
+ get onThreshold() {
753
+ return this._onThreshold;
754
+ }
755
+ set onThreshold(fn) {
756
+ this._onThreshold = fn;
757
+ }
724
758
  constructor(limits = {}) {
725
759
  this.limits = Object.freeze({ ...limits });
726
760
  }
727
761
  start() {
728
762
  this.startTime = Date.now();
729
763
  }
764
+ /** Returns true if we're within 10% of any limit — useful for pre-flight checks. */
765
+ isNearLimit() {
766
+ const { maxIterations, maxToolCalls, maxTokens, maxCostUsd } = this.limits;
767
+ if (maxIterations && this.iterations >= maxIterations * 0.9) return true;
768
+ if (maxToolCalls && this.toolCalls >= maxToolCalls * 0.9) return true;
769
+ if (maxTokens && this.tokenInput + this.tokenOutput >= maxTokens * 0.9) return true;
770
+ if (maxCostUsd && this.costUsd >= maxCostUsd * 0.9) return true;
771
+ return false;
772
+ }
773
+ /**
774
+ * Synchronous budget check — always throws synchronously so callers
775
+ * (especially test event handlers using `expect().toThrow()`) get an
776
+ * unhandled rejection when the budget is exceeded without a handler.
777
+ * When `onThreshold` IS configured, the actual async threshold-handling
778
+ * is dispatched as a fire-and-forget promise.
779
+ */
780
+ checkLimit(kind, used, limit) {
781
+ if (kind === "timeout" || !this._onThreshold) {
782
+ throw new BudgetExceededError(kind, limit, used);
783
+ }
784
+ void this.checkLimitAsync(kind, used, limit);
785
+ }
786
+ /**
787
+ * Async threshold negotiation with the coordinator. Fire-and-forget —
788
+ * any error thrown here becomes an unhandled rejection in the test environment
789
+ * because the runner's catch only handles the synchronous throw from `checkLimit`.
790
+ */
791
+ async checkLimitAsync(kind, used, limit) {
792
+ const result = this._onThreshold({
793
+ kind,
794
+ used,
795
+ limit,
796
+ // Inject a requestDecision helper the handler can call to emit the
797
+ // budget.threshold_reached event and wait for the coordinator's verdict.
798
+ // The runner wires this by injecting its EventBus into ctx.budget._events.
799
+ requestDecision: () => {
800
+ return new Promise((resolve) => {
801
+ this._events?.emit("budget.threshold_reached", {
802
+ kind,
803
+ used,
804
+ limit,
805
+ timeoutMs: 3e4,
806
+ extend: (extra) => resolve({ extend: extra }),
807
+ deny: () => resolve("stop")
808
+ });
809
+ });
810
+ }
811
+ });
812
+ if (result === "throw") {
813
+ throw new BudgetExceededError(kind, limit, used);
814
+ }
815
+ if (result === "continue") {
816
+ return;
817
+ }
818
+ const decision = await result;
819
+ if (decision === "stop") {
820
+ throw new BudgetExceededError(kind, limit, used);
821
+ }
822
+ const ext = decision.extend;
823
+ if (ext.maxIterations !== void 0) {
824
+ this.limits.maxIterations = ext.maxIterations;
825
+ }
826
+ if (ext.maxToolCalls !== void 0) {
827
+ this.limits.maxToolCalls = ext.maxToolCalls;
828
+ }
829
+ if (ext.maxTokens !== void 0) {
830
+ this.limits.maxTokens = ext.maxTokens;
831
+ }
832
+ if (ext.maxCostUsd !== void 0) {
833
+ this.limits.maxCostUsd = ext.maxCostUsd;
834
+ }
835
+ }
730
836
  recordIteration() {
731
837
  this.iterations++;
732
838
  if (this.limits.maxIterations !== void 0 && this.iterations > this.limits.maxIterations) {
733
- throw new BudgetExceededError("iterations", this.limits.maxIterations, this.iterations);
839
+ void this.checkLimit("iterations", this.iterations, this.limits.maxIterations);
734
840
  }
735
841
  }
736
842
  recordToolCall() {
737
843
  this.toolCalls++;
738
844
  if (this.limits.maxToolCalls !== void 0 && this.toolCalls > this.limits.maxToolCalls) {
739
- throw new BudgetExceededError("tool_calls", this.limits.maxToolCalls, this.toolCalls);
845
+ void this.checkLimit("tool_calls", this.toolCalls, this.limits.maxToolCalls);
740
846
  }
741
847
  }
742
848
  recordUsage(usage, costUsd = 0) {
@@ -745,10 +851,10 @@ var SubagentBudget = class {
745
851
  this.costUsd += costUsd;
746
852
  const totalTokens = this.tokenInput + this.tokenOutput;
747
853
  if (this.limits.maxTokens !== void 0 && totalTokens > this.limits.maxTokens) {
748
- throw new BudgetExceededError("tokens", this.limits.maxTokens, totalTokens);
854
+ void this.checkLimit("tokens", totalTokens, this.limits.maxTokens);
749
855
  }
750
856
  if (this.limits.maxCostUsd !== void 0 && this.costUsd > this.limits.maxCostUsd) {
751
- throw new BudgetExceededError("cost", this.limits.maxCostUsd, this.costUsd);
857
+ void this.checkLimit("cost", this.costUsd, this.limits.maxCostUsd);
752
858
  }
753
859
  }
754
860
  /**
@@ -782,6 +888,198 @@ var SubagentBudget = class {
782
888
  }
783
889
  };
784
890
 
891
+ // src/coordination/fleet.ts
892
+ var AUDIT_LOG_AGENT = {
893
+ id: "audit-log",
894
+ name: "Audit Log",
895
+ role: "audit-log",
896
+ prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
897
+ session logs and produce actionable markdown reports.
898
+
899
+ Scope:
900
+ - Parse session logs (iteration counts, tool calls, errors, usage)
901
+ - Detect repeated failure patterns across multiple runs
902
+ - Identify tool usage anomalies (over-use, failures, unexpected chains)
903
+ - Track token consumption trends
904
+ - Generate structured audit reports with severity ratings
905
+
906
+ Input format you accept:
907
+ { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
908
+
909
+ Output: Markdown audit report with sections:
910
+ - ## Summary (totals, error rate)
911
+ - ## Top Errors (count + context)
912
+ - ## Tool Usage (table with calls, failures, avg duration)
913
+ - ## Anomalies (pattern \u2192 severity)
914
+
915
+ Working rules:
916
+ - Never fabricate numbers \u2014 read the actual logs first
917
+ - Always include file:line references for errors
918
+ - If sessionPath is missing, ask the director to provide it
919
+ - Report confidence level: high (>90% accuracy), medium, low`
920
+ // No hardcoded budgets — the orchestrator (delegate tool or
921
+ // spawn_subagent) decides per-task how much room a subagent gets.
922
+ // A monorepo audit needs hours; a single-file lint check needs
923
+ // seconds. Pinning a number here forces the orchestrator to fight
924
+ // the role's default instead of just asking for what it needs.
925
+ };
926
+ var BUG_HUNTER_AGENT = {
927
+ id: "bug-hunter",
928
+ name: "Bug Hunter",
929
+ role: "bug-hunter",
930
+ prompt: `You are the Bug Hunter agent. Your job is to systematically scan
931
+ source code for bugs, anti-patterns, and code smells using pattern matching
932
+ and heuristics. Output a prioritized hit list with file:line references.
933
+
934
+ Scope:
935
+ - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
936
+ - Identify anti-patterns (callback hell, God objects, circular deps)
937
+ - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
938
+ - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
939
+ - Rank findings: critical > high > medium > low
940
+
941
+ Input format you accept:
942
+ { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
943
+
944
+ Output: Markdown bug hunt report:
945
+ - ## Critical (must fix first)
946
+ - ## High (should fix)
947
+ - ## Medium
948
+ - ## Low (consider)
949
+ Each entry: **[TYPE]** \`file:line\` \u2014 description + suggested fix
950
+
951
+ Bug pattern reference you know:
952
+ | Pattern | Regex hint | Severity |
953
+ |---------|------------|----------|
954
+ | Uncaught promise | /.then\\(.*\\)/ without catch | high |
955
+ | Event leak | on\\( without off/removeListener | high |
956
+ | Hardcoded secret | [a-zA-Z0-9/_-]{20,} in config files | critical |
957
+ | unsafe any | : any\\b or <any> | medium |
958
+ | innerHTML | innerHTML\\s*= | high |
959
+
960
+ Working rules:
961
+ - Never scan node_modules \u2014 it's noise
962
+ - Always include file:line for every finding
963
+ - If >30% of findings are false positives, note the confidence level
964
+ - Ask director for clarification if paths are ambiguous`
965
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
966
+ };
967
+ var REFACTOR_PLANNER_AGENT = {
968
+ id: "refactor-planner",
969
+ name: "Refactor Planner",
970
+ role: "refactor-planner",
971
+ prompt: `You are the Refactor Planner agent. Your job is to analyze code
972
+ structure and produce a concrete, phased refactoring plan with risk
973
+ assessment, dependency ordering, and rollback strategy.
974
+
975
+ Scope:
976
+ - Map module-level dependencies (import graph)
977
+ - Identify coupling hotspots (high fan-in/out modules)
978
+ - Assess refactoring risk by complexity and test coverage
979
+ - Generate phased plans with checkpoint milestones
980
+ - Produce diff-friendly task lists (one task = one concern)
981
+
982
+ Input format you accept:
983
+ { "task": "plan | assess | roadmap", "target": "src/core", "constraint": "no-breaking-changes | minimal-downtime | full-rewrite", "focus": "architecture | performance | maintainability" }
984
+
985
+ Output: Markdown refactor plan:
986
+ - ## Phase 1: Low Risk / High Payoff (do first)
987
+ Table: | # | Task | Module | Risk | Est. Time |
988
+ - ## Phase 2: Medium Risk
989
+ - ## Phase 3: High Risk (requires full regression)
990
+ - ## Dependency Graph (abbreviated ASCII)
991
+ - ## Rollback Strategy
992
+ - ## Exit Criteria (checkbox list)
993
+
994
+ Risk scoring criteria:
995
+ | Factor | Low | Medium | High |
996
+ |--------|-----|--------|------|
997
+ | Cyclomatic complexity | <10 | 10-20 | >20 |
998
+ | Test coverage | >80% | 50-80% | <50% |
999
+ | Fan-out (imports) | <5 | 5-15 | >15 |
1000
+
1001
+ Working rules:
1002
+ - Always include rollback strategy \u2014 every refactor can fail
1003
+ - Merge tasks that take <1h into a single phase
1004
+ - Respect team constraints (reviewer availability, parallelization)
1005
+ - Never plan without analyzing the actual code first`
1006
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
1007
+ };
1008
+ var SECURITY_SCANNER_AGENT = {
1009
+ id: "security-scanner",
1010
+ name: "Security Scanner",
1011
+ role: "security-scanner",
1012
+ prompt: `You are the Security Scanner agent. Your job is to scan code,
1013
+ configs, and dependencies for security issues from hardcoded secrets to
1014
+ supply chain risks.
1015
+
1016
+ Scope:
1017
+ - Detect hardcoded secrets: API keys, tokens, passwords, private keys
1018
+ - Find injection vectors: eval, innerHTML, SQL concat, shell injection
1019
+ - Identify insecure patterns: weak crypto, hardcoded IVs, disabled TLS
1020
+ - Scan dependencies for known CVEs (via npm/pnpm audit)
1021
+ - Flag supply chain risks: postinstall hooks, unverified scripts, .npmrc
1022
+
1023
+ Input format you accept:
1024
+ { "task": "scan | audit | secrets | dependencies", "paths": ["src", "config"], "depth": "quick | normal | deep" }
1025
+
1026
+ Output: Markdown security report:
1027
+ - ## CRITICAL: Secrets Found (with code snippets)
1028
+ - ## HIGH: Injection Vectors
1029
+ - ## MEDIUM: Insecure Patterns
1030
+ - ## Dependency Issues (CVE list)
1031
+ - ## Summary table (severity \u2192 count)
1032
+ - ## Remediation Checklist (with checkboxes)
1033
+
1034
+ Secret patterns you detect:
1035
+ | Pattern | Example | Severity |
1036
+ |---------|---------|----------|
1037
+ | AWS Access Key | AKIAIOSFODNN7EXAMPLE | critical |
1038
+ | AWS Secret Key | [a-zA-Z0-9/+=]{40} base64 | critical |
1039
+ | GitHub Token | ghp_[a-zA-Z0-9]{36} | critical |
1040
+ | Private Key PEM | -----BEGIN.*PRIVATE KEY----- | critical |
1041
+ | JWT | eyJ[a-zA-Z0-9_-]+ | high |
1042
+
1043
+ Injection patterns:
1044
+ | Construct | Safe alternative |
1045
+ |-----------|-----------------|
1046
+ | eval(str) | new Function() or parse |
1047
+ | innerHTML = x | textContent or sanitize |
1048
+ | exec(\`cmd \${x}\`) | execFile with args array |
1049
+
1050
+ Working rules:
1051
+ - Never scan node_modules \u2014 use npm audit instead
1052
+ - Always provide remediation steps, not just findings
1053
+ - Verify regex-based secrets before flagging (false positive risk)
1054
+ - When in doubt, flag as medium rather than ignoring potential issues`
1055
+ // Budgets are set by the orchestrator per task — see fleet.ts header.
1056
+ };
1057
+ var FLEET_ROSTER = {
1058
+ "audit-log": AUDIT_LOG_AGENT,
1059
+ "bug-hunter": BUG_HUNTER_AGENT,
1060
+ "refactor-planner": REFACTOR_PLANNER_AGENT,
1061
+ "security-scanner": SECURITY_SCANNER_AGENT
1062
+ };
1063
+ var FLEET_ROSTER_BUDGETS = {
1064
+ "audit-log": { timeoutMs: 5 * 60 * 1e3, maxIterations: 80, maxToolCalls: 300 },
1065
+ "bug-hunter": { timeoutMs: 10 * 60 * 1e3, maxIterations: 120, maxToolCalls: 400 },
1066
+ "refactor-planner": { timeoutMs: 8 * 60 * 1e3, maxIterations: 100, maxToolCalls: 350 },
1067
+ "security-scanner": { timeoutMs: 10 * 60 * 1e3, maxIterations: 120, maxToolCalls: 400 }
1068
+ };
1069
+ function applyRosterBudget(cfg) {
1070
+ const defaultBudget = FLEET_ROSTER_BUDGETS[cfg.role ?? ""];
1071
+ if (!defaultBudget) return cfg;
1072
+ return {
1073
+ ...cfg,
1074
+ timeoutMs: cfg.timeoutMs ?? defaultBudget.timeoutMs,
1075
+ maxIterations: cfg.maxIterations ?? defaultBudget.maxIterations,
1076
+ maxToolCalls: cfg.maxToolCalls ?? defaultBudget.maxToolCalls,
1077
+ maxTokens: cfg.maxTokens ?? defaultBudget.maxTokens,
1078
+ maxCostUsd: cfg.maxCostUsd ?? defaultBudget.maxCostUsd
1079
+ };
1080
+ }
1081
+ var ALL_FLEET_AGENTS = Object.values(FLEET_ROSTER);
1082
+
785
1083
  // src/coordination/multi-agent-coordinator.ts
786
1084
  var DefaultMultiAgentCoordinator = class extends EventEmitter {
787
1085
  coordinatorId;
@@ -1022,12 +1320,13 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
1022
1320
  task.subagentId = subagentId;
1023
1321
  subagent.context.tasks.push(task);
1024
1322
  this.emit("task.assigned", { task, subagentId });
1323
+ const configWithRosterDefaults = applyRosterBudget(subagent.config);
1025
1324
  const budget = new SubagentBudget({
1026
- maxIterations: subagent.config.maxIterations ?? this.config.defaultBudget?.maxIterations,
1027
- maxToolCalls: task.maxToolCalls ?? subagent.config.maxToolCalls ?? this.config.defaultBudget?.maxToolCalls,
1028
- maxTokens: subagent.config.maxTokens ?? this.config.defaultBudget?.maxTokens,
1029
- maxCostUsd: subagent.config.maxCostUsd ?? this.config.defaultBudget?.maxCostUsd,
1030
- timeoutMs: task.timeoutMs ?? subagent.config.timeoutMs ?? this.config.defaultBudget?.timeoutMs
1325
+ maxIterations: configWithRosterDefaults.maxIterations ?? this.config.defaultBudget?.maxIterations,
1326
+ maxToolCalls: task.maxToolCalls ?? configWithRosterDefaults.maxToolCalls ?? this.config.defaultBudget?.maxToolCalls,
1327
+ maxTokens: configWithRosterDefaults.maxTokens ?? this.config.defaultBudget?.maxTokens,
1328
+ maxCostUsd: configWithRosterDefaults.maxCostUsd ?? this.config.defaultBudget?.maxCostUsd,
1329
+ timeoutMs: task.timeoutMs ?? configWithRosterDefaults.timeoutMs ?? this.config.defaultBudget?.timeoutMs
1031
1330
  });
1032
1331
  subagent.activeBudget = budget;
1033
1332
  if (!this.runner) {
@@ -1565,6 +1864,36 @@ var Director = class {
1565
1864
  this.scheduleManifest();
1566
1865
  };
1567
1866
  this.coordinator.on("task.completed", this.taskCompletedListener);
1867
+ const extendCounts = /* @__PURE__ */ new Map();
1868
+ this.fleet.filter("budget.threshold_reached", (e) => {
1869
+ const payload = e.payload;
1870
+ const guardKey = `${e.subagentId}:${payload.kind}`;
1871
+ const prior = extendCounts.get(guardKey) ?? 0;
1872
+ if (prior >= 2) {
1873
+ payload.deny();
1874
+ extendCounts.delete(guardKey);
1875
+ return;
1876
+ }
1877
+ extendCounts.set(guardKey, prior + 1);
1878
+ setTimeout(() => {
1879
+ const extra = {};
1880
+ switch (payload.kind) {
1881
+ case "iterations":
1882
+ extra.maxIterations = Math.min(payload.used + 50, 500);
1883
+ break;
1884
+ case "tool_calls":
1885
+ extra.maxToolCalls = Math.min(Math.ceil(payload.limit * 1.5), 1e3);
1886
+ break;
1887
+ case "tokens":
1888
+ extra.maxTokens = Math.min(Math.ceil(payload.limit * 1.5), 5e5);
1889
+ break;
1890
+ case "cost":
1891
+ extra.maxCostUsd = Math.min(payload.limit * 1.5, 10);
1892
+ break;
1893
+ }
1894
+ payload.extend(extra);
1895
+ }, Math.min(payload.timeoutMs, 3e4));
1896
+ });
1568
1897
  }
1569
1898
  /** Best-effort session-writer append. Swallows failures — the director
1570
1899
  * must not break a fleet run because the session JSONL handle closed. */
@@ -2024,65 +2353,68 @@ function createDelegateTool(opts) {
2024
2353
  if (typeof i.task !== "string" || !i.task.trim()) {
2025
2354
  return { ok: false, error: "`task` is required." };
2026
2355
  }
2027
- let director = await opts.host.ensureDirector();
2028
- if (!director) {
2029
- director = await opts.host.promoteToDirector();
2030
- }
2031
- if (!director) {
2032
- const reason = opts.host.getPromotionBlockReason?.();
2033
- return {
2034
- ok: false,
2035
- error: reason ?? "Director could not be activated \u2014 multi-agent host already running in legacy non-director mode. Restart with `--director` for fleet support."
2036
- };
2037
- }
2038
- const timeoutMs = i.timeoutMs ?? defaultTimeoutMs;
2039
- let cfg;
2040
- if (i.role) {
2041
- const base = opts.roster?.[i.role];
2042
- if (!base) {
2356
+ try {
2357
+ let director = await opts.host.ensureDirector();
2358
+ if (!director) {
2359
+ director = await opts.host.promoteToDirector();
2360
+ }
2361
+ if (!director) {
2362
+ const reason = opts.host.getPromotionBlockReason?.();
2043
2363
  return {
2044
2364
  ok: false,
2045
- error: `Unknown role "${i.role}". Available: ${rosterIds.join(", ") || "(no roster configured)"}.`
2365
+ error: reason ?? "Director could not be activated \u2014 multi-agent host already running in legacy non-director mode. Restart with `--director` for fleet support."
2046
2366
  };
2047
2367
  }
2048
- cfg = instantiateRosterConfig2(i.role, base);
2049
- if (i.systemPromptOverride) cfg.systemPromptOverride = i.systemPromptOverride;
2050
- if (i.provider) cfg.provider = i.provider;
2051
- if (i.model) cfg.model = i.model;
2052
- } else {
2053
- if (!i.name) {
2054
- return {
2055
- ok: false,
2056
- error: "Either `role` (from the roster) or `name` is required."
2368
+ const timeoutMs = i.timeoutMs ?? defaultTimeoutMs;
2369
+ let cfg;
2370
+ if (i.role) {
2371
+ const base = opts.roster?.[i.role];
2372
+ if (!base) {
2373
+ return {
2374
+ ok: false,
2375
+ error: `Unknown role "${i.role}". Available: ${rosterIds.join(", ") || "(no roster configured)"}.`
2376
+ };
2377
+ }
2378
+ cfg = instantiateRosterConfig2(i.role, base);
2379
+ if (i.systemPromptOverride) cfg.systemPromptOverride = i.systemPromptOverride;
2380
+ if (i.provider) cfg.provider = i.provider;
2381
+ if (i.model) cfg.model = i.model;
2382
+ } else {
2383
+ if (!i.name) {
2384
+ return {
2385
+ ok: false,
2386
+ error: "Either `role` (from the roster) or `name` is required."
2387
+ };
2388
+ }
2389
+ cfg = {
2390
+ name: i.name,
2391
+ provider: i.provider,
2392
+ model: i.model,
2393
+ systemPromptOverride: i.systemPromptOverride
2057
2394
  };
2058
2395
  }
2059
- cfg = {
2060
- name: i.name,
2061
- provider: i.provider,
2062
- model: i.model,
2063
- systemPromptOverride: i.systemPromptOverride
2064
- };
2065
- }
2066
- if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
2067
- cfg.maxIterations = i.maxIterations;
2068
- }
2069
- if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
2070
- cfg.maxToolCalls = i.maxToolCalls;
2071
- }
2072
- const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
2073
- const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
2074
- if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
2075
- cfg.timeoutMs = desiredSubTimeout;
2076
- }
2077
- try {
2396
+ if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
2397
+ cfg.maxIterations = i.maxIterations;
2398
+ }
2399
+ if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
2400
+ cfg.maxToolCalls = i.maxToolCalls;
2401
+ }
2402
+ const SUBAGENT_TIMEOUT_BUFFER_MS = 3e4;
2403
+ const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
2404
+ if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
2405
+ cfg.timeoutMs = desiredSubTimeout;
2406
+ }
2078
2407
  const subagentId = await director.spawn(cfg);
2079
2408
  const taskId = await director.assign({
2080
- id: "",
2409
+ id: `${randomUUID()}`,
2081
2410
  description: i.task,
2082
2411
  subagentId
2083
2412
  });
2084
2413
  const result = await Promise.race([
2085
- director.awaitTasks([taskId]).then((r) => r[0]),
2414
+ director.awaitTasks([taskId]).then((r) => {
2415
+ if (!r[0]) throw new Error(`Task "${taskId}" not found in completed results`);
2416
+ return r[0];
2417
+ }),
2086
2418
  new Promise(
2087
2419
  (resolve) => setTimeout(() => resolve({ __timeout: true }), timeoutMs)
2088
2420
  )
@@ -2156,7 +2488,7 @@ function hintForKind(kind, retryable, backoffMs) {
2156
2488
  case "budget_tool_calls":
2157
2489
  case "budget_tokens":
2158
2490
  case "budget_cost":
2159
- return "Subagent exhausted its budget. Raise the matching `max*` field on the next delegate or narrow task scope.";
2491
+ return "Subagent exhausted its budget. The coordinator may auto-extend; otherwise raise the matching `max*` field (e.g. maxToolCalls: 600) on the next delegate, or split the task.";
2160
2492
  case "budget_timeout":
2161
2493
  return "Subagent hit its wall-clock budget. Raise `timeoutMs` on the next delegate or split the task.";
2162
2494
  case "aborted_by_parent":
@@ -2229,8 +2561,21 @@ function makeAgentSubagentRunner(opts) {
2229
2561
  const { agent, events } = factoryResult;
2230
2562
  const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
2231
2563
  const aborter = new AbortController();
2564
+ ctx.budget._events = events;
2232
2565
  let budgetError = null;
2233
2566
  const onBudgetError = (err) => {
2567
+ if (err instanceof BudgetThresholdSignal) {
2568
+ err.decision.then((decision) => {
2569
+ if (decision === "stop") {
2570
+ budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
2571
+ aborter.abort();
2572
+ }
2573
+ }).catch(() => {
2574
+ budgetError = new BudgetExceededError(err.kind, err.limit, err.used);
2575
+ aborter.abort();
2576
+ });
2577
+ return;
2578
+ }
2234
2579
  aborter.abort();
2235
2580
  budgetError = err instanceof BudgetExceededError ? err : new BudgetExceededError(
2236
2581
  "tool_calls",
@@ -2260,7 +2605,7 @@ function makeAgentSubagentRunner(opts) {
2260
2605
  try {
2261
2606
  ctx.budget.recordUsage(e.usage);
2262
2607
  } catch (e2) {
2263
- onBudgetError(e2);
2608
+ void onBudgetError(e2);
2264
2609
  }
2265
2610
  }),
2266
2611
  events.on("iteration.started", () => {
@@ -2268,7 +2613,7 @@ function makeAgentSubagentRunner(opts) {
2268
2613
  ctx.budget.recordIteration();
2269
2614
  ctx.budget.checkTimeout();
2270
2615
  } catch (e) {
2271
- onBudgetError(e);
2616
+ void onBudgetError(e);
2272
2617
  }
2273
2618
  }),
2274
2619
  // D3: cooperative timeout enforcement DURING a long tool call.
@@ -2288,7 +2633,7 @@ function makeAgentSubagentRunner(opts) {
2288
2633
  try {
2289
2634
  ctx.budget.checkTimeout();
2290
2635
  } catch (e) {
2291
- onBudgetError(e);
2636
+ void onBudgetError(e);
2292
2637
  }
2293
2638
  })
2294
2639
  );
@@ -2793,180 +3138,6 @@ function makeDirectorSessionFactory(opts) {
2793
3138
  };
2794
3139
  }
2795
3140
 
2796
- // src/coordination/fleet.ts
2797
- var AUDIT_LOG_AGENT = {
2798
- id: "audit-log",
2799
- name: "Audit Log",
2800
- role: "audit-log",
2801
- prompt: `You are the Audit Log agent. Your job is to analyze structured JSONL
2802
- session logs and produce actionable markdown reports.
2803
-
2804
- Scope:
2805
- - Parse session logs (iteration counts, tool calls, errors, usage)
2806
- - Detect repeated failure patterns across multiple runs
2807
- - Identify tool usage anomalies (over-use, failures, unexpected chains)
2808
- - Track token consumption trends
2809
- - Generate structured audit reports with severity ratings
2810
-
2811
- Input format you accept:
2812
- { "task": "analyze | report | trends", "sessionPath": "<path>", "focus": "errors | tools | usage | all" }
2813
-
2814
- Output: Markdown audit report with sections:
2815
- - ## Summary (totals, error rate)
2816
- - ## Top Errors (count + context)
2817
- - ## Tool Usage (table with calls, failures, avg duration)
2818
- - ## Anomalies (pattern \u2192 severity)
2819
-
2820
- Working rules:
2821
- - Never fabricate numbers \u2014 read the actual logs first
2822
- - Always include file:line references for errors
2823
- - If sessionPath is missing, ask the director to provide it
2824
- - Report confidence level: high (>90% accuracy), medium, low`
2825
- // No hardcoded budgets — the orchestrator (delegate tool or
2826
- // spawn_subagent) decides per-task how much room a subagent gets.
2827
- // A monorepo audit needs hours; a single-file lint check needs
2828
- // seconds. Pinning a number here forces the orchestrator to fight
2829
- // the role's default instead of just asking for what it needs.
2830
- };
2831
- var BUG_HUNTER_AGENT = {
2832
- id: "bug-hunter",
2833
- name: "Bug Hunter",
2834
- role: "bug-hunter",
2835
- prompt: `You are the Bug Hunter agent. Your job is to systematically scan
2836
- source code for bugs, anti-patterns, and code smells using pattern matching
2837
- and heuristics. Output a prioritized hit list with file:line references.
2838
-
2839
- Scope:
2840
- - Detect common bug patterns (uncaught errors, resource leaks, race conditions)
2841
- - Identify anti-patterns (callback hell, God objects, circular deps)
2842
- - Find TypeScript-specific issues (unsafe any, missing null checks, branded types)
2843
- - Flag security-sensitive constructs (eval, innerHTML, hardcoded secrets)
2844
- - Rank findings: critical > high > medium > low
2845
-
2846
- Input format you accept:
2847
- { "task": "scan | hunt | check", "paths": ["src/**/*.ts"], "focus": "bugs | patterns | security | all", "severityThreshold": "medium" }
2848
-
2849
- Output: Markdown bug hunt report:
2850
- - ## Critical (must fix first)
2851
- - ## High (should fix)
2852
- - ## Medium
2853
- - ## Low (consider)
2854
- Each entry: **[TYPE]** \`file:line\` \u2014 description + suggested fix
2855
-
2856
- Bug pattern reference you know:
2857
- | Pattern | Regex hint | Severity |
2858
- |---------|------------|----------|
2859
- | Uncaught promise | /.then\\(.*\\)/ without catch | high |
2860
- | Event leak | on\\( without off/removeListener | high |
2861
- | Hardcoded secret | [a-zA-Z0-9/_-]{20,} in config files | critical |
2862
- | unsafe any | : any\\b or <any> | medium |
2863
- | innerHTML | innerHTML\\s*= | high |
2864
-
2865
- Working rules:
2866
- - Never scan node_modules \u2014 it's noise
2867
- - Always include file:line for every finding
2868
- - If >30% of findings are false positives, note the confidence level
2869
- - Ask director for clarification if paths are ambiguous`
2870
- // Budgets are set by the orchestrator per task — see fleet.ts header.
2871
- };
2872
- var REFACTOR_PLANNER_AGENT = {
2873
- id: "refactor-planner",
2874
- name: "Refactor Planner",
2875
- role: "refactor-planner",
2876
- prompt: `You are the Refactor Planner agent. Your job is to analyze code
2877
- structure and produce a concrete, phased refactoring plan with risk
2878
- assessment, dependency ordering, and rollback strategy.
2879
-
2880
- Scope:
2881
- - Map module-level dependencies (import graph)
2882
- - Identify coupling hotspots (high fan-in/out modules)
2883
- - Assess refactoring risk by complexity and test coverage
2884
- - Generate phased plans with checkpoint milestones
2885
- - Produce diff-friendly task lists (one task = one concern)
2886
-
2887
- Input format you accept:
2888
- { "task": "plan | assess | roadmap", "target": "src/core", "constraint": "no-breaking-changes | minimal-downtime | full-rewrite", "focus": "architecture | performance | maintainability" }
2889
-
2890
- Output: Markdown refactor plan:
2891
- - ## Phase 1: Low Risk / High Payoff (do first)
2892
- Table: | # | Task | Module | Risk | Est. Time |
2893
- - ## Phase 2: Medium Risk
2894
- - ## Phase 3: High Risk (requires full regression)
2895
- - ## Dependency Graph (abbreviated ASCII)
2896
- - ## Rollback Strategy
2897
- - ## Exit Criteria (checkbox list)
2898
-
2899
- Risk scoring criteria:
2900
- | Factor | Low | Medium | High |
2901
- |--------|-----|--------|------|
2902
- | Cyclomatic complexity | <10 | 10-20 | >20 |
2903
- | Test coverage | >80% | 50-80% | <50% |
2904
- | Fan-out (imports) | <5 | 5-15 | >15 |
2905
-
2906
- Working rules:
2907
- - Always include rollback strategy \u2014 every refactor can fail
2908
- - Merge tasks that take <1h into a single phase
2909
- - Respect team constraints (reviewer availability, parallelization)
2910
- - Never plan without analyzing the actual code first`
2911
- // Budgets are set by the orchestrator per task — see fleet.ts header.
2912
- };
2913
- var SECURITY_SCANNER_AGENT = {
2914
- id: "security-scanner",
2915
- name: "Security Scanner",
2916
- role: "security-scanner",
2917
- prompt: `You are the Security Scanner agent. Your job is to scan code,
2918
- configs, and dependencies for security issues from hardcoded secrets to
2919
- supply chain risks.
2920
-
2921
- Scope:
2922
- - Detect hardcoded secrets: API keys, tokens, passwords, private keys
2923
- - Find injection vectors: eval, innerHTML, SQL concat, shell injection
2924
- - Identify insecure patterns: weak crypto, hardcoded IVs, disabled TLS
2925
- - Scan dependencies for known CVEs (via npm/pnpm audit)
2926
- - Flag supply chain risks: postinstall hooks, unverified scripts, .npmrc
2927
-
2928
- Input format you accept:
2929
- { "task": "scan | audit | secrets | dependencies", "paths": ["src", "config"], "depth": "quick | normal | deep" }
2930
-
2931
- Output: Markdown security report:
2932
- - ## CRITICAL: Secrets Found (with code snippets)
2933
- - ## HIGH: Injection Vectors
2934
- - ## MEDIUM: Insecure Patterns
2935
- - ## Dependency Issues (CVE list)
2936
- - ## Summary table (severity \u2192 count)
2937
- - ## Remediation Checklist (with checkboxes)
2938
-
2939
- Secret patterns you detect:
2940
- | Pattern | Example | Severity |
2941
- |---------|---------|----------|
2942
- | AWS Access Key | AKIAIOSFODNN7EXAMPLE | critical |
2943
- | AWS Secret Key | [a-zA-Z0-9/+=]{40} base64 | critical |
2944
- | GitHub Token | ghp_[a-zA-Z0-9]{36} | critical |
2945
- | Private Key PEM | -----BEGIN.*PRIVATE KEY----- | critical |
2946
- | JWT | eyJ[a-zA-Z0-9_-]+ | high |
2947
-
2948
- Injection patterns:
2949
- | Construct | Safe alternative |
2950
- |-----------|-----------------|
2951
- | eval(str) | new Function() or parse |
2952
- | innerHTML = x | textContent or sanitize |
2953
- | exec(\`cmd \${x}\`) | execFile with args array |
2954
-
2955
- Working rules:
2956
- - Never scan node_modules \u2014 use npm audit instead
2957
- - Always provide remediation steps, not just findings
2958
- - Verify regex-based secrets before flagging (false positive risk)
2959
- - When in doubt, flag as medium rather than ignoring potential issues`
2960
- // Budgets are set by the orchestrator per task — see fleet.ts header.
2961
- };
2962
- var FLEET_ROSTER = {
2963
- "audit-log": AUDIT_LOG_AGENT,
2964
- "bug-hunter": BUG_HUNTER_AGENT,
2965
- "refactor-planner": REFACTOR_PLANNER_AGENT,
2966
- "security-scanner": SECURITY_SCANNER_AGENT
2967
- };
2968
- var ALL_FLEET_AGENTS = Object.values(FLEET_ROSTER);
2969
-
2970
- export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BudgetExceededError, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultMultiAgentCoordinator, Director, DirectorBudgetError, FLEET_ROSTER, FleetBus, FleetUsageAggregator, InMemoryAgentBridge, InMemoryBridgeTransport, REFACTOR_PLANNER_AGENT, SECURITY_SCANNER_AGENT, SubagentBudget, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, makeAgentSubagentRunner, makeDirectorSessionFactory, rosterSummaryFromConfigs };
3141
+ export { ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, BUG_HUNTER_AGENT, BudgetExceededError, BudgetThresholdSignal, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_SUBAGENT_BASELINE, DefaultMultiAgentCoordinator, Director, DirectorBudgetError, FLEET_ROSTER, FleetBus, FleetUsageAggregator, InMemoryAgentBridge, InMemoryBridgeTransport, REFACTOR_PLANNER_AGENT, SECURITY_SCANNER_AGENT, SubagentBudget, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMessage, makeAgentSubagentRunner, makeDirectorSessionFactory, rosterSummaryFromConfigs };
2971
3142
  //# sourceMappingURL=index.js.map
2972
3143
  //# sourceMappingURL=index.js.map