@wrongstack/core 0.302.2 → 0.305.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 (97) hide show
  1. package/README.md +1 -1
  2. package/dist/chronicle/index.js +117 -30
  3. package/dist/chronicle/project-server.js +12 -5
  4. package/dist/coordination/agents/index.js +4645 -3448
  5. package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
  6. package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
  7. package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
  8. package/dist/coordination/agents/project-agent-consolidation.d.ts +34 -2
  9. package/dist/coordination/agents/project-agent-directive-outcome.d.ts +57 -0
  10. package/dist/coordination/agents/project-agent-files.d.ts +12 -3
  11. package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
  12. package/dist/coordination/agents/project-agent-identity.d.ts +38 -11
  13. package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
  14. package/dist/coordination/agents/project-agent-learning-policy.d.ts +22 -1
  15. package/dist/coordination/agents/project-agent-learning-structured.d.ts +72 -1
  16. package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
  17. package/dist/coordination/agents/project-agent-quarantine.d.ts +63 -0
  18. package/dist/coordination/agents/project-agent-skill-layer.d.ts +146 -0
  19. package/dist/coordination/agents/role-skills.d.ts +11 -1
  20. package/dist/coordination/agents/types.d.ts +10 -2
  21. package/dist/coordination/director-prompts.d.ts +19 -6
  22. package/dist/coordination/director-tools.d.ts +2 -2
  23. package/dist/coordination/fleet.d.ts +0 -6
  24. package/dist/coordination/index.d.ts +1 -1
  25. package/dist/coordination/index.js +5746 -4326
  26. package/dist/coordination/mail-tools.d.ts +1 -1
  27. package/dist/core/agent-types.d.ts +4 -2
  28. package/dist/core/agent.d.ts +1 -0
  29. package/dist/core/context.d.ts +19 -0
  30. package/dist/core/conversation-state.d.ts +14 -0
  31. package/dist/core/fallback-profile-manager.d.ts +70 -2
  32. package/dist/core/index.js +317 -108
  33. package/dist/core/system-prompt-blocks.d.ts +1 -1
  34. package/dist/core/system-prompt-builder.d.ts +13 -1
  35. package/dist/core/system-prompt-glossary.d.ts +73 -0
  36. package/dist/core/system-prompt-memory-skills.d.ts +2 -2
  37. package/dist/defaults/index.js +1635 -1120
  38. package/dist/execution/council-orchestrator.d.ts +3 -13
  39. package/dist/execution/index.js +3136 -2706
  40. package/dist/execution/one-shot-llm.d.ts +5 -0
  41. package/dist/goal/index.js +7 -0
  42. package/dist/hq/index.js +17 -7
  43. package/dist/hq/protocol/kanban.d.ts +21 -0
  44. package/dist/hq/protocol.js +5 -1
  45. package/dist/hq/redaction.d.ts +14 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.js +5502 -3483
  48. package/dist/infrastructure/index.js +247 -122
  49. package/dist/kernel/events/agent-events.d.ts +28 -0
  50. package/dist/plugin/index.js +111 -7
  51. package/dist/registry/index.js +11 -0
  52. package/dist/registry/tool-registry.d.ts +8 -0
  53. package/dist/replay/hash.d.ts +9 -0
  54. package/dist/replay/index.js +14 -4
  55. package/dist/replay/replay-provider-runner.d.ts +31 -1
  56. package/dist/security/index.js +92 -21
  57. package/dist/security/kanban-boundary.d.ts +5 -1
  58. package/dist/security/secret-vault.d.ts +2 -0
  59. package/dist/session-catalog/index.js +86 -10
  60. package/dist/session-catalog/project-server.js +100 -17
  61. package/dist/session-catalog/protocol.d.ts +20 -4
  62. package/dist/session-catalog/store.d.ts +19 -3
  63. package/dist/storage/index.js +305 -69
  64. package/dist/storage/memory-consolidator.d.ts +4 -2
  65. package/dist/storage/plan-store.d.ts +1 -1
  66. package/dist/storage/session-resume-validation.d.ts +24 -0
  67. package/dist/storage/session-store/directory-scan.d.ts +5 -1
  68. package/dist/storage/session-store/fork-session.d.ts +13 -1
  69. package/dist/storage/session-store/load-cache.d.ts +11 -0
  70. package/dist/storage/session-store/prune-helpers.d.ts +5 -0
  71. package/dist/storage/session-store.d.ts +18 -0
  72. package/dist/tasking/index.js +5 -0
  73. package/dist/tools/index.js +1151 -831
  74. package/dist/types/config/mcp-features.d.ts +31 -1
  75. package/dist/types/config/root.d.ts +23 -1
  76. package/dist/types/config/skills-fleet-brain.d.ts +34 -0
  77. package/dist/types/config/tools.d.ts +22 -0
  78. package/dist/types/config/ui.d.ts +14 -0
  79. package/dist/types/config.d.ts +1 -0
  80. package/dist/types/default-config.d.ts +1 -0
  81. package/dist/types/index.d.ts +2 -2
  82. package/dist/types/index.js +23 -0
  83. package/dist/types/multi-agent.d.ts +7 -0
  84. package/dist/types/session.d.ts +9 -1
  85. package/dist/types/task-graph.d.ts +2 -0
  86. package/dist/types/tool-executor.d.ts +2 -0
  87. package/dist/utils/index.d.ts +1 -0
  88. package/dist/utils/index.js +217 -76
  89. package/dist/utils/project-state-guard.d.ts +21 -0
  90. package/dist/utils/session-scoped-path.d.ts +17 -0
  91. package/dist/utils/todos-format.d.ts +20 -0
  92. package/instructions/leader-after-task.md +3 -4
  93. package/instructions/system-lite.md +17 -14
  94. package/instructions/system-pro.md +29 -30
  95. package/instructions/system.md +29 -28
  96. package/package.json +3 -3
  97. package/skills/wrongstack-kanban/SKILL.md +95 -93
@@ -496,9 +496,9 @@ function normalizeOptions(options) {
496
496
  ...option.consequence?.trim() ? { consequence: option.consequence.trim() } : {}
497
497
  }));
498
498
  }
