@wrongstack/core 0.308.6 → 0.308.7

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.
@@ -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`
@@ -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.308.7",
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/persistence": "0.308.7",
186
+ "@wrongstack/kanban": "0.308.7"
187
187
  },
188
188
  "devDependencies": {
189
189
  "@types/node": "^26.2.0",