@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.
- package/README.md +1 -1
- package/dist/chronicle/index.js +117 -30
- package/dist/chronicle/project-server.js +12 -5
- package/dist/coordination/agents/index.js +4645 -3448
- package/dist/coordination/agents/project-agent-auto-optimize.d.ts +116 -0
- package/dist/coordination/agents/project-agent-capture-window.d.ts +29 -0
- package/dist/coordination/agents/project-agent-config-io.d.ts +11 -0
- package/dist/coordination/agents/project-agent-consolidation.d.ts +34 -2
- package/dist/coordination/agents/project-agent-directive-outcome.d.ts +57 -0
- package/dist/coordination/agents/project-agent-files.d.ts +12 -3
- package/dist/coordination/agents/project-agent-identity-types.d.ts +4 -0
- package/dist/coordination/agents/project-agent-identity.d.ts +38 -11
- package/dist/coordination/agents/project-agent-learning-entries.d.ts +8 -2
- package/dist/coordination/agents/project-agent-learning-policy.d.ts +22 -1
- package/dist/coordination/agents/project-agent-learning-structured.d.ts +72 -1
- package/dist/coordination/agents/project-agent-optimizer.d.ts +49 -0
- package/dist/coordination/agents/project-agent-quarantine.d.ts +63 -0
- package/dist/coordination/agents/project-agent-skill-layer.d.ts +146 -0
- package/dist/coordination/agents/role-skills.d.ts +11 -1
- package/dist/coordination/agents/types.d.ts +10 -2
- package/dist/coordination/director-prompts.d.ts +19 -6
- package/dist/coordination/director-tools.d.ts +2 -2
- package/dist/coordination/fleet.d.ts +0 -6
- package/dist/coordination/index.d.ts +1 -1
- package/dist/coordination/index.js +5746 -4326
- package/dist/coordination/mail-tools.d.ts +1 -1
- package/dist/core/agent-types.d.ts +4 -2
- package/dist/core/agent.d.ts +1 -0
- package/dist/core/context.d.ts +19 -0
- package/dist/core/conversation-state.d.ts +14 -0
- package/dist/core/fallback-profile-manager.d.ts +70 -2
- package/dist/core/index.js +317 -108
- package/dist/core/system-prompt-blocks.d.ts +1 -1
- package/dist/core/system-prompt-builder.d.ts +13 -1
- package/dist/core/system-prompt-glossary.d.ts +73 -0
- package/dist/core/system-prompt-memory-skills.d.ts +2 -2
- package/dist/defaults/index.js +1635 -1120
- package/dist/execution/council-orchestrator.d.ts +3 -13
- package/dist/execution/index.js +3136 -2706
- package/dist/execution/one-shot-llm.d.ts +5 -0
- package/dist/goal/index.js +7 -0
- package/dist/hq/index.js +17 -7
- package/dist/hq/protocol/kanban.d.ts +21 -0
- package/dist/hq/protocol.js +5 -1
- package/dist/hq/redaction.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5502 -3483
- package/dist/infrastructure/index.js +247 -122
- package/dist/kernel/events/agent-events.d.ts +28 -0
- package/dist/plugin/index.js +111 -7
- package/dist/registry/index.js +11 -0
- package/dist/registry/tool-registry.d.ts +8 -0
- package/dist/replay/hash.d.ts +9 -0
- package/dist/replay/index.js +14 -4
- package/dist/replay/replay-provider-runner.d.ts +31 -1
- package/dist/security/index.js +92 -21
- package/dist/security/kanban-boundary.d.ts +5 -1
- package/dist/security/secret-vault.d.ts +2 -0
- package/dist/session-catalog/index.js +86 -10
- package/dist/session-catalog/project-server.js +100 -17
- package/dist/session-catalog/protocol.d.ts +20 -4
- package/dist/session-catalog/store.d.ts +19 -3
- package/dist/storage/index.js +305 -69
- package/dist/storage/memory-consolidator.d.ts +4 -2
- package/dist/storage/plan-store.d.ts +1 -1
- package/dist/storage/session-resume-validation.d.ts +24 -0
- package/dist/storage/session-store/directory-scan.d.ts +5 -1
- package/dist/storage/session-store/fork-session.d.ts +13 -1
- package/dist/storage/session-store/load-cache.d.ts +11 -0
- package/dist/storage/session-store/prune-helpers.d.ts +5 -0
- package/dist/storage/session-store.d.ts +18 -0
- package/dist/tasking/index.js +5 -0
- package/dist/tools/index.js +1151 -831
- package/dist/types/config/mcp-features.d.ts +31 -1
- package/dist/types/config/root.d.ts +23 -1
- package/dist/types/config/skills-fleet-brain.d.ts +34 -0
- package/dist/types/config/tools.d.ts +22 -0
- package/dist/types/config/ui.d.ts +14 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/default-config.d.ts +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +23 -0
- package/dist/types/multi-agent.d.ts +7 -0
- package/dist/types/session.d.ts +9 -1
- package/dist/types/task-graph.d.ts +2 -0
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +217 -76
- package/dist/utils/project-state-guard.d.ts +21 -0
- package/dist/utils/session-scoped-path.d.ts +17 -0
- package/dist/utils/todos-format.d.ts +20 -0
- package/instructions/leader-after-task.md +3 -4
- package/instructions/system-lite.md +17 -14
- package/instructions/system-pro.md +29 -30
- package/instructions/system.md +29 -28
- package/package.json +3 -3
- package/skills/wrongstack-kanban/SKILL.md +95 -93
package/dist/tools/index.js
CHANGED
|
@@ -496,9 +496,9 @@ function normalizeOptions(options) {
|
|
|
496
496
|
...option.consequence?.trim() ? { consequence: option.consequence.trim() } : {}
|
|
497
497
|
}));
|
|
498
498
|
}
|
|
499
|
-
function requiredInstruction(
|
|
500
|
-
const text = readBundledInstructionText(
|
|
501
|
-
if (!text) throw new Error(`Council instruction file is unavailable: ${
|
|
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
|
|
648
|
-
*
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
2308
|
-
import * as
|
|
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
|
|
2452
|
-
import * as
|
|
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-
|
|
2971
|
-
|
|
2972
|
-
|
|
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
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
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 =
|
|
3781
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
3200
3782
|
const profileInstructions = resolveWstackPaths({ projectRoot: process.cwd() }).globalInstructions;
|
|
3201
3783
|
return [
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
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 =
|
|
3794
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
3213
3795
|
const roots = [
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
3857
|
+
const here = path7.dirname(fileURLToPath2(import.meta.url));
|
|
3276
3858
|
const explicitDir = envDir || void 0;
|
|
3277
3859
|
const candidates = [
|
|
3278
|
-
...explicitDir ? [
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
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
|
-
/**
|
|
3342
|
-
|
|
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
|
|
6017
|
-
"
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
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
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
7751
|
-
for (const segment of
|
|
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,
|
|
7758
|
-
assertSafePath(
|
|
7759
|
-
if (
|
|
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,
|
|
7764
|
-
const leaf = lastPathSegment(
|
|
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(
|
|
7779
|
-
const segment =
|
|
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,
|
|
7784
|
-
assertSafePath(
|
|
8127
|
+
function ensureJsonParent(root, path8) {
|
|
8128
|
+
assertSafePath(path8);
|
|
7785
8129
|
let current = root;
|
|
7786
|
-
for (let i = 0; i <
|
|
7787
|
-
const segment =
|
|
7788
|
-
const nextSegment =
|
|
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
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
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__${
|
|
8419
|
+
return `mcp__${collisionSafeMcpSegment(serverName, MCP_SERVER_SEGMENT_MAX_LENGTH)}__`;
|
|
8072
8420
|
}
|
|
8073
8421
|
function mcpQualifiedToolName(serverName, toolName) {
|
|
8074
|
-
|
|
8075
|
-
|
|
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
|
-
|
|
8355
|
-
|
|
8356
|
-
|
|
8357
|
-
|
|
8358
|
-
|
|
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,
|
|
8967
|
+
function walk(value, schema, path8, errors, depth) {
|
|
8648
8968
|
if (depth > MAX_SCHEMA_DEPTH) {
|
|
8649
8969
|
errors.push({
|
|
8650
|
-
path:
|
|
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:
|
|
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:
|
|
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(
|
|
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(
|
|
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, `${
|
|
9014
|
+
walk(value[i], schema.items, `${path8}[${i}]`, errors, depth + 1);
|
|
8695
9015
|
}
|
|
8696
9016
|
}
|
|
8697
9017
|
}
|