@wrongstack/core 0.308.6 → 0.309.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/coordination/agents/index.js +1 -0
  2. package/dist/coordination/agents/role-skills.d.ts +1 -0
  3. package/dist/coordination/director/director-toolset.d.ts +2 -2
  4. package/dist/coordination/director-mutation-test-tool.d.ts +29 -0
  5. package/dist/coordination/director-tools.d.ts +2 -0
  6. package/dist/coordination/director.d.ts +9 -0
  7. package/dist/coordination/explore-companion.d.ts +191 -0
  8. package/dist/coordination/fleet.d.ts +26 -0
  9. package/dist/coordination/index.d.ts +2 -1
  10. package/dist/coordination/index.js +1396 -370
  11. package/dist/coordination/mail-tools.d.ts +10 -6
  12. package/dist/coordination/mailbox-codecs.d.ts +31 -0
  13. package/dist/coordination/multi-agent-coordinator.d.ts +14 -0
  14. package/dist/coordination/multi-agent-timeout.d.ts +11 -1
  15. package/dist/coordination/mutation-engine.d.ts +74 -0
  16. package/dist/coordination/subagent-budget.d.ts +54 -0
  17. package/dist/coordination/subagent-finish.d.ts +78 -0
  18. package/dist/core/index.js +19 -4
  19. package/dist/defaults/index.js +731 -52
  20. package/dist/execution/compaction-core.d.ts +1 -1
  21. package/dist/execution/compaction-elision.d.ts +0 -10
  22. package/dist/execution/index.js +269 -16
  23. package/dist/goal/index.js +54 -27
  24. package/dist/goal/phase-orchestrator.d.ts +7 -0
  25. package/dist/goal/types.d.ts +1 -1
  26. package/dist/index.d.ts +1 -1
  27. package/dist/index.js +1280 -201
  28. package/dist/kernel/events/agent-events.d.ts +31 -2
  29. package/dist/models/index.js +11 -1
  30. package/dist/plugin/discovery.d.ts +73 -0
  31. package/dist/plugin/index.d.ts +2 -0
  32. package/dist/plugin/index.js +270 -29
  33. package/dist/plugin/loader.d.ts +5 -1
  34. package/dist/plugin/trust.d.ts +78 -0
  35. package/dist/tools/index.js +1 -0
  36. package/dist/types/config/mcp-features.d.ts +21 -0
  37. package/dist/types/config/skills-fleet-brain.d.ts +18 -0
  38. package/dist/types/index.d.ts +1 -1
  39. package/dist/types/index.js +14 -0
  40. package/dist/types/multi-agent.d.ts +15 -0
  41. package/dist/types/provider.d.ts +29 -1
  42. package/instructions/agents/chaos-monkey.md +57 -0
  43. package/instructions/agents/explore-companion.md +35 -0
  44. package/package.json +3 -3
