@neuroverseos/governance 0.2.2 → 0.3.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 (51) hide show
  1. package/.well-known/ai-plugin.json +26 -0
  2. package/.well-known/mcp.json +68 -0
  3. package/AGENTS.md +219 -0
  4. package/README.md +64 -4
  5. package/dist/adapters/autoresearch.cjs +196 -0
  6. package/dist/adapters/autoresearch.d.cts +103 -0
  7. package/dist/adapters/autoresearch.d.ts +103 -0
  8. package/dist/adapters/autoresearch.js +7 -0
  9. package/dist/adapters/express.d.cts +1 -1
  10. package/dist/adapters/express.d.ts +1 -1
  11. package/dist/adapters/express.js +1 -1
  12. package/dist/adapters/index.cjs +171 -0
  13. package/dist/adapters/index.d.cts +2 -1
  14. package/dist/adapters/index.d.ts +2 -1
  15. package/dist/adapters/index.js +8 -4
  16. package/dist/adapters/langchain.d.cts +1 -1
  17. package/dist/adapters/langchain.d.ts +1 -1
  18. package/dist/adapters/langchain.js +2 -2
  19. package/dist/adapters/openai.d.cts +1 -1
  20. package/dist/adapters/openai.d.ts +1 -1
  21. package/dist/adapters/openai.js +2 -2
  22. package/dist/adapters/openclaw.d.cts +1 -1
  23. package/dist/adapters/openclaw.d.ts +1 -1
  24. package/dist/adapters/openclaw.js +2 -2
  25. package/dist/chunk-T5EUJQE5.js +172 -0
  26. package/dist/cli/neuroverse.cjs +1157 -184
  27. package/dist/cli/neuroverse.js +18 -6
  28. package/dist/cli/run.js +2 -2
  29. package/dist/{doctor-QV6HELS5.js → doctor-XPDLEYXN.js} +1 -0
  30. package/dist/{guard-contract-Cm91Kp4j.d.cts → guard-contract-WZx__PmU.d.cts} +1 -1
  31. package/dist/{guard-contract-Cm91Kp4j.d.ts → guard-contract-WZx__PmU.d.ts} +1 -1
  32. package/dist/index.d.cts +2 -2
  33. package/dist/index.d.ts +2 -2
  34. package/dist/index.js +28 -28
  35. package/dist/infer-world-7GVZWFX4.js +543 -0
  36. package/dist/init-world-VWMQZQC7.js +223 -0
  37. package/dist/{mcp-server-LZVJHBT5.js → mcp-server-FPVSU32Z.js} +2 -2
  38. package/dist/{session-VISISNWJ.js → session-EKTRSR7C.js} +2 -2
  39. package/dist/worlds/autoresearch.nv-world.md +230 -0
  40. package/llms.txt +79 -0
  41. package/openapi.yaml +230 -0
  42. package/package.json +15 -4
  43. package/dist/{chunk-SKU3GAPD.js → chunk-2PQU3VAN.js} +3 -3
  44. package/dist/{chunk-KEST3MWO.js → chunk-4A7LISES.js} +3 -3
  45. package/dist/{chunk-RWXVAH6P.js → chunk-COT5XS4V.js} +3 -3
  46. package/dist/{chunk-OHAC6HJE.js → chunk-ER62HNGF.js} +3 -3
  47. package/dist/{chunk-DPVS43ZT.js → chunk-OGL7QXZS.js} +3 -3
  48. package/dist/{guard-GFLQZY6U.js → guard-RV65TT4L.js} +1 -1
  49. package/dist/{playground-FGOMASHN.js → playground-E664U4T6.js} +1 -1
  50. package/dist/{redteam-SK7AMIG3.js → redteam-Z7WREJ44.js} +1 -1
  51. package/dist/{test-75AVHC3R.js → test-OGXJK4QU.js} +1 -1
