sibyl-system 1.2.1 → 1.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 (4) hide show
  1. package/README.md +35 -1
  2. package/dist/cli.js +356 -187
  3. package/dist/index.js +571 -174
  4. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -12,6 +12,35 @@ import { copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6 } from "n
12
12
  import { basename, dirname as dirname4, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "node:path";
13
13
 
14
14
  // src/state/record.ts
15
+ var INDEPENDENCE_STATUSES = ["INDEPENDENT", "NOT-INDEPENDENT", "UNVERIFIABLE"];
16
+ function isIndependenceShape(v) {
17
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
18
+ const o = v;
19
+ if (!INDEPENDENCE_STATUSES.includes(o["status"])) return false;
20
+ if (!Array.isArray(o["convenerChain"]) || !o["convenerChain"].every((s) => typeof s === "string" && s.length > 0)) return false;
21
+ return typeof o["evidence"] === "string";
22
+ }
23
+ function rebuildIndependence(v) {
24
+ return { status: v.status, convenerChain: [...v.convenerChain], evidence: v.evidence };
25
+ }
26
+ var INSTRUMENT_NAME = /^[a-z0-9-]{1,40}$/;
27
+ var HEX64 = /^[0-9a-f]{64}$/;
28
+ function isInstrumentShape(v) {
29
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
30
+ const o = v;
31
+ if (typeof o["rulesHash"] !== "string" || !HEX64.test(o["rulesHash"])) return false;
32
+ const comps = o["components"];
33
+ if (typeof comps !== "object" || comps === null || Array.isArray(comps)) return false;
34
+ return Object.entries(comps).every(
35
+ ([name, hash2]) => INSTRUMENT_NAME.test(name) && typeof hash2 === "string" && HEX64.test(hash2)
36
+ );
37
+ }
38
+ function rebuildInstrument(v) {
39
+ return {
40
+ rulesHash: v.rulesHash,
41
+ components: Object.fromEntries(Object.entries(v.components).map(([name, hash2]) => [name, hash2]))
42
+ };
43
+ }
15
44
  function isNonEmptyString(v) {
16
45
  return typeof v === "string" && v.length > 0;
17
46
  }
@@ -19,8 +48,142 @@ function compactIso(now) {
19
48
  return now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
20
49
  }
21
50
 
22
- // src/state/chamber.ts
51
+ // src/instrument.ts
52
+ import { createHash as createHash3 } from "node:crypto";
53
+
54
+ // src/council/index.ts
55
+ var COUNCILORS = ["MELCHIOR", "BALTHASAR", "CASPER"];
56
+ 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).';
57
+ var COUNCILOR_PERSONAS = {
58
+ MELCHIOR: {
59
+ title: "correctness & safety skeptic",
60
+ 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
61
+ },
62
+ BALTHASAR: {
63
+ title: "adversarial red-teamer",
64
+ 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
65
+ },
66
+ CASPER: {
67
+ title: "pragmatic utilist",
68
+ 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
69
+ }
70
+ };
71
+ var COUNCIL_SIZE = COUNCILORS.length;
72
+ var KNOWN_IDS = new Set(COUNCILORS);
73
+
74
+ // src/chamber/protocol.ts
75
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
76
+ import { join as join2 } from "node:path";
77
+
78
+ // src/lane/seating.ts
23
79
  import { createHash } from "node:crypto";
