sibyl-system 1.3.6 → 1.4.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.
- package/README.md +11 -2
- package/dist/cli.js +65 -22
- package/dist/index.js +24 -11
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -186,6 +186,13 @@ Plainly stated:
|
|
|
186
186
|
A kinship hit stamps the run `independence=NOT-INDEPENDENT`: the ballot is
|
|
187
187
|
preserved as data but is not in any effective path. A chain that cannot be
|
|
188
188
|
read stamps `UNVERIFIABLE` — unreadable is never silently clean.
|
|
189
|
+
- **Refused launches leave records (v1.4.0, council 16a2):** an explicit `--run-id`
|
|
190
|
+
whose tool-staged `launch-context.json` is missing or malformed is REFUSED, with a
|
|
191
|
+
`launch-refusal.json` written into the private run dir — a pinned id claiming a
|
|
192
|
+
tool launch that never staged its finding is a provenance lie, not an absence.
|
|
193
|
+
The bare CLI lane (no `--run-id`) keeps the named `UNVERIFIABLE` leg, and once the
|
|
194
|
+
chamber runner regenerates its run record, that face outvouches any contradicted
|
|
195
|
+
staged claim ("record wins").
|
|
189
196
|
- **Absence is a disability (W2).** Before any tally is honored, every declared
|
|
190
197
|
voter/worker must have left a terminal row (`ok|error|timeout` + detail) in
|
|
191
198
|
the run's `TERMINALS.txt`. A declared seat without its row is counted
|
|
@@ -204,7 +211,9 @@ Plainly stated:
|
|
|
204
211
|
- Consult/swarm send the artifact text to your configured model pool providers.
|
|
205
212
|
- The plugin writes under its state paths (the runs file and the per-run space) and,
|
|
206
213
|
for chamber/exam lanes, sealed run directories under `lane.runRoot`
|
|
207
|
-
(default `/tmp`)
|
|
214
|
+
(default `/tmp`), every plugin-created dir `0700` (mkdir mode + explicit chmod:
|
|
215
|
+
unreachable to group/other regardless of umask — v1.4.0 private-dir law) —
|
|
216
|
+
role homes, transcripts, pidfiles, CHECKSUMS.
|
|
208
217
|
- Voter/worker turns run with `bash`, `edit`, and `write` disabled per prompt.
|
|
209
218
|
|
|
210
219
|
## Architecture (`src/`)
|
|
@@ -263,7 +272,7 @@ PLAN→MINT→DISPATCH→AGGREGATE pipeline over ordinary child sessions.
|
|
|
263
272
|
|
|
264
273
|
```bash
|
|
265
274
|
npm run typecheck # tsc --noEmit, strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes
|
|
266
|
-
npm test # full unit suite, fully offline (no network, no LLM;
|
|
275
|
+
npm test # full unit suite, fully offline (no network, no LLM; 459 at v1.4.0 — the run prints the exact count)
|
|
267
276
|
npm run build # esbuild bundle → dist/index.js + dist/cli.js (ESM)
|
|
268
277
|
node smoke/run-smoke.mjs # offline smoke of the shipped surface (see smoke/README.md)
|
|
269
278
|
./ship.sh # the full release gate: all of the above + portability scan of the packed tarball
|
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,16 @@ var __export = (target, all) => {
|
|
|
9
9
|
import { randomBytes } from "node:crypto";
|
|
10
10
|
import { spawn as spawn2 } from "node:child_process";
|
|
11
11
|
import { existsSync as existsSync3 } from "node:fs";
|
|
12
|
-
import { copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6 } from "node:fs/promises";
|
|
12
|
+
import { access, copyFile as copyFile2, mkdir as mkdir6, readFile as readFile6, writeFile as writeFile7 } from "node:fs/promises";
|
|
13
|
+
|
|
14
|
+
// src/fsperm.ts
|
|
15
|
+
import { chmod, mkdir } from "node:fs/promises";
|
|
16
|
+
async function mkdirPrivate(dir) {
|
|
17
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
18
|
+
await chmod(dir, 448);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/cli.ts
|
|
13
22
|
import { basename, dirname as dirname4, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "node:path";
|
|
14
23
|
|
|
15
24
|
// src/state/record.ts
|
|
@@ -73,7 +82,7 @@ var COUNCIL_SIZE = COUNCILORS.length;
|
|
|
73
82
|
var KNOWN_IDS = new Set(COUNCILORS);
|
|
74
83
|
|
|
75
84
|
// src/chamber/protocol.ts
|
|
76
|
-
import { mkdir as
|
|
85
|
+
import { mkdir as mkdir3, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
77
86
|
import { join as join2 } from "node:path";
|
|
78
87
|
|
|
79
88
|
// src/lane/seating.ts
|
|
@@ -185,7 +194,7 @@ var FAIL_CLOSED_REJECT = rejectVerdict("no-parse-performed");
|
|
|
185
194
|
|
|
186
195
|
// src/state/chamber.ts
|
|
187
196
|
import { createHash as createHash2 } from "node:crypto";
|
|
188
|
-
import { mkdir, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
197
|
+
import { mkdir as mkdir2, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
189
198
|
import { homedir } from "node:os";
|
|
190
199
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
191
200
|
var DEFAULT_CHAMBER_LEDGER = join(homedir(), ".sibyl", "chamber-ledger.jsonl");
|
|
@@ -293,7 +302,7 @@ async function appendLedgerRow(ledgerPath, record2) {
|
|
|
293
302
|
const stamped = { ...record2, serial };
|
|
294
303
|
const line = JSON.stringify(stamped);
|
|
295
304
|
if (line.includes("\n")) return { ok: false, reason: "row serializes with a newline \u2014 refused (ledger is one-JSON-per-line)" };
|
|
296
|
-
await
|
|
305
|
+
await mkdir2(dirname(ledgerPath), { recursive: true });
|
|
297
306
|
await writeFile(ledgerPath, `${line}
|
|
298
307
|
`, { flag: "a" });
|
|
299
308
|
return { ok: true, serial, line };
|
|
@@ -334,7 +343,7 @@ var RECORD_FILE = "run-record.json";
|
|
|
334
343
|
var CHECKSUMS_FILE = "CHECKSUMS.txt";
|
|
335
344
|
var LEDGER_LINE_FILE = "ledger-line.txt";
|
|
336
345
|
async function atomicWrite(path, content) {
|
|
337
|
-
await
|
|
346
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
338
347
|
const tmp = `${path}.${String(process.pid)}.tmp`;
|
|
339
348
|
try {
|
|
340
349
|
await writeFile(tmp, content, "utf8");
|
|
@@ -636,9 +645,9 @@ var Protocol = class {
|
|
|
636
645
|
}
|
|
637
646
|
};
|
|
638
647
|
async function runChamber(cfg, onRecord) {
|
|
639
|
-
await
|
|
640
|
-
await
|
|
641
|
-
for (const role of CHAMBER_ROLES) await
|
|
648
|
+
await mkdir3(join2(cfg.runDir, "pids"), { recursive: true });
|
|
649
|
+
await mkdir3(join2(cfg.runDir, "evidence"), { recursive: true });
|
|
650
|
+
for (const role of CHAMBER_ROLES) await mkdir3(join2(cfg.runDir, role), { recursive: true });
|
|
642
651
|
const roster = CHAMBER_ROLES.map((role) => {
|
|
643
652
|
const d = resolveSeat(role, cfg.slots[role], cfg.pool, cfg.policy);
|
|
644
653
|
return d.ok ? { role, slot: d.slot, modelId: d.modelId, policyCheck: "allow" } : { role, slot: cfg.slots[role], modelId: "", policyCheck: "deny", denyReason: d.deny };
|
|
@@ -852,7 +861,7 @@ function renderVoice(voice) {
|
|
|
852
861
|
// src/lane/isolated.ts
|
|
853
862
|
import { spawn } from "node:child_process";
|
|
854
863
|
import { createWriteStream, existsSync } from "node:fs";
|
|
855
|
-
import { copyFile,
|
|
864
|
+
import { copyFile, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
856
865
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
857
866
|
var DEFAULT_PINNED_PATH = "/usr/bin:/bin";
|
|
858
867
|
function composeSandboxArgs(opts) {
|
|
@@ -882,12 +891,12 @@ function rolePaths(cfg, role) {
|
|
|
882
891
|
}
|
|
883
892
|
async function prepareRoleHome(cfg, role) {
|
|
884
893
|
const paths = rolePaths(cfg, role);
|
|
885
|
-
await
|
|
886
|
-
await
|
|
887
|
-
await
|
|
888
|
-
await
|
|
889
|
-
await
|
|
890
|
-
await
|
|
894
|
+
await mkdirPrivate(join3(paths.home, ".config", "opencode"));
|
|
895
|
+
await mkdirPrivate(dirname2(paths.dbPath));
|
|
896
|
+
await mkdirPrivate(join3(paths.home, ".cache", "opencode"));
|
|
897
|
+
await mkdirPrivate(join3(paths.home, ".local", "state", "opencode"));
|
|
898
|
+
await mkdirPrivate(paths.workspace);
|
|
899
|
+
await mkdirPrivate(join3(cfg.runDir, "pids"));
|
|
891
900
|
try {
|
|
892
901
|
if (cfg.configSource.length > 0) {
|
|
893
902
|
await copyFile(cfg.configSource, join3(paths.home, ".config", "opencode", "opencode.jsonc"));
|
|
@@ -20381,17 +20390,51 @@ async function resolveRun(flags) {
|
|
|
20381
20390
|
return { ok: false, error: `--run-id must be a bare sibyl run id, got "${flagRunId}"` };
|
|
20382
20391
|
}
|
|
20383
20392
|
const runId = flagRunId ?? `sibyl-${compactIso(now)}-${randomBytes(2).toString("hex")}`;
|
|
20393
|
+
if (targetRaw !== "-") {
|
|
20394
|
+
const abs = isAbsolute2(targetRaw) ? targetRaw : resolve2(process.cwd(), targetRaw);
|
|
20395
|
+
try {
|
|
20396
|
+
await access(abs);
|
|
20397
|
+
} catch (e) {
|
|
20398
|
+
return { ok: false, error: `--target unreadable: ${e instanceof Error ? e.message : String(e)}` };
|
|
20399
|
+
}
|
|
20400
|
+
}
|
|
20384
20401
|
const runDir = join7(laneCfg.runRoot, `sibyl-run-${runId}`);
|
|
20385
|
-
await
|
|
20386
|
-
let
|
|
20402
|
+
await mkdirPrivate(runDir);
|
|
20403
|
+
let contextLeg = null;
|
|
20404
|
+
let contextProblem = null;
|
|
20387
20405
|
try {
|
|
20388
20406
|
const lc = JSON.parse(await readFile6(join7(runDir, "launch-context.json"), "utf8"));
|
|
20389
|
-
|
|
20390
|
-
|
|
20391
|
-
|
|
20392
|
-
|
|
20407
|
+
if (isIndependenceShape(lc["independence"])) contextLeg = lc;
|
|
20408
|
+
else contextProblem = "launch-context staged but its independence leg is malformed";
|
|
20409
|
+
} catch (err) {
|
|
20410
|
+
contextProblem = err instanceof Error && err.name === "SyntaxError" ? "launch-context exists but is not valid JSON" : "no launch-context staged in the run dir";
|
|
20411
|
+
}
|
|
20412
|
+
if (flagRunId !== null && contextProblem !== null) {
|
|
20413
|
+
const refusal = {
|
|
20414
|
+
kind: "refused-launch",
|
|
20415
|
+
runId: flagRunId,
|
|
20416
|
+
reason: `${contextProblem} \u2014 a --run-id launch claims a tool-staged review that never happened; rerun through the sibyl_review tool, or drop --run-id for the supported bare headless launch`,
|
|
20417
|
+
at: now.toISOString()
|
|
20393
20418
|
};
|
|
20394
|
-
|
|
20419
|
+
await writeFile7(join7(runDir, "launch-refusal.json"), `${JSON.stringify(refusal, null, 2)}
|
|
20420
|
+
`, "utf8");
|
|
20421
|
+
return { ok: false, error: refusal.reason };
|
|
20422
|
+
}
|
|
20423
|
+
let launchIndependence;
|
|
20424
|
+
if (contextLeg !== null) {
|
|
20425
|
+
launchIndependence = contextLeg["independence"];
|
|
20426
|
+
try {
|
|
20427
|
+
const face = JSON.parse(await readFile6(join7(runDir, "run-record.json"), "utf8"));
|
|
20428
|
+
const faceInd = face["independence"];
|
|
20429
|
+
if (isIndependenceShape(faceInd) && faceInd.status !== launchIndependence.status) {
|
|
20430
|
+
launchIndependence = {
|
|
20431
|
+
...faceInd,
|
|
20432
|
+
evidence: `context claimed ${launchIndependence.status}, run record carries ${faceInd.status} \u2014 record wins. ${faceInd.evidence}`
|
|
20433
|
+
};
|
|
20434
|
+
}
|
|
20435
|
+
} catch {
|
|
20436
|
+
}
|
|
20437
|
+
} else {
|
|
20395
20438
|
launchIndependence = {
|
|
20396
20439
|
status: "UNVERIFIABLE",
|
|
20397
20440
|
convenerChain: [],
|
package/dist/index.js
CHANGED
|
@@ -18981,7 +18981,16 @@ function parseOptions(raw) {
|
|
|
18981
18981
|
|
|
18982
18982
|
// src/state/index.ts
|
|
18983
18983
|
import { randomBytes } from "node:crypto";
|
|
18984
|
-
import {
|
|
18984
|
+
import { readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
18985
|
+
|
|
18986
|
+
// src/fsperm.ts
|
|
18987
|
+
import { chmod, mkdir } from "node:fs/promises";
|
|
18988
|
+
async function mkdirPrivate(dir) {
|
|
18989
|
+
await mkdir(dir, { recursive: true, mode: 448 });
|
|
18990
|
+
await chmod(dir, 448);
|
|
18991
|
+
}
|
|
18992
|
+
|
|
18993
|
+
// src/state/index.ts
|
|
18985
18994
|
import { homedir as homedir2 } from "node:os";
|
|
18986
18995
|
import { dirname, join as join2 } from "node:path";
|
|
18987
18996
|
|
|
@@ -19176,7 +19185,7 @@ var RunStore = class {
|
|
|
19176
19185
|
* which briefly widens the crash window but never writes torn JSON. */
|
|
19177
19186
|
async save(records, opts) {
|
|
19178
19187
|
const dir = dirname(this.runsFile);
|
|
19179
|
-
await
|
|
19188
|
+
await mkdirPrivate(dir);
|
|
19180
19189
|
const tmp = join2(dir, `runs.${process.pid}.${randomBytes(4).toString("hex")}.tmp`);
|
|
19181
19190
|
const renameStep = opts?.rename ?? this.renameFile;
|
|
19182
19191
|
try {
|
|
@@ -19219,7 +19228,7 @@ var RunStore = class {
|
|
|
19219
19228
|
const now = /* @__PURE__ */ new Date();
|
|
19220
19229
|
const runId = `sibyl-${compactIso(now)}-${randomBytes(2).toString("hex")}`;
|
|
19221
19230
|
const spaceDir = join2(input2.spaceRootDir ?? this.spaceRoot, runId);
|
|
19222
|
-
await
|
|
19231
|
+
await mkdirPrivate(spaceDir);
|
|
19223
19232
|
const record3 = {
|
|
19224
19233
|
runId,
|
|
19225
19234
|
kind: input2.kind,
|
|
@@ -32870,7 +32879,7 @@ function ballotTag(vote) {
|
|
|
32870
32879
|
import { spawn } from "node:child_process";
|
|
32871
32880
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
32872
32881
|
import { existsSync as existsSync2 } from "node:fs";
|
|
32873
|
-
import {
|
|
32882
|
+
import { writeFile as writeFile4 } from "node:fs/promises";
|
|
32874
32883
|
import { dirname as dirname3, join as join6 } from "node:path";
|
|
32875
32884
|
import { fileURLToPath } from "node:url";
|
|
32876
32885
|
var REVIEW_TOOL_NAME = "sibyl_review";
|
|
@@ -32879,12 +32888,16 @@ function nodeBinary() {
|
|
|
32879
32888
|
}
|
|
32880
32889
|
function cliCommand(thisUrl, node2) {
|
|
32881
32890
|
const moduleDir = dirname3(fileURLToPath(thisUrl));
|
|
32882
|
-
const
|
|
32883
|
-
const
|
|
32884
|
-
|
|
32885
|
-
|
|
32886
|
-
|
|
32887
|
-
|
|
32891
|
+
const roots = [dirname3(moduleDir), dirname3(dirname3(moduleDir))];
|
|
32892
|
+
for (const root of roots) {
|
|
32893
|
+
const distCli = join6(root, "dist", "cli.js");
|
|
32894
|
+
if (existsSync2(distCli)) return { bin: node2, args: [distCli] };
|
|
32895
|
+
const srcCli = join6(root, "src", "cli.ts");
|
|
32896
|
+
const hooks = join6(root, "tools", "register-ts.mjs");
|
|
32897
|
+
if (existsSync2(srcCli) && existsSync2(hooks)) return { bin: node2, args: ["--import", hooks, srcCli] };
|
|
32898
|
+
}
|
|
32899
|
+
const repoRoot = roots[1] ?? dirname3(dirname3(moduleDir));
|
|
32900
|
+
return { bin: node2, args: ["--import", join6(repoRoot, "tools", "register-ts.mjs"), join6(repoRoot, "src", "cli.ts")] };
|
|
32888
32901
|
}
|
|
32889
32902
|
async function reviewExecute(deps, args, context) {
|
|
32890
32903
|
try {
|
|
@@ -32893,7 +32906,7 @@ async function reviewExecute(deps, args, context) {
|
|
|
32893
32906
|
const now = /* @__PURE__ */ new Date();
|
|
32894
32907
|
const runId = `sibyl-${compactIso(now)}-${randomBytes2(2).toString("hex")}`;
|
|
32895
32908
|
const runDir = join6(opts.lane.runRoot, `sibyl-run-${runId}`);
|
|
32896
|
-
await
|
|
32909
|
+
await mkdirPrivate(runDir);
|
|
32897
32910
|
const artifact = await readArtifact(args.target, context.directory);
|
|
32898
32911
|
if (!artifact.ok) return `SIBYL review: ${artifact.error}`;
|
|
32899
32912
|
let targetArg;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sibyl-system",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Sibyl-System: democratic-centralism review chamber (one conclusion, one voice) + fail-closed council voting + dynamic swarm plugin for OpenCode",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"prepack": "npm run build"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@opencode-ai/plugin": "1.
|
|
38
|
+
"@opencode-ai/plugin": "1.19.4",
|
|
39
39
|
"zod": "^4"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -45,5 +45,11 @@
|
|
|
45
45
|
},
|
|
46
46
|
"bin": {
|
|
47
47
|
"sibyl-chamber": "./dist/cli.js"
|
|
48
|
+
},
|
|
49
|
+
"overrides": {
|
|
50
|
+
"toml": "5.0.1"
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=22"
|
|
48
54
|
}
|
|
49
55
|
}
|