@@ -0,0 +1,26 @@
1
+ {
2
+ "schema_version": "v1",
3
+ "name": "neuroverse-governance",
4
+ "description": "Enforce governance rules on AI agent actions. Turn plans into enforceable constraints. Deterministic evaluation — no LLM in the loop.",
5
+ "capabilities": {
6
+ "plan_enforcement": {
7
+ "description": "Compile a plan into enforceable rules. Block actions outside the plan. Track progress.",
8
+ "input": "Plan markdown or JSON",
9
+ "output": "ON_PLAN / OFF_PLAN / CONSTRAINT_VIOLATED / PLAN_COMPLETE verdict with evidence"
10
+ },
11
+ "world_governance": {
12
+ "description": "Full governance engine with invariants, guards, roles, kernel rules, and audit trails.",
13
+ "input": "GuardEvent JSON",
14
+ "output": "ALLOW / BLOCK / PAUSE verdict with trace"
15
+ },
16
+ "plan_derive": {
17
+ "description": "Generate a full governance world from a plan markdown file.",
18
+ "input": "Plan markdown",
19
+ "output": "World definition files (world.json, invariants.json, guards.json)"
20
+ }
21
+ },
22
+ "install": "npm install @neuroverseos/governance",
23
+ "adapters": ["openclaw", "langchain", "openai", "express"],
24
+ "cli": "neuroverse",
25
+ "repository": "https://github.com/NeuroverseOS/Neuroverseos-governance"
26
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "server": {
4
+ "name": "neuroverse-governance",
5
+ "version": "0.2.2",
6
+ "description": "Deterministic governance engine for AI agents. Evaluates actions against world rules and plan constraints. Returns ALLOW, BLOCK, or PAUSE verdicts.",
7
+ "vendor": "NeuroverseOS",
8
+ "homepage": "https://neuroverseos.com",
9
+ "repository": "https://github.com/NeuroverseOS/Neuroverseos-governance"
10
+ },
11
+ "install": {
12
+ "npm": "@neuroverseos/governance",
13
+ "command": "npx @neuroverseos/governance mcp --world ./world"
14
+ },
15
+ "capabilities": {
16
+ "tools": true,
17
+ "resources": false,
18
+ "prompts": false
19
+ },
20
+ "tools": [
21
+ {
22
+ "name": "neuroverse_guard",
23
+ "description": "Evaluate whether an agent action is allowed by governance rules. Returns ALLOW, BLOCK, or PAUSE.",
24
+ "inputSchema": {
25
+ "type": "object",
26
+ "required": ["intent"],
27
+ "properties": {
28
+ "intent": { "type": "string", "description": "What the agent wants to do" },
29
+ "tool": { "type": "string", "description": "Tool being invoked" },
30
+ "scope": { "type": "string", "description": "Resource being accessed" }
31
+ }
32
+ }
33
+ },
34
+ {
35
+ "name": "neuroverse_plan_check",
36
+ "description": "Check whether an action matches a step in the active plan. Returns ON_PLAN, OFF_PLAN, CONSTRAINT_VIOLATED, or PLAN_COMPLETE.",
37
+ "inputSchema": {
38
+ "type": "object",
39
+ "required": ["intent"],
40
+ "properties": {
41
+ "intent": { "type": "string", "description": "What the agent wants to do" }
42
+ }
43
+ }
44
+ },
45
+ {
46
+ "name": "neuroverse_plan_advance",
47
+ "description": "Mark a plan step as completed. In verified mode, requires evidence for steps with a verify field.",
48
+ "inputSchema": {
49
+ "type": "object",
50
+ "required": ["stepId"],
51
+ "properties": {
52
+ "stepId": { "type": "string", "description": "Step ID to advance" },
53
+ "evidenceType": { "type": "string", "description": "Evidence type (must match step verify field)" },
54
+ "evidenceProof": { "type": "string", "description": "Proof payload (URL, hash, etc.)" }
55
+ }
56
+ }
57
+ },
58
+ {
59
+ "name": "neuroverse_plan_status",
60
+ "description": "Get current plan progress — completed steps, total steps, percentage.",
61
+ "inputSchema": {
62
+ "type": "object",
63
+ "properties": {}
64
+ }
65
+ }
66
+ ],
67
+ "tags": ["governance", "ai-safety", "agent-guardrails", "deterministic", "plan-enforcement"]
68
+ }
package/AGENTS.md ADDED
@@ -0,0 +1,219 @@
1
+ # NeuroVerse Governance — Agent Integration Guide
2
+
3
+ ## What This Package Does
4
+
5
+ Runtime containment for AI agents. Define what an agent can and cannot do, then enforce it at every action. Deterministic — same event + same rules = same verdict. No LLM in the evaluation loop.
6
+
7
+ ## Quick Start for Agents
8
+
9
+ ### Install
10
+
11
+ ```bash
12
+ npm install @neuroverseos/governance
13
+ ```
14
+
15
+ ### Enforce a world (permanent rules)
16
+
17
+ ```javascript
18
+ const { evaluateGuard, loadWorld } = require('@neuroverseos/governance');
19
+
20
+ const world = await loadWorld('./world/');
21
+ const verdict = evaluateGuard({ intent: 'delete user', tool: 'db' }, world);
22
+ // → { status: 'BLOCK', reason: 'destructive database operation' }
23
+ ```
24
+
25
+ ### Enforce a plan (temporary task scope)
26
+
27
+ ```javascript
28
+ const { evaluatePlan, parsePlanMarkdown } = require('@neuroverseos/governance');
29
+
30
+ const plan = parsePlanMarkdown(planMarkdownString);
31
+ const verdict = evaluatePlan({ intent: 'send email', tool: 'smtp' }, plan.plan);
32
+ // → { allowed: false, status: 'OFF_PLAN', reason: 'Action does not match any plan step.' }
33
+ ```
34
+
35
+ ### Enforce both (plan on top of world)
36
+
37
+ ```javascript
38
+ const verdict = evaluateGuard(event, world, { plan });
39
+ // Plan rules AND world rules both apply
40
+ // Plan can only restrict, never expand
41
+ ```
42
+
43
+ ### Guard with trace (see what fired)
44
+
45
+ ```javascript
46
+ const verdict = evaluateGuard(event, world, { trace: true, level: 'strict' });
47
+ // verdict.trace contains:
48
+ // safetyChecks, guardChecks, kernelRuleChecks, levelChecks, invariantChecks
49
+ ```
50
+
51
+ ## Available Commands
52
+
53
+ ```
54
+ neuroverse init Scaffold a .nv-world.md template
55
+ neuroverse bootstrap --input <.md> --output <dir> Compile to world JSON
56
+ neuroverse build <input.md> Derive + compile in one step
57
+ neuroverse validate --world <dir> 9 static analysis checks
58
+ neuroverse guard --world <dir> Evaluate action (stdin → verdict)
59
+ neuroverse test --world <dir> 14 standard + fuzz guard tests
60
+ neuroverse redteam --world <dir> 28 adversarial attacks, containment score
61
+ neuroverse doctor Environment sanity check
62
+ neuroverse playground --world <dir> Interactive web demo at localhost:4242
63
+ neuroverse explain <world> Human-readable world summary
64
+ neuroverse simulate <world> State evolution simulation
65
+ neuroverse improve <world> Actionable improvement suggestions
66
+ neuroverse plan compile <plan.md> Parse plan markdown into plan.json
67
+ neuroverse plan check --plan plan.json Check action against plan (stdin)
68
+ neuroverse plan status --plan plan.json Show plan progress
69
+ neuroverse plan advance <step_id> Mark a step as completed
70
+ neuroverse plan derive <plan.md> Generate a full world from a plan
71
+ neuroverse run --world <dir> Governed runtime (pipe or interactive)
72
+ neuroverse mcp --world <dir> MCP governance server
73
+ neuroverse trace --log <path> Action audit log
74
+ neuroverse impact --log <path> Counterfactual impact report
75
+ neuroverse world status|diff|snapshot|rollback World management
76
+ neuroverse worlds List available worlds
77
+ neuroverse derive --input <path> AI-assisted world synthesis
78
+ neuroverse configure-ai Configure AI provider credentials
79
+ ```
80
+
81
+ ## Plan Markdown Format
82
+
83
+ Plans are written in simple markdown that any LLM can produce:
84
+
85
+ ```markdown
86
+ ---
87
+ plan_id: product_launch
88
+ objective: Launch the NeuroVerse governance plugin
89
+ sequential: false
90
+ completion: verified
91
+ ---
92
+
93
+ # Steps
94
+ - Write announcement blog post [tag: content, marketing]
95
+ - Publish GitHub release [tag: deploy] [verify: github_release_created]
96
+ - Post on Product Hunt (after: publish_github_release) [tag: marketing]
97
+
98
+ # Constraints
99
+ - No spending above $500
100
+ - All external posts require human review [type: approval]
101
+ ```
102
+
103
+ ### Completion modes
104
+
105
+ - `completion: trust` (default) — caller says "done", step advances
106
+ - `completion: verified` — steps with `[verify: ...]` require evidence to advance
107
+
108
+ ```javascript
109
+ // Trust mode — just advance
110
+ const result = advancePlan(plan, 'write_announcement_blog_post');
111
+ // → { success: true, plan: <updated> }
112
+
113
+ // Verified mode — evidence required for steps with verify
114
+ const result = advancePlan(plan, 'publish_github_release', {
115
+ type: 'github_release_created',
116
+ proof: 'https://github.com/org/repo/releases/v1.0',
117
+ });
118
+ // → { success: true, plan: <updated>, evidence: { ... } }
119
+
120
+ // Verified mode — missing evidence
121
+ const result = advancePlan(plan, 'publish_github_release');
122
+ // → { success: false, reason: 'Step requires evidence (verify: github_release_created)' }
123
+ ```
124
+
125
+ ## Governance Model
126
+
127
+ ```
128
+ Safety checks → Plan enforcement → Role rules → Guards → Kernel
129
+ (country laws) (mom's trip rules) (driving laws) (domain) (boundaries)
130
+ ```
131
+
132
+ Plans are temporary guard overlays. They define task scope.
133
+ Worlds are permanent governance. They define domain rules.
134
+ Both layers must pass for an action to be allowed.
135
+
136
+ ## Evaluation Pipeline
137
+
138
+ Every action passes through 6 phases:
139
+
140
+ 1. **Safety** — prompt injection, scope escape, data exfil detection (always on)
141
+ 2. **Plan** — is the action within the current mission scope?
142
+ 3. **Roles** — does the actor have permission?
143
+ 4. **Guards** — do domain-specific rules allow it?
144
+ 5. **Kernel** — does it violate LLM boundary rules?
145
+ 6. **Level** — does enforcement strictness allow it?
146
+
147
+ First BLOCK wins. If nothing blocks, ALLOW.
148
+
149
+ ## Adapters
150
+
151
+ ### OpenAI
152
+
153
+ ```javascript
154
+ import { createGovernedToolExecutor } from '@neuroverseos/governance/adapters/openai';
155
+
156
+ const executor = await createGovernedToolExecutor('./world/', { trace: true, plan });
157
+ const result = await executor.execute(toolCall, myToolRunner);
158
+ // ALLOW → runs tool | BLOCK → returns blocked | PAUSE → throws
159
+ ```
160
+
161
+ ### LangChain
162
+
163
+ ```javascript
164
+ import { createNeuroVerseCallbackHandler } from '@neuroverseos/governance/adapters/langchain';
165
+
166
+ const handler = await createNeuroVerseCallbackHandler('./world/', {
167
+ plan,
168
+ onBlock: (verdict) => console.log('Blocked:', verdict.reason),
169
+ });
170
+ const agent = new AgentExecutor({ ..., callbacks: [handler] });
171
+ ```
172
+
173
+ ### OpenClaw
174
+
175
+ ```javascript
176
+ import { createNeuroVersePlugin } from '@neuroverseos/governance/adapters/openclaw';
177
+
178
+ const plugin = await createNeuroVersePlugin('./world/', { plan });
179
+ agent.use(plugin.hooks());
180
+ ```
181
+
182
+ ### Express / Fastify
183
+
184
+ ```javascript
185
+ import { createGovernanceMiddleware } from '@neuroverseos/governance/adapters/express';
186
+
187
+ const middleware = await createGovernanceMiddleware('./world/', { level: 'strict' });
188
+ app.use('/api', middleware);
189
+ // Returns 403 on BLOCK
190
+ ```
191
+
192
+ ### MCP Server
193
+
194
+ ```bash
195
+ neuroverse mcp --world ./world --plan plan.json
196
+ ```
197
+
198
+ Exposes governed tools over Model Context Protocol. Works with Claude, Cursor, and any MCP client.
199
+
200
+ ## Exit Codes
201
+
202
+ - 0 = ALLOW / ON_PLAN / SUCCESS
203
+ - 1 = BLOCK / OFF_PLAN / FAIL
204
+ - 2 = PAUSE / CONSTRAINT_VIOLATED
205
+ - 3 = ERROR
206
+ - 4 = PLAN_COMPLETE
207
+
208
+ ## Containment Testing
209
+
210
+ ```bash
211
+ # Standard guard test suite (14 tests + optional fuzz)
212
+ neuroverse test --world ./world --fuzz --count 50
213
+
214
+ # Adversarial red team (28 attacks, 6 categories)
215
+ neuroverse redteam --world ./world --level strict
216
+ # → Containment score: 91%
217
+ ```
218
+
219
+ Red team categories: prompt injection (8), tool escalation (4), scope escape (5), data exfiltration (3), identity manipulation (3), constraint bypass (3).
package/README.md CHANGED
@@ -15,6 +15,14 @@ Deterministic. No LLM in the evaluation loop. Same event + same rules = same ver
15
15
  npm install @neuroverseos/governance