80
+ function toModelId(model) {
81
+ return `${model.providerID}/${model.modelID}`;
82
+ }
83
+ function policyAllows(modelId, policy) {
84
+ return policy.allowedPrefixes.some((prefix) => prefix.length > 0 && modelId.startsWith(prefix));
85
+ }
86
+ function resolveSeat(role, requestedSlot, pool, policy) {
87
+ const found = pool[requestedSlot] ?? pool["default"];
88
+ if (found === void 0) {
89
+ return {
90
+ ok: false,
91
+ role,
92
+ deny: `seat denied: slot "${requestedSlot}" (and fallback "default") absent from pool`
93
+ };
94
+ }
95
+ const modelId = toModelId(found);
96
+ if (found.providerID.length === 0 || found.modelID.length === 0) {
97
+ return { ok: false, role, deny: `seat denied: slot "${requestedSlot}" is an empty placeholder (${modelId})` };
98
+ }
99
+ if (!policyAllows(modelId, policy)) {
100
+ return {
101
+ ok: false,
102
+ role,
103
+ deny: `seat denied by modelPolicy (E4/F1): "${modelId}" is not under any allowed prefix [${policy.allowedPrefixes.join(", ")}] \u2014 launch must not be attempted`
104
+ };
105
+ }
106
+ return { ok: true, role, slot: requestedSlot, modelId };
107
+ }
108
+ var SEP = "";
109
+ function sha256Hex(text) {
110
+ return createHash("sha256").update(text, "utf8").digest("hex");
111
+ }
112
+ function computeDrawCommit(poolIds, seed) {
113
+ return sha256Hex(`sibyl-draw-v1${SEP}${poolIds.join(SEP)}${SEP}${seed}`);
114
+ }
115
+ function drawJudge(poolIds, seed, salt) {
116
+ if (poolIds.length === 0) {
117
+ return { ok: false, deny: "judge draw denied: empty judge pool" };
118
+ }
119
+ const drawCommit = computeDrawCommit(poolIds, seed);
120
+ const digest = sha256Hex(`${drawCommit}${SEP}${salt}`);
121
+ const uint = Number.parseInt(digest.slice(0, 8), 16);
122
+ const index = uint % poolIds.length;
123
+ const modelId = poolIds[index];
124
+ return { ok: true, draw: { index, modelId, drawCommit } };
125
+ }
126
+
127
+ // src/verdict/index.ts
128
+ var OPEN_BRACE = 123;
129
+ var CLOSE_BRACE = 125;
130
+ var QUOTE = 34;
131
+ var BACKSLASH = 92;
132
+ var FENCE_RE = /```(?:json)?[ \t]*\r?\n?([\s\S]*?)```/gi;
133
+ function findBalancedObject(text, start) {
134
+ if (text.charCodeAt(start) !== OPEN_BRACE) return null;
135
+ let depth = 0;
136
+ let inString = false;
137
+ let escaped = false;
138
+ for (let i = start; i < text.length; i++) {
139
+ const c = text.charCodeAt(i);
140
+ if (inString) {
141
+ if (escaped) escaped = false;
142
+ else if (c === BACKSLASH) escaped = true;
143
+ else if (c === QUOTE) inString = false;
144
+ continue;
145
+ }
146
+ if (c === QUOTE) inString = true;
147
+ else if (c === OPEN_BRACE) depth++;
148
+ else if (c === CLOSE_BRACE) {
149
+ depth--;
150
+ if (depth === 0) return text.slice(start, i + 1);
151
+ }
152
+ }
153
+ return null;
154
+ }
155
+ function firstParseableObject(text) {
156
+ for (let i = 0; i < text.length; i++) {
157
+ if (text.charCodeAt(i) !== OPEN_BRACE) continue;
158
+ const candidate = findBalancedObject(text, i);
159
+ if (candidate === null) continue;
160
+ try {
161
+ JSON.parse(candidate);
162
+ return candidate;
163
+ } catch {
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ function extractVerdictJson(text) {
169
+ for (const m of text.matchAll(FENCE_RE)) {
170
+ const fenced = firstParseableObject(m[1] ?? "");
171
+ if (fenced !== null) return fenced;
172
+ }
173
+ return firstParseableObject(text);
174
+ }
175
+ function rejectVerdict(reason) {
176
+ return {
177
+ verdict: "REJECT",
178
+ confidence: 0,
179
+ reasons: [`verdict-unparseable: ${reason}`],
180
+ must_fix: []
181
+ };
182
+ }
183
+ var FAIL_CLOSED_REJECT = rejectVerdict("no-parse-performed");
184
+
185
+ // src/state/chamber.ts
186
+ import { createHash as createHash2 } from "node:crypto";
24
187
  import { mkdir, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
25
188
  import { homedir } from "node:os";
26
189
  import { dirname, isAbsolute, join, resolve } from "node:path";
@@ -29,7 +192,7 @@ var TERMINAL_STATES = ["CONVERGED", "NEEDS_ROUND", "MEMBER_LOST", "TIMEOUT"];
29
192
  var RUN_PROFILES = ["review", "exam"];
30
193
  var RECORD_SCHEMA = 1;
31
194
  function sha256Text(text) {
32
- return createHash("sha256").update(text, "utf8").digest("hex");
195
+ return createHash2("sha256").update(text, "utf8").digest("hex");
33
196
  }
34
197
  async function sha256File(path) {
35
198
  let buf;
@@ -39,7 +202,7 @@ async function sha256File(path) {
39
202
  const code = typeof err === "object" && err !== null ? String(Reflect.get(err, "code")) : "unknown";
40
203
  return { ok: false, reason: `read failed (${code})` };
41
204
  }
42
- return { ok: true, sha256: createHash("sha256").update(buf).digest("hex"), bytes: buf.byteLength };
205
+ return { ok: true, sha256: createHash2("sha256").update(buf).digest("hex"), bytes: buf.byteLength };
43
206
  }
44
207
  async function assertOnDisk(path, claim2) {
45
208
  const now = await sha256File(path);
@@ -85,6 +248,12 @@ function validateChamberRecord(raw) {
85
248
  if (!Array.isArray(o["artifacts"]) || !o["artifacts"].every(isArtifactRef)) return { ok: false, reason: "artifacts must be an array of ArtifactRef" };
86
249
  if (typeof o["evidenceRows"] !== "number" || !Number.isInteger(o["evidenceRows"]) || o["evidenceRows"] < 0) return { ok: false, reason: "evidenceRows must be a non-negative integer" };
87
250
  if (o["notes"] !== void 0 && typeof o["notes"] !== "string") return { ok: false, reason: "notes must be a string when present" };
251
+ if (o["instrument"] !== void 0 && !isInstrumentShape(o["instrument"])) {
252
+ return { ok: false, reason: "instrument must be {rulesHash: 64-hex, components: name->64-hex} when present" };
253
+ }
254
+ if (o["independence"] !== void 0 && !isIndependenceShape(o["independence"])) {
255
+ return { ok: false, reason: "independence must be {status: INDEPENDENT|NOT-INDEPENDENT|UNVERIFIABLE, convenerChain: string[], evidence: string} when present" };
256
+ }
88
257
  const record2 = {
89
258
  schema: RECORD_SCHEMA,
90
259
  runId: o["runId"],
@@ -106,6 +275,8 @@ function validateChamberRecord(raw) {
106
275
  evidenceRows: o["evidenceRows"]
107
276
  };
108
277
  if (typeof o["notes"] === "string") record2.notes = o["notes"];
278
+ if (o["instrument"] !== void 0) record2.instrument = rebuildInstrument(o["instrument"]);
279
+ if (o["independence"] !== void 0) record2.independence = rebuildIndependence(o["independence"]);
109
280
  return { ok: true, record: record2 };
110
281
  }
111
282
  async function appendLedgerRow(ledgerPath, record2) {
@@ -245,176 +416,6 @@ function spotcheckCommand(runDir) {
245
416
  return `cd ${runDir} && sha256sum -c ${CHECKSUMS_FILE}`;
246
417
  }
247
418
 
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
419
  // src/chamber/protocol.ts
419
420
  var CHAMBER_ROLES = ["evidence", "pro", "con", "judge"];
420
421
  function artifactRel(role, round, kind) {
@@ -679,11 +680,167 @@ async function startRecord(cfg, roster, drawCommit) {
679
680
  artifacts: [],
680
681
  evidenceRows: 0
681
682
  };
683
+ if (cfg.instrument !== void 0) base.instrument = cfg.instrument;
684
+ if (cfg.independence !== void 0) base.independence = cfg.independence;
682
685
  const first = await finalizeRecord(base);
683
686
  if (!first.ok) throw new Error(`START record refused: ${first.drift.join("; ")}`);
684
687
  return first.record;
685
688
  }
686
689
 
690
+ // src/personas.ts
691
+ 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).';
692
+ 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;
693
+ var ARCHITECT_PERSONA = {
694
+ id: "ARCHITECT",
695
+ title: "workflow architect (planner)",
696
+ system: ARCHITECT_SYSTEM,
697
+ modelSlot: "architect"
698
+ };
699
+ function repairDemand(why) {
700
+ 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>]}`;
701
+ }
702
+ var SWARM_JUDGE_SYSTEM = "You are the SIBYL swarm judge. You rule on pipeline reports with exactly one word: APPROVE, REJECT, or EXHAUSTED.";
703
+ var SWARM_JUDGE_WORD_CONTRACT = "Respond with EXACTLY one word and nothing else: APPROVE, REJECT, or EXHAUSTED.";
704
+ var PERSONAS = {
705
+ MELCHIOR: {
706
+ id: "MELCHIOR",
707
+ title: COUNCILOR_PERSONAS.MELCHIOR.title,
708
+ system: COUNCILOR_PERSONAS.MELCHIOR.system,
709
+ modelSlot: "melchior"
710
+ },
711
+ BALTHASAR: {
712
+ id: "BALTHASAR",
713
+ title: COUNCILOR_PERSONAS.BALTHASAR.title,
714
+ system: COUNCILOR_PERSONAS.BALTHASAR.system,
715
+ modelSlot: "balthasar"
716
+ },
717
+ CASPER: {
718
+ id: "CASPER",
719
+ title: COUNCILOR_PERSONAS.CASPER.title,
720
+ system: COUNCILOR_PERSONAS.CASPER.system,
721
+ modelSlot: "casper"
722
+ },
723
+ ARCHITECT: ARCHITECT_PERSONA
724
+ };
725
+ var PERSONA_IDS = [...COUNCILORS, "ARCHITECT"];
726
+ var BY_ID = new Map(
727
+ PERSONA_IDS.map((id) => [id, PERSONAS[id]])
728
+ );
729
+
730
+ // src/instrument.ts
731
+ function sha256Hex2(text) {
732
+ return createHash3("sha256").update(text, "utf8").digest("hex");
733
+ }
734
+ var REPAIR_SENTINEL = "<parse-failure-why>";
735
+ var SENTINEL_CFG = {
736
+ runId: "sibyl-sentinel",
737
+ runDir: "/sentinel/run",
738
+ goal: "<goal>",
739
+ targetPath: "/sentinel/target.md",
740
+ ledgerPath: "/sentinel/ledger.jsonl",
741
+ seed: "<seed>",
742
+ maxRounds: 1,
743
+ pool: { default: { providerID: "", modelID: "" } },
744
+ policy: null,
745
+ slots: { evidence: "default", pro: "default", con: "default", judge: "default" },
746
+ judgePoolIds: [],
747
+ lane: null
748
+ };
749
+ function chamberRoleTexts() {
750
+ return {
751
+ "chamber-evidence-prompt": evidencePrompt(SENTINEL_CFG, "/sentinel/evidence.jsonl"),
752
+ "chamber-pro-prompt": proPrompt(SENTINEL_CFG, 1, "/sentinel/pro.md", []),
753
+ "chamber-con-prompt": conPrompt(SENTINEL_CFG, 1, "/sentinel/con.md", []),
754
+ "chamber-rebuttal-prompt": rebuttalPrompt(SENTINEL_CFG, "pro", 1, "/sentinel/con.md", "/sentinel/pro-rebuttal.md"),
755
+ "chamber-judge-prompt": judgePrompt(SENTINEL_CFG, 1, ["/sentinel/pro.md", "/sentinel/con.md"], "/sentinel/judge.json")
756
+ };
757
+ }
758
+ function componentTexts() {
759
+ const texts = {
760
+ "repair-grammar": repairDemand(REPAIR_SENTINEL),
761
+ "swarm-judge-system": SWARM_JUDGE_SYSTEM,
762
+ "swarm-judge-word-contract": SWARM_JUDGE_WORD_CONTRACT,
763
+ ...chamberRoleTexts()
764
+ };
765
+ for (const id of COUNCILORS) {
766
+ texts[`councilor-${id.toLowerCase()}`] = COUNCILOR_PERSONAS[id].system;
767
+ }
768
+ texts["architect-system"] = ARCHITECT_SYSTEM;
769
+ return texts;
770
+ }
771
+ var cached = null;
772
+ function instrumentFace() {
773
+ if (cached !== null) return cached;
774
+ const components = {};
775
+ for (const [name, text] of Object.entries(componentTexts())) {
776
+ components[name] = sha256Hex2(text);
777
+ }
778
+ const canonical = JSON.stringify(
779
+ Object.keys(components).sort().map((name) => [name, components[name]])
780
+ );
781
+ cached = { rulesHash: sha256Hex2(canonical), components };
782
+ return cached;
783
+ }
784
+
785
+ // src/chamber/synthesis.ts
786
+ var PERFORMANCE_ONLY_IN_LOOP = "PERFORMANCE-ONLY-IN-LOOP";
787
+ var MAX_RATIONALE = 6;
788
+ function conclude(result) {
789
+ const { terminal, judge, gaps } = result;
790
+ if (gaps.length > 0 && (terminal === "MEMBER_LOST" || terminal === "TIMEOUT")) {
791
+ return { conclusion: "NEEDS_HUMAN", confidence: 0 };
792
+ }
793
+ if (terminal === "CONVERGED" && judge !== null) {
794
+ const base = judge.conclusion;
795
+ const conclusion = base === "NEEDS_HUMAN" ? "NEEDS_HUMAN" : gaps.length > 0 ? "NEEDS_HUMAN" : base;
796
+ return { conclusion, confidence: conclusion === "NEEDS_HUMAN" ? Math.min(judge.confidence, 0.5) : judge.confidence };
797
+ }
798
+ if (judge !== null && judge.convergence === "NEEDS_ROUND") {
799
+ return { conclusion: "NEEDS_HUMAN", confidence: Math.min(judge.confidence, 0.5) };
800
+ }
801
+ return { conclusion: "NEEDS_HUMAN", confidence: 0 };
802
+ }
803
+ function buildVoice(result) {
804
+ const { record: record2, judge, terminal, gaps } = result;
805
+ const { conclusion, confidence } = conclude(result);
806
+ 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);
807
+ const sealed = [
808
+ "DISAGREEMENT-LOG.md",
809
+ ...new Set(record2.artifacts.map((a) => a.relPath))
810
+ ];
811
+ return {
812
+ label: "SIBYL-ONE-VOICE",
813
+ honesty: PERFORMANCE_ONLY_IN_LOOP,
814
+ conclusion,
815
+ confidence: conclusion === "NEEDS_HUMAN" ? Number(confidence.toFixed(2)) : confidence,
816
+ rationale,
817
+ must_fix: judge?.must_fix ?? [],
818
+ open_charges: judge?.charges.length ?? 0,
819
+ dissent_sealed_in: sealed,
820
+ terminal,
821
+ run_id: record2.runId,
822
+ serial: record2.serial,
823
+ spotcheck: spotcheckCommand(record2.runDir)
824
+ };
825
+ }
826
+ function renderVoice(voice) {
827
+ const lines = [
828
+ `SIBYL \u2014 ONE CONCLUSION, ONE VOICE [${voice.label}]`,
829
+ `conclusion: ${voice.conclusion} confidence: ${String(voice.confidence)} terminal: ${voice.terminal}`,
830
+ `run: ${voice.run_id} (serial ${String(voice.serial)}) open charges: ${String(voice.open_charges)}`,
831
+ `honesty: ${voice.honesty} (until C-09/C-14 containment rulings \u2014 see docs/isolation-laws.md)`,
832
+ "rationale:",
833
+ ...voice.rationale.map((r, i) => ` ${String(i + 1)}. ${r}`)
834
+ ];
835
+ if (voice.must_fix.length > 0) {
836
+ lines.push("must_fix:");
837
+ lines.push(...voice.must_fix.map((m) => ` - ${m}`));
838
+ }
839
+ lines.push(`internal debate sealed in run dir: ${voice.dissent_sealed_in.length} artifacts + DISAGREEMENT-LOG.md (hashes in CHECKSUMS.txt)`);
840
+ lines.push(`verify every recorded byte: ${voice.spotcheck}`);
841
+ return lines.join("\n");
842
+ }
843
+
687
844
  // src/lane/isolated.ts
688
845
  import { spawn } from "node:child_process";
689
846
  import { createWriteStream, existsSync } from "node:fs";
@@ -1737,7 +1894,7 @@ __export(util_exports, {
1737
1894
  attachSchema: () => attachSchema,
1738
1895
  base64ToUint8Array: () => base64ToUint8Array,
1739
1896
  base64urlToUint8Array: () => base64urlToUint8Array,
1740
- cached: () => cached,
1897
+ cached: () => cached2,
1741
1898
  captureStackTrace: () => captureStackTrace,
1742
1899
  cleanEnum: () => cleanEnum,
1743
1900
  cleanRegex: () => cleanRegex,
@@ -1825,7 +1982,7 @@ function jsonStringifyReplacer(_, value) {
1825
1982
  return value.toString();
1826
1983
  return value;
1827
1984
  }
1828
- function cached(getter) {
1985
+ function cached2(getter) {
1829
1986
  const set2 = false;
1830
1987
  return {
1831
1988
  get value() {
@@ -1934,7 +2091,7 @@ var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace :
1934
2091
  function isObject(data) {
1935
2092
  return typeof data === "object" && data !== null && !Array.isArray(data);
1936
2093
  }
1937
- var allowsEval = /* @__PURE__ */ cached(() => {
2094
+ var allowsEval = /* @__PURE__ */ cached2(() => {
1938
2095
  if (globalConfig.jitless) {
1939
2096
  return false;
1940
2097
  }
@@ -4628,7 +4785,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
4628
4785
  }
4629
4786
  });
4630
4787
  }
4631
- const _normalized = cached(() => normalizeDef(def));
4788
+ const _normalized = cached2(() => normalizeDef(def));
4632
4789
  defineLazyInternal(inst, "propValues", (zod) => {
4633
4790
  const shape = zod.def.shape;
4634
4791
  const propValues = {};
@@ -4688,7 +4845,7 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
4688
4845
  var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) => {
4689
4846
  $ZodObject.init(inst, def);
4690
4847
  const superParse = inst._zod.parse;
4691
- const _normalized = cached(() => normalizeDef(def));
4848
+ const _normalized = cached2(() => normalizeDef(def));
4692
4849
  const memo2 = globalConfig.memoizer;
4693
4850
  const generateFastpass = (shape) => {
4694
4851
  const normalized = _normalized.value;
@@ -4967,7 +5124,7 @@ var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnio
4967
5124
  throw new Error(`Invalid discriminated union option at index "${i}"`);
4968
5125
  }
4969
5126
  });
4970
- const disc = cached(() => {
5127
+ const disc = cached2(() => {
4971
5128
  const opts = def.options;
4972
5129
  const map2 = /* @__PURE__ */ new Map();
4973
5130
  for (const o of opts) {
@@ -6050,9 +6207,9 @@ function cloneIssues(issues) {
6050
6207
  }
6051
6208
  var recursive = /* @__PURE__ */ new WeakMap();
6052
6209
  function isRecursive(inst, stack) {
6053
- const cached2 = recursive.get(inst);
6054
- if (cached2 !== void 0)
6055
- return cached2;
6210
+ const cached3 = recursive.get(inst);
6211
+ if (cached3 !== void 0)
6212
+ return cached3;
6056
6213
  if (stack.has(inst))
6057
6214
  return true;
6058
6215
  stack.add(inst);
@@ -19808,15 +19965,15 @@ function visit(schema, fnOrHandlers) {
19808
19965
  };
19809
19966
  const cache = /* @__PURE__ */ new Map();
19810
19967
  function run(s) {
19811
- const cached2 = cache.get(s);
19812
- if (cached2 === RESOLVING) {
19968
+ const cached3 = cache.get(s);
19969
+ if (cached3 === RESOLVING) {
19813
19970
  return new $ZodLazy({
19814
19971
  type: "lazy",
19815
19972
  getter: () => cache.get(s)
19816
19973
  });
19817
19974
  }
19818
- if (cached2 !== void 0)
19819
- return cached2;
19975
+ if (cached3 !== void 0)
19976
+ return cached3;
19820
19977
  cache.set(s, RESOLVING);
19821
19978
  const inner = mapInner(s);
19822
19979
  const mapped = fn(inner, inner !== s);
@@ -20192,9 +20349,19 @@ async function resolveRun(flags) {
20192
20349
  judgePoolIds.push(...flags["judge-pool"].split(",").map((s) => s.trim()).filter((s) => s.length > 0));
20193
20350
  }
20194
20351
  const now = /* @__PURE__ */ new Date();
20195
- const runId = `sibyl-${compactIso(now)}-${randomBytes(2).toString("hex")}`;
20352
+ const flagRunId = typeof flags["run-id"] === "string" ? flags["run-id"] : null;
20353
+ if (flagRunId !== null && !/^sibyl-[A-Za-z0-9-]+$/.test(flagRunId)) {
20354
+ return { ok: false, error: `--run-id must be a bare sibyl run id, got "${flagRunId}"` };
20355
+ }
20356
+ const runId = flagRunId ?? `sibyl-${compactIso(now)}-${randomBytes(2).toString("hex")}`;
20196
20357
  const runDir = join7(laneCfg.runRoot, `sibyl-run-${runId}`);
20197
20358
  await mkdir6(runDir, { recursive: true });
20359
+ let launchIndependence;
20360
+ try {
20361
+ const lc = JSON.parse(await readFile6(join7(runDir, "launch-context.json"), "utf8"));
20362
+ if (isIndependenceShape(lc["independence"])) launchIndependence = lc["independence"];
20363
+ } catch {
20364
+ }
20198
20365
  let targetPath = targetRaw;
20199
20366
  if (targetRaw !== "-") {
20200
20367
  const abs = isAbsolute2(targetRaw) ? targetRaw : resolve2(process.cwd(), targetRaw);
@@ -20252,6 +20419,8 @@ async function resolveRun(flags) {
20252
20419
  maxRounds,
20253
20420
  pool: effectivePool,
20254
20421
  policy,
20422
+ instrument: instrumentFace(),
20423
+ independence: launchIndependence,
20255
20424
  slots,
20256
20425
  judgePoolIds: judgePoolIds.length > 0 ? judgePoolIds : [...new Set(judgeSlotNames.map((s) => slotToId(s, effectivePool)).filter((x) => x !== null))],
20257
20426
  lane: null