499
- function requiredInstruction(path7) {
500
- const text = readBundledInstructionText(path7);
501
- if (!text) throw new Error(`Council instruction file is unavailable: ${path7}`);
499
+ function requiredInstruction(path8) {
500
+ const text = readBundledInstructionText(path8);
501
+ if (!text) throw new Error(`Council instruction file is unavailable: ${path8}`);
502
502
  return text;
503
503
  }
504
504
 
@@ -614,17 +614,6 @@ var CouncilOrchestrator = class {
614
614
  fallbackProfileManager;
615
615
  seatCaller;
616
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();
628
617
  constructor(opts) {
629
618
  if (!opts.caller && !opts.seatCaller && !opts.judgeCaller) {
630
619
  throw new Error("CouncilOrchestrator: provide `caller`, `seatCaller`, or `judgeCaller`.");
@@ -644,8 +633,9 @@ var CouncilOrchestrator = class {
644
633
  /**
645
634
  * Resolve the effective profile for a question. String ids and the default
646
635
  * 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().
636
+ * normalized on each call. Callers are allowed to reuse and mutate an
637
+ * ad-hoc object between decisions, so object-identity caching would serve a
638
+ * stale snapshot.
649
639
  */
650
640
  resolveProfile(profile) {
651
641
  if (typeof profile === "string" || profile === void 0) {
@@ -655,15 +645,11 @@ var CouncilOrchestrator = class {
655
645
  defaultProfile: this.defaultProfile
656
646
  });
657
647
  }
658
- const cached = this.profileCache.get(profile);
659
- if (cached) return cached;
660
- const resolved = resolveCouncilProfile(profile, {
648
+ return resolveCouncilProfile(profile, {
661
649
  registry: this.profiles,
662
650
  personas: this.personas,
663
651
  defaultProfile: this.defaultProfile
664
652
  });
665
- this.profileCache.set(profile, resolved);
666
- return resolved;
667
653
  }
668
654
  async ask(question) {
669
655
  const startedAt = Date.now();
@@ -2164,12 +2150,97 @@ var FallbackProfileManager = class {
2164
2150
  return this.resolveRefs([ref], exclude);
2165
2151
  }
2166
2152
  /**
2167
- * Resolve every usable configured target as an uncapped last-resort chain.
2153
+ * Resolve every usable configured target as a bounded last-resort chain.
2168
2154
  * Normal smart defaults stay bounded; callers append this only after the
2169
- * preferred chain and only when automatic fallback is enabled.
2155
+ * preferred chain and only when automatic fallback is enabled. The cap
2156
+ * ({@link MAX_LAST_RESORT_CANDIDATES}) prevents a config with many providers
2157
+ * from producing a degenerate chain of doomed requests during a systemic
2158
+ * outage — by this point the smart default, bridge, and default profile
2159
+ * have already failed.
2170
2160
  */
2171
2161
  resolveAllConfigured(exclude) {
2172
- return this.smartDefault(exclude, Number.POSITIVE_INFINITY);
2162
+ return this.smartDefault(exclude, this.lastResortCap());
2163
+ }
2164
+ /**
2165
+ * Effective cap for the last-resort append. Reads the user-configurable
2166
+ * {@link Config.fallbackMaxLastResortCandidates} when set and valid;
2167
+ * otherwise falls back to the compiled-in default
2168
+ * {@link MAX_LAST_RESORT_CANDIDATES}.
2169
+ */
2170
+ lastResortCap() {
2171
+ const configured = this.config.fallbackMaxLastResortCandidates;
2172
+ if (typeof configured === "number" && Number.isFinite(configured) && configured >= 0) {
2173
+ return Math.floor(configured);
2174
+ }
2175
+ return MAX_LAST_RESORT_CANDIDATES;
2176
+ }
2177
+ /**
2178
+ * Build the complete fallback candidate chain shared by the agent-loop
2179
+ * extension and the one-shot orchestrator. Centralizes the bridge → primary →
2180
+ * selected → default-profile → all-configured ladder and the fromExplicitSource
2181
+ * gate so both consumers produce identical ordering and depth semantics.
2182
+ *
2183
+ * Layering (each step deduped against all prior):
2184
+ * 1. Bridge (emergency continuity route).
2185
+ * 2. Configured primary, when the live context drifted from it.
2186
+ * 3. The selected chain (explicit refs → named profile → smart default),
2187
+ * via {@link resolveEffective}.
2188
+ * 4. The "default" profile — extra depth, ONLY when the chain was auto-derived.
2189
+ * 5. Every other configured provider — last resort, ONLY when the chain was
2190
+ * auto-derived AND `effectiveFallbackAuto` is true.
2191
+ *
2192
+ * Returns the empty chain when `closedWorld` is true and no explicit
2193
+ * refs/profile resolved — a model allowlist never leaks to unlisted models.
2194
+ *
2195
+ * @internal caller-aware options (primary, closedWorld) are accepted because
2196
+ * the agent loop has context the manager does not own; the resolution
2197
+ * pipeline itself is identical for both callers.
2198
+ */
2199
+ resolveCandidates(current, opts = {}) {
2200
+ const configuredPrimary = opts.primary ?? {
2201
+ providerId: this.config.provider,
2202
+ model: this.config.model
2203
+ };
2204
+ const configFallbackAuto = this.config.fallbackAuto;
2205
+ const effectiveFallbackAuto = configFallbackAuto !== void 0 && configFallbackAuto !== null ? configFallbackAuto : !opts.closedWorld;
2206
+ const explicitRefs = opts.fallbackModels ?? this.config.fallbackModels;
2207
+ const explicitUsable = explicitRefs !== void 0 && explicitRefs.length > 0 && this.resolveRefs(explicitRefs, current).length > 0;
2208
+ const profileUsable = opts.fallbackProfile !== void 0 && this.hasProfile(opts.fallbackProfile) && this.resolve(opts.fallbackProfile, { exclude: current }).length > 0;
2209
+ const fromExplicitSource = explicitUsable || profileUsable;
2210
+ const selectedChain = opts.closedWorld ? explicitRefs && explicitRefs.length > 0 ? this.resolveRefs(explicitRefs, current) : opts.fallbackProfile ? this.resolve(opts.fallbackProfile, { exclude: current }) : FREEZER_EMPTY : this.resolveEffective({
2211
+ fallbackModels: explicitRefs,
2212
+ fallbackProfile: opts.fallbackProfile,
2213
+ fallbackAuto: effectiveFallbackAuto,
2214
+ exclude: current
2215
+ });
2216
+ const candidates = [];
2217
+ if (opts.closedWorld) {
2218
+ candidates.push(...selectedChain);
2219
+ } else {
2220
+ candidates.push(...this.resolveBridge(current));
2221
+ if (!(configuredPrimary.providerId === current.providerId && configuredPrimary.model === current.model)) {
2222
+ candidates.push({
2223
+ providerId: configuredPrimary.providerId,
2224
+ model: configuredPrimary.model,
2225
+ providerSwitched: configuredPrimary.providerId !== current.providerId
2226
+ });
2227
+ }
2228
+ candidates.push(...selectedChain);
2229
+ if (!fromExplicitSource && effectiveFallbackAuto && opts.fallbackProfile !== "default") {
2230
+ candidates.push(...this.resolve("default", { exclude: current }));
2231
+ }
2232
+ if (!fromExplicitSource && effectiveFallbackAuto) {
2233
+ const cap = this.lastResortCap();
2234
+ if (cap > 0) {
2235
+ const usedKeys = new Set(
2236
+ candidates.map((c) => `${c.providerId}/${c.model}`)
2237
+ );
2238
+ const lastResort = this.smartDefault(current, Number.POSITIVE_INFINITY).filter((c) => !usedKeys.has(`${c.providerId}/${c.model}`)).slice(0, cap);
2239
+ candidates.push(...lastResort);
2240
+ }
2241
+ }
2242
+ }
2243
+ return dedupeChain(candidates, current);
2173
2244
  }
2174
2245
  // ── Provider availability (read-only) ──────────────────────────────────
2175
2246
  checkProvider(providerId) {
@@ -2293,7 +2364,20 @@ var FallbackProfileManager = class {
2293
2364
  );
2294
2365
  }
2295
2366
  };
2367
+ var MAX_LAST_RESORT_CANDIDATES = 12;
2296
2368
  var FREEZER_EMPTY = Object.freeze([]);
2369
+ function dedupeChain(entries, current) {
2370
+ const seen = /* @__PURE__ */ new Set();
2371
+ const currentKey = `${current.providerId}/${current.model}`;
2372
+ return Object.freeze(
2373
+ entries.filter((entry) => {
2374
+ const key = `${entry.providerId}/${entry.model}`;
2375
+ if (key === currentKey || seen.has(key)) return false;
2376
+ seen.add(key);
2377
+ return true;
2378
+ })
2379
+ );
2380
+ }
2297
2381
 
2298
2382
  // src/core/fallback-model.ts
2299
2383
  function fallbackProfileChain(config, profileName) {
@@ -2304,8 +2388,8 @@ function fallbackProfileChain(config, profileName) {
2304
2388
  var DEFAULT_PRIMARY_COOLDOWN_MAX_MS = 10 * 6e4;
2305
2389
 
2306
2390
  // src/coordination/agents/agent-prompts.ts
2307
- import { readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
2308
- import * as path6 from "node:path";
2391
+ import { readFileSync as readFileSync7, statSync as statSync3 } from "node:fs";
2392
+ import * as path7 from "node:path";
2309
2393
  import { fileURLToPath as fileURLToPath2 } from "node:url";
2310
2394
 
2311
2395
  // src/utils/wstack-paths.ts
@@ -2448,8 +2532,8 @@ function resolveWstackPaths(opts) {
2448
2532
  }
2449
2533
 
2450
2534
  // src/coordination/agents/project-agent-identity.ts
2451
- import { existsSync as existsSync2, readFileSync as readFileSync5 } from "node:fs";
2452
- import * as path5 from "node:path";
2535
+ import { existsSync as existsSync2, readFileSync as readFileSync6 } from "node:fs";
2536
+ import * as path6 from "node:path";
2453
2537
 
2454
2538
  // src/security/capabilities.ts
2455
2539
  var ToolCapabilities = {
@@ -2816,22 +2900,6 @@ function inferRuntimeCapabilities(toolNames) {
2816
2900
  return [...ids];
2817
2901
  }
2818
2902
 
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
2903
  // src/coordination/agents/project-agent-paths.ts
2836
2904
  import * as path3 from "node:path";
2837
2905
  var AGENT_ROLE_PATTERN = /^[a-z0-9][a-z0-9._-]{0,95}$/i;
@@ -2853,6 +2921,201 @@ function learningPolicyPath(role, projectRoot) {
2853
2921
  return path3.join(roleDir(role, projectRoot), "learning.json");
2854
2922
  }
2855
2923
 
2924
+ // src/coordination/agents/project-agent-consolidation.ts
2925
+ import { existsSync, mkdirSync, readFileSync as readFileSync3, rmSync } from "node:fs";
2926
+ import * as path4 from "node:path";
2927
+
2928
+ // src/coordination/agents/project-agent-learning-entries.ts
2929
+ function splitLearnedEntries(body) {
2930
+ return body.split(/\n---\n+/).map((entry) => entry.trim()).map(
2931
+ (entry) => entry.replace(/^#\s+Learned (?:wisdom|instructions) for .+$/im, "").replace(/^>\s*Project-specific learning data for[\s\S]*?$/im, "").replace(/^_No learned entries yet\._$/im, "").replace(/^\*Last capture:[^\n]*\*$/im, "").replace(/<!--[\s\S]*?-->/g, "").trim()
2932
+ ).filter(Boolean);
2933
+ }
2934
+
2935
+ // src/coordination/agents/project-agent-learning-normalize.ts
2936
+ var MIN_INSTRUCTIVE_LENGTH = 30;
2937
+ function classifyLearnedEntry(text) {
2938
+ const lower = text.toLowerCase();
2939
+ if (/\b(?:avoid|never|don'?t|do not|must not|prevent|watch out|beware|pitfall|gotcha|hazard|risk)\b/.test(
2940
+ lower
2941
+ ))
2942
+ return "warning";
2943
+ if (/\b(?:use|prefer|choose|adopt|leverage|apply|switch to|migrate to)\b/.test(lower) || /\b(?:pattern|approach|strategy|technique|idiom)\b/.test(lower))
2944
+ return "pattern";
2945
+ if (/\b(?:always|must|should|require|ensure|verify|check|run|execute|before|after|when)\b/.test(
2946
+ lower
2947
+ ))
2948
+ return "convention";
2949
+ return "fact";
2950
+ }
2951
+ function normalizeForComparison(text) {
2952
+ return text.toLowerCase().replace(/[^\w\s]/g, " ").replace(/\s+/g, " ").trim().split(/\s+/).sort().join(" ");
2953
+ }
2954
+
2955
+ // src/coordination/agents/project-agent-learning-structured.ts
2956
+ function directiveTrials(entry) {
2957
+ const count = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
2958
+ const applied = count(entry.applied);
2959
+ const wins = Math.min(applied, count(entry.wins));
2960
+ return { applied, wins, losses: applied - wins };
2961
+ }
2962
+ function parseLearnedEntryStamp(entry) {
2963
+ const structuredMatch = entry.match(
2964
+ /<!--\s*learned-stamp:\s*category=([\w-]+);\s*capturedAt=([^;]+?)\s*-->/
2965
+ );
2966
+ if (structuredMatch) {
2967
+ const rawCategory = structuredMatch[1];
2968
+ const candidate = parseLearnedCategory(rawCategory);
2969
+ return {
2970
+ capturedAt: typeof structuredMatch[2] === "string" ? structuredMatch[2].trim() : "",
2971
+ category: candidate ?? null
2972
+ };
2973
+ }
2974
+ const stampMatch = entry.match(/^>\s*(?:\[\s*([\w-]+)\s*\]\s+)?(?:Captured|Taught)\s+(\S+)/m);
2975
+ if (!stampMatch) {
2976
+ return { capturedAt: "", category: null };
2977
+ }
2978
+ return {
2979
+ capturedAt: typeof stampMatch[2] === "string" ? stampMatch[2] : "",
2980
+ category: parseLearnedCategory(stampMatch[1]) ?? null
2981
+ };
2982
+ }
2983
+ function parseLearnedCategory(value) {
2984
+ return value === "convention" || value === "pattern" || value === "warning" || value === "fact" ? value : void 0;
2985
+ }
2986
+ function stripStamp(entry) {
2987
+ return entry.replace(/<!--\s*learned-stamp:[\s\S]*?-->/g, "").replace(/^>\s*(?:\[[\w-]+\]\s+)?(?:Captured|Taught)\s+.+$/m, "").replace(/\n{3,}/g, "\n\n").trim();
2988
+ }
2989
+ function directiveKey(text) {
2990
+ return normalizeForComparison(text);
2991
+ }
2992
+ function decomposeLearnedEntry(text, category) {
2993
+ const what = text;
2994
+ const why = deriveWhy(category, text);
2995
+ const how = extractHow(text);
2996
+ return { what, why, how };
2997
+ }
2998
+ var WHY_BY_CATEGORY = {
2999
+ convention: "Established convention for this codebase \u2014 skipping it risks regressions, merge friction, or out-of-sync state with peers.",
3000
+ pattern: "This project's chosen approach \u2014 alternatives were considered and either conflict with existing architecture or were rejected for known reasons.",
3001
+ warning: "Known failure mode \u2014 skipping this has caused real defects in this codebase. The cost of getting it wrong outweighs the cost of the check.",
3002
+ fact: "Current state of the project \u2014 assumed by other conventions, build steps, or peers, so acting on a stale assumption wastes a cycle."
3003
+ };
3004
+ function deriveWhy(category, text) {
3005
+ const base = WHY_BY_CATEGORY[category];
3006
+ const signals = [];
3007
+ const lower = text.toLowerCase();
3008
+ if (/\bbefore\s+(?:merge|commit|deploy|release|shipping|publishing)\b/.test(lower))
3009
+ signals.push("guard before shipping");
3010
+ if (/\bto\s+avoid\b/.test(lower)) {
3011
+ const m = text.match(/to avoid ([^.!?]+)/i);
3012
+ if (m?.[1]) signals.push(`avoid ${m[1].trim()}`);
3013
+ }
3014
+ if (/\bso\s+(?:that|we|the project)\b/.test(lower)) {
3015
+ const m = text.match(/so (?:that |we |the project )?([^.!?]+)/i);
3016
+ if (m?.[1]) signals.push(`so ${m[1].trim()}`);
3017
+ }
3018
+ if (signals.length === 0) return base;
3019
+ return `${base} Project signals: ${signals.join("; ")}.`;
3020
+ }
3021
+ function extractHow(text) {
3022
+ const anchors = /* @__PURE__ */ new Set();
3023
+ const backticked = text.match(/`([^`]+)`/g) ?? [];
3024
+ for (const raw of backticked) {
3025
+ const inner = raw.replace(/`/g, "").trim();
3026
+ if (inner.length > 0 && inner.length <= 120) anchors.add(inner);
3027
+ }
3028
+ const pathMatches = text.match(
3029
+ /(?:[a-zA-Z0-9_.-]+\/)+[a-zA-Z0-9_.-]+\.(?:tsx|jsonc|json|jsx|yaml|mjs|cjs|yml|ts|js|md)\b/g
3030
+ ) ?? [];
3031
+ for (const p of pathMatches) anchors.add(p);
3032
+ const scoped = text.match(/@[a-z0-9][\w.-]*\/[a-z0-9][\w.-]*/gi) ?? [];
3033
+ for (const p of scoped) anchors.add(p);
3034
+ if (anchors.size === 0) return "";
3035
+ return [...anchors].map((anchor) => `\`${anchor}\``).join("\n");
3036
+ }
3037
+ function cleanHowLine(line) {
3038
+ let value = line.trim();
3039
+ let previous;
3040
+ do {
3041
+ previous = value;
3042
+ value = value.replace(/^[-*]\s+/, "").replace(/^\*How:\*\s*/i, "").trim();
3043
+ } while (value !== previous);
3044
+ return value;
3045
+ }
3046
+ function parseStampAttributes(body) {
3047
+ const attributes = {};
3048
+ for (const part of body.split(";")) {
3049
+ const eq = part.indexOf("=");
3050
+ if (eq === -1) continue;
3051
+ const key = part.slice(0, eq).trim();
3052
+ const value = part.slice(eq + 1).trim();
3053
+ if (key) attributes[key] = value;
3054
+ }
3055
+ return attributes;
3056
+ }
3057
+ function parseEntryBody(body) {
3058
+ const lines = [];
3059
+ for (const line of body.split("\n")) {
3060
+ if (/^##\s/.test(line) || /^---\s*$/.test(line) || /^<!--/.test(line)) break;
3061
+ lines.push(line);
3062
+ }
3063
+ const text = lines.join("\n");
3064
+ const what = /^-\s+\*\*([\s\S]+?)\*\*\s*$/m.exec(text)?.[1]?.replace(/\s+/g, " ").trim() ?? "";
3065
+ if (!what) return void 0;
3066
+ const why = /^\s+-\s+\*Why:\*\s+(.+)$/m.exec(text)?.[1]?.trim() ?? "";
3067
+ const how = lines.filter((line) => /^\s+-\s+\*How:\*/i.test(line)).map((line) => cleanHowLine(line)).filter(Boolean).join("\n");
3068
+ return { what, why, how };
3069
+ }
3070
+ function parseStructuredLearnedEntriesFromContent(raw, legacyEntries = splitLearnedEntries(raw)) {
3071
+ if (!raw) return [];
3072
+ const structured = [];
3073
+ const stampPattern = /<!--\s*learned-stamp:\s*([^>]*?)\s*-->/g;
3074
+ const stamps = [...raw.matchAll(stampPattern)];
3075
+ for (const [index, stamp] of stamps.entries()) {
3076
+ const attributes = parseStampAttributes(stamp[1] ?? "");
3077
+ const category = parseLearnedCategory(attributes["category"]) ?? "fact";
3078
+ const capturedAt = attributes["capturedAt"] ?? "";
3079
+ const skill = attributes["skill"];
3080
+ const trials = directiveTrials({
3081
+ applied: Number(attributes["applied"]),
3082
+ wins: Number(attributes["wins"])
3083
+ });
3084
+ const start = (stamp.index ?? 0) + stamp[0].length;
3085
+ const end = stamps[index + 1]?.index ?? raw.length;
3086
+ const parsed = parseEntryBody(raw.slice(start, end));
3087
+ if (!parsed || parsed.what.length < MIN_INSTRUCTIVE_LENGTH) continue;
3088
+ structured.push({
3089
+ key: directiveKey(parsed.what),
3090
+ category,
3091
+ what: parsed.what,
3092
+ why: parsed.why || WHY_BY_CATEGORY[category],
3093
+ how: parsed.how,
3094
+ capturedAt,
3095
+ ...skill ? { skill } : {},
3096
+ ...trials.applied > 0 ? { applied: trials.applied, wins: trials.wins } : {}
3097
+ });
3098
+ }
3099
+ if (structured.length === 0) {
3100
+ for (const chunk of legacyEntries) {
3101
+ const stamp = parseLearnedEntryStamp(chunk);
3102
+ const directive = stamp.capturedAt || stamp.category ? stripStamp(chunk) : chunk;
3103
+ if (directive.length < MIN_INSTRUCTIVE_LENGTH) continue;
3104
+ const category = stamp.category ?? classifyLearnedEntry(directive);
3105
+ const { what, why, how } = decomposeLearnedEntry(directive, category);
3106
+ structured.push({
3107
+ key: directiveKey(directive),
3108
+ category,
3109
+ what,
3110
+ why,
3111
+ how,
3112
+ capturedAt: stamp.capturedAt
3113
+ });
3114
+ }
3115
+ }
3116
+ return structured;
3117
+ }
3118
+
2856
3119
  // src/coordination/agents/project-agent-consolidation.ts
2857
3120
  function consolidationPath(role, projectRoot) {
2858
3121
  return path4.join(roleDir(role, projectRoot), "consolidated.md");
@@ -2960,213 +3223,532 @@ function loadProjectAgentLearningPolicy(role, projectRoot) {
2960
3223
  enabled: parsed.enabled !== false,
2961
3224
  lifetimeCaptureCount: typeof parsed.lifetimeCaptureCount === "number" && Number.isInteger(parsed.lifetimeCaptureCount) && parsed.lifetimeCaptureCount >= 0 ? parsed.lifetimeCaptureCount : 0,
2962
3225
  ...typeof parsed.lastCaptureAt === "string" ? { lastCaptureAt: parsed.lastCaptureAt } : {},
2963
- ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {}
3226
+ ...parsed.lastCaptureSource === "automatic" || parsed.lastCaptureSource === "manual" || parsed.lastCaptureSource === "taught" ? { lastCaptureSource: parsed.lastCaptureSource } : {},
3227
+ ...typeof parsed.lastOptimizeAt === "string" ? { lastOptimizeAt: parsed.lastOptimizeAt } : {}
2964
3228
  };
2965
3229
  } catch {
2966
3230
  return { ...DEFAULT_LEARNING_POLICY };
2967
3231
  }
2968
3232
  }
2969
3233
 
2970
- // src/coordination/agents/project-agent-identity.ts
2971
- function loadProjectAgentIdentity(role, projectRoot) {
2972
- const identityPath = path5.join(roleDir(role, projectRoot), "identity.md");
2973
- try {
2974
- return readFileSync5(identityPath, "utf8").trim();
2975
- } catch {
2976
- return "";
2977
- }
2978
- }
2979
- function loadProjectAgentLearned(role, projectRoot) {
2980
- const learnedPath = path5.join(roleDir(role, projectRoot), "learned.md");
2981
- try {
2982
- return readFileSync5(learnedPath, "utf8").trim();
2983
- } catch {
2984
- return "";
2985
- }
2986
- }
2987
- function loadRoleKnowledgeManifest(role, projectRoot) {
2988
- const projectPath = path5.join(roleDir(role, projectRoot), "knowledge.json");
2989
- try {
2990
- const raw = readFileSync5(projectPath, "utf8");
2991
- return JSON.parse(raw);
2992
- } catch {
2993
- return BUILT_IN_KNOWLEDGE_MANIFESTS[role];
2994
- }
2995
- }
2996
- function buildProjectContextualizedPrompt(basePrompt, role, projectRoot, options = {}) {
2997
- const contextStart = "<!-- wrongstack:project-agent-context:start -->";
2998
- const contextEnd = "<!-- wrongstack:project-agent-context:end -->";
2999
- if (process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"]) return basePrompt;
3000
- const cleanBase = basePrompt.replace(
3001
- /\s*<!-- wrongstack:project-agent-context:start -->[\s\S]*?<!-- wrongstack:project-agent-context:end -->\s*/g,
3002
- "\n"
3003
- ).trim();
3004
- const parts = [cleanBase];
3005
- const identity = options.identityOverride ?? loadProjectAgentIdentity(role, projectRoot);
3006
- if (identity) {
3007
- parts.push(`
3008
-
3009
- # Project custom identity
3010
-
3011
- ${identity}`);
3012
- }
3013
- const learningPolicy = loadProjectAgentLearningPolicy(role, projectRoot);
3014
- let learnedContent = "";
3015
- let learnedLabel = "";
3016
- if (learningPolicy.enabled) {
3017
- const consolidated = loadProjectAgentConsolidated(role, projectRoot);
3018
- if (consolidated) {
3019
- const meta = loadConsolidationMetadata(role, projectRoot);
3020
- const rawLearned = loadProjectAgentLearned(role, projectRoot);
3021
- const rawEntries = splitLearnedEntries(rawLearned);
3022
- const rawBytes = Buffer.byteLength(rawLearned, "utf8");
3023
- const stale = meta === void 0 || rawEntries.length > meta.sourceEntryCount || rawBytes > meta.sourceBytes;
3024
- if (stale) {
3025
- if (meta !== void 0 && meta.sourceEntryCount < rawEntries.length) {
3026
- const deltaEntries = rawEntries.slice(meta.sourceEntryCount);
3027
- learnedContent = `${consolidated}
3028
-
3029
- ---
3030
-
3031
- ## Recently captured (pending next optimization)
3032
-
3033
- ${deltaEntries.join("\n\n---\n\n")}`;
3034
- } else {
3035
- learnedContent = rawLearned;
3036
- }
3037
- } else {
3038
- learnedContent = consolidated;
3039
- }
3040
- learnedLabel = "Consolidated knowledge for this project";
3041
- } else {
3042
- learnedContent = loadProjectAgentLearned(role, projectRoot);
3043
- learnedLabel = "Learned instructions for this project (structured: what / why / how)";
3044
- }
3045
- }
3046
- if (learnedContent) {
3047
- const meaningful = learnedContent.replace(/<!--[\s\S]*?-->/g, "").trim();
3048
- if (meaningful.length > 0) {
3049
- parts.push(`
3050
-
3051
- # ${learnedLabel}
3052
-
3053
- ${learnedContent}`);
3054
- }
3055
- }
3056
- const effectiveProjectRoot = projectRoot || process.cwd();
3057
- const learnedFilePath = path5.join(
3058
- effectiveProjectRoot,
3059
- ".wrongstack",
3060
- "agents",
3061
- role,
3062
- "learned.md"
3063
- );
3064
- if (learningPolicy.enabled) {
3065
- parts.push(
3066
- `
3067
-
3068
- ## Knowledge capture
3069
-
3070
- The file below stores **learning data for this project's "${role}" agent** \u2014 not your memory, not a session log. It is read back into the system prompt of every future "${role}" invocation, so each entry must teach a future agent how to act. On every capture the runtime **merges your new entry with every prior entry and rewrites the whole buffer as a structured instruction list** \u2014 grouped by category ("What to do", "What to avoid", "Patterns to follow", "Project facts"), with each item decomposed into **what** (the rule), **why** (the reason), and **how** (the concrete commands, file paths, or package names that anchor the rule). When you discover a durable principle that future invocations should follow, end your response with a \`## LEARNED\` block. The runtime persists it to:
3071
-
3072
- \`\`\`
3073
- ${learnedFilePath}
3074
- \`\`\`
3075
-
3076
- **Write directives, not narratives.** Each LEARNED entry should be:
3077
-
3078
- - A **rule or principle** that applies across sessions, not a description of what happened in this one.
3079
- - A short **imperative or statement** (1\u20133 sentences), starting with a verb like "Always verify\u2026", "Use X for Y", "Avoid\u2026", "Never assume\u2026".
3080
- - **Generic** \u2014 no commit SHAs, timestamps, specific line numbers, or PR/issue numbers. File paths, package names, and command names are fine when they anchor the lesson.
3081
- - **Self-contained** \u2014 understandable without the surrounding session context.
3082
- - **Front-load concrete anchors** \u2014 commands in backticks, package names like \`@wrongstack/core\`, file paths like \`packages/core/src/.../foo.ts\`. The structured-list renderer extracts these as the "how" for the entry.
3083
-
3084
- **Bad** (session log \u2014 rejected at capture time):
3085
-
3086
- \`\`\`
3087
- ## LEARNED
3088
- When I worked on the telegram plugin today, commit 9c7682b84 had a race condition in poll-lock at line 42 because writeFileSync wasn't using the 'wx' flag.
3089
- \`\`\`
3090
-
3091
- **Good** (directive \u2014 persists, then merges into the structured list):
3092
-
3093
- \`\`\`
3094
- ## LEARNED
3095
- Always use the 'wx' (exclusive create) flag with writeFileSync when implementing concurrent lock acquisition in \`packages/core/src/.../poll-lock.ts\` \u2014 filesystem-level atomicity guarantees only one writer wins.
3096
- \`\`\`
3097
-
3098
- Capture only durable, cross-session knowledge \u2014 never ephemeral task details.`
3099
- );
3100
- }
3101
- const knowledge = loadRoleKnowledgeManifest(role, projectRoot);
3102
- if (knowledge && knowledge.checklist.length > 0) {
3103
- const checklist = knowledge.checklist.map((item) => `- ${item}`).join("\n");
3104
- parts.push(
3105
- `
3106
-
3107
- ## Current knowledge requirements
3108
-
3109
- Verify these before answering:
3110
- ${checklist}`
3111
- );
3112
- }
3113
- const additions = parts.slice(1).join("\n\n").trim();
3114
- if (!additions) return cleanBase;
3115
- return `${cleanBase}
3116
-
3117
- ${contextStart}
3118
- ${additions}
3119
- ${contextEnd}`;
3120
- }
3121
-
3122
- // src/coordination/agents/agent-prompts.ts
3123
- var promptCache = /* @__PURE__ */ new Map();
3124
- var candidateCache = /* @__PURE__ */ new Map();
3125
- var TECH_VERSION_POLICY_KEY = "\0__tech_version_policy__";
3126
- var techVersionPolicyBody;
3127
- var INLINE_TECH_VERSION_POLICY = `# Mandatory modern technology policy
3128
-
3129
- You are bound to the current stable version of every library, framework and
3130
- package you use. The latest stable is preferred; the latest *published* is
3131
- *required*.
3132
-
3133
- ## Non-negotiable rules
3134
-
3135
- 1. **Verify the version, never trust a number from training data.** Before
3136
- recommending a package or framework version, fetch the live registry
3137
- (npm, PyPI, crates, Go proxy, GitHub releases). Treat the value you read
3138
- as the only acceptable reference; do not interpolate from memory.
3139
-
3140
- 2. **Latest stable by default.** Pick the highest stable release that
3141
- satisfies the project runtime, the pinned peer-dependency range, and
3142
- the role's required APIs. Pin to \`^<major>.<minor>.0\` and report the
3143
- exact version.
3144
-
3145
- 3. **Reject alpha / beta / RC / nightly** unless the user explicitly
3146
- requests a pre-release. State the rejection reason in the output.
3147
-
3148
- 4. **Reject EOL, deprecated and unmaintained packages** \u2014 a package with
3149
- no release in >2 years **and** open critical issues is *dead*. Reject
3150
- it on the same day you find it and propose a maintained replacement.
3151
-
3152
- 5. **Reject prehistoric technology.** The well-known blocklist:
3153
- - \`axios\` / \`node-fetch\` / \`got\` / \`request\` \u2192 native \`fetch\` (Node 18+).
3154
- - \`moment\` \u2192 \`date-fns\`, \`luxon\` or \`Temporal\`.
3155
- - jQuery on new projects \u2192 vanilla DOM or React.
3156
- - Gulp / Grunt \u2192 \`tsup\`, \`esbuild\` or \`vite\`.
3157
- - CoffeeScript / Flow \u2192 TypeScript.
3158
- - \`Bluebird\` \u2192 native Promises.
3159
- - \`crypto-js\` \u2192 \`node:crypto\` or Web Crypto.
3160
- - Bower \u2192 npm or pnpm.
3161
- - \`underscore\` \u2192 \`lodash\` or native ES2020+.
3162
- Use the intervention phrase: **"This isn't code, this is X-year-old
3163
- technology."** Follow with the modern replacement and a one-step
3164
- migration path.
3234
+ // src/coordination/agents/project-agent-skill-layer.ts
3235
+ import { readdirSync, readFileSync as readFileSync5, rmSync as rmSync2 } from "node:fs";
3236
+ import * as path5 from "node:path";
3165
3237
 
3166
- 6. **Prefer built-in over third-party.** Before adding a dependency, check
3167
- the language or runtime native API. Examples:
3168
- - Node \u226522.19 \u2192 \`node:test\`, \`node:sqlite\`, \`node:fs/promises\`,
3169
- \`fetch\`, \`WebSocket\`, \`node:crypto\`, \`node:stream/web\`, AbortSignal.
3238
+ // src/coordination/agents/role-skills.ts
3239
+ var skillSet = (...names) => names;
3240
+ var ROLE_SKILL_SETS = {
3241
+ explore: skillSet("research-web", "node-modern", "typescript-strict"),
3242
+ search: skillSet("bug-hunter", "typescript-strict", "research-web"),
3243
+ research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
3244
+ analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
3245
+ planner: skillSet("sdd", "multi-agent", "refactor-planner", "testing"),
3246
+ architect: skillSet("sdd", "api-design", "refactor-planner", "security-scanner", "observability"),
3247
+ critic: skillSet("chimera", "bug-hunter", "security-scanner", "refactor-planner"),
3248
+ "refactor-planner": skillSet(
3249
+ "refactor-planner",
3250
+ "sdd",
3251
+ "typescript-strict",
3252
+ "testing",
3253
+ "bug-hunter"
3254
+ ),
3255
+ executor: skillSet("sdd", "typescript-strict", "node-modern", "testing", "git-flow"),
3256
+ refactor: skillSet("refactor-planner", "typescript-strict", "testing", "bug-hunter"),
3257
+ simplifier: skillSet("bug-hunter", "typescript-strict", "testing", "refactor-planner"),
3258
+ migration: skillSet("tech-stack", "node-modern", "typescript-strict", "testing", "git-flow"),
3259
+ vision: skillSet("react-modern", "testing", "typescript-strict", "security-scanner"),
3260
+ debugger: skillSet("bug-hunter", "testing", "typescript-strict", "observability", "audit-log"),
3261
+ tracer: skillSet("observability", "audit-log", "bug-hunter", "testing"),
3262
+ verifier: skillSet("testing", "typescript-strict", "security-scanner", "chimera"),
3263
+ test: skillSet("testing", "typescript-strict", "bug-hunter", "security-scanner"),
3264
+ e2e: skillSet("testing", "react-modern", "api-design", "security-scanner"),
3265
+ browser: skillSet("testing", "react-modern", "security-scanner", "research-web"),
3266
+ performance: skillSet("observability", "testing", "node-modern", "audit-log"),
3267
+ chaos: skillSet("testing", "observability", "security-scanner", "audit-log"),
3268
+ "security-scanner": skillSet("security-scanner", "bug-hunter", "api-design", "tech-stack"),
3269
+ "bug-hunter": skillSet("bug-hunter", "typescript-strict", "testing", "security-scanner"),
3270
+ "audit-log": skillSet("audit-log", "observability", "bug-hunter", "output-standards"),
3271
+ reviewer: skillSet("chimera", "bug-hunter", "security-scanner", "testing"),
3272
+ "code-reviewer": skillSet("chimera", "bug-hunter", "typescript-strict", "testing"),
3273
+ "security-reviewer": skillSet("security-scanner", "chimera", "api-design", "tech-stack"),
3274
+ accessibility: skillSet("react-modern", "testing", "chimera", "security-scanner"),
3275
+ compliance: skillSet("security-scanner", "audit-log", "tech-stack", "chimera"),
3276
+ database: skillSet("api-design", "testing", "security-scanner", "observability"),
3277
+ api: skillSet("api-design", "typescript-strict", "node-modern", "testing", "security-scanner"),
3278
+ auth: skillSet("security-scanner", "api-design", "testing", "typescript-strict", "audit-log"),
3279
+ data: skillSet("typescript-strict", "testing", "observability", "security-scanner"),
3280
+ frontend: skillSet("react-modern", "typescript-strict", "testing", "security-scanner"),
3281
+ backend: skillSet("node-modern", "typescript-strict", "api-design", "testing", "observability"),
3282
+ designer: skillSet("react-modern", "prompt-engineering", "research-web", "output-standards"),
3283
+ ios: skillSet("sdd", "testing", "security-scanner", "research-web"),
3284
+ document: skillSet("output-standards", "research-web", "api-design", "prompt-engineering"),
3285
+ uml: skillSet("sdd", "refactor-planner", "api-design", "output-standards"),
3286
+ i18n: skillSet("react-modern", "testing", "typescript-strict", "output-standards"),
3287
+ prompt: skillSet("prompt-engineering", "skill-creator", "output-standards", "testing"),
3288
+ git: skillSet("git-flow", "chimera", "testing", "security-scanner"),
3289
+ release: skillSet("git-flow", "tech-stack", "chimera", "security-scanner"),
3290
+ devops: skillSet("docker-deploy", "observability", "security-scanner", "git-flow", "tech-stack"),
3291
+ observability: skillSet("observability", "node-modern", "testing", "audit-log"),
3292
+ dependency: skillSet("tech-stack", "security-scanner", "node-modern", "git-flow"),
3293
+ "skill-manage": skillSet(
3294
+ "skill-creator",
3295
+ "prompt-engineering",
3296
+ "security-scanner",
3297
+ "output-standards"
3298
+ ),
3299
+ "self-improving": skillSet(
3300
+ "audit-log",
3301
+ "mnemosyne",
3302
+ "prompt-engineering",
3303
+ "bug-hunter",
3304
+ "observability"
3305
+ ),
3306
+ context: skillSet("mnemosyne", "output-standards", "prompt-engineering", "audit-log"),
3307
+ cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
3308
+ "tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
3309
+ // ── Wave 1: Platform and systems engineering ──────────────────────────
3310
+ android: skillSet(
3311
+ "react-modern",
3312
+ "typescript-strict",
3313
+ "testing",
3314
+ "security-scanner",
3315
+ "tech-stack"
3316
+ ),
3317
+ desktop: skillSet(
3318
+ "node-modern",
3319
+ "typescript-strict",
3320
+ "testing",
3321
+ "security-scanner",
3322
+ "tech-stack"
3323
+ ),
3324
+ realtime: skillSet(
3325
+ "api-design",
3326
+ "observability",
3327
+ "node-modern",
3328
+ "typescript-strict",
3329
+ "testing",
3330
+ "tech-stack"
3331
+ ),
3332
+ "distributed-systems": skillSet(
3333
+ "api-design",
3334
+ "observability",
3335
+ "typescript-strict",
3336
+ "testing",
3337
+ "security-scanner",
3338
+ "tech-stack"
3339
+ ),
3340
+ concurrency: skillSet(
3341
+ "bug-hunter",
3342
+ "typescript-strict",
3343
+ "testing",
3344
+ "observability",
3345
+ "node-modern",
3346
+ "tech-stack"
3347
+ ),
3348
+ "platform-engineer": skillSet(
3349
+ "node-modern",
3350
+ "typescript-strict",
3351
+ "git-flow",
3352
+ "docker-deploy",
3353
+ "tech-stack"
3354
+ ),
3355
+ // ── Wave 3: Product and data specialists ──────────────────────────────
3356
+ payments: skillSet(
3357
+ "api-design",
3358
+ "security-scanner",
3359
+ "audit-log",
3360
+ "testing",
3361
+ "typescript-strict",
3362
+ "tech-stack"
3363
+ ),
3364
+ messaging: skillSet(
3365
+ "api-design",
3366
+ "observability",
3367
+ "node-modern",
3368
+ "testing",
3369
+ "security-scanner",
3370
+ "tech-stack"
3371
+ ),
3372
+ "search-relevance": skillSet(
3373
+ "api-design",
3374
+ "observability",
3375
+ "testing",
3376
+ "typescript-strict",
3377
+ "data-governance",
3378
+ "tech-stack"
3379
+ ),
3380
+ storage: skillSet(
3381
+ "api-design",
3382
+ "security-scanner",
3383
+ "testing",
3384
+ "observability",
3385
+ "node-modern",
3386
+ "tech-stack"
3387
+ ),
3388
+ "ml-engineer": skillSet(
3389
+ "observability",
3390
+ "typescript-strict",
3391
+ "testing",
3392
+ "security-scanner",
3393
+ "data-governance",
3394
+ "tech-stack"
3395
+ ),
3396
+ "data-governance": skillSet(
3397
+ "audit-log",
3398
+ "security-scanner",
3399
+ "observability",
3400
+ "testing",
3401
+ "output-standards",
3402
+ "tech-stack"
3403
+ ),
3404
+ // ── Wave 4: Agent platform and quality ───────────────────────────────
3405
+ "plugin-author": skillSet(
3406
+ "plugin-author",
3407
+ "node-modern",
3408
+ "typescript-strict",
3409
+ "testing",
3410
+ "security-scanner",
3411
+ "tech-stack"
3412
+ ),
3413
+ "tool-author": skillSet(
3414
+ "typescript-strict",
3415
+ "security-scanner",
3416
+ "testing",
3417
+ "output-standards",
3418
+ "tech-stack"
3419
+ ),
3420
+ "prompt-evaluator": skillSet(
3421
+ "prompt-engineering",
3422
+ "testing",
3423
+ "output-standards",
3424
+ "security-scanner",
3425
+ "tech-stack"
3426
+ ),
3427
+ "benchmark-engineer": skillSet("observability", "testing", "output-standards", "tech-stack"),
3428
+ "memory-curator": skillSet("mnemosyne", "audit-log", "testing", "output-standards", "tech-stack"),
3429
+ "fleet-coordinator": skillSet("multi-agent", "sdd", "output-standards", "testing", "tech-stack"),
3430
+ // ── Wave 2: Security, resilience, governance and agent platform ────────
3431
+ "threat-modeler": skillSet(
3432
+ "security-scanner",
3433
+ "api-design",
3434
+ "tech-stack",
3435
+ "audit-log",
3436
+ "testing"
3437
+ ),
3438
+ "secure-coding-coach": skillSet(
3439
+ "security-scanner",
3440
+ "testing",
3441
+ "typescript-strict",
3442
+ "api-design",
3443
+ "tech-stack"
3444
+ ),
3445
+ "resilience-engineer": skillSet(
3446
+ "observability",
3447
+ "testing",
3448
+ "typescript-strict",
3449
+ "api-design",
3450
+ "tech-stack"
3451
+ ),
3452
+ "chaos-engineer": skillSet(
3453
+ "observability",
3454
+ "testing",
3455
+ "security-scanner",
3456
+ "git-flow",
3457
+ "tech-stack"
3458
+ ),
3459
+ "compliance-auditor": skillSet(
3460
+ "audit-log",
3461
+ "security-scanner",
3462
+ "output-standards",
3463
+ "testing",
3464
+ "tech-stack"
3465
+ ),
3466
+ "privacy-engineer": skillSet(
3467
+ "security-scanner",
3468
+ "audit-log",
3469
+ "api-design",
3470
+ "data-governance",
3471
+ "tech-stack"
3472
+ )
3473
+ };
3474
+ var SHADOW_AGENT_SKILLS = skillSet(
3475
+ "wrongstack-mailbox",
3476
+ "multi-agent",
3477
+ "audit-log",
3478
+ "observability",
3479
+ "security-scanner"
3480
+ );
3481
+ var MAX_EAGER_ROSTER_SKILLS = 3;
3482
+ function assignSkillsToAgents(definitions) {
3483
+ return definitions.map((definition) => {
3484
+ const role = definition.config.role;
3485
+ if (!role || !Object.hasOwn(ROLE_SKILL_SETS, role)) {
3486
+ throw new Error(
3487
+ `Missing curated skill set for roster role: "${role ?? definition.config.name}"`
3488
+ );
3489
+ }
3490
+ const skills = ROLE_SKILL_SETS[role];
3491
+ return {
3492
+ ...definition,
3493
+ config: {
3494
+ ...definition.config,
3495
+ capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
3496
+ skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS),
3497
+ skillPool: [...skills]
3498
+ }
3499
+ };
3500
+ });
3501
+ }
3502
+
3503
+ // src/coordination/agents/project-agent-skill-layer.ts
3504
+ var SKILL_NAME_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/;
3505
+ function isProjectSkillName(name) {
3506
+ return SKILL_NAME_PATTERN.test(name);
3507
+ }
3508
+ function projectSkillsDir(role, projectRoot) {
3509
+ return path5.join(roleDir(role, projectRoot), "skills");
3510
+ }
3511
+ function listProjectSkillAugmentations(role, projectRoot) {
3512
+ try {
3513
+ return readdirSync(projectSkillsDir(role, projectRoot)).filter((name) => name.endsWith(".md")).map((name) => name.slice(0, -3)).filter(isProjectSkillName).sort();
3514
+ } catch {
3515
+ return [];
3516
+ }
3517
+ }
3518
+
3519
+ // src/coordination/agents/project-agent-identity.ts
3520
+ function loadProjectAgentIdentity(role, projectRoot) {
3521
+ const identityPath = path6.join(roleDir(role, projectRoot), "identity.md");
3522
+ try {
3523
+ return readFileSync6(identityPath, "utf8").trim();
3524
+ } catch {
3525
+ return "";
3526
+ }
3527
+ }
3528
+ function loadProjectAgentLearned(role, projectRoot) {
3529
+ const learnedPath = path6.join(roleDir(role, projectRoot), "learned.md");
3530
+ try {
3531
+ return readFileSync6(learnedPath, "utf8").trim();
3532
+ } catch {
3533
+ return "";
3534
+ }
3535
+ }
3536
+ function loadRoleKnowledgeManifest(role, projectRoot) {
3537
+ const projectPath = path6.join(roleDir(role, projectRoot), "knowledge.json");
3538
+ try {
3539
+ const raw = readFileSync6(projectPath, "utf8");
3540
+ return JSON.parse(raw);
3541
+ } catch {
3542
+ return BUILT_IN_KNOWLEDGE_MANIFESTS[role];
3543
+ }
3544
+ }
3545
+ function buildProjectContextualizedPrompt(basePrompt, role, projectRoot, options = {}) {
3546
+ const contextStart = "<!-- wrongstack:project-agent-context:start -->";
3547
+ const contextEnd = "<!-- wrongstack:project-agent-context:end -->";
3548
+ if (process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"]) return basePrompt;
3549
+ const cleanBase = basePrompt.replace(
3550
+ /\s*<!-- wrongstack:project-agent-context:start -->[\s\S]*?<!-- wrongstack:project-agent-context:end -->\s*/g,
3551
+ "\n"
3552
+ ).trim();
3553
+ const parts = [cleanBase];
3554
+ const identity = options.identityOverride ?? loadProjectAgentIdentity(role, projectRoot);
3555
+ if (identity) {
3556
+ parts.push(`
3557
+
3558
+ # Project custom identity
3559
+
3560
+ ${identity}`);
3561
+ }
3562
+ const learningPolicy = loadProjectAgentLearningPolicy(role, projectRoot);
3563
+ let learnedContent = "";
3564
+ let learnedLabel = "";
3565
+ if (learningPolicy.enabled) {
3566
+ const consolidated = loadProjectAgentConsolidated(role, projectRoot);
3567
+ const rawLearned = loadProjectAgentLearned(role, projectRoot);
3568
+ if (consolidated) {
3569
+ const meta = loadConsolidationMetadata(role, projectRoot);
3570
+ const rawEntries = parseStructuredLearnedEntriesFromContent(
3571
+ rawLearned,
3572
+ splitLearnedEntries(rawLearned)
3573
+ );
3574
+ const rawBytes = Buffer.byteLength(rawLearned, "utf8");
3575
+ const freshEntries = meta === void 0 ? [] : rawEntries.filter((entry) => entry.capturedAt > meta.consolidatedAt);
3576
+ const stale = meta === void 0 || freshEntries.length > 0 || rawEntries.length > meta.sourceEntryCount || rawBytes > meta.sourceBytes;
3577
+ if (!stale) {
3578
+ learnedContent = consolidated;
3579
+ learnedLabel = "Consolidated knowledge for this project";
3580
+ } else if (freshEntries.length > 0) {
3581
+ const delta = freshEntries.map((entry) => `- **${entry.what}**`).join("\n");
3582
+ learnedContent = `${consolidated}
3583
+
3584
+ ---
3585
+
3586
+ ## Recently captured (pending next optimization)
3587
+
3588
+ ${delta}`;
3589
+ learnedLabel = "Consolidated knowledge for this project";
3590
+ } else {
3591
+ learnedContent = rawLearned;
3592
+ learnedLabel = "Learned instructions for this project (structured: what / why / how)";
3593
+ }
3594
+ } else {
3595
+ learnedContent = rawLearned;
3596
+ learnedLabel = "Learned instructions for this project (structured: what / why / how)";
3597
+ }
3598
+ }
3599
+ if (learnedContent) {
3600
+ const meaningful = learnedContent.replace(/<!--[\s\S]*?-->/g, "").trim();
3601
+ if (meaningful.length > 0) {
3602
+ parts.push(`
3603
+
3604
+ # ${learnedLabel}
3605
+
3606
+ ${learnedContent}`);
3607
+ }
3608
+ }
3609
+ const effectiveProjectRoot = projectRoot || process.cwd();
3610
+ const learnedFilePath = path6.join(
3611
+ effectiveProjectRoot,
3612
+ ".wrongstack",
3613
+ "agents",
3614
+ role,
3615
+ "learned.md"
3616
+ );
3617
+ if (learningPolicy.enabled) {
3618
+ parts.push(
3619
+ `
3620
+
3621
+ ## Knowledge capture
3622
+
3623
+ The file below stores **learning data for this project's "${role}" agent** \u2014 not your memory, not a session log. It is read back into the system prompt of every future "${role}" invocation, so each entry must teach a future agent how to act. On every capture the runtime **merges your new entry with every prior entry and rewrites the whole buffer as a structured instruction list** \u2014 grouped by category ("What to do", "What to avoid", "Patterns to follow", "Project facts"), with each item decomposed into **what** (the rule), **why** (the reason), and **how** (the concrete commands, file paths, or package names that anchor the rule). When you discover a durable principle that future invocations should follow, end your response with a \`## LEARNED\` block. The runtime persists it to:
3624
+
3625
+ \`\`\`
3626
+ ${learnedFilePath}
3627
+ \`\`\`
3628
+
3629
+ **Write directives, not narratives.** Each LEARNED entry should be:
3630
+
3631
+ - A **rule or principle** that applies across sessions, not a description of what happened in this one.
3632
+ - A short **imperative or statement** (1\u20133 sentences), starting with a verb like "Always verify\u2026", "Use X for Y", "Avoid\u2026", "Never assume\u2026".
3633
+ - **Generic** \u2014 no commit SHAs, timestamps, specific line numbers, or PR/issue numbers. File paths, package names, and command names are fine when they anchor the lesson.
3634
+ - **Self-contained** \u2014 understandable without the surrounding session context.
3635
+ - **Front-load concrete anchors** \u2014 commands in backticks, package names like \`@wrongstack/core\`, file paths like \`packages/core/src/.../foo.ts\`. The structured-list renderer extracts these as the "how" for the entry.
3636
+
3637
+ **Your directives are scored against real outcomes.** After every task the runtime checks which stored directives were actually exercised \u2014 it matches their anchors against the report \u2014 and folds that task's success or failure into each one's record. A directive that keeps correlating with success outlives newer arrivals and survives rewording; one that has been exercised repeatedly and kept correlating with failure is retired and stops being injected. Two consequences for how you write them: anchors are what make a directive *measurable*, not just runnable, so an anchorless directive can never earn a record; and a directive you are unsure about costs nothing to write, because the loop will find out.
3638
+
3639
+ **Tag the skill you are developing.** When a directive refines one of your skills, mark it: \`## LEARNED [skill: testing]\`. Tagged directives are distilled into that skill's project addendum, so the lesson arrives as part of the skill itself on every future run instead of as a loose fact. Untagged directives are routed automatically when the wording makes the target obvious, and stay role-level otherwise.
3640
+
3641
+ **Bad** (session log \u2014 rejected at capture time):
3642
+
3643
+ \`\`\`
3644
+ ## LEARNED
3645
+ When I worked on the telegram plugin today, commit 9c7682b84 had a race condition in poll-lock at line 42 because writeFileSync wasn't using the 'wx' flag.
3646
+ \`\`\`
3647
+
3648
+ **Good** (directive \u2014 persists, then merges into the structured list):
3649
+
3650
+ \`\`\`
3651
+ ## LEARNED
3652
+ Always use the 'wx' (exclusive create) flag with writeFileSync when implementing concurrent lock acquisition in \`packages/core/src/.../poll-lock.ts\` \u2014 filesystem-level atomicity guarantees only one writer wins.
3653
+ \`\`\`
3654
+
3655
+ Capture only durable, cross-session knowledge \u2014 never ephemeral task details.`
3656
+ );
3657
+ }
3658
+ const knowledge = loadRoleKnowledgeManifest(role, projectRoot);
3659
+ if (knowledge) {
3660
+ const knowledgeSections = [];
3661
+ if (knowledge.checklist.length > 0) {
3662
+ knowledgeSections.push(
3663
+ `Verify these before answering:
3664
+ ${knowledge.checklist.map((item) => `- ${item}`).join("\n")}`
3665
+ );
3666
+ }
3667
+ const liveQueries = Object.entries(knowledge.liveQueries ?? {});
3668
+ if (liveQueries.length > 0) {
3669
+ const threshold = Number.isFinite(knowledge.verifyThreshold) ? knowledge.verifyThreshold : 0.5;
3670
+ knowledgeSections.push(
3671
+ `When your confidence in any of the following is below ${threshold}, fetch the live value rather than recalling it:
3672
+ ` + liveQueries.map(
3673
+ ([topic, query]) => `- **${topic}** \u2014 ${query.description}: \`${query.registry}\` \u2192 field \`${query.key}\``
3674
+ ).join("\n")
3675
+ );
3676
+ }
3677
+ if (knowledgeSections.length > 0) {
3678
+ parts.push(`
3679
+
3680
+ ## Current knowledge requirements
3681
+
3682
+ ${knowledgeSections.join("\n\n")}`);
3683
+ }
3684
+ }
3685
+ const developedSkills = listProjectSkillAugmentations(role, projectRoot);
3686
+ if (developedSkills.length > 0) {
3687
+ parts.push(
3688
+ `
3689
+
3690
+ ## Project-developed skills
3691
+
3692
+ These skills have been extended with practice learned in this project: ${developedSkills.map((skill) => `\`${skill}\``).join(", ")}. Their project addendum is attached to the skill body \u2014 follow it over the generic method when the two differ.`
3693
+ );
3694
+ }
3695
+ const additions = parts.slice(1).join("\n\n").trim();
3696
+ if (!additions) return cleanBase;
3697
+ return `${cleanBase}
3698
+
3699
+ ${contextStart}
3700
+ ${additions}
3701
+ ${contextEnd}`;
3702
+ }
3703
+
3704
+ // src/coordination/agents/agent-prompts.ts
3705
+ var promptCache = /* @__PURE__ */ new Map();
3706
+ var candidateCache = /* @__PURE__ */ new Map();
3707
+ var TECH_VERSION_POLICY_KEY = "\0__tech_version_policy__";
3708
+ var techVersionPolicyBody;
3709
+ var INLINE_TECH_VERSION_POLICY = `# Mandatory modern technology policy
3710
+
3711
+ You are bound to the current stable version of every library, framework and
3712
+ package you use. The latest stable is preferred; the latest *published* is
3713
+ *required*.
3714
+
3715
+ ## Non-negotiable rules
3716
+
3717
+ 1. **Verify the version, never trust a number from training data.** Before
3718
+ recommending a package or framework version, fetch the live registry
3719
+ (npm, PyPI, crates, Go proxy, GitHub releases). Treat the value you read
3720
+ as the only acceptable reference; do not interpolate from memory.
3721
+
3722
+ 2. **Latest stable by default.** Pick the highest stable release that
3723
+ satisfies the project runtime, the pinned peer-dependency range, and
3724
+ the role's required APIs. Pin to \`^<major>.<minor>.0\` and report the
3725
+ exact version.
3726
+
3727
+ 3. **Reject alpha / beta / RC / nightly** unless the user explicitly
3728
+ requests a pre-release. State the rejection reason in the output.
3729
+
3730
+ 4. **Reject EOL, deprecated and unmaintained packages** \u2014 a package with
3731
+ no release in >2 years **and** open critical issues is *dead*. Reject
3732
+ it on the same day you find it and propose a maintained replacement.
3733
+
3734
+ 5. **Reject prehistoric technology.** The well-known blocklist:
3735
+ - \`axios\` / \`node-fetch\` / \`got\` / \`request\` \u2192 native \`fetch\` (Node 18+).
3736
+ - \`moment\` \u2192 \`date-fns\`, \`luxon\` or \`Temporal\`.
3737
+ - jQuery on new projects \u2192 vanilla DOM or React.
3738
+ - Gulp / Grunt \u2192 \`tsup\`, \`esbuild\` or \`vite\`.
3739
+ - CoffeeScript / Flow \u2192 TypeScript.
3740
+ - \`Bluebird\` \u2192 native Promises.
3741
+ - \`crypto-js\` \u2192 \`node:crypto\` or Web Crypto.
3742
+ - Bower \u2192 npm or pnpm.
3743
+ - \`underscore\` \u2192 \`lodash\` or native ES2020+.
3744
+ Use the intervention phrase: **"This isn't code, this is X-year-old
3745
+ technology."** Follow with the modern replacement and a one-step
3746
+ migration path.
3747
+
3748
+ 6. **Prefer built-in over third-party.** Before adding a dependency, check
3749
+ the language or runtime native API. Examples:
3750
+ - Node \u226522.19 \u2192 \`node:test\`, \`node:sqlite\`, \`node:fs/promises\`,
3751
+ \`fetch\`, \`WebSocket\`, \`node:crypto\`, \`node:stream/web\`, AbortSignal.
3170
3752
  - Browsers \u2192 \`fetch\`, \`AbortController\`, \`structuredClone\`,
3171
3753
  \`URLPattern\`, Web Streams.
3172
3754
  - TypeScript \u22655.6 \u2192 \`using\` declarations, \`await using\`, the new
@@ -3196,38 +3778,38 @@ function loadTechVersionPolicy() {
3196
3778
  return techVersionPolicyBody;
3197
3779
  }
3198
3780
  function policyCandidateDirs() {
3199
- const here = path6.dirname(fileURLToPath2(import.meta.url));
3781
+ const here = path7.dirname(fileURLToPath2(import.meta.url));
3200
3782
  const profileInstructions = resolveWstackPaths({ projectRoot: process.cwd() }).globalInstructions;
3201
3783
  return [
3202
- path6.resolve(here, "../../../../instructions/agents"),
3203
- path6.resolve(here, "../../../instructions/agents"),
3204
- path6.resolve(here, "../../instructions/agents"),
3205
- path6.resolve(here, "../instructions/agents"),
3206
- path6.resolve(here, "instructions/agents"),
3207
- path6.join(profileInstructions, "agents"),
3784
+ path7.resolve(here, "../../../../instructions/agents"),
3785
+ path7.resolve(here, "../../../instructions/agents"),
3786
+ path7.resolve(here, "../../instructions/agents"),
3787
+ path7.resolve(here, "../instructions/agents"),
3788
+ path7.resolve(here, "instructions/agents"),
3789
+ path7.join(profileInstructions, "agents"),
3208
3790
  profileInstructions
3209
3791
  ].filter((dir, index, all) => all.indexOf(dir) === index);
3210
3792
  }
3211
3793
  function policyBody() {
3212
- const here = path6.dirname(fileURLToPath2(import.meta.url));
3794
+ const here = path7.dirname(fileURLToPath2(import.meta.url));
3213
3795
  const roots = [
3214
- path6.resolve(here, "_policy/tech-version.md"),
3215
- path6.resolve(here, "../_policy/tech-version.md"),
3216
- path6.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
3217
- path6.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
3218
- path6.resolve(here, "../instructions/agents/_policy/tech-version.md"),
3219
- path6.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
3220
- path6.resolve(here, "instructions/agents/_policy/tech-version.md")
3796
+ path7.resolve(here, "_policy/tech-version.md"),
3797
+ path7.resolve(here, "../_policy/tech-version.md"),
3798
+ path7.resolve(here, "../../../instructions/agents/_policy/tech-version.md"),
3799
+ path7.resolve(here, "../../instructions/agents/_policy/tech-version.md"),
3800
+ path7.resolve(here, "../instructions/agents/_policy/tech-version.md"),
3801
+ path7.resolve(here, "../../../../instructions/agents/_policy/tech-version.md"),
3802
+ path7.resolve(here, "instructions/agents/_policy/tech-version.md")
3221
3803
  ];
3222
3804
  for (const file of roots) {
3223
3805
  try {
3224
- return readFileSync6(file, "utf8").trim();
3806
+ return readFileSync7(file, "utf8").trim();
3225
3807
  } catch {
3226
3808
  }
3227
3809
  }
3228
3810
  for (const dir of policyCandidateDirs()) {
3229
3811
  try {
3230
- return readFileSync6(path6.join(dir, "_policy", "tech-version.md"), "utf8").trim();
3812
+ return readFileSync7(path7.join(dir, "_policy", "tech-version.md"), "utf8").trim();
3231
3813
  } catch {
3232
3814
  }
3233
3815
  }
@@ -3238,14 +3820,15 @@ function agentPrompt(id) {
3238
3820
  const envDir = process.env["WRONGSTACK_AGENT_INSTRUCTIONS_DIR"] ?? "";
3239
3821
  const policyOn = process.env["WRONGSTACK_AGENT_POLICY"] === "on";
3240
3822
  const cacheBypass = globalThis.__WS_DISABLE_PROMPT_CACHE__ === true;
3241
- const cacheKey = `${envDir}\0${policyOn ? 1 : 0}\0${id}`;
3823
+ const promptProjectRoot = process.env["WRONGSTACK_PROJECT_ROOT"] || process.cwd();
3824
+ const cacheKey = `${envDir}\0${policyOn ? 1 : 0}\0${promptProjectRoot}\0${id}`;
3242
3825
  const cached = cacheBypass ? void 0 : promptCache.get(cacheKey);
3243
3826
  if (cached !== void 0) return cached;
3244
3827
  const fileName = `${id}.md`;
3245
3828
  let resolved = "";
3246
3829
  for (const dir of agentPromptDirCandidates(envDir)) {
3247
3830
  try {
3248
- resolved = readFileSync6(path6.join(dir, fileName), "utf8").trim();
3831
+ resolved = readFileSync7(path7.join(dir, fileName), "utf8").trim();
3249
3832
  break;
3250
3833
  } catch {
3251
3834
  }
@@ -3262,8 +3845,7 @@ function agentPrompt(id) {
3262
3845
  ${policy}`;
3263
3846
  }
3264
3847
  }
3265
- const projectRoot = process.env["WRONGSTACK_PROJECT_ROOT"] || process.cwd();
3266
- resolved = buildProjectContextualizedPrompt(resolved, id, projectRoot);
3848
+ resolved = buildProjectContextualizedPrompt(resolved, id, promptProjectRoot);
3267
3849
  if (!cacheBypass) promptCache.set(cacheKey, resolved);
3268
3850
  return resolved;
3269
3851
  }
@@ -3272,16 +3854,16 @@ function agentPromptDirCandidates(envDir) {
3272
3854
  const candKey = `${envDir}\0${profileInstructions}`;
3273
3855
  const cached = candidateCache.get(candKey);
3274
3856
  if (cached !== void 0) return cached;
3275
- const here = path6.dirname(fileURLToPath2(import.meta.url));
3857
+ const here = path7.dirname(fileURLToPath2(import.meta.url));
3276
3858
  const explicitDir = envDir || void 0;
3277
3859
  const candidates = [
3278
- ...explicitDir ? [path6.resolve(explicitDir)] : [],
3279
- path6.join(profileInstructions, "agents"),
3280
- path6.resolve(here, "../../../../instructions/agents"),
3281
- path6.resolve(here, "../../../instructions/agents"),
3282
- path6.resolve(here, "../../instructions/agents"),
3283
- path6.resolve(here, "../instructions/agents"),
3284
- path6.resolve(here, "instructions/agents")
3860
+ ...explicitDir ? [path7.resolve(explicitDir)] : [],
3861
+ path7.join(profileInstructions, "agents"),
3862
+ path7.resolve(here, "../../../../instructions/agents"),
3863
+ path7.resolve(here, "../../../instructions/agents"),
3864
+ path7.resolve(here, "../../instructions/agents"),
3865
+ path7.resolve(here, "../instructions/agents"),
3866
+ path7.resolve(here, "instructions/agents")
3285
3867
  ];
3286
3868
  const ordered = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
3287
3869
  candidateCache.set(candKey, ordered);
@@ -3338,8 +3920,16 @@ var TOOLS = {
3338
3920
  "test",
3339
3921
  "mailbox"
3340
3922
  ],
3341
- /** Version control. */
3342
- vcs: ["read", "grep", "glob", "git", "diff"],
3923
+ /**
3924
+ * Version control.
3925
+ *
3926
+ * `mailbox` is in every preset on purpose: a subagent that hits a wall must
3927
+ * be able to say so. This was the one preset without it, which left the `git`
3928
+ * and `release` roles able to fail but not to ask — the two roles whose work
3929
+ * most often needs a decision from the leader (force-push, tag collision,
3930
+ * dirty tree) and least often has a safe default.
3931
+ */
3932
+ vcs: ["read", "grep", "glob", "git", "diff", "mailbox"],
3343
3933
  /** Dependency management + CVE audit. */
3344
3934
  deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
3345
3935
  /** Documentation authoring. */
@@ -5552,494 +6142,230 @@ var META_AGENTS = [
5552
6142
  }
5553
6143
  ];
5554
6144
 
5555
- // src/coordination/agents/phase9-wave4-platform-meta.ts
5556
- var PROMPT_evaluator_META = {
5557
- rationale: "Prompt evaluator: builds rubrics, adversarial tests and regression suites. Independent measurement counterpart to the `prompt` author role.",
5558
- signals: [
5559
- "prompt eval",
5560
- "prompt regression",
5561
- "rubric",
5562
- "adversarial prompt",
5563
- "prompt benchmark",
5564
- "judge prompt",
5565
- "eval suite"
5566
- ],
5567
- differentiatesFrom: "prompt authors prompts; prompt-evaluator independently measures them with rubrics and adversarial inputs."
5568
- };
5569
- var BENCHMARK_ENGINEER_META2 = {
5570
- rationale: "Benchmark engineer: reproducible benchmark design and methodology. The measurement-infrastructure counterpart to `performance`.",
5571
- signals: [
5572
- "benchmark",
5573
- "reproducible",
5574
- "evaluation harness",
5575
- "regression suite",
5576
- "metric methodology",
5577
- "baseline",
5578
- "deterministic run"
5579
- ],
5580
- differentiatesFrom: "performance optimizes product code; benchmark-engineer builds the measurement infrastructure that supports comparison."
5581
- };
5582
- var MEMORY_CURATOR_META2 = {
5583
- rationale: "Memory curator: validates, merges and de-duplicates long-term memory with audience scope. Quality counterpart to `context`.",
5584
- signals: [
5585
- "memory",
5586
- "curate",
5587
- "memory audit",
5588
- "memory merge",
5589
- "memory expiry",
5590
- "audience scope",
5591
- "memory recall quality"
5592
- ],
5593
- differentiatesFrom: "context manages the active context window; memory-curator curates the long-term memory store for accuracy."
5594
- };
5595
- var FLEET_COORDINATOR_META2 = {
5596
- rationale: "Fleet coordinator: multi-agent job partitioning, capacity planning and result synthesis. Operational counterpart to `planner`.",
5597
- signals: [
5598
- "fleet",
5599
- "fan out",
5600
- "parallel agents",
5601
- "synthesis",
5602
- "subagent capacity",
5603
- "coordinator",
5604
- "result aggregation"
5605
- ],
5606
- differentiatesFrom: "planner designs ordered steps for one worker; fleet-coordinator runs many workers in parallel and merges their results."
5607
- };
5608
- var PLUGIN_AUTHOR_META2 = {
5609
- rationale: "Plugin author: build, package and ship runnable WrongStack plugins. The production counterpart to `skill-manage`.",
5610
- signals: [
5611
- "plugin",
5612
- "plugin api",
5613
- "lifecycle",
5614
- "health",
5615
- "teardown",
5616
- "packaging",
5617
- "distribution"
5618
- ],
5619
- differentiatesFrom: "skill-manage authors skills; plugin-author authors full lifecycle plugins with health and teardown."
5620
- };
5621
- var TOOL_AUTHOR_META2 = {
5622
- rationale: "Tool author: tool schema, capability declaration, permission and error contract. The narrow, security-focused counterpart to `plugin-author`.",
5623
- signals: [
5624
- "tool",
5625
- "tool schema",
5626
- "capability",
5627
- "permission",
5628
- "tool contract",
5629
- "tool error",
5630
- "tool lifecycle"
5631
- ],
5632
- differentiatesFrom: "plugin-author writes full plugin lifecycle; tool-author focuses on a single tool contract and capability set."
5633
- };
5634
- var WAVE4_AGENTS = [
5635
- {
5636
- config: {
5637
- id: "plugin-author",
5638
- name: "Plugin Author",
5639
- role: "plugin-author",
5640
- tools: [...TOOLS.write, "fetch"],
5641
- prompt: agentPrompt("plugin-author")
5642
- },
5643
- budget: MEDIUM_BUDGET,
5644
- capability: {
5645
- phase: "meta",
5646
- summary: "Plugin author: build, package and ship runnable WrongStack plugins with full lifecycle, health and teardown.",
5647
- keywords: [
5648
- ...PLUGIN_AUTHOR_META2.signals,
5649
- "plugin",
5650
- "plugin-author",
5651
- "lifecycle",
5652
- "health",
5653
- "teardown",
5654
- "plugin-packaging"
5655
- ]
5656
- }
5657
- },
5658
- {
5659
- config: {
5660
- id: "tool-author",
5661
- name: "Tool Author",
5662
- role: "tool-author",
5663
- tools: [...TOOLS.write, "install"],
5664
- prompt: agentPrompt("tool-author")
5665
- },
5666
- budget: LIGHT_BUDGET,
5667
- capability: {
5668
- phase: "meta",
5669
- summary: "Tool author: design tool schemas, capability declarations, permissions and error contracts with security as the first concern.",
5670
- keywords: [
5671
- ...TOOL_AUTHOR_META2.signals,
5672
- "tool",
5673
- "tool-author",
5674
- "capability",
5675
- "permission",
5676
- "tool-schema",
5677
- "tool-error"
5678
- ]
5679
- }
5680
- },
5681
- {
5682
- config: {
5683
- id: "prompt-evaluator",
5684
- name: "Prompt Evaluator",
5685
- role: "prompt-evaluator",
5686
- tools: [...TOOLS.read, "write"],
5687
- prompt: agentPrompt("prompt-evaluator")
5688
- },
5689
- budget: MEDIUM_BUDGET,
5690
- capability: {
5691
- phase: "meta",
5692
- summary: "Prompt evaluator: build rubrics, adversarial prompt tests and regression suites for independent measurement.",
5693
- keywords: [
5694
- ...PROMPT_evaluator_META.signals,
5695
- "prompt-evaluator",
5696
- "rubric",
5697
- "adversarial",
5698
- "regression",
5699
- "judge"
5700
- ]
5701
- }
5702
- },
5703
- {
5704
- config: {
5705
- id: "benchmark-engineer",
5706
- name: "Benchmark Engineer",
5707
- role: "benchmark-engineer",
5708
- tools: [...TOOLS.read, "write", "logs"],
5709
- prompt: agentPrompt("benchmark-engineer")
5710
- },
5711
- budget: MEDIUM_BUDGET,
5712
- capability: {
5713
- phase: "meta",
5714
- summary: "Benchmark engineer: design reproducible benchmarks and methodology with deterministic baselines.",
5715
- keywords: [
5716
- ...BENCHMARK_ENGINEER_META2.signals,
5717
- "benchmark-engineer",
5718
- "benchmark",
5719
- "baseline",
5720
- "eval-harness",
5721
- "deterministic"
5722
- ]
5723
- }
5724
- },
5725
- {
5726
- config: {
5727
- id: "memory-curator",
5728
- name: "Memory Curator",
5729
- role: "memory-curator",
5730
- tools: [...TOOLS.read, ...SPECIALIST_TOOLS.memory],
5731
- prompt: agentPrompt("memory-curator")
5732
- },
5733
- budget: LIGHT_BUDGET,
5734
- capability: {
5735
- phase: "meta",
5736
- summary: "Memory curator: validate, merge and de-duplicate long-term memory entries with audience scoping.",
5737
- keywords: [
5738
- ...MEMORY_CURATOR_META2.signals,
5739
- "memory-curator",
5740
- "curate",
5741
- "memory-audit",
5742
- "memory-merge",
5743
- "audience-scope"
5744
- ]
5745
- }
5746
- },
5747
- {
5748
- config: {
5749
- id: "fleet-coordinator",
5750
- name: "Fleet Coordinator",
5751
- role: "fleet-coordinator",
5752
- tools: [...TOOLS.read, "plan"],
5753
- prompt: agentPrompt("fleet-coordinator")
5754
- },
5755
- budget: MEDIUM_BUDGET,
5756
- capability: {
5757
- phase: "meta",
5758
- summary: "Fleet coordinator: own multi-agent job partitioning, capacity planning and result synthesis across many parallel workers.",
5759
- keywords: [
5760
- ...FLEET_COORDINATOR_META2.signals,
5761
- "fleet-coordinator",
5762
- "fan-out",
5763
- "parallel",
5764
- "synthesis",
5765
- "subagent-capacity"
5766
- ]
5767
- }
5768
- }
5769
- ];
5770
- var WAVE4_ROLE_METAS = {
5771
- "plugin-author": PLUGIN_AUTHOR_META2,
5772
- "tool-author": TOOL_AUTHOR_META2,
5773
- "prompt-evaluator": PROMPT_evaluator_META,
5774
- "benchmark-engineer": BENCHMARK_ENGINEER_META2,
5775
- "memory-curator": MEMORY_CURATOR_META2,
5776
- "fleet-coordinator": FLEET_COORDINATOR_META2
5777
- };
5778
- var WAVE4_ROLES = new Set(Object.keys(WAVE4_ROLE_METAS));
5779
-
5780
- // src/coordination/agents/role-skills.ts
5781
- var skillSet = (...names) => names;
5782
- var ROLE_SKILL_SETS = {
5783
- explore: skillSet("research-web", "node-modern", "typescript-strict"),
5784
- search: skillSet("bug-hunter", "typescript-strict", "research-web"),
5785
- research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
5786
- analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
5787
- planner: skillSet("sdd", "multi-agent", "refactor-planner", "testing"),
5788
- architect: skillSet("sdd", "api-design", "refactor-planner", "security-scanner", "observability"),
5789
- critic: skillSet("chimera", "bug-hunter", "security-scanner", "refactor-planner"),
5790
- "refactor-planner": skillSet(
5791
- "refactor-planner",
5792
- "sdd",
5793
- "typescript-strict",
5794
- "testing",
5795
- "bug-hunter"
5796
- ),
5797
- executor: skillSet("sdd", "typescript-strict", "node-modern", "testing", "git-flow"),
5798
- refactor: skillSet("refactor-planner", "typescript-strict", "testing", "bug-hunter"),
5799
- simplifier: skillSet("bug-hunter", "typescript-strict", "testing", "refactor-planner"),
5800
- migration: skillSet("tech-stack", "node-modern", "typescript-strict", "testing", "git-flow"),
5801
- vision: skillSet("react-modern", "testing", "typescript-strict", "security-scanner"),
5802
- debugger: skillSet("bug-hunter", "testing", "typescript-strict", "observability", "audit-log"),
5803
- tracer: skillSet("observability", "audit-log", "bug-hunter", "testing"),
5804
- verifier: skillSet("testing", "typescript-strict", "security-scanner", "chimera"),
5805
- test: skillSet("testing", "typescript-strict", "bug-hunter", "security-scanner"),
5806
- e2e: skillSet("testing", "react-modern", "api-design", "security-scanner"),
5807
- browser: skillSet("testing", "react-modern", "security-scanner", "research-web"),
5808
- performance: skillSet("observability", "testing", "node-modern", "audit-log"),
5809
- chaos: skillSet("testing", "observability", "security-scanner", "audit-log"),
5810
- "security-scanner": skillSet("security-scanner", "bug-hunter", "api-design", "tech-stack"),
5811
- "bug-hunter": skillSet("bug-hunter", "typescript-strict", "testing", "security-scanner"),
5812
- "audit-log": skillSet("audit-log", "observability", "bug-hunter", "output-standards"),
5813
- reviewer: skillSet("chimera", "bug-hunter", "security-scanner", "testing"),
5814
- "code-reviewer": skillSet("chimera", "bug-hunter", "typescript-strict", "testing"),
5815
- "security-reviewer": skillSet("security-scanner", "chimera", "api-design", "tech-stack"),
5816
- accessibility: skillSet("react-modern", "testing", "chimera", "security-scanner"),
5817
- compliance: skillSet("security-scanner", "audit-log", "tech-stack", "chimera"),
5818
- database: skillSet("api-design", "testing", "security-scanner", "observability"),
5819
- api: skillSet("api-design", "typescript-strict", "node-modern", "testing", "security-scanner"),
5820
- auth: skillSet("security-scanner", "api-design", "testing", "typescript-strict", "audit-log"),
5821
- data: skillSet("typescript-strict", "testing", "observability", "security-scanner"),
5822
- frontend: skillSet("react-modern", "typescript-strict", "testing", "security-scanner"),
5823
- backend: skillSet("node-modern", "typescript-strict", "api-design", "testing", "observability"),
5824
- designer: skillSet("react-modern", "prompt-engineering", "research-web", "output-standards"),
5825
- ios: skillSet("sdd", "testing", "security-scanner", "research-web"),
5826
- document: skillSet("output-standards", "research-web", "api-design", "prompt-engineering"),
5827
- uml: skillSet("sdd", "refactor-planner", "api-design", "output-standards"),
5828
- i18n: skillSet("react-modern", "testing", "typescript-strict", "output-standards"),
5829
- prompt: skillSet("prompt-engineering", "skill-creator", "output-standards", "testing"),
5830
- git: skillSet("git-flow", "chimera", "testing", "security-scanner"),
5831
- release: skillSet("git-flow", "tech-stack", "chimera", "security-scanner"),
5832
- devops: skillSet("docker-deploy", "observability", "security-scanner", "git-flow", "tech-stack"),
5833
- observability: skillSet("observability", "node-modern", "testing", "audit-log"),
5834
- dependency: skillSet("tech-stack", "security-scanner", "node-modern", "git-flow"),
5835
- "skill-manage": skillSet(
5836
- "skill-creator",
5837
- "prompt-engineering",
5838
- "security-scanner",
5839
- "output-standards"
5840
- ),
5841
- "self-improving": skillSet(
5842
- "audit-log",
5843
- "mnemosyne",
5844
- "prompt-engineering",
5845
- "bug-hunter",
5846
- "observability"
5847
- ),
5848
- context: skillSet("mnemosyne", "output-standards", "prompt-engineering", "audit-log"),
5849
- cost: skillSet("audit-log", "tech-stack", "observability", "research-web"),
5850
- "tech-stack": skillSet("tech-stack", "research-web", "security-scanner", "node-modern"),
5851
- // ── Wave 1: Platform and systems engineering ──────────────────────────
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
- ),
5866
- realtime: skillSet(
5867
- "api-design",
5868
- "observability",
5869
- "node-modern",
5870
- "typescript-strict",
5871
- "testing",
5872
- "tech-stack"
5873
- ),
5874
- "distributed-systems": skillSet(
5875
- "api-design",
5876
- "observability",
5877
- "typescript-strict",
5878
- "testing",
5879
- "security-scanner",
5880
- "tech-stack"
5881
- ),
5882
- concurrency: skillSet(
5883
- "bug-hunter",
5884
- "typescript-strict",
5885
- "testing",
5886
- "observability",
5887
- "node-modern",
5888
- "tech-stack"
5889
- ),
5890
- "platform-engineer": skillSet(
5891
- "node-modern",
5892
- "typescript-strict",
5893
- "git-flow",
5894
- "docker-deploy",
5895
- "tech-stack"
5896
- ),
5897
- // ── Wave 3: Product and data specialists ──────────────────────────────
5898
- payments: skillSet(
5899
- "api-design",
5900
- "security-scanner",
5901
- "audit-log",
5902
- "testing",
5903
- "typescript-strict",
5904
- "tech-stack"
5905
- ),
5906
- messaging: skillSet(
5907
- "api-design",
5908
- "observability",
5909
- "node-modern",
5910
- "testing",
5911
- "security-scanner",
5912
- "tech-stack"
5913
- ),
5914
- "search-relevance": skillSet(
5915
- "api-design",
5916
- "observability",
5917
- "testing",
5918
- "typescript-strict",
5919
- "data-governance",
5920
- "tech-stack"
5921
- ),
5922
- storage: skillSet(
5923
- "api-design",
5924
- "security-scanner",
5925
- "testing",
5926
- "observability",
5927
- "node-modern",
5928
- "tech-stack"
5929
- ),
5930
- "ml-engineer": skillSet(
5931
- "observability",
5932
- "typescript-strict",
5933
- "testing",
5934
- "security-scanner",
5935
- "data-governance",
5936
- "tech-stack"
5937
- ),
5938
- "data-governance": skillSet(
5939
- "audit-log",
5940
- "security-scanner",
5941
- "observability",
5942
- "testing",
5943
- "output-standards",
5944
- "tech-stack"
5945
- ),
5946
- // ── Wave 4: Agent platform and quality ───────────────────────────────
5947
- "plugin-author": skillSet(
5948
- "plugin-author",
5949
- "node-modern",
5950
- "typescript-strict",
5951
- "testing",
5952
- "security-scanner",
5953
- "tech-stack"
5954
- ),
5955
- "tool-author": skillSet(
5956
- "typescript-strict",
5957
- "security-scanner",
5958
- "testing",
5959
- "output-standards",
5960
- "tech-stack"
5961
- ),
5962
- "prompt-evaluator": skillSet(
5963
- "prompt-engineering",
5964
- "testing",
5965
- "output-standards",
5966
- "security-scanner",
5967
- "tech-stack"
5968
- ),
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"),
5972
- // ── Wave 2: Security, resilience, governance and agent platform ────────
5973
- "threat-modeler": skillSet(
5974
- "security-scanner",
5975
- "api-design",
5976
- "tech-stack",
5977
- "audit-log",
5978
- "testing"
5979
- ),
5980
- "secure-coding-coach": skillSet(
5981
- "security-scanner",
5982
- "testing",
5983
- "typescript-strict",
5984
- "api-design",
5985
- "tech-stack"
5986
- ),
5987
- "resilience-engineer": skillSet(
5988
- "observability",
5989
- "testing",
5990
- "typescript-strict",
5991
- "api-design",
5992
- "tech-stack"
5993
- ),
5994
- "chaos-engineer": skillSet(
5995
- "observability",
5996
- "testing",
5997
- "security-scanner",
5998
- "git-flow",
5999
- "tech-stack"
6000
- ),
6001
- "compliance-auditor": skillSet(
6002
- "audit-log",
6003
- "security-scanner",
6004
- "output-standards",
6005
- "testing",
6006
- "tech-stack"
6007
- ),
6008
- "privacy-engineer": skillSet(
6009
- "security-scanner",
6010
- "audit-log",
6011
- "api-design",
6012
- "data-governance",
6013
- "tech-stack"
6014
- )
6145
+ // src/coordination/agents/phase9-wave4-platform-meta.ts
6146
+ var PROMPT_evaluator_META = {
6147
+ rationale: "Prompt evaluator: builds rubrics, adversarial tests and regression suites. Independent measurement counterpart to the `prompt` author role.",
6148
+ signals: [
6149
+ "prompt eval",
6150
+ "prompt regression",
6151
+ "rubric",
6152
+ "adversarial prompt",
6153
+ "prompt benchmark",
6154
+ "judge prompt",
6155
+ "eval suite"
6156
+ ],
6157
+ differentiatesFrom: "prompt authors prompts; prompt-evaluator independently measures them with rubrics and adversarial inputs."
6015
6158
  };
6016
- var SHADOW_AGENT_SKILLS = skillSet(
6017
- "wrongstack-mailbox",
6018
- "multi-agent",
6019
- "audit-log",
6020
- "observability",
6021
- "security-scanner"
6022
- );
6023
- var MAX_EAGER_ROSTER_SKILLS = 3;
6024
- function assignSkillsToAgents(definitions) {
6025
- return definitions.map((definition) => {
6026
- const role = definition.config.role;
6027
- if (!role || !Object.hasOwn(ROLE_SKILL_SETS, role)) {
6028
- throw new Error(
6029
- `Missing curated skill set for roster role: "${role ?? definition.config.name}"`
6030
- );
6159
+ var BENCHMARK_ENGINEER_META2 = {
6160
+ rationale: "Benchmark engineer: reproducible benchmark design and methodology. The measurement-infrastructure counterpart to `performance`.",
6161
+ signals: [
6162
+ "benchmark",
6163
+ "reproducible",
6164
+ "evaluation harness",
6165
+ "regression suite",
6166
+ "metric methodology",
6167
+ "baseline",
6168
+ "deterministic run"
6169
+ ],
6170
+ differentiatesFrom: "performance optimizes product code; benchmark-engineer builds the measurement infrastructure that supports comparison."
6171
+ };
6172
+ var MEMORY_CURATOR_META2 = {
6173
+ rationale: "Memory curator: validates, merges and de-duplicates long-term memory with audience scope. Quality counterpart to `context`.",
6174
+ signals: [
6175
+ "memory",
6176
+ "curate",
6177
+ "memory audit",
6178
+ "memory merge",
6179
+ "memory expiry",
6180
+ "audience scope",
6181
+ "memory recall quality"
6182
+ ],
6183
+ differentiatesFrom: "context manages the active context window; memory-curator curates the long-term memory store for accuracy."
6184
+ };
6185
+ var FLEET_COORDINATOR_META2 = {
6186
+ rationale: "Fleet coordinator: multi-agent job partitioning, capacity planning and result synthesis. Operational counterpart to `planner`.",
6187
+ signals: [
6188
+ "fleet",
6189
+ "fan out",
6190
+ "parallel agents",
6191
+ "synthesis",
6192
+ "subagent capacity",
6193
+ "coordinator",
6194
+ "result aggregation"
6195
+ ],
6196
+ differentiatesFrom: "planner designs ordered steps for one worker; fleet-coordinator runs many workers in parallel and merges their results."
6197
+ };
6198
+ var PLUGIN_AUTHOR_META2 = {
6199
+ rationale: "Plugin author: build, package and ship runnable WrongStack plugins. The production counterpart to `skill-manage`.",
6200
+ signals: [
6201
+ "plugin",
6202
+ "plugin api",
6203
+ "lifecycle",
6204
+ "health",
6205
+ "teardown",
6206
+ "packaging",
6207
+ "distribution"
6208
+ ],
6209
+ differentiatesFrom: "skill-manage authors skills; plugin-author authors full lifecycle plugins with health and teardown."
6210
+ };
6211
+ var TOOL_AUTHOR_META2 = {
6212
+ rationale: "Tool author: tool schema, capability declaration, permission and error contract. The narrow, security-focused counterpart to `plugin-author`.",
6213
+ signals: [
6214
+ "tool",
6215
+ "tool schema",
6216
+ "capability",
6217
+ "permission",
6218
+ "tool contract",
6219
+ "tool error",
6220
+ "tool lifecycle"
6221
+ ],
6222
+ differentiatesFrom: "plugin-author writes full plugin lifecycle; tool-author focuses on a single tool contract and capability set."
6223
+ };
6224
+ var WAVE4_AGENTS = [
6225
+ {
6226
+ config: {
6227
+ id: "plugin-author",
6228
+ name: "Plugin Author",
6229
+ role: "plugin-author",
6230
+ tools: [...TOOLS.write, "fetch"],
6231
+ prompt: agentPrompt("plugin-author")
6232
+ },
6233
+ budget: MEDIUM_BUDGET,
6234
+ capability: {
6235
+ phase: "meta",
6236
+ summary: "Plugin author: build, package and ship runnable WrongStack plugins with full lifecycle, health and teardown.",
6237
+ keywords: [
6238
+ ...PLUGIN_AUTHOR_META2.signals,
6239
+ "plugin",
6240
+ "plugin-author",
6241
+ "lifecycle",
6242
+ "health",
6243
+ "teardown",
6244
+ "plugin-packaging"
6245
+ ]
6031
6246
  }
6032
- const skills = ROLE_SKILL_SETS[role];
6033
- return {
6034
- ...definition,
6035
- config: {
6036
- ...definition.config,
6037
- capabilities: inferRuntimeCapabilities(definition.config.tools ?? []),
6038
- skillNames: skills.slice(0, MAX_EAGER_ROSTER_SKILLS)
6039
- }
6040
- };
6041
- });
6042
- }
6247
+ },
6248
+ {
6249
+ config: {
6250
+ id: "tool-author",
6251
+ name: "Tool Author",
6252
+ role: "tool-author",
6253
+ tools: [...TOOLS.write, "install"],
6254
+ prompt: agentPrompt("tool-author")
6255
+ },
6256
+ budget: LIGHT_BUDGET,
6257
+ capability: {
6258
+ phase: "meta",
6259
+ summary: "Tool author: design tool schemas, capability declarations, permissions and error contracts with security as the first concern.",
6260
+ keywords: [
6261
+ ...TOOL_AUTHOR_META2.signals,
6262
+ "tool",
6263
+ "tool-author",
6264
+ "capability",
6265
+ "permission",
6266
+ "tool-schema",
6267
+ "tool-error"
6268
+ ]
6269
+ }
6270
+ },
6271
+ {
6272
+ config: {
6273
+ id: "prompt-evaluator",
6274
+ name: "Prompt Evaluator",
6275
+ role: "prompt-evaluator",
6276
+ tools: [...TOOLS.read, "write"],
6277
+ prompt: agentPrompt("prompt-evaluator")
6278
+ },
6279
+ budget: MEDIUM_BUDGET,
6280
+ capability: {
6281
+ phase: "meta",
6282
+ summary: "Prompt evaluator: build rubrics, adversarial prompt tests and regression suites for independent measurement.",
6283
+ keywords: [
6284
+ ...PROMPT_evaluator_META.signals,
6285
+ "prompt-evaluator",
6286
+ "rubric",
6287
+ "adversarial",
6288
+ "regression",
6289
+ "judge"
6290
+ ]
6291
+ }
6292
+ },
6293
+ {
6294
+ config: {
6295
+ id: "benchmark-engineer",
6296
+ name: "Benchmark Engineer",
6297
+ role: "benchmark-engineer",
6298
+ tools: [...TOOLS.read, "write", "logs"],
6299
+ prompt: agentPrompt("benchmark-engineer")
6300
+ },
6301
+ budget: MEDIUM_BUDGET,
6302
+ capability: {
6303
+ phase: "meta",
6304
+ summary: "Benchmark engineer: design reproducible benchmarks and methodology with deterministic baselines.",
6305
+ keywords: [
6306
+ ...BENCHMARK_ENGINEER_META2.signals,
6307
+ "benchmark-engineer",
6308
+ "benchmark",
6309
+ "baseline",
6310
+ "eval-harness",
6311
+ "deterministic"
6312
+ ]
6313
+ }
6314
+ },
6315
+ {
6316
+ config: {
6317
+ id: "memory-curator",
6318
+ name: "Memory Curator",
6319
+ role: "memory-curator",
6320
+ tools: [...TOOLS.read, ...SPECIALIST_TOOLS.memory],
6321
+ prompt: agentPrompt("memory-curator")
6322
+ },
6323
+ budget: LIGHT_BUDGET,
6324
+ capability: {
6325
+ phase: "meta",
6326
+ summary: "Memory curator: validate, merge and de-duplicate long-term memory entries with audience scoping.",
6327
+ keywords: [
6328
+ ...MEMORY_CURATOR_META2.signals,
6329
+ "memory-curator",
6330
+ "curate",
6331
+ "memory-audit",
6332
+ "memory-merge",
6333
+ "audience-scope"
6334
+ ]
6335
+ }
6336
+ },
6337
+ {
6338
+ config: {
6339
+ id: "fleet-coordinator",
6340
+ name: "Fleet Coordinator",
6341
+ role: "fleet-coordinator",
6342
+ tools: [...TOOLS.read, "plan"],
6343
+ prompt: agentPrompt("fleet-coordinator")
6344
+ },
6345
+ budget: MEDIUM_BUDGET,
6346
+ capability: {
6347
+ phase: "meta",
6348
+ summary: "Fleet coordinator: own multi-agent job partitioning, capacity planning and result synthesis across many parallel workers.",
6349
+ keywords: [
6350
+ ...FLEET_COORDINATOR_META2.signals,
6351
+ "fleet-coordinator",
6352
+ "fan-out",
6353
+ "parallel",
6354
+ "synthesis",
6355
+ "subagent-capacity"
6356
+ ]
6357
+ }
6358
+ }
6359
+ ];
6360
+ var WAVE4_ROLE_METAS = {
6361
+ "plugin-author": PLUGIN_AUTHOR_META2,
6362
+ "tool-author": TOOL_AUTHOR_META2,
6363
+ "prompt-evaluator": PROMPT_evaluator_META,
6364
+ "benchmark-engineer": BENCHMARK_ENGINEER_META2,
6365
+ "memory-curator": MEMORY_CURATOR_META2,
6366
+ "fleet-coordinator": FLEET_COORDINATOR_META2
6367
+ };
6368
+ var WAVE4_ROLES = new Set(Object.keys(WAVE4_ROLE_METAS));
6043
6369
 
6044
6370
  // src/coordination/agents/index.ts
6045
6371
  var ALL_AGENT_DEFINITIONS = assignSkillsToAgents([
@@ -6772,7 +7098,9 @@ ${content}`);
6772
7098
  }
6773
7099
  if (typeof config.maxConcurrent === "number") {
6774
7100
  if (!Number.isFinite(config.maxConcurrent) || config.maxConcurrent < 0) {
6775
- issues.push(`maxConcurrent must be a non-negative number (got ${config.maxConcurrent})`);
7101
+ issues.push(
7102
+ `maxConcurrent must be a non-negative number (got ${config.maxConcurrent})`
7103
+ );
6776
7104
  } else if (config.maxConcurrent === 0) {
6777
7105
  warnings.push("maxConcurrent is 0 \u2014 subagent concurrency effectively disabled");
6778
7106
  } else {
@@ -6784,12 +7112,28 @@ ${content}`);
6784
7112
  const v = fleetBudget[key];
6785
7113
  if (v === void 0) continue;
6786
7114
  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)})`);
7115
+ issues.push(
7116
+ `fleet.budget.${key} must be a non-negative number (got ${JSON.stringify(v)})`
7117
+ );
6788
7118
  } else {
6789
7119
  ok.push(`fleet.budget.${key} ${v}`);
6790
7120
  }
6791
7121
  }
6792
7122
  }
7123
+ const lastResortCap = config.fallbackMaxLastResortCandidates;
7124
+ if (lastResortCap !== void 0) {
7125
+ if (typeof lastResortCap !== "number" || !Number.isFinite(lastResortCap) || lastResortCap < 0) {
7126
+ issues.push(
7127
+ `fallbackMaxLastResortCandidates must be a non-negative number (got ${String(lastResortCap)})`
7128
+ );
7129
+ } else if (Math.floor(lastResortCap) === 0) {
7130
+ warnings.push(
7131
+ `fallbackMaxLastResortCandidates is ${lastResortCap} \u2014 floors to 0, last-resort auto-discovery append is disabled`
7132
+ );
7133
+ } else {
7134
+ ok.push(`fallbackMaxLastResortCandidates ${Math.floor(lastResortCap)}`);
7135
+ }
7136
+ }
6793
7137
  for (const [key, entry] of Object.entries(matrix)) {
6794
7138
  const eProvider = entry.provider ?? config.provider;
6795
7139
  const eModel = entry.model;
@@ -7747,21 +8091,21 @@ async function updateJsonObjectFile(filePath, mutator) {
7747
8091
  function isForbiddenSegment(segment) {
7748
8092
  return typeof segment === "string" && FORBIDDEN_PROTO_KEYS.has(segment);
7749
8093
  }
7750
- function assertSafePath(path7) {
7751
- for (const segment of path7) {
8094
+ function assertSafePath(path8) {
8095
+ for (const segment of path8) {
7752
8096
  if (isForbiddenSegment(segment)) {
7753
8097
  throw new Error(`Refusing to use reserved key "${String(segment)}" in a JSON path`);
7754
8098
  }
7755
8099
  }
7756
8100
  }
7757
- function setJsonPath(root, path7, value) {
7758
- assertSafePath(path7);
7759
- if (path7.length === 0) {
8101
+ function setJsonPath(root, path8, value) {
8102
+ assertSafePath(path8);
8103
+ if (path8.length === 0) {
7760
8104
  if (!isJsonObject(value)) throw new Error("Root config value must be an object");
7761
8105
  return value;
7762
8106
  }
7763
- const parent = ensureJsonParent(root, path7);
7764
- const leaf = lastPathSegment(path7);
8107
+ const parent = ensureJsonParent(root, path8);
8108
+ const leaf = lastPathSegment(path8);
7765
8109
  if (typeof leaf === "number") {
7766
8110
  if (!Array.isArray(parent))
7767
8111
  throw new Error(`Cannot set numeric segment ${leaf} on non-array parent`);
@@ -7775,17 +8119,17 @@ function setJsonPath(root, path7, value) {
7775
8119
  function isJsonObject(value) {
7776
8120
  return typeof value === "object" && value !== null && !Array.isArray(value);
7777
8121
  }
7778
- function lastPathSegment(path7) {
7779
- const segment = path7[path7.length - 1];
8122
+ function lastPathSegment(path8) {
8123
+ const segment = path8[path8.length - 1];
7780
8124
  if (segment === void 0) throw new Error("Invalid empty JSON path");
7781
8125
  return segment;
7782
8126
  }
7783
- function ensureJsonParent(root, path7) {
7784
- assertSafePath(path7);
8127
+ function ensureJsonParent(root, path8) {
8128
+ assertSafePath(path8);
7785
8129
  let current = root;
7786
- for (let i = 0; i < path7.length - 1; i += 1) {
7787
- const segment = path7[i];
7788
- const nextSegment = path7[i + 1];
8130
+ for (let i = 0; i < path8.length - 1; i += 1) {
8131
+ const segment = path8[i];
8132
+ const nextSegment = path8[i + 1];
7789
8133
  if (segment === void 0) throw new Error("Invalid empty JSON path segment");
7790
8134
  const nextContainer = typeof nextSegment === "number" ? [] : {};
7791
8135
  if (typeof segment === "number") {
@@ -8061,19 +8405,23 @@ function badge(state) {
8061
8405
  var GOVERNED_TOOL_EXECUTOR_META_KEY = "toolExecutor.executeGoverned";
8062
8406
 
8063
8407
  // src/utils/tool-name.ts
8408
+ import { createHash as createHash2 } from "node:crypto";
8064
8409
  var WIRE_TOOL_NAME_MAX_LENGTH = 128;
8065
- function sanitizeWireToolName(name) {
8066
- const replaced = name.replace(/[^a-zA-Z0-9_-]/g, "_");
8067
- const clamped = replaced.slice(0, WIRE_TOOL_NAME_MAX_LENGTH);
8068
- return clamped.length > 0 ? clamped : "tool";
8410
+ var MCP_SERVER_SEGMENT_MAX_LENGTH = 48;
8411
+ var MCP_IDENTITY_HASH_LENGTH = 10;
8412
+ function collisionSafeMcpSegment(value, maxLength) {
8413
+ const replaced = value.replace(/[^a-zA-Z0-9_-]/g, "_") || "tool";
8414
+ if (replaced === value && replaced.length <= maxLength) return replaced;
8415
+ const suffix = `_${createHash2("sha256").update(value, "utf8").digest("hex").slice(0, MCP_IDENTITY_HASH_LENGTH)}`;
8416
+ return `${replaced.slice(0, Math.max(1, maxLength - suffix.length))}${suffix}`;
8069
8417
  }
8070
8418
  function mcpServerToolPrefix(serverName) {
8071
- return `mcp__${sanitizeWireToolName(serverName)}__`;
8419
+ return `mcp__${collisionSafeMcpSegment(serverName, MCP_SERVER_SEGMENT_MAX_LENGTH)}__`;
8072
8420
  }
8073
8421
  function mcpQualifiedToolName(serverName, toolName) {
8074
- return sanitizeWireToolName(
8075
- `${mcpServerToolPrefix(serverName)}${sanitizeWireToolName(toolName)}`
8076
- );
8422
+ const prefix = mcpServerToolPrefix(serverName);
8423
+ const toolSegment = collisionSafeMcpSegment(toolName, WIRE_TOOL_NAME_MAX_LENGTH - prefix.length);
8424
+ return `${prefix}${toolSegment}`;
8077
8425
  }
8078
8426
 
8079
8427
  // src/tools/mcp-use.ts
@@ -8345,52 +8693,24 @@ var OneShotOrchestrator = class {
8345
8693
  }
8346
8694
  /**
8347
8695
  * Build the fallback model chain from input + config + current target.
8696
+ * Delegates to {@link FallbackProfileManager.resolveCandidates} — the
8697
+ * shared constructor used by both the agent loop and the one-shot path,
8698
+ * so both produce identical ordering, depth, and fromExplicitSource
8699
+ * semantics.
8700
+ *
8348
8701
  * The injected {@link OneShotOrchestratorOptions.fallbackProfileManager}
8349
8702
  * is the only allowed manager — OneShot never owns a private snapshot
8350
8703
  * so a live `ConfigStore` change reaches every call without rebuilding
8351
8704
  * the manager.
8352
8705
  */
8353
8706
  resolveFallbackChain(input, config, target) {
8354
- const mgr = this.opts.fallbackProfileManager;
8355
- let fromExplicitSource = false;
8356
- let selected = mgr.resolveEffective({
8357
- fallbackAuto: false,
8358
- exclude: target
8707
+ return this.opts.fallbackProfileManager.resolveCandidates(target, {
8708
+ fallbackModels: input.fallbackModels && input.fallbackModels.length > 0 ? input.fallbackModels : config.fallbackModels,
8709
+ // The one-shot already tried `target` as the primary before entering
8710
+ // the chain. Passing it as `primary` suppresses the primary-insertion
8711
+ // step in resolveCandidates (primary === current → not pushed).
8712
+ primary: target
8359
8713
  });
8360
- if (input.fallbackModels && input.fallbackModels.length > 0) {
8361
- const resolved = mgr.resolveEffective({
8362
- fallbackModels: input.fallbackModels,
8363
- fallbackAuto: false,
8364
- exclude: target
8365
- });
8366
- selected = resolved;
8367
- fromExplicitSource = resolved.length > 0;
8368
- } else if (config.fallbackModels && config.fallbackModels.length > 0) {
8369
- const resolved = mgr.resolveEffective({
8370
- fallbackModels: config.fallbackModels,
8371
- fallbackAuto: false,
8372
- exclude: target
8373
- });
8374
- selected = resolved;
8375
- fromExplicitSource = resolved.length > 0;
8376
- } else if (config.fallbackAuto !== false) {
8377
- selected = mgr.resolveEffective({
8378
- fallbackAuto: true,
8379
- exclude: target
8380
- });
8381
- }
8382
- if (config.fallbackAuto === false) return selected;
8383
- if (fromExplicitSource) return selected;
8384
- const combined = [...selected, ...mgr.resolveAllConfigured(target)];
8385
- const seen = /* @__PURE__ */ new Set();
8386
- return Object.freeze(
8387
- combined.filter((entry) => {
8388
- const key = `${entry.providerId}/${entry.model}`;
8389
- if (seen.has(key)) return false;
8390
- seen.add(key);
8391
- return true;
8392
- })
8393
- );
8394
8714
  }
8395
8715
  /** Attempt a provider call while preserving the actual failure for callers. */
8396
8716
  async tryCall(provider, request, signal, providerId, model) {
@@ -8644,10 +8964,10 @@ function validateAgainstSchema(value, schema) {
8644
8964
  return { ok: errors.length === 0, errors };
8645
8965
  }
8646
8966
  var MAX_SCHEMA_DEPTH = 64;
8647
- function walk(value, schema, path7, errors, depth) {
8967
+ function walk(value, schema, path8, errors, depth) {
8648
8968
  if (depth > MAX_SCHEMA_DEPTH) {
8649
8969
  errors.push({
8650
- path: path7 || "<root>",
8970
+ path: path8 || "<root>",
8651
8971
  message: `schema nesting exceeds maximum depth (${MAX_SCHEMA_DEPTH})`
8652
8972
  });
8653
8973
  return;
@@ -8655,7 +8975,7 @@ function walk(value, schema, path7, errors, depth) {
8655
8975
  if (schema.enum !== void 0) {
8656
8976
  if (!enumIncludes(schema.enum, value)) {
8657
8977
  errors.push({
8658
- path: path7 || "<root>",
8978
+ path: path8 || "<root>",
8659
8979
  message: `expected one of ${JSON.stringify(schema.enum)}, got ${JSON.stringify(value)}`
8660
8980
  });
8661
8981
  return;
@@ -8664,7 +8984,7 @@ function walk(value, schema, path7, errors, depth) {
8664
8984
  if (typeof schema.type === "string") {
8665
8985
  if (!checkType(value, schema.type)) {
8666
8986
  errors.push({
8667
- path: path7 || "<root>",
8987
+ path: path8 || "<root>",
8668
8988
  message: `expected ${schema.type}, got ${describeType(value)} (${previewValue(value)})`
8669
8989
  });
8670
8990
  return;
@@ -8676,7 +8996,7 @@ function walk(value, schema, path7, errors, depth) {
8676
8996
  if (!(req in obj)) {
8677
8997
  const expected = schema.properties?.[req]?.type;
8678
8998
  errors.push({
8679
- path: joinPath(path7, req),
8999
+ path: joinPath(path8, req),
8680
9000
  message: `required property missing${typeof expected === "string" ? ` (expected ${expected})` : ""}`
8681
9001
  });
8682
9002
  }
@@ -8684,14 +9004,14 @@ function walk(value, schema, path7, errors, depth) {
8684
9004
  if (schema.properties) {
8685
9005
  for (const [key, subSchema] of Object.entries(schema.properties)) {
8686
9006
  if (key in obj) {
8687
- walk(obj[key], subSchema, joinPath(path7, key), errors, depth + 1);
9007
+ walk(obj[key], subSchema, joinPath(path8, key), errors, depth + 1);
8688
9008
  }
8689
9009
  }
8690
9010
  }
8691
9011
  }
8692
9012
  if (schema.type === "array" && Array.isArray(value) && schema.items) {
8693
9013
  for (let i = 0; i < value.length; i++) {
8694
- walk(value[i], schema.items, `${path7}[${i}]`, errors, depth + 1);
9014
+ walk(value[i], schema.items, `${path8}[${i}]`, errors, depth + 1);
8695
9015
  }
8696
9016
  }
8697
9017
  }