sibyl-system 1.2.2 → 1.3.1
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 +57 -14
- package/dist/cli.js +393 -199
- package/dist/index.js +572 -175
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,10 +8,40 @@ var __export = (target, all) => {
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
import { randomBytes } from "node:crypto";
|
|
10
10
|
import { spawn as spawn2 } from "node:child_process";
|
|
11
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
11
12
|
import { copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6 } from "node:fs/promises";
|
|
12
13
|
import { basename, dirname as dirname4, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "node:path";
|
|
13
14
|
|
|
14
15
|
// src/state/record.ts
|
|
16
|
+
var INDEPENDENCE_STATUSES = ["INDEPENDENT", "NOT-INDEPENDENT", "UNVERIFIABLE"];
|
|
17
|
+
function isIndependenceShape(v) {
|
|
18
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
19
|
+
const o = v;
|
|
20
|
+
if (!INDEPENDENCE_STATUSES.includes(o["status"])) return false;
|
|
21
|
+
if (!Array.isArray(o["convenerChain"]) || !o["convenerChain"].every((s) => typeof s === "string" && s.length > 0)) return false;
|
|
22
|
+
return typeof o["evidence"] === "string";
|
|
23
|
+
}
|
|
24
|
+
function rebuildIndependence(v) {
|
|
25
|
+
return { status: v.status, convenerChain: [...v.convenerChain], evidence: v.evidence };
|
|
26
|
+
}
|
|
27
|
+
var INSTRUMENT_NAME = /^[a-z0-9-]{1,40}$/;
|
|
28
|
+
var HEX64 = /^[0-9a-f]{64}$/;
|
|
29
|
+
function isInstrumentShape(v) {
|
|
30
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
31
|
+
const o = v;
|
|
32
|
+
if (typeof o["rulesHash"] !== "string" || !HEX64.test(o["rulesHash"])) return false;
|
|
33
|
+
const comps = o["components"];
|
|
34
|
+
if (typeof comps !== "object" || comps === null || Array.isArray(comps)) return false;
|
|
35
|
+
return Object.entries(comps).every(
|
|
36
|
+
([name, hash2]) => INSTRUMENT_NAME.test(name) && typeof hash2 === "string" && HEX64.test(hash2)
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function rebuildInstrument(v) {
|
|
40
|
+
return {
|
|
41
|
+
rulesHash: v.rulesHash,
|
|
42
|
+
components: Object.fromEntries(Object.entries(v.components).map(([name, hash2]) => [name, hash2]))
|
|
43
|
+
};
|
|
44
|
+
}
|
|
15
45
|
function isNonEmptyString(v) {
|
|
16
46
|
return typeof v === "string" && v.length > 0;
|
|
17
47
|
}
|
|
@@ -19,8 +49,142 @@ function compactIso(now) {
|
|
|
19
49
|
return now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
20
50
|
}
|
|
21
51
|
|
|
22
|
-
// src/
|
|
52
|
+
// src/instrument.ts
|
|
53
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
54
|
+
|
|
55
|
+
// src/council/index.ts
|
|
56
|
+
var COUNCILORS = ["MELCHIOR", "BALTHASAR", "CASPER"];
|
|
57
|
+
var OUTPUT_CONTRACT = 'Respond with EXACTLY one JSON object and nothing else - no prose, no markdown fences, no extra keys - matching: {"verdict":"APPROVE"|"REJECT","confidence":<number between 0 and 1>,"reasons":[<strings>],"must_fix":[<strings>]}. "verdict" is your final ballot, "confidence" your certainty (0..1), "reasons" the justification, "must_fix" the concrete defects that block approval (empty array if none).';
|
|
58
|
+
var COUNCILOR_PERSONAS = {
|
|
59
|
+
MELCHIOR: {
|
|
60
|
+
title: "correctness & safety skeptic",
|
|
61
|
+
system: "You are MELCHIOR, the correctness-and-safety skeptic on the SIBYL council. Audit the artifact for logical errors, unproven assumptions, correctness bugs, safety hazards, and irreversible side effects. Approve only if it survives strict technical scrutiny; default to REJECT when doubt remains. " + OUTPUT_CONTRACT
|
|
62
|
+
},
|
|
63
|
+
BALTHASAR: {
|
|
64
|
+
title: "adversarial red-teamer",
|
|
65
|
+
system: "You are BALTHASAR, the adversarial red-teamer on the SIBYL council. Attack the artifact as an adversary: probe abuse cases, exploits, race conditions, input-boundary failures, and ways it breaks under hostile use. If you can name a concrete attack or failure mode, REJECT and put it in must_fix. " + OUTPUT_CONTRACT
|
|
66
|
+
},
|
|
67
|
+
CASPER: {
|
|
68
|
+
title: "pragmatic utilist",
|
|
69
|
+
system: "You are CASPER, the pragmatic utilist on the SIBYL council. Judge the artifact by real-world utility: does it solve the stated problem, is it maintainable, worth the complexity, and shippable? Weigh benefit against cost, but REJECT when the defects outweigh the value. " + OUTPUT_CONTRACT
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var COUNCIL_SIZE = COUNCILORS.length;
|
|
73
|
+
var KNOWN_IDS = new Set(COUNCILORS);
|
|
74
|
+
|
|
75
|
+
// src/chamber/protocol.ts
|
|
76
|
+
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
77
|
+
import { join as join2 } from "node:path";
|
|
78
|
+
|
|
79
|
+
// src/lane/seating.ts
|
|
23
80
|
import { createHash } from "node:crypto";
|
|
81
|
+
function toModelId(model) {
|
|
82
|
+
return `${model.providerID}/${model.modelID}`;
|
|
83
|
+
}
|
|
84
|
+
function policyAllows(modelId, policy) {
|
|
85
|
+
return policy.allowedPrefixes.some((prefix) => prefix.length > 0 && modelId.startsWith(prefix));
|
|
86
|
+
}
|
|
87
|
+
function resolveSeat(role, requestedSlot, pool, policy) {
|
|
88
|
+
const found = pool[requestedSlot] ?? pool["default"];
|
|
89
|
+
if (found === void 0) {
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
role,
|
|
93
|
+
deny: `seat denied: slot "${requestedSlot}" (and fallback "default") absent from pool`
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const modelId = toModelId(found);
|
|
97
|
+
if (found.providerID.length === 0 || found.modelID.length === 0) {
|
|
98
|
+
return { ok: false, role, deny: `seat denied: slot "${requestedSlot}" is an empty placeholder (${modelId})` };
|
|
99
|
+
}
|
|
100
|
+
if (!policyAllows(modelId, policy)) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
role,
|
|
104
|
+
deny: `seat denied by modelPolicy (E4/F1): "${modelId}" is not under any allowed prefix [${policy.allowedPrefixes.join(", ")}] \u2014 launch must not be attempted`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return { ok: true, role, slot: requestedSlot, modelId };
|
|
108
|
+
}
|
|
109
|
+
var SEP = "";
|
|
110
|
+
function sha256Hex(text) {
|
|
111
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
112
|
+
}
|
|
113
|
+
function computeDrawCommit(poolIds, seed) {
|
|
114
|
+
return sha256Hex(`sibyl-draw-v1${SEP}${poolIds.join(SEP)}${SEP}${seed}`);
|
|
115
|
+
}
|
|
116
|
+
function drawJudge(poolIds, seed, salt) {
|
|
117
|
+
if (poolIds.length === 0) {
|
|
118
|
+
return { ok: false, deny: "judge draw denied: empty judge pool" };
|
|
119
|
+
}
|
|
120
|
+
const drawCommit = computeDrawCommit(poolIds, seed);
|
|
121
|
+
const digest = sha256Hex(`${drawCommit}${SEP}${salt}`);
|
|
122
|
+
const uint = Number.parseInt(digest.slice(0, 8), 16);
|
|
123
|
+
const index = uint % poolIds.length;
|
|
124
|
+
const modelId = poolIds[index];
|
|
125
|
+
return { ok: true, draw: { index, modelId, drawCommit } };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/verdict/index.ts
|
|
129
|
+
var OPEN_BRACE = 123;
|
|
130
|
+
var CLOSE_BRACE = 125;
|
|
131
|
+
var QUOTE = 34;
|
|
132
|
+
var BACKSLASH = 92;
|
|
133
|
+
var FENCE_RE = /```(?:json)?[ \t]*\r?\n?([\s\S]*?)```/gi;
|
|
134
|
+
function findBalancedObject(text, start) {
|
|
135
|
+
if (text.charCodeAt(start) !== OPEN_BRACE) return null;
|
|
136
|
+
let depth = 0;
|
|
137
|
+
let inString = false;
|
|
138
|
+
let escaped = false;
|
|
139
|
+
for (let i = start; i < text.length; i++) {
|
|
140
|
+
const c = text.charCodeAt(i);
|
|
141
|
+
if (inString) {
|
|
142
|
+
if (escaped) escaped = false;
|
|
143
|
+
else if (c === BACKSLASH) escaped = true;
|
|
144
|
+
else if (c === QUOTE) inString = false;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (c === QUOTE) inString = true;
|
|
148
|
+
else if (c === OPEN_BRACE) depth++;
|
|
149
|
+
else if (c === CLOSE_BRACE) {
|
|
150
|
+
depth--;
|
|
151
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function firstParseableObject(text) {
|
|
157
|
+
for (let i = 0; i < text.length; i++) {
|
|
158
|
+
if (text.charCodeAt(i) !== OPEN_BRACE) continue;
|
|
159
|
+
const candidate = findBalancedObject(text, i);
|
|
160
|
+
if (candidate === null) continue;
|
|
161
|
+
try {
|
|
162
|
+
JSON.parse(candidate);
|
|
163
|
+
return candidate;
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function extractVerdictJson(text) {
|
|
170
|
+
for (const m of text.matchAll(FENCE_RE)) {
|
|
171
|
+
const fenced = firstParseableObject(m[1] ?? "");
|
|
172
|
+
if (fenced !== null) return fenced;
|
|
173
|
+
}
|
|
174
|
+
return firstParseableObject(text);
|
|
175
|
+
}
|
|
176
|
+
function rejectVerdict(reason) {
|
|
177
|
+
return {
|
|
178
|
+
verdict: "REJECT",
|
|
179
|
+
confidence: 0,
|
|
180
|
+
reasons: [`verdict-unparseable: ${reason}`],
|
|
181
|
+
must_fix: []
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
var FAIL_CLOSED_REJECT = rejectVerdict("no-parse-performed");
|
|
185
|
+
|
|
186
|
+
// src/state/chamber.ts
|
|
187
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
24
188
|
import { mkdir, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
25
189
|
import { homedir } from "node:os";
|
|
26
190
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
@@ -29,7 +193,7 @@ var TERMINAL_STATES = ["CONVERGED", "NEEDS_ROUND", "MEMBER_LOST", "TIMEOUT"];
|
|
|
29
193
|
var RUN_PROFILES = ["review", "exam"];
|
|
30
194
|
var RECORD_SCHEMA = 1;
|
|
31
195
|
function sha256Text(text) {
|
|
32
|
-
return
|
|
196
|
+
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
33
197
|
}
|
|
34
198
|
async function sha256File(path) {
|
|
35
199
|
let buf;
|
|
@@ -39,7 +203,7 @@ async function sha256File(path) {
|
|
|
39
203
|
const code = typeof err === "object" && err !== null ? String(Reflect.get(err, "code")) : "unknown";
|
|
40
204
|
return { ok: false, reason: `read failed (${code})` };
|
|
41
205
|
}
|
|
42
|
-
return { ok: true, sha256:
|
|
206
|
+
return { ok: true, sha256: createHash2("sha256").update(buf).digest("hex"), bytes: buf.byteLength };
|
|
43
207
|
}
|
|
44
208
|
async function assertOnDisk(path, claim2) {
|
|
45
209
|
const now = await sha256File(path);
|
|
@@ -85,6 +249,12 @@ function validateChamberRecord(raw) {
|
|
|
85
249
|
if (!Array.isArray(o["artifacts"]) || !o["artifacts"].every(isArtifactRef)) return { ok: false, reason: "artifacts must be an array of ArtifactRef" };
|
|
86
250
|
if (typeof o["evidenceRows"] !== "number" || !Number.isInteger(o["evidenceRows"]) || o["evidenceRows"] < 0) return { ok: false, reason: "evidenceRows must be a non-negative integer" };
|
|
87
251
|
if (o["notes"] !== void 0 && typeof o["notes"] !== "string") return { ok: false, reason: "notes must be a string when present" };
|
|
252
|
+
if (o["instrument"] !== void 0 && !isInstrumentShape(o["instrument"])) {
|
|
253
|
+
return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
|
|
254
|
+
}
|
|
255
|
+
if (o["independence"] !== void 0 && !isIndependenceShape(o["independence"])) {
|
|
256
|
+
return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
|
|
257
|
+
}
|
|
88
258
|
const record2 = {
|
|
89
259
|
schema: RECORD_SCHEMA,
|
|
90
260
|
runId: o["runId"],
|
|
@@ -106,6 +276,8 @@ function validateChamberRecord(raw) {
|
|
|
106
276
|
evidenceRows: o["evidenceRows"]
|
|
107
277
|
};
|
|
108
278
|
if (typeof o["notes"] === "string") record2.notes = o["notes"];
|
|
279
|
+
if (o["instrument"] !== void 0) record2.instrument = rebuildInstrument(o["instrument"]);
|
|
280
|
+
if (o["independence"] !== void 0) record2.independence = rebuildIndependence(o["independence"]);
|
|
109
281
|
return { ok: true, record: record2 };
|
|
110
282
|
}
|
|
111
283
|
async function appendLedgerRow(ledgerPath, record2) {
|
|
@@ -245,176 +417,6 @@ function spotcheckCommand(runDir) {
|
|
|
245
417
|
return `cd ${runDir} && sha256sum -c ${CHECKSUMS_FILE}`;
|
|
246
418
|
}
|
|
247
419
|
|
|
248
|
-
// src/chamber/synthesis.ts
|
|
249
|
-
var PERFORMANCE_ONLY_IN_LOOP = "PERFORMANCE-ONLY-IN-LOOP";
|
|
250
|
-
var MAX_RATIONALE = 6;
|
|
251
|
-
function conclude(result) {
|
|
252
|
-
const { terminal, judge, gaps } = result;
|
|
253
|
-
if (gaps.length > 0 && (terminal === "MEMBER_LOST" || terminal === "TIMEOUT")) {
|
|
254
|
-
return { conclusion: "NEEDS_HUMAN", confidence: 0 };
|
|
255
|
-
}
|
|
256
|
-
if (terminal === "CONVERGED" && judge !== null) {
|
|
257
|
-
const base = judge.conclusion;
|
|
258
|
-
const conclusion = base === "NEEDS_HUMAN" ? "NEEDS_HUMAN" : gaps.length > 0 ? "NEEDS_HUMAN" : base;
|
|
259
|
-
return { conclusion, confidence: conclusion === "NEEDS_HUMAN" ? Math.min(judge.confidence, 0.5) : judge.confidence };
|
|
260
|
-
}
|
|
261
|
-
if (judge !== null && judge.convergence === "NEEDS_ROUND") {
|
|
262
|
-
return { conclusion: "NEEDS_HUMAN", confidence: Math.min(judge.confidence, 0.5) };
|
|
263
|
-
}
|
|
264
|
-
return { conclusion: "NEEDS_HUMAN", confidence: 0 };
|
|
265
|
-
}
|
|
266
|
-
function buildVoice(result) {
|
|
267
|
-
const { record: record2, judge, terminal, gaps } = result;
|
|
268
|
-
const { conclusion, confidence } = conclude(result);
|
|
269
|
-
const rationale = judge !== null && conclusion !== "NEEDS_HUMAN" ? judge.reasons.slice(0, MAX_RATIONALE) : (judge?.reasons ?? []).slice(0, MAX_RATIONALE).concat(gaps).slice(0, MAX_RATIONALE);
|
|
270
|
-
const sealed = [
|
|
271
|
-
"DISAGREEMENT-LOG.md",
|
|
272
|
-
...new Set(record2.artifacts.map((a) => a.relPath))
|
|
273
|
-
];
|
|
274
|
-
return {
|
|
275
|
-
label: "SIBYL-ONE-VOICE",
|
|
276
|
-
honesty: PERFORMANCE_ONLY_IN_LOOP,
|
|
277
|
-
conclusion,
|
|
278
|
-
confidence: conclusion === "NEEDS_HUMAN" ? Number(confidence.toFixed(2)) : confidence,
|
|
279
|
-
rationale,
|
|
280
|
-
must_fix: judge?.must_fix ?? [],
|
|
281
|
-
open_charges: judge?.charges.length ?? 0,
|
|
282
|
-
dissent_sealed_in: sealed,
|
|
283
|
-
terminal,
|
|
284
|
-
run_id: record2.runId,
|
|
285
|
-
serial: record2.serial,
|
|
286
|
-
spotcheck: spotcheckCommand(record2.runDir)
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
function renderVoice(voice) {
|
|
290
|
-
const lines = [
|
|
291
|
-
`SIBYL \u2014 ONE CONCLUSION, ONE VOICE [${voice.label}]`,
|
|
292
|
-
`conclusion: ${voice.conclusion} confidence: ${String(voice.confidence)} terminal: ${voice.terminal}`,
|
|
293
|
-
`run: ${voice.run_id} (serial ${String(voice.serial)}) open charges: ${String(voice.open_charges)}`,
|
|
294
|
-
`honesty: ${voice.honesty} (until C-09/C-14 containment rulings \u2014 see docs/isolation-laws.md)`,
|
|
295
|
-
"rationale:",
|
|
296
|
-
...voice.rationale.map((r, i) => ` ${String(i + 1)}. ${r}`)
|
|
297
|
-
];
|
|
298
|
-
if (voice.must_fix.length > 0) {
|
|
299
|
-
lines.push("must_fix:");
|
|
300
|
-
lines.push(...voice.must_fix.map((m) => ` - ${m}`));
|
|
301
|
-
}
|
|
302
|
-
lines.push(`internal debate sealed in run dir: ${voice.dissent_sealed_in.length} artifacts + DISAGREEMENT-LOG.md (hashes in CHECKSUMS.txt)`);
|
|
303
|
-
lines.push(`verify every recorded byte: ${voice.spotcheck}`);
|
|
304
|
-
return lines.join("\n");
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// src/chamber/protocol.ts
|
|
308
|
-
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
309
|
-
import { join as join2 } from "node:path";
|
|
310
|
-
|
|
311
|
-
// src/lane/seating.ts
|
|
312
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
313
|
-
function toModelId(model) {
|
|
314
|
-
return `${model.providerID}/${model.modelID}`;
|
|
315
|
-
}
|
|
316
|
-
function policyAllows(modelId, policy) {
|
|
317
|
-
return policy.allowedPrefixes.some((prefix) => prefix.length > 0 && modelId.startsWith(prefix));
|
|
318
|
-
}
|
|
319
|
-
function resolveSeat(role, requestedSlot, pool, policy) {
|
|
320
|
-
const found = pool[requestedSlot] ?? pool["default"];
|
|
321
|
-
if (found === void 0) {
|
|
322
|
-
return {
|
|
323
|
-
ok: false,
|
|
324
|
-
role,
|
|
325
|
-
deny: `seat denied: slot "${requestedSlot}" (and fallback "default") absent from pool`
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
const modelId = toModelId(found);
|
|
329
|
-
if (found.providerID.length === 0 || found.modelID.length === 0) {
|
|
330
|
-
return { ok: false, role, deny: `seat denied: slot "${requestedSlot}" is an empty placeholder (${modelId})` };
|
|
331
|
-
}
|
|
332
|
-
if (!policyAllows(modelId, policy)) {
|
|
333
|
-
return {
|
|
334
|
-
ok: false,
|
|
335
|
-
role,
|
|
336
|
-
deny: `seat denied by modelPolicy (E4/F1): "${modelId}" is not under any allowed prefix [${policy.allowedPrefixes.join(", ")}] \u2014 launch must not be attempted`
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
return { ok: true, role, slot: requestedSlot, modelId };
|
|
340
|
-
}
|
|
341
|
-
var SEP = "";
|
|
342
|
-
function sha256Hex(text) {
|
|
343
|
-
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
344
|
-
}
|
|
345
|
-
function computeDrawCommit(poolIds, seed) {
|
|
346
|
-
return sha256Hex(`sibyl-draw-v1${SEP}${poolIds.join(SEP)}${SEP}${seed}`);
|
|
347
|
-
}
|
|
348
|
-
function drawJudge(poolIds, seed, salt) {
|
|
349
|
-
if (poolIds.length === 0) {
|
|
350
|
-
return { ok: false, deny: "judge draw denied: empty judge pool" };
|
|
351
|
-
}
|
|
352
|
-
const drawCommit = computeDrawCommit(poolIds, seed);
|
|
353
|
-
const digest = sha256Hex(`${drawCommit}${SEP}${salt}`);
|
|
354
|
-
const uint = Number.parseInt(digest.slice(0, 8), 16);
|
|
355
|
-
const index = uint % poolIds.length;
|
|
356
|
-
const modelId = poolIds[index];
|
|
357
|
-
return { ok: true, draw: { index, modelId, drawCommit } };
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// src/verdict/index.ts
|
|
361
|
-
var OPEN_BRACE = 123;
|
|
362
|
-
var CLOSE_BRACE = 125;
|
|
363
|
-
var QUOTE = 34;
|
|
364
|
-
var BACKSLASH = 92;
|
|
365
|
-
var FENCE_RE = /```(?:json)?[ \t]*\r?\n?([\s\S]*?)```/gi;
|
|
366
|
-
function findBalancedObject(text, start) {
|
|
367
|
-
if (text.charCodeAt(start) !== OPEN_BRACE) return null;
|
|
368
|
-
let depth = 0;
|
|
369
|
-
let inString = false;
|
|
370
|
-
let escaped = false;
|
|
371
|
-
for (let i = start; i < text.length; i++) {
|
|
372
|
-
const c = text.charCodeAt(i);
|
|
373
|
-
if (inString) {
|
|
374
|
-
if (escaped) escaped = false;
|
|
375
|
-
else if (c === BACKSLASH) escaped = true;
|
|
376
|
-
else if (c === QUOTE) inString = false;
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
if (c === QUOTE) inString = true;
|
|
380
|
-
else if (c === OPEN_BRACE) depth++;
|
|
381
|
-
else if (c === CLOSE_BRACE) {
|
|
382
|
-
depth--;
|
|
383
|
-
if (depth === 0) return text.slice(start, i + 1);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
return null;
|
|
387
|
-
}
|
|
388
|
-
function firstParseableObject(text) {
|
|
389
|
-
for (let i = 0; i < text.length; i++) {
|
|
390
|
-
if (text.charCodeAt(i) !== OPEN_BRACE) continue;
|
|
391
|
-
const candidate = findBalancedObject(text, i);
|
|
392
|
-
if (candidate === null) continue;
|
|
393
|
-
try {
|
|
394
|
-
JSON.parse(candidate);
|
|
395
|
-
return candidate;
|
|
396
|
-
} catch {
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return null;
|
|
400
|
-
}
|
|
401
|
-
function extractVerdictJson(text) {
|
|
402
|
-
for (const m of text.matchAll(FENCE_RE)) {
|
|
403
|
-
const fenced = firstParseableObject(m[1] ?? "");
|
|
404
|
-
if (fenced !== null) return fenced;
|
|
405
|
-
}
|
|
406
|
-
return firstParseableObject(text);
|
|
407
|
-
}
|
|
408
|
-
function rejectVerdict(reason) {
|
|
409
|
-
return {
|
|
410
|
-
verdict: "REJECT",
|
|
411
|
-
confidence: 0,
|
|
412
|
-
reasons: [`verdict-unparseable: ${reason}`],
|
|
413
|
-
must_fix: []
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
var FAIL_CLOSED_REJECT = rejectVerdict("no-parse-performed");
|
|
417
|
-
|
|
418
420
|
// src/chamber/protocol.ts
|
|
419
421
|
var CHAMBER_ROLES = ["evidence", "pro", "con", "judge"];
|
|
420
422
|
function artifactRel(role, round, kind) {
|
|
@@ -679,17 +681,184 @@ async function startRecord(cfg, roster, drawCommit) {
|
|
|
679
681
|
artifacts: [],
|
|
680
682
|
evidenceRows: 0
|
|
681
683
|
};
|
|
684
|
+
if (cfg.instrument !== void 0) base.instrument = cfg.instrument;
|
|
685
|
+
if (cfg.independence !== void 0) base.independence = cfg.independence;
|
|
682
686
|
const first = await finalizeRecord(base);
|
|
683
687
|
if (!first.ok) throw new Error(`START record refused: ${first.drift.join("; ")}`);
|
|
684
688
|
return first.record;
|
|
685
689
|
}
|
|
686
690
|
|
|
691
|
+
// src/personas.ts
|
|
692
|
+
var ARCHITECT_OUTPUT_CONTRACT = 'Respond with EXACTLY one JSON object and nothing else - no prose, no markdown fences, no extra keys - matching: {"tasks":[{"id":<string>,"title":<string>,"instructions":<string>,"dependsOn":[<task id strings>]}],"concurrency":<integer >= 1>,"notes":<string>}. "tasks" are the units of work decomposing the goal, "dependsOn" lists task ids that must finish first, "concurrency" is the maximum number of tasks safe to run in parallel, "notes" carries planning remarks (empty string if none).';
|
|
693
|
+
var ARCHITECT_SYSTEM = "You are ARCHITECT, the SIBYL planning persona. Decompose the given artifact-production goal into a dependency-ordered workflow of minimal, independently executable tasks. " + ARCHITECT_OUTPUT_CONTRACT;
|
|
694
|
+
var ARCHITECT_PERSONA = {
|
|
695
|
+
id: "ARCHITECT",
|
|
696
|
+
title: "workflow architect (planner)",
|
|
697
|
+
system: ARCHITECT_SYSTEM,
|
|
698
|
+
modelSlot: "architect"
|
|
699
|
+
};
|
|
700
|
+
function repairDemand(why) {
|
|
701
|
+
return `Your previous reply was not a valid verdict JSON (${why}). Reply again with EXACTLY one JSON object and nothing else - no prose, no markdown fences: {"verdict":"APPROVE"|"REJECT","confidence":<number between 0 and 1>,"reasons":[<strings>],"must_fix":[<strings>]}`;
|
|
702
|
+
}
|
|
703
|
+
var SWARM_JUDGE_SYSTEM = "You are the SIBYL swarm judge. You rule on pipeline reports with exactly one word: APPROVE, REJECT, or EXHAUSTED.";
|
|
704
|
+
var SWARM_JUDGE_WORD_CONTRACT = "Respond with EXACTLY one word and nothing else: APPROVE, REJECT, or EXHAUSTED.";
|
|
705
|
+
var PERSONAS = {
|
|
706
|
+
MELCHIOR: {
|
|
707
|
+
id: "MELCHIOR",
|
|
708
|
+
title: COUNCILOR_PERSONAS.MELCHIOR.title,
|
|
709
|
+
system: COUNCILOR_PERSONAS.MELCHIOR.system,
|
|
710
|
+
modelSlot: "melchior"
|
|
711
|
+
},
|
|
712
|
+
BALTHASAR: {
|
|
713
|
+
id: "BALTHASAR",
|
|
714
|
+
title: COUNCILOR_PERSONAS.BALTHASAR.title,
|
|
715
|
+
system: COUNCILOR_PERSONAS.BALTHASAR.system,
|
|
716
|
+
modelSlot: "balthasar"
|
|
717
|
+
},
|
|
718
|
+
CASPER: {
|
|
719
|
+
id: "CASPER",
|
|
720
|
+
title: COUNCILOR_PERSONAS.CASPER.title,
|
|
721
|
+
system: COUNCILOR_PERSONAS.CASPER.system,
|
|
722
|
+
modelSlot: "casper"
|
|
723
|
+
},
|
|
724
|
+
ARCHITECT: ARCHITECT_PERSONA
|
|
725
|
+
};
|
|
726
|
+
var PERSONA_IDS = [...COUNCILORS, "ARCHITECT"];
|
|
727
|
+
var BY_ID = new Map(
|
|
728
|
+
PERSONA_IDS.map((id) => [id, PERSONAS[id]])
|
|
729
|
+
);
|
|
730
|
+
|
|
731
|
+
// src/instrument.ts
|
|
732
|
+
function sha256Hex2(text) {
|
|
733
|
+
return createHash3("sha256").update(text, "utf8").digest("hex");
|
|
734
|
+
}
|
|
735
|
+
var REPAIR_SENTINEL = "<parse-failure-why>";
|
|
736
|
+
var SENTINEL_CFG = {
|
|
737
|
+
runId: "sibyl-sentinel",
|
|
738
|
+
runDir: "/sentinel/run",
|
|
739
|
+
goal: "<goal>",
|
|
740
|
+
targetPath: "/sentinel/target.md",
|
|
741
|
+
ledgerPath: "/sentinel/ledger.jsonl",
|
|
742
|
+
seed: "<seed>",
|
|
743
|
+
maxRounds: 1,
|
|
744
|
+
pool: { default: { providerID: "", modelID: "" } },
|
|
745
|
+
policy: null,
|
|
746
|
+
slots: { evidence: "default", pro: "default", con: "default", judge: "default" },
|
|
747
|
+
judgePoolIds: [],
|
|
748
|
+
lane: null
|
|
749
|
+
};
|
|
750
|
+
function chamberRoleTexts() {
|
|
751
|
+
return {
|
|
752
|
+
"chamber-evidence-prompt": evidencePrompt(SENTINEL_CFG, "/sentinel/evidence.jsonl"),
|
|
753
|
+
"chamber-pro-prompt": proPrompt(SENTINEL_CFG, 1, "/sentinel/pro.md", []),
|
|
754
|
+
"chamber-con-prompt": conPrompt(SENTINEL_CFG, 1, "/sentinel/con.md", []),
|
|
755
|
+
"chamber-rebuttal-prompt": rebuttalPrompt(SENTINEL_CFG, "pro", 1, "/sentinel/con.md", "/sentinel/pro-rebuttal.md"),
|
|
756
|
+
"chamber-judge-prompt": judgePrompt(SENTINEL_CFG, 1, ["/sentinel/pro.md", "/sentinel/con.md"], "/sentinel/judge.json")
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
function componentTexts() {
|
|
760
|
+
const texts = {
|
|
761
|
+
"repair-grammar": repairDemand(REPAIR_SENTINEL),
|
|
762
|
+
"swarm-judge-system": SWARM_JUDGE_SYSTEM,
|
|
763
|
+
"swarm-judge-word-contract": SWARM_JUDGE_WORD_CONTRACT,
|
|
764
|
+
...chamberRoleTexts()
|
|
765
|
+
};
|
|
766
|
+
for (const id of COUNCILORS) {
|
|
767
|
+
texts[`councilor-${id.toLowerCase()}`] = COUNCILOR_PERSONAS[id].system;
|
|
768
|
+
}
|
|
769
|
+
texts["architect-system"] = ARCHITECT_SYSTEM;
|
|
770
|
+
return texts;
|
|
771
|
+
}
|
|
772
|
+
var cached = null;
|
|
773
|
+
function instrumentFace() {
|
|
774
|
+
if (cached !== null) return cached;
|
|
775
|
+
const components = {};
|
|
776
|
+
for (const [name, text] of Object.entries(componentTexts())) {
|
|
777
|
+
components[name] = sha256Hex2(text);
|
|
778
|
+
}
|
|
779
|
+
const canonical = JSON.stringify(
|
|
780
|
+
Object.keys(components).sort().map((name) => [name, components[name]])
|
|
781
|
+
);
|
|
782
|
+
cached = { rulesHash: sha256Hex2(canonical), components };
|
|
783
|
+
return cached;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// src/chamber/synthesis.ts
|
|
787
|
+
var PERFORMANCE_ONLY_IN_LOOP = "PERFORMANCE-ONLY-IN-LOOP";
|
|
788
|
+
var MAX_RATIONALE = 6;
|
|
789
|
+
function conclude(result) {
|
|
790
|
+
const { terminal, judge, gaps } = result;
|
|
791
|
+
if (gaps.length > 0 && (terminal === "MEMBER_LOST" || terminal === "TIMEOUT")) {
|
|
792
|
+
return { conclusion: "NEEDS_HUMAN", confidence: 0 };
|
|
793
|
+
}
|
|
794
|
+
if (terminal === "CONVERGED" && judge !== null) {
|
|
795
|
+
const base = judge.conclusion;
|
|
796
|
+
const conclusion = base === "NEEDS_HUMAN" ? "NEEDS_HUMAN" : gaps.length > 0 ? "NEEDS_HUMAN" : base;
|
|
797
|
+
return { conclusion, confidence: conclusion === "NEEDS_HUMAN" ? Math.min(judge.confidence, 0.5) : judge.confidence };
|
|
798
|
+
}
|
|
799
|
+
if (judge !== null && judge.convergence === "NEEDS_ROUND") {
|
|
800
|
+
return { conclusion: "NEEDS_HUMAN", confidence: Math.min(judge.confidence, 0.5) };
|
|
801
|
+
}
|
|
802
|
+
return { conclusion: "NEEDS_HUMAN", confidence: 0 };
|
|
803
|
+
}
|
|
804
|
+
function buildVoice(result) {
|
|
805
|
+
const { record: record2, judge, terminal, gaps } = result;
|
|
806
|
+
const { conclusion, confidence } = conclude(result);
|
|
807
|
+
const rationale = judge !== null && conclusion !== "NEEDS_HUMAN" ? judge.reasons.slice(0, MAX_RATIONALE) : (judge?.reasons ?? []).slice(0, MAX_RATIONALE).concat(gaps).slice(0, MAX_RATIONALE);
|
|
808
|
+
const sealed = [
|
|
809
|
+
"DISAGREEMENT-LOG.md",
|
|
810
|
+
...new Set(record2.artifacts.map((a) => a.relPath))
|
|
811
|
+
];
|
|
812
|
+
return {
|
|
813
|
+
label: "SIBYL-ONE-VOICE",
|
|
814
|
+
honesty: PERFORMANCE_ONLY_IN_LOOP,
|
|
815
|
+
conclusion,
|
|
816
|
+
confidence: conclusion === "NEEDS_HUMAN" ? Number(confidence.toFixed(2)) : confidence,
|
|
817
|
+
rationale,
|
|
818
|
+
must_fix: judge?.must_fix ?? [],
|
|
819
|
+
open_charges: judge?.charges.length ?? 0,
|
|
820
|
+
dissent_sealed_in: sealed,
|
|
821
|
+
terminal,
|
|
822
|
+
run_id: record2.runId,
|
|
823
|
+
serial: record2.serial,
|
|
824
|
+
spotcheck: spotcheckCommand(record2.runDir)
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
function renderVoice(voice) {
|
|
828
|
+
const lines = [
|
|
829
|
+
`SIBYL \u2014 ONE CONCLUSION, ONE VOICE [${voice.label}]`,
|
|
830
|
+
`conclusion: ${voice.conclusion} confidence: ${String(voice.confidence)} terminal: ${voice.terminal}`,
|
|
831
|
+
`run: ${voice.run_id} (serial ${String(voice.serial)}) open charges: ${String(voice.open_charges)}`,
|
|
832
|
+
`honesty: ${voice.honesty} (until C-09/C-14 containment rulings \u2014 see docs/isolation-laws.md)`,
|
|
833
|
+
"rationale:",
|
|
834
|
+
...voice.rationale.map((r, i) => ` ${String(i + 1)}. ${r}`)
|
|
835
|
+
];
|
|
836
|
+
if (voice.must_fix.length > 0) {
|
|
837
|
+
lines.push("must_fix:");
|
|
838
|
+
lines.push(...voice.must_fix.map((m) => ` - ${m}`));
|
|
839
|
+
}
|
|
840
|
+
lines.push(`internal debate sealed in run dir: ${voice.dissent_sealed_in.length} artifacts + DISAGREEMENT-LOG.md (hashes in CHECKSUMS.txt)`);
|
|
841
|
+
lines.push(`verify every recorded byte: ${voice.spotcheck}`);
|
|
842
|
+
return lines.join("\n");
|
|
843
|
+
}
|
|
844
|
+
|
|
687
845
|
// src/lane/isolated.ts
|
|
688
846
|
import { spawn } from "node:child_process";
|
|
689
847
|
import { createWriteStream, existsSync } from "node:fs";
|
|
690
848
|
import { copyFile, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
691
849
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
692
850
|
var DEFAULT_PINNED_PATH = "/usr/bin:/bin";
|
|
851
|
+
function composeSandboxArgs(opts) {
|
|
852
|
+
const argv = [opts.bwrapBin, "--ro-bind", "/", "/", "--dev", "/dev", "--die-with-parent"];
|
|
853
|
+
for (const m of opts.maskDirs) argv.push("--tmpfs", m);
|
|
854
|
+
for (const k of opts.keepDirs) argv.push("--dir", posixDirname(k), "--bind", k, k);
|
|
855
|
+
argv.push("--", opts.bin, ...opts.args);
|
|
856
|
+
return argv;
|
|
857
|
+
}
|
|
858
|
+
function posixDirname(p) {
|
|
859
|
+
const i = p.lastIndexOf("/");
|
|
860
|
+
return i <= 0 ? "/" : p.slice(0, i);
|
|
861
|
+
}
|
|
693
862
|
function rolePaths(cfg, role) {
|
|
694
863
|
const roleDir = join3(cfg.runDir, role);
|
|
695
864
|
const home = join3(roleDir, "home");
|
|
@@ -754,8 +923,12 @@ async function launchRole(cfg, role, spec) {
|
|
|
754
923
|
await writeFile3(transcriptPath, "", "utf8");
|
|
755
924
|
await writeFile3(stderrPath, "", "utf8");
|
|
756
925
|
const args = composeRunArgs(spec);
|
|
926
|
+
const bwrap = cfg.bwrapBin ?? "/usr/bin/bwrap";
|
|
927
|
+
const mask = cfg.sandboxMaskDirs ?? [];
|
|
928
|
+
const sandboxed = mask.length > 0 && existsSync(bwrap);
|
|
929
|
+
const spawnArgv = sandboxed ? composeSandboxArgs({ bwrapBin: bwrap, maskDirs: mask, keepDirs: [cfg.runDir], bin: cfg.opencodeBin, args }) : [cfg.opencodeBin, ...args];
|
|
757
930
|
return new Promise((resolvePromise) => {
|
|
758
|
-
const child = spawn(
|
|
931
|
+
const child = spawn(spawnArgv[0], spawnArgv.slice(1), {
|
|
759
932
|
cwd: paths.workspace,
|
|
760
933
|
env,
|
|
761
934
|
detached: true,
|
|
@@ -777,7 +950,8 @@ async function launchRole(cfg, role, spec) {
|
|
|
777
950
|
transcriptPath,
|
|
778
951
|
sessionId: extractSessionId(transcript),
|
|
779
952
|
startedAt,
|
|
780
|
-
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
953
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
954
|
+
sandboxed
|
|
781
955
|
};
|
|
782
956
|
if (spawnError2 !== void 0) result.error = spawnError2;
|
|
783
957
|
resolvePromise(result);
|
|
@@ -1737,7 +1911,7 @@ __export(util_exports, {
|
|
|
1737
1911
|
attachSchema: () => attachSchema,
|
|
1738
1912
|
base64ToUint8Array: () => base64ToUint8Array,
|
|
1739
1913
|
base64urlToUint8Array: () => base64urlToUint8Array,
|
|
1740
|
-
cached: () =>
|
|
1914
|
+
cached: () => cached2,
|
|
1741
1915
|
captureStackTrace: () => captureStackTrace,
|
|
1742
1916
|
cleanEnum: () => cleanEnum,
|
|
1743
1917
|
cleanRegex: () => cleanRegex,
|
|
@@ -1825,7 +1999,7 @@ function jsonStringifyReplacer(_, value) {
|
|
|
1825
1999
|
return value.toString();
|
|
1826
2000
|
return value;
|
|
1827
2001
|
}
|
|
1828
|
-
function
|
|
2002
|
+
function cached2(getter) {
|
|
1829
2003
|
const set2 = false;
|
|
1830
2004
|
return {
|
|
1831
2005
|
get value() {
|
|
@@ -1934,7 +2108,7 @@ var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace :
|
|
|
1934
2108
|
function isObject(data) {
|
|
1935
2109
|
return typeof data === "object" && data !== null && !Array.isArray(data);
|
|
1936
2110
|
}
|
|
1937
|
-
var allowsEval = /* @__PURE__ */
|
|
2111
|
+
var allowsEval = /* @__PURE__ */ cached2(() => {
|
|
1938
2112
|
if (globalConfig.jitless) {
|
|
1939
2113
|
return false;
|
|
1940
2114
|
}
|
|
@@ -4628,7 +4802,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
4628
4802
|
}
|
|
4629
4803
|
});
|
|
4630
4804
|
}
|
|
4631
|
-
const _normalized =
|
|
4805
|
+
const _normalized = cached2(() => normalizeDef(def));
|
|
4632
4806
|
defineLazyInternal(inst, "propValues", (zod) => {
|
|
4633
4807
|
const shape = zod.def.shape;
|
|
4634
4808
|
const propValues = {};
|
|
@@ -4688,7 +4862,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
|
|
|
4688
4862
|
var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) => {
|
|
4689
4863
|
$ZodObject.init(inst, def);
|
|
4690
4864
|
const superParse = inst._zod.parse;
|
|
4691
|
-
const _normalized =
|
|
4865
|
+
const _normalized = cached2(() => normalizeDef(def));
|
|
4692
4866
|
const memo2 = globalConfig.memoizer;
|
|
4693
4867
|
const generateFastpass = (shape) => {
|
|
4694
4868
|
const normalized = _normalized.value;
|
|
@@ -4967,7 +5141,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
|
|
|
4967
5141
|
throw new Error(`Invalid discriminated union option at index "${i}"`);
|
|
4968
5142
|
}
|
|
4969
5143
|
});
|
|
4970
|
-
const disc =
|
|
5144
|
+
const disc = cached2(() => {
|
|
4971
5145
|
const opts = def.options;
|
|
4972
5146
|
const map2 = /* @__PURE__ */ new Map();
|
|
4973
5147
|
for (const o of opts) {
|
|
@@ -6050,9 +6224,9 @@ function cloneIssues(issues) {
|
|
|
6050
6224
|
}
|
|
6051
6225
|
var recursive = /* @__PURE__ */ new WeakMap();
|
|
6052
6226
|
function isRecursive(inst, stack) {
|
|
6053
|
-
const
|
|
6054
|
-
if (
|
|
6055
|
-
return
|
|
6227
|
+
const cached3 = recursive.get(inst);
|
|
6228
|
+
if (cached3 !== void 0)
|
|
6229
|
+
return cached3;
|
|
6056
6230
|
if (stack.has(inst))
|
|
6057
6231
|
return true;
|
|
6058
6232
|
stack.add(inst);
|
|
@@ -19808,15 +19982,15 @@ function visit(schema, fnOrHandlers) {
|
|
|
19808
19982
|
};
|
|
19809
19983
|
const cache = /* @__PURE__ */ new Map();
|
|
19810
19984
|
function run(s) {
|
|
19811
|
-
const
|
|
19812
|
-
if (
|
|
19985
|
+
const cached3 = cache.get(s);
|
|
19986
|
+
if (cached3 === RESOLVING) {
|
|
19813
19987
|
return new $ZodLazy({
|
|
19814
19988
|
type: "lazy",
|
|
19815
19989
|
getter: () => cache.get(s)
|
|
19816
19990
|
});
|
|
19817
19991
|
}
|
|
19818
|
-
if (
|
|
19819
|
-
return
|
|
19992
|
+
if (cached3 !== void 0)
|
|
19993
|
+
return cached3;
|
|
19820
19994
|
cache.set(s, RESOLVING);
|
|
19821
19995
|
const inner = mapInner(s);
|
|
19822
19996
|
const mapped = fn(inner, inner !== s);
|
|
@@ -20065,7 +20239,10 @@ var pluginOptionsSchema = external_exports.object({
|
|
|
20065
20239
|
runRoot: external_exports.string().min(1).default("/tmp"),
|
|
20066
20240
|
opencodeBin: external_exports.string().min(1).prefault(discoverOpencodeBin),
|
|
20067
20241
|
configSource: external_exports.string().prefault(() => join6(homedir2(), ".config", "opencode", "opencode.jsonc")),
|
|
20068
|
-
roleTimeoutMs: external_exports.number().int().min(5e3).default(6e5)
|
|
20242
|
+
roleTimeoutMs: external_exports.number().int().min(5e3).default(6e5),
|
|
20243
|
+
/** Exam venue: absolute dirs tmpfs-masked from the candidate (grader files,
|
|
20244
|
+
* other runs). Empty = venue runs unsandboxed and SAYS SO on the voice face. */
|
|
20245
|
+
examMaskDirs: external_exports.array(external_exports.string().min(2)).default([])
|
|
20069
20246
|
}).prefault({}),
|
|
20070
20247
|
/** v1.1 E4/F1: seats only from allowed model-id prefixes; deny at launch. */
|
|
20071
20248
|
modelPolicy: external_exports.object({
|
|
@@ -20192,9 +20369,19 @@ async function resolveRun(flags) {
|
|
|
20192
20369
|
judgePoolIds.push(...flags["judge-pool"].split(",").map((s) => s.trim()).filter((s) => s.length > 0));
|
|
20193
20370
|
}
|
|
20194
20371
|
const now = /* @__PURE__ */ new Date();
|
|
20195
|
-
const
|
|
20372
|
+
const flagRunId = typeof flags["run-id"] === "string" ? flags["run-id"] : null;
|
|
20373
|
+
if (flagRunId !== null && !/^sibyl-[A-Za-z0-9-]+$/.test(flagRunId)) {
|
|
20374
|
+
return { ok: false, error: `--run-id must be a bare sibyl run id, got "${flagRunId}"` };
|
|
20375
|
+
}
|
|
20376
|
+
const runId = flagRunId ?? `sibyl-${compactIso(now)}-${randomBytes(2).toString("hex")}`;
|
|
20196
20377
|
const runDir = join7(laneCfg.runRoot, `sibyl-run-${runId}`);
|
|
20197
20378
|
await mkdir6(runDir, { recursive: true });
|
|
20379
|
+
let launchIndependence;
|
|
20380
|
+
try {
|
|
20381
|
+
const lc = JSON.parse(await readFile6(join7(runDir, "launch-context.json"), "utf8"));
|
|
20382
|
+
if (isIndependenceShape(lc["independence"])) launchIndependence = lc["independence"];
|
|
20383
|
+
} catch {
|
|
20384
|
+
}
|
|
20198
20385
|
let targetPath = targetRaw;
|
|
20199
20386
|
if (targetRaw !== "-") {
|
|
20200
20387
|
const abs = isAbsolute2(targetRaw) ? targetRaw : resolve2(process.cwd(), targetRaw);
|
|
@@ -20227,7 +20414,7 @@ async function resolveRun(flags) {
|
|
|
20227
20414
|
if (scenarios.length === 0) return { ok: false, error: "--profile exam needs at least one --scenario" };
|
|
20228
20415
|
const only = splitModelId(modelFlag ?? "") ?? null;
|
|
20229
20416
|
if (only === null) return { ok: false, error: "--profile exam requires --model provider/model" };
|
|
20230
|
-
return { ok: true, run: { runId, runDir, laneCfg, timeoutMs, chamber: null, exam: { scenarios, modelId: modelFlag }, killInjection } };
|
|
20417
|
+
return { ok: true, run: { runId, runDir, laneCfg, timeoutMs, chamber: null, exam: { scenarios, modelId: modelFlag }, killInjection, examMaskDirs: opts?.lane.examMaskDirs ?? [] } };
|
|
20231
20418
|
}
|
|
20232
20419
|
if (profile !== "review") return { ok: false, error: `unknown --profile ${profile}` };
|
|
20233
20420
|
const slots = {
|
|
@@ -20252,13 +20439,15 @@ async function resolveRun(flags) {
|
|
|
20252
20439
|
maxRounds,
|
|
20253
20440
|
pool: effectivePool,
|
|
20254
20441
|
policy,
|
|
20442
|
+
instrument: instrumentFace(),
|
|
20443
|
+
independence: launchIndependence,
|
|
20255
20444
|
slots,
|
|
20256
20445
|
judgePoolIds: judgePoolIds.length > 0 ? judgePoolIds : [...new Set(judgeSlotNames.map((s) => slotToId(s, effectivePool)).filter((x) => x !== null))],
|
|
20257
20446
|
lane: null
|
|
20258
20447
|
// wired below (type seam: lane needs cfg, cfg needs lane)
|
|
20259
20448
|
};
|
|
20260
20449
|
chamber.lane = makeIsolatedLane(chamber, laneCfg, timeoutMs);
|
|
20261
|
-
return { ok: true, run: { runId, runDir, laneCfg, timeoutMs, chamber, exam: null, killInjection } };
|
|
20450
|
+
return { ok: true, run: { runId, runDir, laneCfg, timeoutMs, chamber, exam: null, killInjection, examMaskDirs: opts?.lane.examMaskDirs ?? [] } };
|
|
20262
20451
|
}
|
|
20263
20452
|
function slotToId(slot, pool) {
|
|
20264
20453
|
const m = pool[slot] ?? pool["default"];
|
|
@@ -20282,8 +20471,8 @@ function makeIsolatedLane(cfg, laneCfg, timeoutMs) {
|
|
|
20282
20471
|
}
|
|
20283
20472
|
};
|
|
20284
20473
|
}
|
|
20285
|
-
function makeExamDriver(laneCfgBase, runDir, modelId, timeoutMs) {
|
|
20286
|
-
const lc = { runDir, opencodeBin: laneCfgBase.opencodeBin, configSource: laneCfgBase.configSource };
|
|
20474
|
+
function makeExamDriver(laneCfgBase, runDir, modelId, timeoutMs, maskDirs, bwrapProbe) {
|
|
20475
|
+
const lc = { runDir, opencodeBin: laneCfgBase.opencodeBin, configSource: laneCfgBase.configSource, sandboxMaskDirs: maskDirs, bwrapBin: bwrapProbe };
|
|
20287
20476
|
let turnNo = 0;
|
|
20288
20477
|
const run = async (message, resume) => {
|
|
20289
20478
|
turnNo += 1;
|
|
@@ -20300,11 +20489,14 @@ function makeExamDriver(laneCfgBase, runDir, modelId, timeoutMs) {
|
|
|
20300
20489
|
return { facts: { ok: o.ok, rc: o.rc, signal: o.signal, timedOut: o.timedOut }, transcript, sessionId: o.sessionId };
|
|
20301
20490
|
};
|
|
20302
20491
|
return {
|
|
20303
|
-
|
|
20304
|
-
|
|
20492
|
+
sandboxed: maskDirs.length > 0 && existsSync3(bwrapProbe),
|
|
20493
|
+
driver: {
|
|
20494
|
+
start: async (prompt) => await run(prompt),
|
|
20495
|
+
continueTurn: async (sid, message) => await run(message, sid)
|
|
20496
|
+
}
|
|
20305
20497
|
};
|
|
20306
20498
|
}
|
|
20307
|
-
function examVoice(results, runId, runDir) {
|
|
20499
|
+
function examVoice(results, runId, runDir, venueSandbox) {
|
|
20308
20500
|
const lines = ["SIBYL \u2014 ONE CONCLUSION, ONE VOICE [SIBYL-ONE-VOICE]"];
|
|
20309
20501
|
let anyFail = false;
|
|
20310
20502
|
let anyHuman = false;
|
|
@@ -20318,6 +20510,7 @@ function examVoice(results, runId, runDir) {
|
|
|
20318
20510
|
const conclusion = anyFail ? "REJECT" : anyHuman ? "NEEDS_HUMAN" : "APPROVE";
|
|
20319
20511
|
lines.splice(1, 0, `conclusion: ${conclusion} terminal: CONVERGED run: ${runId}`);
|
|
20320
20512
|
lines.push(`honesty: PERFORMANCE-ONLY-IN-LOOP (single-uid box until C-09/C-14 rulings)`);
|
|
20513
|
+
lines.push(`venue sandbox: ${venueSandbox}`);
|
|
20321
20514
|
lines.push(`verify every recorded byte: ${spotcheckCommand(runDir)}`);
|
|
20322
20515
|
return lines.join("\n");
|
|
20323
20516
|
}
|
|
@@ -20381,7 +20574,7 @@ async function main(argv) {
|
|
|
20381
20574
|
return 0;
|
|
20382
20575
|
}
|
|
20383
20576
|
if (run.exam !== null) {
|
|
20384
|
-
const driver = makeExamDriver(run.laneCfg, run.runDir, run.exam.modelId, run.timeoutMs);
|
|
20577
|
+
const { driver, sandboxed } = makeExamDriver(run.laneCfg, run.runDir, run.exam.modelId, run.timeoutMs, run.examMaskDirs, "/usr/bin/bwrap");
|
|
20385
20578
|
const results = [];
|
|
20386
20579
|
const scPaths = typeof flags["scenario"] === "string" ? [flags["scenario"]] : [];
|
|
20387
20580
|
if (flags["allow-unbellied"] !== true) {
|
|
@@ -20395,7 +20588,8 @@ async function main(argv) {
|
|
|
20395
20588
|
}
|
|
20396
20589
|
}
|
|
20397
20590
|
for (const sc of run.exam.scenarios) results.push(await runExamScenario(sc, run.runDir, driver));
|
|
20398
|
-
|
|
20591
|
+
await writeChecksums(run.runDir, await listChecksumTargets(run.runDir));
|
|
20592
|
+
console.log(examVoice(results, run.runId, run.runDir, sandboxed ? `ON (masked ${String(run.examMaskDirs.length)} dir(s))` : run.examMaskDirs.length === 0 ? "OFF (lane.examMaskDirs empty \u2014 grader files are candidate-readable)" : "OFF (bwrap missing at /usr/bin/bwrap)"));
|
|
20399
20593
|
return 0;
|
|
20400
20594
|
}
|
|
20401
20595
|
return 2;
|