16
16
  ```
17
17
 
18
+ ### Quick test (no install required)
19
+
20
+ ```bash
21
+ npx @neuroverseos/governance init
22
+ npx @neuroverseos/governance build
23
+ npx @neuroverseos/governance guard
24
+ ```
25
+
18
26
  ---
19
27
 
20
28
  ## The 5-Minute Demo
@@ -109,7 +117,7 @@ Returns ALLOW, BLOCK, or PAUSE. No network calls. No async. Pure function.
109
117
 
110
118
  ---
111
119
 
112
- ## The "Oh Shit" Moment
120
+ ## The Moment Governance Matters
113
121
 
114
122
  Your AI agent decides to clean up the production database:
115
123
 
@@ -259,6 +267,47 @@ import { parsePlanMarkdown, evaluatePlan, advancePlan } from '@neuroverseos/gove
259
267
  const { plan } = parsePlanMarkdown(markdown);
260
268
  const verdict = evaluatePlan({ intent: 'write blog post' }, plan);
261
269
  // → { status: 'ON_PLAN', matchedStep: 'write_announcement_blog_post' }
270
+
271
+ const result = advancePlan(plan, 'write_announcement_blog_post');
272
+ // → { success: true, plan: <updated plan> }
273
+ ```
274
+
275
+ ### Completion modes
276
+
277
+ Plans support two completion modes, set in frontmatter:
278
+
279
+ **Trust** (default) — caller asserts "done", step advances:
280
+
281
+ ```markdown
282
+ ---
283
+ plan_id: product_launch
284
+ completion: trust
285
+ ---
286
+ ```
287
+
288
+ **Verified** — steps with `[verify: ...]` require evidence to advance:
289
+
290
+ ```markdown
291
+ ---
292
+ plan_id: product_launch
293
+ completion: verified
294
+ ---
295
+
296
+ # Steps
297
+ - Write blog post [tag: content]
298
+ - Publish GitHub release [verify: github_release_created]
299
+ ```
300
+
301
+ Steps without `verify` still advance on trust, even in verified mode.
302
+
303
+ ```bash
304
+ # Trust mode — just advance:
305
+ neuroverse plan advance write_blog_post --plan plan.json
306
+
307
+ # Verified mode — evidence required for steps with verify:
308
+ neuroverse plan advance publish_github_release --plan plan.json \
309
+ --evidence github_release_created \
310
+ --proof "https://github.com/org/repo/releases/v1.0"
262
311
  ```