@@ -144,8 +144,8 @@ export interface AgentEventMap {
144
144
  /**
145
145
  * A spawn resolved fewer skills than it selected. Emitted so a skill that
146
146
  * silently failed to load — missing from the loader, gated by a capability
147
- * the subagent lacks, or cut by the prompt budget — is observable instead of
148
- * leaving the agent believing it received guidance it never got.
147
+ * the subagent lacks, or cut by the prompt budget — is observable instead
148
+ * of leaving the agent believing it received guidance it never got.
149
149
  */
150
150
  'subagent.skills.dropped': {
151
151
  sessionId?: string | undefined;
@@ -155,6 +155,35 @@ export interface AgentEventMap {
155
155
  /** skill → reason it was dropped. */
156
156
  dropped: Record<string, string>;
157
157
  };
158
+ /**
159
+ * In-band graceful-finish request for a background subagent (see
160
+ * coordination/subagent-finish.ts). Emitted on the subagent's OWN EventBus
161
+ * when its wall-clock deadline is crossed — or the leader explicitly asked
162
+ * it to finish (session shutdown) — and the subagent's config opted into
163
+ * `gracefulFinish`. The runner folds the notice into the conversation as a
164
+ * `/btw` note at the next iteration boundary; the model then completes its
165
+ * task in its own turn within the granted grace window. This event is a
166
+ * notification, never an interrupt: nothing aborts when it fires.
167
+ */
168
+ 'subagent.finish_requested': {
169
+ /** Parent/host session id. */
170
+ sessionId?: string | undefined;
171
+ /**
172
+ * Owning subagent id when the budget knows it; omitted when the budget
173
+ * was constructed without one. An empty string would look like an
174
+ * address that matches nothing — the runner treats an omitted id as
175
+ * deliverable (`if (e.subagentId && …)`), so omission is safe.
176
+ */
177
+ subagentId?: string | undefined;
178
+ /** Why the finish was requested (deadline crossed / leader finished). */
179
+ reason: string;
180
+ /** Epoch ms by which the subagent should have produced its final output. */
181
+ deadlineMs: number;
182
+ /** Granted working-time window in ms. */
183
+ graceMs: number;
184
+ /** Ready-to-read notice text; the loop folds it in as a `/btw` note. */
185
+ notice: string;
186
+ };
158
187
  /**
159
188
  * A background learning-distillation pass finished for a roster role.
160
189
  * Emitted by the fleet host's auto-optimize scheduler so surfaces can show
@@ -877,7 +877,17 @@ function normalizeModelsDevModel(model) {
877
877
  const reasoningConfig = {
878
878
  default: disableSupported ? "enabled" : "always_on",
879
879
  disableSupported,
880
- effortSupported: effortLevels.length > 0,
880
+ // Tri-state (see ReasoningConfig.effortSupported):
881
+ // options present → documented answer (true when effort values exist;
882
+ // an explicitly EMPTY array is a documented "no
883
+ // effort control", not an absent field).
884
+ // field ABSENT → the model is known to reason but its vocabulary is
885
+ // undocumented → `undefined`, so the resolver forwards
886
+ // the request and each wire adapter applies its own
887
+ // transport gating. Sending `false` here would make
888
+ // the resolver claim "does not support effort" — an
889
+ // assertion the catalog never made.
890
+ ...raw === void 0 ? {} : { effortSupported: effortLevels.length > 0 },
881
891
  effortLevels,
882
892
  preserveThinking: model.interleaved ? "always_on" : "unsupported"
883
893
  };
@@ -0,0 +1,73 @@
1
+ /**
2
+ * External plugin discovery — filesystem convention for third-party plugins.
3
+ *
4
+ * Two roots are scanned (when present):
5
+ * - `~/.wrongstack/plugins/` — user-global plugins
6
+ * - `<projectRoot>/.wrongstack/plugins/` — project-local plugins
7
+ *
8
+ * Each immediate child of a root is a candidate:
9
+ * - a directory named `<plugin-name>/` whose entry is resolved from
10
+ * `package.json` (`main` / `exports["."]`) or falls back to
11
+ * `index.js` / `index.mjs` / `index.cjs`,
12
+ * - or a single entry file (`<plugin-name>.js` / `.mjs` / `.cjs`).
13
+ *
14
+ * `node_modules` and dot-entries are skipped: packages installed by
15
+ * `wstack plugin add --install` are loaded through their explicit
16
+ * `path` config entry, not through directory discovery, so they are
17
+ * never double-loaded. Scan order is alphabetical per root for
18
+ * deterministic load order; roots are scanned in the order given.
19
+ *
20
+ * Discovery is read-only and never imports plugin code — resolving an
21
+ * entry does not execute it. The host decides what to do with the
22
+ * candidates (enablement, trust pinning, import).
23
+ */
24
+ import type { Dirent } from 'node:fs';
25
+ export interface ExternalPluginCandidate {
26
+ /** Candidate name — the directory or file basename without extension. */
27
+ name: string;
28
+ /** Absolute path of the resolved JavaScript entry file. */
29
+ entryPath: string;
30
+ /** Discovery root the candidate was found under. */
31
+ root: string;
32
+ }
33
+ export interface SkippedPluginCandidate {
34
+ name: string;
35
+ root: string;
36
+ reason: string;
37
+ }
38
+ export interface PluginDiscoveryResult {
39
+ candidates: ExternalPluginCandidate[];
40
+ /** Candidates that were found but could not be resolved to an entry. */
41
+ skipped: SkippedPluginCandidate[];
42
+ }
43
+ /** Injectable filesystem access so discovery is unit-testable. */
44
+ export interface DiscoveryIo {
45
+ readdir(root: string): Promise<Dirent[]>;
46
+ stat(path: string): Promise<{
47
+ isFile(): boolean;
48
+ isDirectory(): boolean;
49
+ }>;
50
+ readFile(path: string): Promise<string>;
51
+ }
52
+ export declare const DEFAULT_PLUGIN_DISCOVERY_IO: DiscoveryIo;
53
+ /**
54
+ * Resolve the JavaScript entry file for a plugin directory: `package.json`
55
+ * `main`/`exports["."]` first, then `index.js`/`index.mjs`/`index.cjs`
56
+ * probing. Returns null when no entry can be resolved. Shared by directory
57
+ * discovery and explicit `config.plugins[].path` resolution. Paths are
58
+ * returned with forward separators on every platform so trust pin keys and
59
+ * import targets stay canonical.
60
+ */
61
+ export declare function resolvePluginEntryPath(dir: string, io?: DiscoveryIo): Promise<string | null>;
62
+ /**
63
+ * Resolve a configured target (entry file OR directory) to its entry file.
64
+ * Returns the input (forward-slash normalized) when it is already a file;
65
+ * null when it is a directory without a resolvable entry or does not exist.
66
+ */
67
+ export declare function resolvePluginTarget(target: string, io?: DiscoveryIo): Promise<string | null>;
68
+ /**
69
+ * Scan discovery roots for external plugin candidates. Missing roots are
70
+ * not an error — they simply contribute no candidates.
71
+ */
72
+ export declare function discoverExternalPlugins(roots: readonly string[], io?: DiscoveryIo): Promise<PluginDiscoveryResult>;
73
+ //# sourceMappingURL=discovery.d.ts.map
@@ -1,6 +1,8 @@
1
1
  export { DefaultPluginAPI, definePlugin, type PluginAPIInit } from './api.js';
2
+ export { DEFAULT_PLUGIN_DISCOVERY_IO, discoverExternalPlugins, type DiscoveryIo, type ExternalPluginCandidate, type PluginDiscoveryResult, resolvePluginEntryPath, resolvePluginTarget, type SkippedPluginCandidate, } from './discovery.js';
2
3
  export { diffPluginConfig, type PluginConfigChange, type PluginConfigSource, type PluginEnablementSource, pluginEntryMatchesName, type ResolvePluginConfigInput, type ResolvePluginEnablementInput, type ResolvedPluginConfig, type ResolvedPluginEnablement, redactPluginConfig, resolvePluginConfig, resolvePluginEnablement, resolvePluginManifestConfig, validatePluginConfigMetadata, } from './config.js';
3
4
  export { KERNEL_API_VERSION, loadPlugins, type LoadPluginsOptions, type PluginHostHandle, type PluginLoadFailure, unloadPlugins, } from './loader.js';
5
+ export { defaultPluginTrustPath, hashFileContents, normalizeTrustKey, type PluginTrustEntry, type PluginTrustStore, pinPluginTrust, readPluginTrustStore, unpinPluginTrust, verifyPluginTrust, type PluginTrustVerification, writePluginTrustStore, } from './trust.js';
4
6
  export type { PluginAPI } from '../types/plugin.js';
5
7
  export { buildReviewerModelPool, createAutoReviewPlugin, parseReviewSeverity, type ReviewerModelAssignment, selectRoundRobinReviewerAssignment, } from '../plugins/auto-review-plugin.js';
6
8
  export { type CascadeAgentKind, type CascadeEvidenceCheckResult, type CascadeEvidenceStatus, CHIMERA_REVIEW_PROMPT, createChimeraPlugin, type ChimeraCascadeNeededPayload, type ChimeraReviewCompletePayload, type ChimeraReviewNeededPayload, type ReviewContextBundle, } from '../plugins/chimera-plugin.js';
@@ -217,7 +217,7 @@ var init_atomic_write = __esm({
217
217
  });
218
218
 
219
219
  // src/plugins/review-finding-types.ts
220
- import { createHash as createHash8 } from "node:crypto";
220
+ import { createHash as createHash9 } from "node:crypto";
221
221
  function normalizeFingerprintTitle(title) {
222
222
  return title.trim().toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
223
223
  }
@@ -225,7 +225,7 @@ function computeFindingFingerprint(file, line, title) {
225
225
  const normalizedTitle = normalizeFingerprintTitle(title);
226
226
  const normalizedFile = file.replace(/\\/g, "/").trim().toLowerCase();
227
227
  const lineStr = line != null && line >= 0 ? String(line) : "0";
228
- const hash = createHash8("sha256");
228
+ const hash = createHash9("sha256");
229
229
  hash.update(`${normalizedFile}:${lineStr}:${normalizedTitle}`);
230
230
  return hash.digest("hex");
231
231
  }
@@ -1664,7 +1664,8 @@ async function loadPlugins(plugins, opts) {
1664
1664
  plugin,
1665
1665
  resolution.options
1666
1666
  );
1667
- const api = plugin.capabilities ? wrapApiForCapabilityCheck(plugin, rawApi, opts.log, opts.enforceCapabilities) : rawApi;
1667
+ const enforceForPlugin = typeof opts.enforceCapabilities === "function" ? opts.enforceCapabilities(plugin) : opts.enforceCapabilities ?? false;
1668
+ const api = plugin.capabilities ? wrapApiForCapabilityCheck(plugin, rawApi, opts.log, enforceForPlugin) : rawApi;
1668
1669
  registration = {
1669
1670
  plugin,
1670
1671
  api,
@@ -2241,6 +2242,234 @@ function definePlugin(metadata, factory) {
2241
2242
  };
2242
2243
  }
2243
2244
 
2245
+ // src/plugin/discovery.ts
2246
+ var DEFAULT_PLUGIN_DISCOVERY_IO = {
2247
+ async readdir(root) {
2248
+ const { readdir: readdir8 } = await import("node:fs/promises");
2249
+ return readdir8(root, { withFileTypes: true });
2250
+ },
2251
+ async stat(path35) {
2252
+ const { stat: stat9 } = await import("node:fs/promises");
2253
+ return stat9(path35);
2254
+ },
2255
+ async readFile(path35) {
2256
+ const { readFile: readFile22 } = await import("node:fs/promises");
2257
+ return readFile22(path35, "utf8");
2258
+ }
2259
+ };
2260
+ var ENTRY_EXTENSIONS = [".js", ".mjs", ".cjs"];
2261
+ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set(["node_modules"]);
2262
+ function isDotEntry(name) {
2263
+ return name.startsWith(".");
2264
+ }
2265
+ function hasEntryExtension(name) {
2266
+ return ENTRY_EXTENSIONS.some((ext) => name.endsWith(ext));
2267
+ }
2268
+ async function isFile(io, path35) {
2269
+ try {
2270
+ return (await io.stat(path35)).isFile();
2271
+ } catch {
2272
+ return false;
2273
+ }
2274
+ }
2275
+ async function isDirectory(io, path35) {
2276
+ try {
2277
+ return (await io.stat(path35)).isDirectory();
2278
+ } catch {
2279
+ return false;
2280
+ }
2281
+ }
2282
+ async function resolvePackageEntry(io, dir) {
2283
+ const pkgPath = `${dir}/package.json`;
2284
+ if (!await isFile(io, pkgPath)) {
2285
+ return { entry: void 0, problem: void 0 };
2286
+ }
2287
+ let parsed;
2288
+ try {
2289
+ parsed = JSON.parse(await io.readFile(pkgPath));
2290
+ } catch (err) {
2291
+ return {
2292
+ entry: void 0,
2293
+ problem: `invalid package.json (${err instanceof Error ? err.message : String(err)})`
2294
+ };
2295
+ }
2296
+ if (parsed === null || typeof parsed !== "object") {
2297
+ return { entry: void 0, problem: "package.json is not an object" };
2298
+ }
2299
+ const pkg = parsed;
2300
+ const candidates = [];
2301
+ const dotExport = pkg.exports?.["."];
2302
+ if (typeof dotExport === "string") {
2303
+ candidates.push(dotExport);
2304
+ } else if (dotExport !== null && typeof dotExport === "object") {
2305
+ const conditions = dotExport;
2306
+ for (const key of ["import", "module", "default"]) {
2307
+ const value = conditions[key];
2308
+ if (typeof value === "string") candidates.push(value);
2309
+ }
2310
+ }
2311
+ if (typeof pkg.main === "string" && pkg.main.length > 0) {
2312
+ candidates.push(pkg.main);
2313
+ }
2314
+ for (const candidate of candidates) {
2315
+ const normalized = candidate.startsWith(".") ? candidate : `./${candidate}`;
2316
+ const base = `${dir}/${normalized.slice(2)}`;
2317
+ if (await isFile(io, base)) return { entry: base, problem: void 0 };
2318
+ for (const ext of ENTRY_EXTENSIONS) {
2319
+ if (await isFile(io, `${base}${ext}`)) {
2320
+ return { entry: `${base}${ext}`, problem: void 0 };
2321
+ }
2322
+ }
2323
+ }
2324
+ return {
2325
+ entry: void 0,
2326
+ problem: 'package.json declares no resolvable entry (checked main, exports["."], index fallbacks)'
2327
+ };
2328
+ }
2329
+ async function resolvePluginEntryPath(dir, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
2330
+ const { entry: resolved } = await resolvePackageEntry(io, dir);
2331
+ if (resolved) return resolved;
2332
+ for (const ext of ENTRY_EXTENSIONS) {
2333
+ const candidate = `${dir}/index${ext}`;
2334
+ if (await isFile(io, candidate)) return candidate;
2335
+ }
2336
+ return null;
2337
+ }
2338
+ function canon(path35) {
2339
+ return path35.replaceAll("\\", "/");
2340
+ }
2341
+ async function resolvePluginTarget(target, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
2342
+ if (await isFile(io, target)) return canon(target);
2343
+ if (await isDirectory(io, target)) return resolvePluginEntryPath(canon(target), io);
2344
+ return null;
2345
+ }
2346
+ async function discoverExternalPlugins(roots, io = DEFAULT_PLUGIN_DISCOVERY_IO) {
2347
+ const candidates = [];
2348
+ const skipped = [];
2349
+ for (const root of roots) {
2350
+ let entries;
2351
+ try {
2352
+ entries = await io.readdir(root);
2353
+ } catch {
2354
+ continue;
2355
+ }
2356
+ const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
2357
+ for (const entry of sorted) {
2358
+ if (isDotEntry(entry.name) || SKIP_DIR_NAMES.has(entry.name)) continue;
2359
+ const full = canon(`${root}/${entry.name}`);
2360
+ if (entry.isDirectory() || await isDirectory(io, full)) {
2361
+ const resolved = await resolvePluginEntryPath(full, io);
2362
+ if (resolved) {
2363
+ candidates.push({ name: entry.name, entryPath: resolved, root });
2364
+ } else {
2365
+ skipped.push({
2366
+ name: entry.name,
2367
+ root,
2368
+ reason: "no resolvable entry (package.json main/exports or index fallbacks)"
2369
+ });
2370
+ }
2371
+ } else if (entry.isFile() && hasEntryExtension(entry.name)) {
2372
+ candidates.push({
2373
+ name: entry.name.slice(0, entry.name.length - extOf(entry.name).length),
2374
+ entryPath: full,
2375
+ root
2376
+ });
2377
+ }
2378
+ }
2379
+ }
2380
+ return { candidates, skipped };
2381
+ }
2382
+ function extOf(name) {
2383
+ const index = name.lastIndexOf(".");
2384
+ return index === -1 ? "" : name.slice(index);
2385
+ }
2386
+
2387
+ // src/plugin/trust.ts
2388
+ init_errors();
2389
+ import { createHash } from "node:crypto";
2390
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2391
+ import { dirname, join } from "node:path";
2392
+ function defaultPluginTrustPath(globalRoot) {
2393
+ return join(globalRoot, "plugin-trust.json");
2394
+ }
2395
+ function normalizeTrustKey(path35) {
2396
+ return path35.replaceAll("\\", "/");
2397
+ }
2398
+ async function hashFileContents(entryPath, readFileFn = defaultReadFile) {
2399
+ const contents = await readFileFn(entryPath);
2400
+ return `sha256-${createHash("sha256").update(contents).digest("hex")}`;
2401
+ }
2402
+ async function defaultReadFile(path35) {
2403
+ return readFile(path35);
2404
+ }
2405
+ async function readPluginTrustStore(storePath, readFileFn = (path35) => readFile(path35, "utf8")) {
2406
+ let raw;
2407
+ try {
2408
+ raw = await readFileFn(storePath);
2409
+ } catch {
2410
+ return { pinned: {} };
2411
+ }
2412
+ let parsed;
2413
+ try {
2414
+ parsed = JSON.parse(raw);
2415
+ } catch (err) {
2416
+ throw new FsError({
2417
+ message: `Plugin trust store "${storePath}" is not valid JSON \u2014 fix or delete the file before loading external plugins (${err instanceof Error ? err.message : String(err)})`,
2418
+ code: ERROR_CODES.FS_READ_FAILED,
2419
+ path: storePath
2420
+ });
2421
+ }
2422
+ if (parsed === null || typeof parsed !== "object") {
2423
+ throw new FsError({
2424
+ message: `Plugin trust store "${storePath}" has an unexpected shape (expected { pinned: {...} }) \u2014 fix or delete the file`,
2425
+ code: ERROR_CODES.FS_READ_FAILED,
2426
+ path: storePath
2427
+ });
2428
+ }
2429
+ const pinned = parsed.pinned;
2430
+ if (pinned === void 0) return { pinned: {} };
2431
+ if (pinned === null || typeof pinned !== "object" || Array.isArray(pinned)) {
2432
+ throw new FsError({
2433
+ message: `Plugin trust store "${storePath}" has an invalid "pinned" section \u2014 fix or delete the file`,
2434
+ code: ERROR_CODES.FS_READ_FAILED,
2435
+ path: storePath
2436
+ });
2437
+ }
2438
+ return { pinned };
2439
+ }
2440
+ async function writePluginTrustStore(storePath, store) {
2441
+ await mkdir(dirname(storePath), { recursive: true });
2442
+ const tmp = `${storePath}.tmp`;
2443
+ await writeFile(tmp, `${JSON.stringify(store, null, 2)}
2444
+ `, { mode: 384 });
2445
+ await rename(tmp, storePath);
2446
+ }
2447
+ function verifyPluginTrust(name, integrity, store) {
2448
+ const pinned = store.pinned[name];
2449
+ if (!pinned) return { status: "unpinned", integrity };
2450
+ if (pinned.integrity === integrity) return { status: "trusted", integrity };
2451
+ return {
2452
+ status: "changed",
2453
+ expected: pinned.integrity,
2454
+ actual: integrity,
2455
+ pinnedAt: pinned.pinnedAt
2456
+ };
2457
+ }
2458
+ async function pinPluginTrust(storePath, name, entry, integrity, spec) {
2459
+ const store = await readPluginTrustStore(storePath);
2460
+ store.pinned[name] = { entry, integrity, pinnedAt: (/* @__PURE__ */ new Date()).toISOString(), spec };
2461
+ await writePluginTrustStore(storePath, store);
2462
+ return store;
2463
+ }
2464
+ async function unpinPluginTrust(storePath, name) {
2465
+ const store = await readPluginTrustStore(storePath);
2466
+ if (store.pinned[name] !== void 0) {
2467
+ delete store.pinned[name];
2468
+ await writePluginTrustStore(storePath, store);
2469
+ }
2470
+ return store;
2471
+ }
2472
+
2244
2473
  // src/plugins/auto-review-plugin.ts
2245
2474
  init_error();
2246
2475
  import * as fsp3 from "node:fs/promises";
@@ -2899,7 +3128,7 @@ function shouldCascade(cascadeOn, severities) {
2899
3128
 
2900
3129
  // src/plugins/auto-review-git.ts
2901
3130
  import { spawn } from "node:child_process";
2902
- import { createHash } from "node:crypto";
3131
+ import { createHash as createHash2 } from "node:crypto";
2903
3132
  import * as fsp from "node:fs/promises";
2904
3133
  import * as path from "node:path";
2905
3134
  var MAX_SNAPSHOT_FILE_BYTES = 256 * 1024;
@@ -2966,7 +3195,7 @@ async function snapshotChangedFiles(cwd) {
2966
3195
  snapshots.push({
2967
3196
  ...file,
2968
3197
  content,
2969
- fingerprint: createHash("sha256").update(content).digest("hex")
3198
+ fingerprint: createHash2("sha256").update(content).digest("hex")
2970
3199
  });
2971
3200
  } catch {
2972
3201
  }
@@ -2975,7 +3204,7 @@ async function snapshotChangedFiles(cwd) {
2975
3204
  }
2976
3205
 
2977
3206
  // src/plugins/review-claim-registry.ts
2978
- import { createHash as createHash2, randomUUID } from "node:crypto";
3207
+ import { createHash as createHash3, randomUUID } from "node:crypto";
2979
3208
  import * as fsp2 from "node:fs/promises";
2980
3209
  import { hostname } from "node:os";
2981
3210
  import * as path2 from "node:path";
@@ -3119,7 +3348,7 @@ var claimsByEventBus = /* @__PURE__ */ new WeakMap();
3119
3348
  var startedReviews = /* @__PURE__ */ new WeakMap();
3120
3349
  var pendingStartedReviews = /* @__PURE__ */ new WeakMap();
3121
3350
  function fingerprint(content) {
3122
- return createHash2("sha256").update(content).digest("hex");
3351
+ return createHash3("sha256").update(content).digest("hex");
3123
3352
  }
3124
3353
  function normalizeKeyPart(p) {
3125
3354
  const forward = p.replace(/\\/g, "/").replace(/\/+$/, "");
@@ -3474,7 +3703,7 @@ import * as os4 from "node:os";
3474
3703
  import * as path16 from "node:path";
3475
3704
 
3476
3705
  // src/utils/wstack-paths.ts
3477
- import { createHash as createHash3 } from "node:crypto";
3706
+ import { createHash as createHash4 } from "node:crypto";
3478
3707
  import * as fs from "node:fs";
3479
3708
  import * as os from "node:os";
3480
3709
  import * as path3 from "node:path";
@@ -3500,12 +3729,12 @@ function canonicalProjectRoot(absRoot) {
3500
3729
  }
3501
3730
  }
3502
3731
  function projectHash(absRoot) {
3503
- return createHash3("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
3732
+ return createHash4("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
3504
3733
  }
3505
3734
  function projectSlug(absRoot) {
3506
3735
  const identityRoot = canonicalProjectRoot(absRoot);
3507
3736
  const base = slugify(path3.basename(identityRoot));
3508
- const hash = createHash3("sha256").update(identityRoot).digest("hex").slice(0, 6);
3737
+ const hash = createHash4("sha256").update(identityRoot).digest("hex").slice(0, 6);
3509
3738
  return `${base}-${hash}`;
3510
3739
  }
3511
3740
  function slugify(name) {
@@ -3613,7 +3842,7 @@ function resolveWstackPaths(opts) {
3613
3842
  }
3614
3843
 
3615
3844
  // src/chronicle/identity.ts
3616
- import { createHash as createHash4 } from "node:crypto";
3845
+ import { createHash as createHash5 } from "node:crypto";
3617
3846
  import * as os2 from "node:os";
3618
3847
  import * as path4 from "node:path";
3619
3848
  function resolveChronicleRuntimeLocation(input) {
@@ -3627,7 +3856,7 @@ function resolveChronicleRuntimeLocation(input) {
3627
3856
  };
3628
3857
  }
3629
3858
  function stableId(prefix, value) {
3630
- return `${prefix}_${createHash4("sha256").update(value).digest("hex").slice(0, 24)}`;
3859
+ return `${prefix}_${createHash5("sha256").update(value).digest("hex").slice(0, 24)}`;
3631
3860
  }
3632
3861
 
3633
3862
  // src/chronicle/journal.ts
@@ -3640,7 +3869,7 @@ import * as path6 from "node:path";
3640
3869
  import { createInterface } from "node:readline";
3641
3870
 
3642
3871
  // src/chronicle/event-hash.ts
3643
- import { createHash as createHash5 } from "node:crypto";
3872
+ import { createHash as createHash6 } from "node:crypto";
3644
3873
  var GENESIS_HASH = "0".repeat(64);
3645
3874
  function stableStringify(value) {
3646
3875
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -3651,7 +3880,7 @@ function stableStringify(value) {
3651
3880
  return `{${Object.keys(obj).sort().filter((key) => obj[key] !== void 0).map((key) => `${JSON.stringify(key)}:${stableStringify(obj[key])}`).join(",")}}`;
3652
3881
  }
3653
3882
  function hashValue(value) {
3654
- return createHash5("sha256").update(stableStringify(value), "utf8").digest("hex");
3883
+ return createHash6("sha256").update(stableStringify(value), "utf8").digest("hex");
3655
3884
  }
3656
3885
  function chronicleEventHash(event) {
3657
3886
  const { hash: _hash, ...unhashed } = event;
@@ -4278,7 +4507,7 @@ import * as fs7 from "node:fs/promises";
4278
4507
  import * as path11 from "node:path";
4279
4508
 
4280
4509
  // src/chronicle/query.ts
4281
- import { createHash as createHash6 } from "node:crypto";
4510
+ import { createHash as createHash7 } from "node:crypto";
4282
4511
  import { createReadStream as createReadStream2 } from "node:fs";
4283
4512
  import * as fs4 from "node:fs/promises";
4284
4513
  import * as path8 from "node:path";
@@ -4937,7 +5166,7 @@ function orderKey(event) {
4937
5166
  }
4938
5167
  function hashQuery(query) {
4939
5168
  const { cursor: _cursor, limit: _limit, order: _order, ...filters } = query;
4940
- return createHash6("sha256").update(stableStringify2(filters), "utf8").digest("base64url");
5169
+ return createHash7("sha256").update(stableStringify2(filters), "utf8").digest("base64url");
4941
5170
  }
4942
5171
  function encodeCursor(cursor) {
4943
5172
  return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
@@ -4996,7 +5225,7 @@ async function resolveSnapshotFiles(files, snapshot) {
4996
5225
  }));
4997
5226
  }
4998
5227
  function fileId(file) {
4999
- return createHash6("sha256").update(path8.resolve(file), "utf8").digest("base64url");
5228
+ return createHash7("sha256").update(path8.resolve(file), "utf8").digest("base64url");
5000
5229
  }
5001
5230
  function stableStringify2(value) {
5002
5231
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -5533,10 +5762,10 @@ function instructionRootCandidates() {
5533
5762
  path9.resolve(here, "../instructions"),
5534
5763
  path9.resolve(here, "instructions")
5535
5764
  ];
5536
- rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
5765
+ rootCandidates = candidates.sort((a, b) => Number(!isDirectory2(a)) - Number(!isDirectory2(b)));
5537
5766
  return rootCandidates;
5538
5767
  }
5539
- function isDirectory(candidate) {
5768
+ function isDirectory2(candidate) {
5540
5769
  try {
5541
5770
  return statSync3(candidate).isDirectory();
5542
5771
  } catch {
@@ -7400,7 +7629,7 @@ import * as path15 from "node:path";
7400
7629
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7401
7630
 
7402
7631
  // src/chronicle/project-server-endpoint.ts
7403
- import { createHash as createHash7 } from "node:crypto";
7632
+ import { createHash as createHash8 } from "node:crypto";
7404
7633
  import * as os3 from "node:os";
7405
7634
  import * as path14 from "node:path";
7406
7635
 
@@ -7419,7 +7648,7 @@ function normalizedPath(value) {
7419
7648
  return process.platform === "win32" ? resolved.toLowerCase() : resolved;
7420
7649
  }
7421
7650
  function chronicleProjectServerKey(projectDir) {
7422
- return createHash7("sha256").update(normalizedPath(path14.join(projectDir, "chronicle"))).digest("hex").slice(0, 24);
7651
+ return createHash8("sha256").update(normalizedPath(path14.join(projectDir, "chronicle"))).digest("hex").slice(0, 24);
7423
7652
  }
7424
7653
  function chronicleProjectServerEndpoint(projectDir) {
7425
7654
  const key = chronicleProjectServerKey(projectDir);
@@ -10925,7 +11154,7 @@ function resolveFindingPath(raw, cwd) {
10925
11154
  async function verifyFindingsAgainstDisk(findings, opts) {
10926
11155
  const window = opts.anchorWindow ?? DEFAULT_ANCHOR_WINDOW;
10927
11156
  const cache = /* @__PURE__ */ new Map();
10928
- const readFile21 = async (abs) => {
11157
+ const readFile22 = async (abs) => {
10929
11158
  const cached = cache.get(abs);
10930
11159
  if (cached) return cached;
10931
11160
  let result;
@@ -10947,7 +11176,7 @@ async function verifyFindingsAgainstDisk(findings, opts) {
10947
11176
  if (abs === null) {
10948
11177
  return { ...finding, verification: { status: "failed", reason: "outside_workspace" } };
10949
11178
  }
10950
- const file = await readFile21(abs);
11179
+ const file = await readFile22(abs);
10951
11180
  if ("error" in file) {
10952
11181
  return {
10953
11182
  ...finding,
@@ -12848,7 +13077,7 @@ init_atomic_write();
12848
13077
  init_errors();
12849
13078
  import * as fs18 from "node:fs/promises";
12850
13079
  import * as path31 from "node:path";
12851
- import { createHash as createHash9 } from "node:crypto";
13080
+ import { createHash as createHash10 } from "node:crypto";
12852
13081
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
12853
13082
  var CloudSync = class {
12854
13083
  constructor(paths, getConfig, setConfig, getSettingsConfigPath) {
@@ -13154,7 +13383,7 @@ var CloudSync = class {
13154
13383
  } catch {
13155
13384
  }
13156
13385
  }
13157
- const rev = createHash9("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
13386
+ const rev = createHash10("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
13158
13387
  return { treeEntries: entries, rev };
13159
13388
  }
13160
13389
  async hashLocalCategories(categories) {
@@ -13179,7 +13408,7 @@ var CloudSync = class {
13179
13408
  } catch {
13180
13409
  }
13181
13410
  }
13182
- return createHash9("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
13411
+ return createHash10("sha256").update(hashes.join("")).digest("hex").slice(0, 12);
13183
13412
  }
13184
13413
  categoryToPath(cat) {
13185
13414
  switch (cat) {
@@ -13570,7 +13799,7 @@ function isSecretField(name) {
13570
13799
 
13571
13800
  // src/storage/cloud-config-sync.ts
13572
13801
  init_atomic_write();
13573
- import { createHash as createHash10, randomUUID as nodeRandomUUID } from "node:crypto";
13802
+ import { createHash as createHash11, randomUUID as nodeRandomUUID } from "node:crypto";
13574
13803
  import * as fs19 from "node:fs/promises";
13575
13804
  import * as path33 from "node:path";
13576
13805
 
@@ -14031,7 +14260,7 @@ function stableStringify3(value) {
14031
14260
  return JSON.stringify(value) ?? "null";
14032
14261
  }
14033
14262
  function hashPayload(payload) {
14034
- return createHash10("sha256").update(stableStringify3(payload)).digest("hex");
14263
+ return createHash11("sha256").update(stableStringify3(payload)).digest("hex");
14035
14264
  }
14036
14265
  function deepEquals(a, b) {
14037
14266
  return stableStringify3(a) === stableStringify3(b);
@@ -14519,6 +14748,7 @@ ${first}` };
14519
14748
  }
14520
14749
  export {
14521
14750
  CHIMERA_REVIEW_PROMPT,
14751
+ DEFAULT_PLUGIN_DISCOVERY_IO,
14522
14752
  DefaultPluginAPI,
14523
14753
  KERNEL_API_VERSION,
14524
14754
  buildReviewerModelPool,
@@ -14529,26 +14759,37 @@ export {
14529
14759
  createPromptsPlugin,
14530
14760
  createSkillsPlugin,
14531
14761
  createSyncPlugin,
14762
+ defaultPluginTrustPath,
14532
14763
  definePlugin,
14533
14764
  diffPluginConfig,
14765
+ discoverExternalPlugins,
14534
14766
  emitReviewIfChanged,
14767
+ hashFileContents,
14535
14768
  integrateFindings,
14536
14769
  loadPlugins,
14537
14770
  maybeCompactReviewStores,
14771
+ normalizeTrustKey,
14538
14772
  parseChimeraReviewReport,
14539
14773
  parseReviewSeverity,
14540
14774
  persistReviewReport,
14775
+ pinPluginTrust,
14541
14776
  pluginEntryMatchesName,
14777
+ readPluginTrustStore,
14542
14778
  recordCompletedReview,
14543
14779
  recordStartedReview,
14544
14780
  redactPluginConfig,
14545
14781
  resolvePluginConfig,
14546
14782
  resolvePluginEnablement,
14783
+ resolvePluginEntryPath,
14547
14784
  resolvePluginManifestConfig,
14785
+ resolvePluginTarget,
14548
14786
  selectRoundRobinReviewerAssignment,
14549
14787
  unloadPlugins,
14788
+ unpinPluginTrust,
14550
14789
  updateReviewReportEvidence,
14551
14790
  validatePluginConfigMetadata,
14552
- verifyFindingsAgainstDisk
14791
+ verifyFindingsAgainstDisk,
14792
+ verifyPluginTrust,
14793
+ writePluginTrustStore
14553
14794
  };
14554
14795
  //# sourceMappingURL=index.js.map