@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
@@ -49,8 +49,12 @@ export interface LoadPluginsOptions {
49
49
  * method that contradicts its declared `capabilities` — instead of
50
50
  * just logging a warning. Use in CI/strict deployments to enforce
51
51
  * manifest honesty. Default: false (log-only, backward-compatible).
52
+ *
53
+ * A predicate form is also accepted so hosts can enforce selectively —
54
+ * e.g. strictly for external (third-party) plugins while keeping
55
+ * first-party plugins warn-only.
52
56
  */
53
- enforceCapabilities?: boolean | undefined;
57
+ enforceCapabilities?: boolean | ((plugin: Plugin) => boolean) | undefined;
54
58
  /**
55
59
  * Timeout in milliseconds for each plugin's `setup()` call. If the
56
60
  * plugin's setup exceeds this deadline it is treated as a failure
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Trust-on-first-use (TOFU) pinning for external plugins.
3
+ *
4
+ * In-process plugins run with full host privileges, so the only practical
5
+ * supply-chain signal available before plugin code executes is "has this
6
+ * plugin's entry file changed since the user first ran it?". On first load
7
+ * the host records a SHA-256 of the resolved entry file in
8
+ * `~/.wrongstack/plugin-trust.json`. Later loads compare hashes:
9
+ *
10
+ * - match → plugin loads silently
11
+ * - no pin yet → pin is written now, plugin loads (first use)
12
+ * - hash mismatch → plugin is REFUSED until the user re-pins it with
13
+ * `wstack plugin trust <name>`
14
+ *
15
+ * The pin covers the entry file only (not the whole dependency tree) —
16
+ * it catches swapped/updated plugin builds, not deep transitive changes.
17
+ * It is a tamper signal, not a signature scheme.
18
+ *
19
+ * Disable entirely with `features.pluginsTrust: false` in config.
20
+ */
21
+ export interface PluginTrustEntry {
22
+ /** Absolute entry file path that was hashed at pin time. */
23
+ entry: string;
24
+ /** `sha256-<hex>` of the entry file contents at pin time. */
25
+ integrity: string;
26
+ /** ISO timestamp of when the pin was written. */
27
+ pinnedAt: string;
28
+ /** Optional provenance — the config spec/path the plugin was loaded from. */
29
+ spec?: string | undefined;
30
+ }
31
+ export interface PluginTrustStore {
32
+ pinned: Record<string, PluginTrustEntry>;
33
+ }
34
+ export type PluginTrustVerification = {
35
+ status: 'unpinned';
36
+ integrity: string;
37
+ } | {
38
+ status: 'trusted';
39
+ integrity: string;
40
+ } | {
41
+ status: 'changed';
42
+ expected: string;
43
+ actual: string;
44
+ pinnedAt: string;
45
+ };
46
+ /** Default trust store location: `~/.wrongstack/plugin-trust.json`. */
47
+ export declare function defaultPluginTrustPath(globalRoot: string): string;
48
+ /**
49
+ * Canonical pin-key form: forward slashes on every platform, so a plugin
50
+ * pinned by discovery (forward-slash paths) and re-pinned from a config
51
+ * entry (backslashes on Windows) addresses the same store key.
52
+ */
53
+ export declare function normalizeTrustKey(path: string): string;
54
+ export declare function hashFileContents(entryPath: string, readFileFn?: (path: string) => Promise<Buffer>): Promise<string>;
55
+ /**
56
+ * Read the trust store. A missing file is an empty store (first run).
57
+ * A corrupt file is a hard error — silently ignoring it would downgrade
58
+ * every existing pin to "unpinned" and re-trust changed code, which is
59
+ * exactly the attack this file exists to catch.
60
+ */
61
+ export declare function readPluginTrustStore(storePath: string, readFileFn?: (path: string) => Promise<string>): Promise<PluginTrustStore>;
62
+ /**
63
+ * Write the trust store atomically (temp file + rename, 0o600 — it is a
64
+ * security-relevant record, same treatment as the profile config).
65
+ */
66
+ export declare function writePluginTrustStore(storePath: string, store: PluginTrustStore): Promise<void>;
67
+ /**
68
+ * Pure verification: compare a freshly computed integrity hash against the
69
+ * pinned entry. Keyed by plugin name; the pinned `entry` path is advisory
70
+ * (an npm update can legitimately move the entry inside node_modules while
71
+ * the content hash is what decides trust).
72
+ */
73
+ export declare function verifyPluginTrust(name: string, integrity: string, store: PluginTrustStore): PluginTrustVerification;
74
+ /** Insert or replace the pin for `name` and persist the store. */
75
+ export declare function pinPluginTrust(storePath: string, name: string, entry: string, integrity: string, spec?: string): Promise<PluginTrustStore>;
76
+ /** Remove the pin for `name` (if present) and persist the store. */
77
+ export declare function unpinPluginTrust(storePath: string, name: string): Promise<PluginTrustStore>;
78
+ //# sourceMappingURL=trust.d.ts.map
@@ -3499,6 +3499,7 @@ function inferRuntimeCapabilities(toolNames) {
3499
3499
  var skillSet = (...names) => names;
3500
3500
  var ROLE_SKILL_SETS = {
3501
3501
  explore: skillSet("research-web", "node-modern", "typescript-strict"),
3502
+ "explore-companion": skillSet("node-modern", "typescript-strict"),
3502
3503
  search: skillSet("bug-hunter", "typescript-strict", "research-web"),
3503
3504
  research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
3504
3505
  analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
@@ -79,6 +79,17 @@ export interface PluginConfig {
79
79
  name: string;
80
80
  enabled?: boolean | undefined;
81
81
  options?: Record<string, unknown>;
82
+ /**
83
+ * Load this plugin from an explicit location instead of resolving the
84
+ * npm specifier through the host's own module resolution. Accepts:
85
+ * - a relative path (resolved against the project root),
86
+ * - an absolute path,
87
+ * - a `file:` URL.
88
+ * The target may be an entry file (`plugin.js`) or a directory that
89
+ * contains `package.json` / `index.js`. External plugins loaded through
90
+ * `path` are subject to the TOFU trust pin (`~/.wrongstack/plugin-trust.json`).
91
+ */
92
+ path?: string | undefined;
82
93
  }
83
94
  /**
84
95
  * Human-owned policy for the LLM-facing `plugin_manager` tool.
@@ -105,6 +116,16 @@ export interface FeaturesConfig {
105
116
  mcp: boolean;
106
117
  /** Load + initialise npm plugins declared in `plugins`. */
107
118
  plugins: boolean;
119
+ /**
120
+ * Trust-on-first-use pinning for external (third-party) plugins.
121
+ * The first load of an external plugin records a SHA-256 of its entry
122
+ * file in `~/.wrongstack/plugin-trust.json`; subsequent loads refuse to
123
+ * run the plugin when the hash changes until the user re-pins it via
124
+ * `wstack plugin trust <name>`. Set to false to disable pinning
125
+ * (not recommended — this is the only supply-chain signal for in-process
126
+ * plugins). Default: true.
127
+ */
128
+ pluginsTrust?: boolean | undefined;
108
129
  /** Register `remember` / `forget` tools backed by memory store. */
109
130
  memory: boolean;
110
131
  /**
@@ -128,6 +128,13 @@ export interface FleetConfig {
128
128
  } | undefined;
129
129
  /** Brain-gated fleet supervisor (rebalance/steer/spawn-helper). */
130
130
  supervisor?: FleetSupervisorConfig | undefined;
131
+ /**
132
+ * Explore Companion — state-triggered background codebase explorer behind
133
+ * the leader. Watches the leader's in-progress work state and assigns
134
+ * read-only exploration probes to a resident `explore-companion`
135
+ * subagent; findings return via mailbox. Default enabled.
136
+ */
137
+ exploreCompanion?: ExploreCompanionConfig | undefined;
131
138
  /** Roster-agent self-learning: capture → optimize → per-skill addenda. */
132
139
  learning?: AgentLearningConfig | undefined;
133
140
  }
@@ -188,6 +195,17 @@ export interface FleetSupervisorConfig {
188
195
  /** Allow the supervisor to terminate subagents (highest risk). Default false. */
189
196
  allowTerminate?: boolean | undefined;
190
197
  }
198
+ /** Config surface for the ExploreCompanion host wiring. */
199
+ export interface ExploreCompanionConfig {
200
+ /** Kill switch. Default true. */
201
+ enabled?: boolean | undefined;
202
+ /** Min gap between probes on the same subject (ms). Default 120000. */
203
+ cooldownMs?: number | undefined;
204
+ /** Pending probe queue cap (drop oldest when full). Default 8. */
205
+ maxPending?: number | undefined;
206
+ /** Mailbox poll interval for explicit asks (ms). Default 5000. */
207
+ pollIntervalMs?: number | undefined;
208
+ }
191
209
  /**
192
210
  * One member of the Brain's LLM pool or council. String entries elsewhere
193
211
  * (`Config.brain.models`, council voters) parse with the same `parseModelRef`
@@ -34,7 +34,7 @@ export { BUILTIN_PROMPT_CATEGORIES, isBuiltinCategory, PROMPT_CATEGORY_LABELS }
34
34
  export type { InstalledPromptEntry, ManifestValidation, PromptManifestData, PromptRegistryManifest, PromptRegistryRef, RegistryDiff, } from './prompt-registry.js';
35
35
  export { diffRegistry, validateRegistryManifest } from './prompt-registry.js';
36
36
  export type { CacheTtl, Capabilities, JsonSchemaSpec, Provider, ProviderContextLimit, ProviderErrorBody, ProviderErrorKind, ReasoningConfig, ReasoningEffort, ReasoningRequest, Request, RequestCacheControl, Response, ResponseFormat, SafetySetting, StopReason, StreamEvent, Usage, } from './provider.js';
37
- export { classifyProviderError, effectiveInputTokens, isContextOverflowShaped, isFallbackWorthy, isRetryableKind, ProviderError, StreamHangError, } from './provider.js';
37
+ export { classifyProviderError, effectiveInputTokens, isContextOverflowShaped, isFallbackWorthy, isReasoningEffort, isRetryableKind, ProviderError, REASONING_EFFORT_LEVELS, StreamHangError, } from './provider.js';
38
38
  export type { ProviderRunner, RunProviderOptions } from './provider-runner.js';
39
39
  export type { Renderer } from './renderer.js';
40
40
  export type { SecretScrubber } from './secret-scrubber.js';
@@ -914,6 +914,18 @@ function truncate(s, max) {
914
914
  var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)(?:\s+(?:has|have))?(?:\s+been)?[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit|usage[-_\s]*limit[-_\s]*(?:reached|exceeded)/i;
915
915
 
916
916
  // src/types/provider.ts
917
+ var REASONING_EFFORT_LEVELS = [
918
+ "none",
919
+ "minimal",
920
+ "low",
921
+ "medium",
922
+ "high",
923
+ "xhigh",
924
+ "max"
925
+ ];
926
+ function isReasoningEffort(value) {
927
+ return typeof value === "string" && REASONING_EFFORT_LEVELS.includes(value);
928
+ }
917
929
  function effectiveInputTokens(usage) {
918
930
  return usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
919
931
  }
@@ -1346,6 +1358,7 @@ export {
1346
1358
  ParseError,
1347
1359
  PluginError,
1348
1360
  ProviderError,
1361
+ REASONING_EFFORT_LEVELS,
1349
1362
  SESSION_MARKER_EVENT_TYPES,
1350
1363
  SYSTEM_INJECTION_PREFIXES,
1351
1364
  SddError,
@@ -1377,6 +1390,7 @@ export {
1377
1390
  isImageBlock,
1378
1391
  isParseError,
1379
1392
  isPluginError,
1393
+ isReasoningEffort,
1380
1394
  isRetryableKind,
1381
1395
  isSddError,
1382
1396
  isSessionError,
@@ -142,6 +142,21 @@ export interface SubagentConfig {
142
142
  end: string;
143
143
  mode?: 'advisory' | 'enforce' | undefined;
144
144
  } | undefined;
145
+ /**
146
+ * Model-driven completion policy. When set, this subagent is NEVER killed
147
+ * by the wall-clock watchdog at its deadline: instead the deadline (or an
148
+ * explicit `Director.requestFinish()`) triggers an in-band
149
+ * `subagent.finish_requested` notification that the agent loop folds into
150
+ * the conversation between tool batches — the model then finishes its task
151
+ * in its own turn within `graceMs` of legitimate working time. Only after
152
+ * that grace window elapses does the existing terminal stop apply, so the
153
+ * subagent still has a bounded maximum lifetime.
154
+ *
155
+ * `undefined` (default) keeps the legacy watchdog behavior unchanged.
156
+ */
157
+ gracefulFinish?: boolean | {
158
+ graceMs?: number | undefined;
159
+ } | undefined;
145
160
  /**
146
161
  * Runtime request overrides for THIS subagent. When present, these are merged
147
162
  * over the leader's `Config.modelRuntime` before the subagent request pipeline
@@ -23,6 +23,21 @@ import type { Tool } from './tool.js';
23
23
  * cached tokens twice and skew cache-hit-ratio reporting.
24
24
  */
25
25
  export type ReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
26
+ /**
27
+ * The canonical runtime list of {@link ReasoningEffort} values, in
28
+ * menu/display order. Single source of truth for every surface that needs to
29
+ * iterate the levels (CLI `/settings` + `/setmodel`, the TUI picker, the
30
+ * WebUI dropdown) — import this instead of re-declaring a local array, which
31
+ * is how drift crept in before.
32
+ *
33
+ * `satisfies` pins the literal to the union: a value here core's type doesn't
34
+ * know is a compile error. Note the reverse is NOT caught — core adding a
35
+ * level does not force this array to grow, so consumers validating user input
36
+ * against it must decide deliberately whether to expose the new level.
37
+ */
38
+ export declare const REASONING_EFFORT_LEVELS: readonly ["none", "minimal", "low", "medium", "high", "xhigh", "max"];
39
+ /** Type guard for untrusted strings (CLI args, WS payloads, config files). */
40
+ export declare function isReasoningEffort(value: unknown): value is ReasoningEffort;
26
41
  export type CacheTtl = '5m' | '1h';
27
42
  /**
28
43
  * Provider-agnostic response-format directive.
@@ -119,7 +134,20 @@ export interface RequestCacheControl {
119
134
  export interface ReasoningConfig {
120
135
  default: 'enabled' | 'disabled' | 'adaptive' | 'always_on';
121
136
  disableSupported: boolean;
122
- effortSupported: boolean;
137
+ /**
138
+ * Tri-state effort support:
139
+ * `true` — the catalog documents this model's effort levels
140
+ * (`effortLevels` is authoritative).
141
+ * `false` — the catalog documents effort control as absent
142
+ * (toggle-only or budget_tokens-only reasoning options).
143
+ * `undefined` — the model is known to reason (`reasoning: true`) but its
144
+ * effort vocabulary is not documented. The resolver forwards
145
+ * the requested effort; each wire adapter then applies its
146
+ * own transport-level gating (allowlist, mapping, or omit),
147
+ * so an undocumented model can only match-or-omit — never
148
+ * receive a field shape it did not advertise.
149
+ */
150
+ effortSupported?: boolean | undefined;
123
151
  effortLevels: ReasoningEffort[];
124
152
  preserveThinking: 'unsupported' | 'optional' | 'always_on';
125
153
  }
@@ -0,0 +1,57 @@
1
+ You are the Chaos Monkey ("Kaos Maymunu") — a mutation-testing saboteur for the
2
+ WrongStack fleet. Your job is to prove whether a test suite actually pins down
3
+ the code it claims to cover, by deliberately breaking that code and watching
4
+ which mutants survive.
5
+
6
+ Core belief: green tests prove nothing if they cannot detect sabotage. A mutant
7
+ that survives means the tests are fake or insufficient — and that is the most
8
+ valuable finding you can return.
9
+
10
+ ## Your task contract
11
+
12
+ The director hands you a mutation plan: an exact list of mutation ids, each with
13
+ file, line, column, kind, original token and replacement token. The plan is
14
+ authoritative — you NEVER invent, move, or "improve" mutations. Your freedom is
15
+ execution order and diagnosis, never the mutation set.
16
+
17
+ ## Check pass (per mutant)
18
+
19
+ 1. Apply exactly ONE mutation from the plan to its anchored (file, line, column).
20
+ If the anchored token no longer matches `original`, mark the mutant
21
+ `skipped` with the drift as evidence — do not hunt for a "similar" site.
22
+ 2. Run the provided test command exactly as given.
23
+ 3. Record the outcome:
24
+ - Tests fail → mutant `killed` (quote the first failing assertion).
25
+ - Tests pass → mutant `survived` (this is a weak-test finding, not your failure).
26
+ 4. Restore the original source byte-for-byte before moving to the next mutant.
27
+ The suite is only honest if every mutant ran against pristine code except
28
+ its own single mutation.
29
+
30
+ ## Hard rules
31
+
32
+ - **One mutation at a time.** Never stack mutants; a stacked run measures nothing.
33
+ - **Always restore.** Your worktree must be clean of sabotage at the end of the
34
+ pass. If restore fails, stop and report which file is left mutated.
35
+ - **Stay inside the plan's files.** No refactors, no fixes, no formatting churn
36
+ — even when the mutated code looks wrong to you. You are the saboteur, not
37
+ the reviewer.
38
+ - **Deterministic.** Same plan + same suite → same report.
39
+ - **One-shot lifecycle.** Finish the assigned pass, submit the report, stop.
40
+
41
+ ## Report
42
+
43
+ Submit via `submit_result`, then repeat it as your final text (fenced JSON):
44
+
45
+ ```json
46
+ {
47
+ "summary": "<one line: N killed / M survived / K skipped>",
48
+ "mutants": [
49
+ { "id": "<plan id>", "file": "...", "line": 0, "kind": "...",
50
+ "status": "killed | survived | skipped",
51
+ "evidence": "<failing assertion, or 'suite green' for survivors>" }
52
+ ]
53
+ }
54
+ ```
55
+
56
+ Order survivors first — they are the actionable findings. For each survivor,
57
+ name the boundary or behavior the tests failed to assert.
@@ -0,0 +1,35 @@
1
+ You are the Explore Companion — a read-only reconnaissance agent running
2
+ behind a leader agent that is executing the main task. You do not lead
3
+ work, you do not block the leader, and you never modify anything: you
4
+ answer narrow probes by scanning the codebase intensively and feeding
5
+ findings back asynchronously ("this file is here, that component works
6
+ like this").
7
+
8
+ Scope:
9
+ - Answer probes scoped to the leader's current in-progress work
10
+ - Locate files, entry points, symbols, and their callers/dependents
11
+ - Explain how a component works across the files that implement it
12
+ - Stay inside the probe scope; if the probe is ambiguous, state your
13
+ interpretation and answer anyway
14
+
15
+ Input format you accept (a probe task):
16
+ { "probe": "<what to find>", "hint": { "file": "...", "symbol": "..." }, "context": "<what the leader is doing>" }
17
+
18
+ Output: findings, not prose. Markdown block with:
19
+ - ## Findings — table or bullets: `file:line` — what it is, how it works
20
+ - Confidence: 0.0–1.0 for the overall answer
21
+ - Next read: one `file:line` suggestion the leader should read next
22
+
23
+ Working rules:
24
+ - Read-only, always — never edit, write, or run shell commands
25
+ - Always cite file:line; never describe code you have not read
26
+ - Index-first discovery: `codebase-repo-map`, `codebase-search`,
27
+ `codebase-skeleton`, `codebase-incoming-calls`, `codebase-outgoing-calls`
28
+ before `read`/`grep`/`glob`/`tree`
29
+ - Keep the mailbox message compact: findings + confidence + one next-read
30
+ suggestion. The leader reads it inside its own context window.
31
+ - Report findings to the leader via the mailbox (`type=result` for a direct
32
+ probe answer, `type=btw` for ambient/low-urgency context, subject
33
+ prefixed `[explore]`), then always finish with `submit_result`
34
+ (`SubagentStructuredReport`): summary, findings[], files_examined[],
35
+ confidence, suggested_next_steps[].
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/core",
3
- "version": "0.308.6",
3
+ "version": "0.309.0",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
6
6
  "repository": {
@@ -182,8 +182,8 @@
182
182
  "wrongstackApiVersion": "0.1.10",
183
183
  "dependencies": {
184
184
  "zod": "4.4.3",
185
- "@wrongstack/kanban": "0.308.6",
186
- "@wrongstack/persistence": "0.308.6"
185
+ "@wrongstack/kanban": "0.309.0",
186
+ "@wrongstack/persistence": "0.309.0"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@types/node": "^26.2.0",