263
312
 
264
313
  ---
@@ -413,7 +462,7 @@ src/
413
462
  loader/
414
463
  world-loader.ts # Load WorldDefinition from disk
415
464
 
416
- test/ # 293 tests
465
+ test/ # 303 tests
417
466
  ```
418
467
 
419
468
  Zero runtime dependencies. Pure TypeScript. Node.js 18+.
@@ -432,8 +481,19 @@ Zero runtime dependencies. Pure TypeScript. Node.js 18+.
432
481
 
433
482
  This package includes machine-readable manifests for agent ecosystems:
434
483
 
435
- - **`AGENTS.md`** Agent-discoverable integration guide
436
- - **`.well-known/ai-plugin.json`** — Standard capability manifest
484
+ | File | Purpose | Who reads it |
485
+ |------|---------|-------------|
486
+ | **`AGENTS.md`** | Integration guide for coding agents | Claude Code, Cursor, Windsurf |
487
+ | **`llms.txt`** | Package description for any LLM | Any LLM browsing the web or repo |
488
+ | **`.well-known/ai-plugin.json`** | Capability manifest | ChatGPT plugins, OpenAI ecosystem |
489
+ | **`.well-known/mcp.json`** | MCP server registry manifest | MCP clients, tool registries |
490
+ | **`openapi.yaml`** | OpenAPI 3.1 spec for the HTTP API | AutoGPT, CrewAI, API-aware agents |
491
+
492
+ ### GitHub Topics
493
+
494
+ Add these topics to the GitHub repo for search discovery:
495
+
496
+ `ai-governance` `ai-safety` `mcp-server` `agent-guardrails` `deterministic` `plan-enforcement` `model-context-protocol` `ai-agents`
437
497
 
438
498
  ## License
439
499
 
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/adapters/autoresearch.ts
21
+ var autoresearch_exports = {};
22
+ __export(autoresearch_exports, {
23
+ AutoresearchGovernor: () => AutoresearchGovernor
24
+ });
25
+ module.exports = __toCommonJS(autoresearch_exports);
26
+ var AutoresearchGovernor = class {
27
+ config;
28
+ state;
29
+ constructor(config) {
30
+ this.config = config;
31
+ this.state = {
32
+ experiments_run: 0,
33
+ best_result: null,
34
+ architectures_tested: [],
35
+ experiment_log: [],
36
+ total_compute_minutes: 0,
37
+ keep_count: 0
38
+ };
39
+ }
40
+ /**
41
+ * Convert an experiment proposal into a GuardEvent for governance evaluation.
42
+ */
43
+ proposalToGuardEvent(proposal) {
44
+ return {
45
+ intent: `run experiment: ${proposal.description}`,
46
+ tool: "experiment_runner",
47
+ scope: "experiment",
48
+ roleId: "experiment_runner",
49
+ direction: "output",
50
+ actionCategory: "shell",
51
+ args: {
52
+ experiment_id: String(proposal.experiment_id),
53
+ architecture: proposal.architecture,
54
+ estimated_minutes: String(proposal.estimated_minutes || 5)
55
+ }
56
+ };
57
+ }
58
+ /**
59
+ * Evaluate an experiment proposal against governance rules.
60
+ * Returns a simplified verdict without requiring the full guard engine.
61
+ */
62
+ evaluateProposal(proposal) {
63
+ const warnings = [];
64
+ const estimatedMinutes = proposal.estimated_minutes || 5;
65
+ if (this.state.total_compute_minutes + estimatedMinutes > this.config.computeBudgetMinutes) {
66
+ return {
67
+ allowed: false,
68
+ reason: `Compute budget exhausted: ${this.state.total_compute_minutes}/${this.config.computeBudgetMinutes} minutes used`,
69
+ warnings
70
+ };
71
+ }
72
+ if (this.config.constraints) {
73
+ for (const constraint of this.config.constraints) {
74
+ const lower = constraint.toLowerCase();
75
+ const archLower = proposal.architecture.toLowerCase();
76
+ const descLower = proposal.description.toLowerCase();
77
+ if (lower.startsWith("no ")) {
78
+ const forbidden = lower.slice(3).trim();
79
+ if (archLower.includes(forbidden) || descLower.includes(forbidden)) {
80
+ return {
81
+ allowed: false,
82
+ reason: `Architecture constraint violated: ${constraint}`,
83
+ warnings
84
+ };
85
+ }
86
+ }
87
+ }
88
+ }
89
+ const failureCount = this.state.experiment_log.filter((e) => !e.success).length;
90
+ if (failureCount > 5) {
91
+ warnings.push(`High failure rate: ${failureCount} failed experiments. Consider investigating root cause.`);
92
+ }
93
+ const recentArchitectures = this.state.experiment_log.slice(-5).map((e) => e.architecture);
94
+ const uniqueRecent = new Set(recentArchitectures).size;
95
+ if (recentArchitectures.length >= 5 && uniqueRecent === 1) {
96
+ warnings.push("Research may be stuck: last 5 experiments used the same architecture.");
97
+ }
98
+ return { allowed: true, reason: "Experiment approved", warnings };
99
+ }
100
+ /**
101
+ * Record an experiment result and update research state.
102
+ */
103
+ recordResult(result) {
104
+ this.state.experiments_run++;
105
+ this.state.total_compute_minutes += result.wall_clock_minutes;
106
+ this.state.experiment_log.push(result);
107
+ if (!this.state.architectures_tested.includes(result.architecture)) {
108
+ this.state.architectures_tested.push(result.architecture);
109
+ }
110
+ if (!result.success) {
111
+ return { kept: false, improvement: null, state: { ...this.state } };
112
+ }
113
+ let kept = false;
114
+ let improvement = null;
115
+ if (this.state.best_result === null) {
116
+ kept = true;
117
+ this.state.best_result = result;
118
+ this.state.keep_count++;
119
+ } else {
120
+ const prev = this.state.best_result.metric_value;
121
+ const curr = result.metric_value;
122
+ if (this.config.optimize === "minimize") {
123
+ kept = curr < prev;
124
+ improvement = kept ? prev - curr : null;
125
+ } else {
126
+ kept = curr > prev;
127
+ improvement = kept ? curr - prev : null;
128
+ }
129
+ if (kept) {
130
+ this.state.best_result = result;
131
+ this.state.keep_count++;
132
+ }
133
+ }
134
+ return { kept, improvement, state: { ...this.state } };
135
+ }
136
+ /**
137
+ * Export current state as a state snapshot compatible with the world file.
138
+ */
139
+ toWorldState() {
140
+ const successfulExperiments = this.state.experiment_log.filter((e) => e.success);
141
+ const failedCount = this.state.experiment_log.filter((e) => !e.success).length;
142
+ const keepRate = this.state.experiments_run > 0 ? Math.round(this.state.keep_count / this.state.experiments_run * 100) : 0;
143
+ let improvementRate = 0;
144
+ if (successfulExperiments.length >= 2) {
145
+ const recent = successfulExperiments.slice(-10);
146
+ let improvements = 0;
147
+ for (let i = 1; i < recent.length; i++) {
148
+ const prev = recent[i - 1].metric_value;
149
+ const curr = recent[i].metric_value;
150
+ if (this.config.optimize === "minimize" ? curr < prev : curr > prev) {
151
+ improvements++;
152
+ }
153
+ }
154
+ improvementRate = Math.round(improvements / (recent.length - 1) * 100);
155
+ }
156
+ return {
157
+ experiments_run: this.state.experiments_run,
158
+ best_metric_value: this.state.best_result?.metric_value ?? (this.config.optimize === "minimize" ? 100 : -1e3),
159
+ keep_rate: keepRate,
160
+ compute_used_minutes: Math.round(this.state.total_compute_minutes),
161
+ compute_budget_minutes: this.config.computeBudgetMinutes,
162
+ failed_experiments: failedCount,
163
+ metric_improvement_rate: improvementRate,
164
+ research_context_drift: 0
165
+ // would need NLP to compute properly
166
+ };
167
+ }
168
+ /**
169
+ * Get a summary of the current research state.
170
+ */
171
+ getSummary() {
172
+ return {
173
+ experiments_run: this.state.experiments_run,
174
+ best_result: this.state.best_result,
175
+ keep_rate: this.state.experiments_run > 0 ? Math.round(this.state.keep_count / this.state.experiments_run * 100) : 0,
176
+ compute_remaining_minutes: this.config.computeBudgetMinutes - this.state.total_compute_minutes,
177
+ architectures_tested: [...this.state.architectures_tested]
178
+ };
179
+ }
180
+ /**
181
+ * Load state from a persisted research context file.
182
+ */
183
+ loadState(state) {
184
+ this.state = { ...state };
185
+ }
186
+ /**
187
+ * Export state for persistence.
188
+ */
189
+ exportState() {
190
+ return { ...this.state };
191
+ }
192
+ };
193
+ // Annotate the CommonJS export names for ESM import in node:
194
+ 0 && (module.exports = {
195
+ AutoresearchGovernor
196
+ });