@neuroverseos/nv-sim 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +184 -619
- package/dist/assets/index-CH_VswRM.css +1 -0
- package/dist/assets/index-sT4b_z7w.js +686 -0
- package/dist/assets/{reportEngine-CYSZfooa.js → reportEngine-Bu8bB5Yq.js} +1 -1
- package/dist/connectors/nv-scienceclaw-post.js +141 -154
- package/dist/engine/universalAdapter.js +371 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/dist/assets/index-B43_0HyO.css +0 -1
- package/dist/assets/index-CdghpsS8.js +0 -595
|
@@ -1 +1 @@
|
|
|
1
|
-
import{g as d,A as m,e as u,a as v}from"./index-
|
|
1
|
+
import{g as d,A as m,e as u,a as v}from"./index-sT4b_z7w.js";function p(s){const e=s.governed,i=s.governanceStats,c=e.swarm.rounds.map((a,t)=>{var o;return{round:a.round,reactions:a.reactions.map(n=>({agent:n.stakeholder_id,action:n.reaction,impact:n.impact,verdict:void 0})),interventions:((o=a.emergent_dynamics)==null?void 0:o.filter(n=>n.includes("[BLOCK]")||n.includes("[PAUSE]")||n.includes("CIRCUIT BREAKER")||n.includes("intervention")))??[]}}),r=e.swarm.rounds.flatMap(a=>(a.emergent_dynamics??[]).filter(t=>t.includes("[BLOCK]")||t.includes("[PAUSE]")||t.includes("GATE")||t.includes("Rebalanced")||t.includes("Capped"))),l={avgImpact:e.metrics.avgImpact,collapseProbability:e.metrics.collapseProbability,stabilityScore:e.metrics.stabilityScore,maxVolatility:e.metrics.maxVolatility,peakNegativeSentiment:e.metrics.peakNegativeSentiment};return{scenario:s.scenario,rounds:c,metrics:l,interventions:r,governanceStats:{totalEvaluations:i.totalEvaluations,blocks:i.verdicts.block,pauses:i.verdicts.pause,allows:i.verdicts.allow,rulesFired:i.rulesFired}}}async function y(s,e={}){const i=p(s),c=e.aiEnabled??!1,r=c?e.provider??"deterministic":"deterministic",l=d(r),a=m.find(o=>o.id==="ai_analyst"),t=await u(a,"generate_report",async()=>c&&r!=="deterministic"?l.summarize(i):v(i));return{report:t.result,governance:t,aiUsed:c&&r!=="deterministic",provider:r}}export{p as extractTrace,y as generateGovernedReport};
|
|
@@ -5,22 +5,23 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Drop-in replacement for `scienceclaw-post` that adds governance.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* How it works:
|
|
9
|
+
* 1. Entry gate: evaluates the investigation BEFORE ScienceClaw starts
|
|
10
|
+
* 2. Runs scienceclaw-post as a child process (stdout/stderr pass through)
|
|
11
|
+
* 3. Watches ~/.scienceclaw/artifacts/{agent}/store.jsonl for new artifacts
|
|
12
|
+
* 4. Each new artifact line is sent to /api/evaluate for governance
|
|
13
|
+
* 5. Reports final stats when ScienceClaw exits
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* ScienceClaw does NOT print JSON to stdout — it writes JSONL to files.
|
|
16
|
+
* So we watch the file, not stdout.
|
|
15
17
|
*
|
|
16
|
-
*
|
|
18
|
+
* Usage:
|
|
17
19
|
* nv-scienceclaw-post --agent MyAgent --topic "CRISPR risks"
|
|
18
20
|
*
|
|
19
21
|
* Environment variables:
|
|
20
22
|
* NEUROVERSE_URL — Governance server URL (default: http://localhost:3456)
|
|
21
23
|
* SCIENCECLAW_BIN — Path to scienceclaw-post binary (default: scienceclaw-post)
|
|
22
24
|
* NV_VERBOSE — Set to "1" for verbose output
|
|
23
|
-
* NV_ENFORCE_STREAMING — Set to "0" to disable streaming governance (entry gate only)
|
|
24
25
|
*
|
|
25
26
|
* Zero edits to ScienceClaw required.
|
|
26
27
|
*/
|
|
@@ -29,6 +30,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
29
30
|
};
|
|
30
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
32
|
const child_process_1 = require("child_process");
|
|
33
|
+
const fs_1 = require("fs");
|
|
34
|
+
const os_1 = require("os");
|
|
35
|
+
const path_1 = require("path");
|
|
32
36
|
const http_1 = __importDefault(require("http"));
|
|
33
37
|
const https_1 = __importDefault(require("https"));
|
|
34
38
|
// ============================================
|
|
@@ -37,7 +41,6 @@ const https_1 = __importDefault(require("https"));
|
|
|
37
41
|
const NEUROVERSE_URL = process.env.NEUROVERSE_URL ?? "http://localhost:3456";
|
|
38
42
|
const SCIENCECLAW_BIN = process.env.SCIENCECLAW_BIN ?? "scienceclaw-post";
|
|
39
43
|
const VERBOSE = process.env.NV_VERBOSE === "1";
|
|
40
|
-
const ENFORCE_STREAMING = process.env.NV_ENFORCE_STREAMING !== "0";
|
|
41
44
|
// ============================================
|
|
42
45
|
// STATS
|
|
43
46
|
// ============================================
|
|
@@ -48,7 +51,6 @@ const stats = {
|
|
|
48
51
|
penalized: 0,
|
|
49
52
|
rewarded: 0,
|
|
50
53
|
modified: 0,
|
|
51
|
-
cyclesSeen: 0,
|
|
52
54
|
artifactsSeen: 0,
|
|
53
55
|
};
|
|
54
56
|
function parseArgs(argv) {
|
|
@@ -57,9 +59,7 @@ function parseArgs(argv) {
|
|
|
57
59
|
agent: "unknown",
|
|
58
60
|
topic: "",
|
|
59
61
|
skill: null,
|
|
60
|
-
confidence: null,
|
|
61
62
|
passthroughArgs: [],
|
|
62
|
-
noStream: false,
|
|
63
63
|
};
|
|
64
64
|
const nvArgIndices = new Set();
|
|
65
65
|
let i = 0;
|
|
@@ -77,10 +77,6 @@ function parseArgs(argv) {
|
|
|
77
77
|
parsed.skill = args[++i] ?? null;
|
|
78
78
|
i++;
|
|
79
79
|
break;
|
|
80
|
-
case "--confidence":
|
|
81
|
-
parsed.confidence = parseFloat(args[++i] ?? "0.5");
|
|
82
|
-
i++;
|
|
83
|
-
break;
|
|
84
80
|
case "--nv-url":
|
|
85
81
|
nvArgIndices.add(i);
|
|
86
82
|
nvArgIndices.add(i + 1);
|
|
@@ -90,11 +86,6 @@ function parseArgs(argv) {
|
|
|
90
86
|
nvArgIndices.add(i);
|
|
91
87
|
i++;
|
|
92
88
|
break;
|
|
93
|
-
case "--nv-no-stream":
|
|
94
|
-
nvArgIndices.add(i);
|
|
95
|
-
parsed.noStream = true;
|
|
96
|
-
i++;
|
|
97
|
-
break;
|
|
98
89
|
default:
|
|
99
90
|
i++;
|
|
100
91
|
}
|
|
@@ -103,7 +94,7 @@ function parseArgs(argv) {
|
|
|
103
94
|
return parsed;
|
|
104
95
|
}
|
|
105
96
|
// ============================================
|
|
106
|
-
// GOVERNANCE EVALUATION
|
|
97
|
+
// GOVERNANCE EVALUATION
|
|
107
98
|
// ============================================
|
|
108
99
|
function evaluate(actionType, agent, extra) {
|
|
109
100
|
return new Promise((resolve) => {
|
|
@@ -111,7 +102,7 @@ function evaluate(actionType, agent, extra) {
|
|
|
111
102
|
actor: agent,
|
|
112
103
|
action: actionType,
|
|
113
104
|
payload: {
|
|
114
|
-
description: `Agent ${agent} executing ${actionType}`,
|
|
105
|
+
description: extra?.description ?? `Agent ${agent} executing ${actionType}`,
|
|
115
106
|
...extra,
|
|
116
107
|
},
|
|
117
108
|
world: "science_research",
|
|
@@ -158,7 +149,7 @@ function evaluate(actionType, agent, extra) {
|
|
|
158
149
|
req.on("error", () => {
|
|
159
150
|
if (stats.evaluated === 0) {
|
|
160
151
|
console.log(` [NV] WARNING: NeuroVerse server not reachable at ${NEUROVERSE_URL}`);
|
|
161
|
-
console.log(` [NV] Start it with:
|
|
152
|
+
console.log(` [NV] Start it with: npx tsx src/server/index.ts`);
|
|
162
153
|
console.log(` [NV] Falling back to ALLOW (ungoverned mode)\n`);
|
|
163
154
|
}
|
|
164
155
|
resolve({ decision: "ALLOW", reason: "Governance server unavailable" });
|
|
@@ -172,79 +163,111 @@ function evaluate(actionType, agent, extra) {
|
|
|
172
163
|
});
|
|
173
164
|
}
|
|
174
165
|
// ============================================
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
// Schema-gated: only strict JSON lines are governed.
|
|
178
|
-
// Non-JSON output is passed through without evaluation (by design).
|
|
179
|
-
// No regex or heuristic parsing — safety and determinism over flexibility.
|
|
166
|
+
// JSONL FILE WATCHER
|
|
180
167
|
// ============================================
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Watch a JSONL file for new lines appended by ScienceClaw.
|
|
170
|
+
* Each new line is parsed and sent to governance.
|
|
171
|
+
*
|
|
172
|
+
* ScienceClaw writes artifacts to:
|
|
173
|
+
* ~/.scienceclaw/artifacts/{agent}/store.jsonl
|
|
174
|
+
*
|
|
175
|
+
* Each line is a JSON artifact with fields:
|
|
176
|
+
* artifact_id, artifact_type, producer_agent, skill_used,
|
|
177
|
+
* payload, investigation_id, content_hash, parent_artifact_ids,
|
|
178
|
+
* result_quality, needs, timestamp
|
|
179
|
+
*/
|
|
180
|
+
function watchArtifactFile(filePath, agent) {
|
|
181
|
+
let fileSize = 0;
|
|
182
|
+
// Get initial file size (skip existing content)
|
|
191
183
|
try {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (VERBOSE)
|
|
196
|
-
console.log(` [NV] [ungoverned: invalid_json] ${trimmed.slice(0, 120)}`);
|
|
197
|
-
return false;
|
|
184
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
185
|
+
fileSize = (0, fs_1.statSync)(filePath).size;
|
|
186
|
+
}
|
|
198
187
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
188
|
+
catch { /* file doesn't exist yet */ }
|
|
189
|
+
if (VERBOSE)
|
|
190
|
+
console.log(` [NV] Watching: ${filePath}`);
|
|
191
|
+
if (VERBOSE)
|
|
192
|
+
console.log(` [NV] Starting from byte offset: ${fileSize}`);
|
|
193
|
+
// Poll for changes (fs.watch is unreliable across platforms for appends)
|
|
194
|
+
const interval = setInterval(() => {
|
|
195
|
+
try {
|
|
196
|
+
if (!(0, fs_1.existsSync)(filePath))
|
|
197
|
+
return;
|
|
198
|
+
const currentSize = (0, fs_1.statSync)(filePath).size;
|
|
199
|
+
if (currentSize <= fileSize)
|
|
200
|
+
return;
|
|
201
|
+
// Read only the new bytes
|
|
202
|
+
const fd = (0, fs_1.readFileSync)(filePath, "utf-8");
|
|
203
|
+
const newContent = fd.slice(fileSize);
|
|
204
|
+
fileSize = currentSize;
|
|
205
|
+
// Parse each new line
|
|
206
|
+
const lines = newContent.split("\n").filter((l) => l.trim());
|
|
207
|
+
for (const line of lines) {
|
|
208
|
+
try {
|
|
209
|
+
const artifact = JSON.parse(line);
|
|
210
|
+
governArtifact(artifact, agent);
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
if (VERBOSE)
|
|
214
|
+
console.log(` [NV] [skip] non-JSON line in artifact file`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
if (VERBOSE)
|
|
220
|
+
console.log(` [NV] [watch error] ${err}`);
|
|
221
|
+
}
|
|
222
|
+
}, 500); // Check every 500ms
|
|
223
|
+
return {
|
|
224
|
+
stop: () => clearInterval(interval),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Send a ScienceClaw artifact to governance.
|
|
229
|
+
*/
|
|
230
|
+
async function governArtifact(artifact, agent) {
|
|
231
|
+
stats.artifactsSeen++;
|
|
232
|
+
const artifactType = artifact.artifact_type ?? artifact.type ?? "unknown";
|
|
233
|
+
const skillUsed = artifact.skill_used ?? "unknown";
|
|
234
|
+
const quality = artifact.result_quality ?? "ok";
|
|
235
|
+
const producer = artifact.producer_agent ?? agent;
|
|
236
|
+
const description = artifact.payload?.title
|
|
237
|
+
?? artifact.payload?.query
|
|
238
|
+
?? `${artifactType} via ${skillUsed}`;
|
|
239
|
+
const verdict = await evaluate(`publish_${artifactType}`, producer, {
|
|
240
|
+
description,
|
|
241
|
+
artifact_type: artifactType,
|
|
242
|
+
skill_used: skillUsed,
|
|
243
|
+
result_quality: quality,
|
|
244
|
+
artifact_id: artifact.artifact_id,
|
|
245
|
+
investigation_id: artifact.investigation_id,
|
|
246
|
+
parent_artifacts: artifact.parent_artifact_ids?.length ?? 0,
|
|
247
|
+
has_needs: Array.isArray(artifact.needs) && artifact.needs.length > 0,
|
|
248
|
+
});
|
|
249
|
+
const decision = (verdict.decision ?? verdict.status ?? "ALLOW").toUpperCase();
|
|
250
|
+
if (decision === "BLOCK" || decision === "PENALIZE") {
|
|
251
|
+
console.log(`\n [NV] BLOCKED: ${artifactType} by ${producer}`);
|
|
252
|
+
console.log(` Skill: ${skillUsed}`);
|
|
253
|
+
console.log(` Reason: ${verdict.reason ?? "policy violation"}`);
|
|
254
|
+
if (verdict.consequence) {
|
|
255
|
+
console.log(` Consequence: ${verdict.consequence.description ?? verdict.consequence.type}`);
|
|
256
|
+
}
|
|
205
257
|
}
|
|
206
|
-
if (
|
|
258
|
+
else if (decision === "ALLOW") {
|
|
207
259
|
if (VERBOSE)
|
|
208
|
-
console.log(` [NV]
|
|
209
|
-
return false;
|
|
260
|
+
console.log(` [NV] ALLOWED: ${artifactType} by ${producer} (${skillUsed})`);
|
|
210
261
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
for (const art of artifacts) {
|
|
214
|
-
stats.artifactsSeen++;
|
|
215
|
-
const artAgent = art.agent_id ?? art.id ?? art.agent ?? agent;
|
|
216
|
-
const artType = art.type ?? art.action ?? "publish";
|
|
217
|
-
const verdict = await evaluate(`publish_${artType}`, artAgent, {
|
|
218
|
-
description: art.description ?? `${artType} artifact`,
|
|
219
|
-
artifact_type: artType,
|
|
220
|
-
confidence: art.confidence,
|
|
221
|
-
reproduced: art.reproduced ?? false,
|
|
222
|
-
citations: art.citations ?? 0,
|
|
223
|
-
cycle,
|
|
224
|
-
});
|
|
225
|
-
const decision = (verdict.decision ?? verdict.status ?? "ALLOW").toUpperCase();
|
|
226
|
-
if ((decision === "BLOCK" || decision === "PENALIZE") && ENFORCE_STREAMING) {
|
|
227
|
-
console.log(`\n [NV] GOVERNANCE HALT at cycle ${cycle}`);
|
|
228
|
-
console.log(` Artifact: ${art.description ?? artType}`);
|
|
229
|
-
console.log(` Agent: ${artAgent}`);
|
|
230
|
-
console.log(` Decision: ${decision}`);
|
|
231
|
-
console.log(` Reason: ${verdict.reason ?? "policy violation"}`);
|
|
232
|
-
if (decision === "PENALIZE" && verdict.consequence) {
|
|
233
|
-
console.log(` Consequence: ${verdict.consequence.type} for ${verdict.consequence.rounds ?? 1} round(s)`);
|
|
234
|
-
}
|
|
235
|
-
console.log(` Terminating ScienceClaw process...\n`);
|
|
236
|
-
killFn();
|
|
237
|
-
return true; // halted
|
|
238
|
-
}
|
|
262
|
+
else if (decision === "REWARD") {
|
|
263
|
+
console.log(` [NV] REWARDED: ${artifactType} by ${producer} — ${verdict.reward?.description ?? ""}`);
|
|
239
264
|
}
|
|
240
|
-
return false;
|
|
241
265
|
}
|
|
242
266
|
// ============================================
|
|
243
267
|
// MAIN
|
|
244
268
|
// ============================================
|
|
245
269
|
async function main() {
|
|
246
270
|
const parsed = parseArgs(process.argv);
|
|
247
|
-
const streamingEnabled = ENFORCE_STREAMING && !parsed.noStream;
|
|
248
271
|
console.log(`\n NeuroVerse ScienceClaw Connector`);
|
|
249
272
|
console.log(` ${"=".repeat(50)}`);
|
|
250
273
|
console.log(` Agent: ${parsed.agent}`);
|
|
@@ -252,122 +275,86 @@ async function main() {
|
|
|
252
275
|
if (parsed.skill)
|
|
253
276
|
console.log(` Skill: ${parsed.skill}`);
|
|
254
277
|
console.log(` Server: ${NEUROVERSE_URL}`);
|
|
255
|
-
console.log(` Streaming: ${streamingEnabled ? "ON — each artifact evaluated" : "OFF — entry gate only"}`);
|
|
256
278
|
console.log(` ${"=".repeat(50)}\n`);
|
|
257
279
|
// ── Level 1: Entry gate ──
|
|
258
|
-
console.log(` [1/
|
|
280
|
+
console.log(` [1/3] Evaluating launch governance...`);
|
|
259
281
|
const actionType = parsed.skill ? `execute_skill:${parsed.skill}` : "run_investigation";
|
|
260
282
|
const verdict = await evaluate(actionType, parsed.agent, {
|
|
261
283
|
topic: parsed.topic,
|
|
262
284
|
skill: parsed.skill,
|
|
263
|
-
confidence: parsed.confidence,
|
|
264
285
|
description: `Agent ${parsed.agent}: ${actionType}${parsed.topic ? ` on '${parsed.topic}'` : ""}`,
|
|
265
286
|
});
|
|
266
287
|
const decision = (verdict.decision ?? verdict.status ?? "ALLOW").toUpperCase();
|
|
267
288
|
if (decision === "BLOCK" || decision === "PENALIZE") {
|
|
268
289
|
const label = decision === "BLOCK" ? "BLOCKED" : "PENALIZED";
|
|
269
|
-
console.log(`
|
|
290
|
+
console.log(` ${label} by NeuroVerse governance`);
|
|
270
291
|
console.log(` Reason: ${verdict.reason ?? ""}`);
|
|
271
|
-
if (decision === "PENALIZE" && verdict.consequence) {
|
|
272
|
-
console.log(` Consequence: ${verdict.consequence.type} for ${verdict.consequence.rounds ?? 1} round(s)`);
|
|
273
|
-
}
|
|
274
292
|
console.log(`\n ScienceClaw execution SKIPPED`);
|
|
275
293
|
console.log(` ${"=".repeat(50)}\n`);
|
|
276
294
|
process.exit(1);
|
|
277
295
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
296
|
+
console.log(` ${decision} — proceeding\n`);
|
|
297
|
+
// ── Level 2: Run ScienceClaw + watch artifact file ──
|
|
298
|
+
const artifactDir = (0, path_1.join)((0, os_1.homedir)(), ".scienceclaw", "artifacts", parsed.agent);
|
|
299
|
+
const storePath = (0, path_1.join)(artifactDir, "store.jsonl");
|
|
300
|
+
const globalPath = (0, path_1.join)((0, os_1.homedir)(), ".scienceclaw", "artifacts", "global_index.jsonl");
|
|
301
|
+
// Watch both the agent store and global index
|
|
302
|
+
console.log(` [2/3] Running ScienceClaw + watching artifacts...`);
|
|
303
|
+
console.log(` Agent store: ${storePath}`);
|
|
304
|
+
console.log(` Global index: ${globalPath}\n`);
|
|
305
|
+
const storeWatcher = watchArtifactFile(storePath, parsed.agent);
|
|
306
|
+
const globalWatcher = watchArtifactFile(globalPath, parsed.agent);
|
|
289
307
|
const cmd = SCIENCECLAW_BIN;
|
|
290
308
|
const cmdArgs = parsed.passthroughArgs;
|
|
291
|
-
console.log(`
|
|
292
|
-
|
|
293
|
-
console.log(` Streaming governance active — each artifact will be evaluated\n`);
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
console.log();
|
|
297
|
-
}
|
|
298
|
-
console.log(` ${"=".repeat(50)}`);
|
|
309
|
+
console.log(` Executing: ${cmd} ${cmdArgs.join(" ")}`);
|
|
310
|
+
console.log(` ${"=".repeat(50)}\n`);
|
|
299
311
|
const child = (0, child_process_1.spawn)(cmd, cmdArgs, {
|
|
300
312
|
stdio: ["inherit", "pipe", "pipe"],
|
|
301
313
|
shell: process.platform === "win32",
|
|
302
314
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
child.kill();
|
|
308
|
-
}
|
|
309
|
-
catch { /* ignore */ }
|
|
310
|
-
};
|
|
311
|
-
// Pipe stderr through
|
|
315
|
+
// Pass through stdout and stderr so user sees ScienceClaw's normal output
|
|
316
|
+
child.stdout?.on("data", (chunk) => {
|
|
317
|
+
process.stdout.write(chunk);
|
|
318
|
+
});
|
|
312
319
|
child.stderr?.on("data", (chunk) => {
|
|
313
320
|
process.stderr.write(chunk);
|
|
314
321
|
});
|
|
315
|
-
// Pipe stdout through AND evaluate each line
|
|
316
|
-
let lineBuffer = "";
|
|
317
|
-
child.stdout?.on("data", (chunk) => {
|
|
318
|
-
const text = chunk.toString();
|
|
319
|
-
process.stdout.write(text); // always pass through
|
|
320
|
-
if (!streamingEnabled)
|
|
321
|
-
return;
|
|
322
|
-
lineBuffer += text;
|
|
323
|
-
const lines = lineBuffer.split("\n");
|
|
324
|
-
lineBuffer = lines.pop() ?? ""; // keep incomplete line
|
|
325
|
-
for (const line of lines) {
|
|
326
|
-
if (line.trim() && !haltedByGovernance) {
|
|
327
|
-
// Fire and forget — evaluate in background
|
|
328
|
-
governLine(line, parsed.agent, killChild);
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
322
|
child.on("error", (err) => {
|
|
323
|
+
storeWatcher.stop();
|
|
324
|
+
globalWatcher.stop();
|
|
333
325
|
if (err.code === "ENOENT") {
|
|
334
326
|
console.log(`\n ERROR: '${cmd}' not found on PATH`);
|
|
335
|
-
console.log(` Install ScienceClaw
|
|
327
|
+
console.log(` Install ScienceClaw: git clone https://github.com/lamm-mit/scienceclaw && cd scienceclaw && pip install -e .`);
|
|
328
|
+
console.log(` Or set SCIENCECLAW_BIN=/path/to/scienceclaw-post`);
|
|
336
329
|
}
|
|
337
330
|
else {
|
|
338
331
|
console.log(`\n ERROR: Failed to start ScienceClaw: ${err.message}`);
|
|
339
332
|
}
|
|
340
333
|
process.exit(127);
|
|
341
334
|
});
|
|
342
|
-
child.on("close", (code) => {
|
|
335
|
+
child.on("close", async (code) => {
|
|
336
|
+
// Wait a moment for final file writes to flush
|
|
337
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
338
|
+
storeWatcher.stop();
|
|
339
|
+
globalWatcher.stop();
|
|
343
340
|
// ── Level 3: Completion report ──
|
|
344
341
|
console.log(`\n ${"=".repeat(50)}`);
|
|
345
|
-
console.log(` [
|
|
346
|
-
|
|
347
|
-
console.log(` Status: HALTED by governance mid-execution`);
|
|
348
|
-
}
|
|
349
|
-
else if (code === 0) {
|
|
350
|
-
console.log(` Status: Completed successfully`);
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
console.log(` Status: Exited with code ${code}`);
|
|
354
|
-
}
|
|
355
|
-
console.log(` Evaluations: ${stats.evaluated}`);
|
|
356
|
-
console.log(` Cycles: ${stats.cyclesSeen}`);
|
|
342
|
+
console.log(` [3/3] Session complete\n`);
|
|
343
|
+
console.log(` Status: ${code === 0 ? "Completed successfully" : `Exited with code ${code}`}`);
|
|
357
344
|
console.log(` Artifacts: ${stats.artifactsSeen}`);
|
|
345
|
+
console.log(` Evaluations: ${stats.evaluated}`);
|
|
358
346
|
console.log(` Allowed: ${stats.allowed}`);
|
|
359
347
|
console.log(` Blocked: ${stats.blocked}`);
|
|
360
348
|
console.log(` Penalized: ${stats.penalized}`);
|
|
361
349
|
console.log(` Rewarded: ${stats.rewarded}`);
|
|
362
|
-
// Report to governance server
|
|
350
|
+
// Report to governance server
|
|
363
351
|
evaluate("investigation_complete", parsed.agent, {
|
|
364
|
-
description: `Agent ${parsed.agent} investigation
|
|
352
|
+
description: `Agent ${parsed.agent} investigation completed`,
|
|
365
353
|
exit_code: code,
|
|
366
|
-
halted_by_governance: haltedByGovernance,
|
|
367
354
|
stats,
|
|
368
355
|
}).catch(() => { });
|
|
369
356
|
console.log(`\n ${"=".repeat(50)}\n`);
|
|
370
|
-
process.exit(
|
|
357
|
+
process.exit(code ?? 0);
|
|
371
358
|
});
|
|
372
359
|
}
|
|
373
360
|
main().catch((err) => {
|