sibyl-system 1.3.2 → 1.3.4
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 +3 -1
- package/dist/cli.js +17 -1
- package/dist/index.js +96 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,6 +12,7 @@ A standalone [opencode](https://opencode.ai) plugin:
|
|
|
12
12
|
the result is aggregated into a verdict.
|
|
13
13
|
- **`sibyl_review` / `sibyl-chamber`** (v1.1) — a general review chamber with
|
|
14
14
|
isolated role sessions and one conclusion spoken in one voice (see below).
|
|
15
|
+
- **`sibyl_audit`** — the outside lane as a product organ: completion-state ballots convene themselves from a fresh root session (see Tools).
|
|
15
16
|
- **`sibyl_status` + audit primitives** (`sibyl_anchor_check`, `sibyl_time_probe`,
|
|
16
17
|
`sibyl_attribute`) — read-only verification tools: run-store view, human-message
|
|
17
18
|
anchor re-verification, clock integrity, ref-move attribution.
|
|
@@ -98,6 +99,7 @@ stderr, registers nothing, and returns empty hooks.
|
|
|
98
99
|
| Tool | Arguments | Behavior |
|
|
99
100
|
|------|-----------|----------|
|
|
100
101
|
| `sibyl_consult` | `{ artifact, goal }` | `artifact` is a file path or inline multi-line text (≤ 256 KiB). The three councilors audit it in parallel; each reply is parsed into a verdict (one in-session JSON-only repair shot per voter). Returns the tally, merged reasons/must-fix, run id, and per-voter reply file paths. |
|
|
102
|
+
| `sibyl_audit` | `{ artifact, goal }` | Independent audit lane: the council is convened by a FRESH engine-provisioned root session (structurally non-kin to any drafting chain), so the ballot enters the effective path without the caller's identity touching it. Receipt: auditor session id (retained, DB-reconcilable), artifact sha256 bound at start and end (mid-flight drift => VOID), verdict face; anything but INDEPENDENT on the fresh convener => REFUSED, no receipt. |
|
|
101
103
|
| `sibyl_swarm` | `{ artifact, goal, judge? }` | ARCHITECT decomposes goal + artifact into a strict-JSON workflow schema; workers are minted deterministically and dispatched in dependency waves; drafts land in the run's space dir. Verdict: `APPROVE` / `REJECT` / `EXHAUSTED`, forced to `CANNOT_ANSWER` when a declared worker left no terminal row (W2) or zero drafts were produced. With `judge: true`, one extra judge pass may replace the derived verdict — an unrecognized or failed judge reply keeps the derived one. |
|
|
102
104
|
| `sibyl_status` | `{ runId? }` | Read-only. Lists all recorded runs (newest last) + the last chamber-ledger rows, or shows one run's full record and space dir. |
|
|
103
105
|
| `sibyl_review` | `{ target, goal, seed?, maxRounds? }` | v1.1 general democratic-centralism chamber: launches the isolated evidence→clash→judge pipeline detached and returns a receipt (explicitly NOT a verdict); the single voice lands in the run record at terminal state. CLI twin: `sibyl-chamber`. |
|
|
@@ -211,7 +213,7 @@ Dependencies point downward only:
|
|
|
211
213
|
|
|
212
214
|
```
|
|
213
215
|
index.ts plugin entry: parse options → share one RunStore + client
|
|
214
|
-
adapter → register the
|
|
216
|
+
adapter → register the eight sibyl_* tools
|
|
215
217
|
├── engine/ runPersona(): create + prompt one child session through a
|
|
216
218
|
│ structural client seam; per-stage timeouts; never throws —
|
|
217
219
|
│ every failure is a structured PersonaRunResult
|
package/dist/cli.js
CHANGED
|
@@ -801,6 +801,11 @@ function conclude(result) {
|
|
|
801
801
|
}
|
|
802
802
|
return { conclusion: "NEEDS_HUMAN", confidence: 0 };
|
|
803
803
|
}
|
|
804
|
+
function voiceIndependence(record2) {
|
|
805
|
+
const ind = record2.independence;
|
|
806
|
+
if (ind === void 0) return "MISSING-PRE-W1 \u2014 this record predates convener capture; never treat as independent";
|
|
807
|
+
return ind.status === "INDEPENDENT" ? "INDEPENDENT" : `${ind.status} \u2014 ${ind.evidence}`;
|
|
808
|
+
}
|
|
804
809
|
function buildVoice(result) {
|
|
805
810
|
const { record: record2, judge, terminal, gaps } = result;
|
|
806
811
|
const { conclusion, confidence } = conclude(result);
|
|
@@ -819,6 +824,7 @@ function buildVoice(result) {
|
|
|
819
824
|
open_charges: judge?.charges.length ?? 0,
|
|
820
825
|
dissent_sealed_in: sealed,
|
|
821
826
|
terminal,
|
|
827
|
+
independence: voiceIndependence(record2),
|
|
822
828
|
run_id: record2.runId,
|
|
823
829
|
serial: record2.serial,
|
|
824
830
|
spotcheck: spotcheckCommand(record2.runDir)
|
|
@@ -828,6 +834,7 @@ function renderVoice(voice) {
|
|
|
828
834
|
const lines = [
|
|
829
835
|
`SIBYL \u2014 ONE CONCLUSION, ONE VOICE [${voice.label}]`,
|
|
830
836
|
`conclusion: ${voice.conclusion} confidence: ${String(voice.confidence)} terminal: ${voice.terminal}`,
|
|
837
|
+
`independence: ${voice.independence}`,
|
|
831
838
|
`run: ${voice.run_id} (serial ${String(voice.serial)}) open charges: ${String(voice.open_charges)}`,
|
|
832
839
|
`honesty: ${voice.honesty} (until C-09/C-14 containment rulings \u2014 see docs/isolation-laws.md)`,
|
|
833
840
|
"rationale:",
|
|
@@ -20379,8 +20386,17 @@ async function resolveRun(flags) {
|
|
|
20379
20386
|
let launchIndependence;
|
|
20380
20387
|
try {
|
|
20381
20388
|
const lc = JSON.parse(await readFile6(join7(runDir, "launch-context.json"), "utf8"));
|
|
20382
|
-
|
|
20389
|
+
launchIndependence = isIndependenceShape(lc["independence"]) ? lc["independence"] : {
|
|
20390
|
+
status: "UNVERIFIABLE",
|
|
20391
|
+
convenerChain: [],
|
|
20392
|
+
evidence: "launch-context staged but its independence leg is malformed \u2014 not trusted; this review carries no independence anchor"
|
|
20393
|
+
};
|
|
20383
20394
|
} catch {
|
|
20395
|
+
launchIndependence = {
|
|
20396
|
+
status: "UNVERIFIABLE",
|
|
20397
|
+
convenerChain: [],
|
|
20398
|
+
evidence: "bare CLI launch: no convener session staged; the drafting-chain read was impossible by construction"
|
|
20399
|
+
};
|
|
20384
20400
|
}
|
|
20385
20401
|
let targetPath = targetRaw;
|
|
20386
20402
|
if (targetRaw !== "-") {
|
package/dist/index.js
CHANGED
|
@@ -31844,7 +31844,7 @@ async function salvageAfterPromptTimeout(client, directory, sessionID, graceMs,
|
|
|
31844
31844
|
}
|
|
31845
31845
|
return null;
|
|
31846
31846
|
}
|
|
31847
|
-
async function
|
|
31847
|
+
async function runPersonaAttempt(opts, attempt) {
|
|
31848
31848
|
const { client, directory, persona, model, inputText, parentID } = opts;
|
|
31849
31849
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
31850
31850
|
const t0 = Date.now();
|
|
@@ -31863,7 +31863,7 @@ async function runPersona(opts) {
|
|
|
31863
31863
|
const created = await withTimeout(
|
|
31864
31864
|
client.session.create({
|
|
31865
31865
|
body: {
|
|
31866
|
-
title: `sibyl:${persona.agent ?? "persona"}`,
|
|
31866
|
+
title: `sibyl:${persona.agent ?? "persona"}${attempt === 1 ? "" : ` r${String(attempt)}`}`,
|
|
31867
31867
|
...parentID !== void 0 && parentID.length > 0 && { parentID }
|
|
31868
31868
|
},
|
|
31869
31869
|
query: { directory }
|
|
@@ -31908,9 +31908,9 @@ async function runPersona(opts) {
|
|
|
31908
31908
|
graceMs,
|
|
31909
31909
|
opts.salvagePollMs ?? DEFAULT_SALVAGE_POLL_MS
|
|
31910
31910
|
);
|
|
31911
|
-
if (!outcome) return fail(promptFailure);
|
|
31911
|
+
if (!outcome) return { ...fail(promptFailure), retryable: true };
|
|
31912
31912
|
if (outcome.kind === "error") {
|
|
31913
|
-
return fail(`${promptFailure}; salvage read failed: ${outcome.error}`);
|
|
31913
|
+
return { ...fail(`${promptFailure}; salvage read failed: ${outcome.error}`), retryable: true };
|
|
31914
31914
|
}
|
|
31915
31915
|
return {
|
|
31916
31916
|
text: outcome.text,
|
|
@@ -31922,7 +31922,10 @@ async function runPersona(opts) {
|
|
|
31922
31922
|
salvageExtraMs: Date.now() - raceExpiredAt
|
|
31923
31923
|
};
|
|
31924
31924
|
}
|
|
31925
|
-
if (prompted.error)
|
|
31925
|
+
if (prompted.error) {
|
|
31926
|
+
const channel = describeError2(prompted.error);
|
|
31927
|
+
return SALVAGE_ELIGIBLE_PROMPT_FAILURE.test(channel) ? { ...fail(channel), retryable: true } : fail(channel);
|
|
31928
|
+
}
|
|
31926
31929
|
if (!prompted.data) return fail("session.prompt: response missing data payload");
|
|
31927
31930
|
const info = prompted.data.info;
|
|
31928
31931
|
if (info?.error) return fail(describeError2(info.error));
|
|
@@ -31937,9 +31940,18 @@ async function runPersona(opts) {
|
|
|
31937
31940
|
}
|
|
31938
31941
|
return { text, ok: true, latencyMs: Date.now() - t0, sessionID, modelApplied };
|
|
31939
31942
|
} catch (e) {
|
|
31940
|
-
|
|
31943
|
+
const thrown = e instanceof Error ? e.message : describeError2(e);
|
|
31944
|
+
return SALVAGE_ELIGIBLE_PROMPT_FAILURE.test(thrown) ? { ...fail(thrown), retryable: true } : fail(thrown);
|
|
31941
31945
|
}
|
|
31942
31946
|
}
|
|
31947
|
+
async function runPersona(opts) {
|
|
31948
|
+
const first = await runPersonaAttempt(opts, 1);
|
|
31949
|
+
if (first.ok || first.retryable !== true) return first;
|
|
31950
|
+
const second = await runPersonaAttempt(opts, 2);
|
|
31951
|
+
if (second.ok) return { ...second, retried: true };
|
|
31952
|
+
const { retryable: _dropped, ...settled } = second;
|
|
31953
|
+
return { ...settled, retried: true, error: `${second.error} (one transport retry spent; first death: ${first.error})` };
|
|
31954
|
+
}
|
|
31943
31955
|
|
|
31944
31956
|
// src/independence.ts
|
|
31945
31957
|
var MAX_CHAIN_DEPTH = 16;
|
|
@@ -32676,6 +32688,7 @@ function formatRunLine(record3) {
|
|
|
32676
32688
|
|
|
32677
32689
|
// src/tools/consult.ts
|
|
32678
32690
|
var CONSULT_TOOL_NAME = "sibyl_consult";
|
|
32691
|
+
var BALLOT_BUDGET_LINES = 25;
|
|
32679
32692
|
function buildConsultInput(goal, artifactText) {
|
|
32680
32693
|
return `Audit the artifact below against the stated goal and cast your verdict.
|
|
32681
32694
|
|
|
@@ -32683,7 +32696,9 @@ GOAL:
|
|
|
32683
32696
|
${goal}
|
|
32684
32697
|
|
|
32685
32698
|
ARTIFACT:
|
|
32686
|
-
${artifactText}
|
|
32699
|
+
${artifactText}
|
|
32700
|
+
|
|
32701
|
+
BALLOT BUDGET (hard): total output at most ${String(BALLOT_BUDGET_LINES)} lines; one line per reason and per must-fix; no headings, no blank lines, no code blocks. The cap binds every seat alike \u2014 it is a transport protection, not a depth limit.`;
|
|
32687
32702
|
}
|
|
32688
32703
|
var REPLY_HEADER = "<!-- SIBYL consult voter reply (plan task 8). Full raw session text below; parse/repair metadata at the end. -->\n";
|
|
32689
32704
|
function renderReply(voter, text, meta3) {
|
|
@@ -33031,6 +33046,79 @@ function buildAnchorTool(deps) {
|
|
|
33031
33046
|
});
|
|
33032
33047
|
}
|
|
33033
33048
|
|
|
33049
|
+
// src/tools/audit.ts
|
|
33050
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
33051
|
+
var AUDIT_TOOL_NAME = "sibyl_audit";
|
|
33052
|
+
var RUN_ID_RE = /run (sibyl-[0-9TZ-]+-[0-9a-f]+)/;
|
|
33053
|
+
function sha2562(text) {
|
|
33054
|
+
return createHash3("sha256").update(text, "utf8").digest("hex");
|
|
33055
|
+
}
|
|
33056
|
+
async function artifactHash(raw, dir) {
|
|
33057
|
+
const art = await readArtifact(raw, dir);
|
|
33058
|
+
return art.ok ? sha2562(art.text) : null;
|
|
33059
|
+
}
|
|
33060
|
+
function buildAuditTool(deps) {
|
|
33061
|
+
return tool({
|
|
33062
|
+
description: "SIBYL independent audit lane: convenes the three-councilor consult from a FRESH engine-provisioned root session (structurally non-kin to any drafting chain), so the ballot enters the effective path without the caller's identity touching it. Emits an audit receipt \u2014 auditor session id (retained, DB-reconcilable), artifact sha256 bound at start and end, verdict face. REFUSES (no receipt) if the fresh convener resolves to anything but INDEPENDENT, or the artifact drifts mid-ballot (VOID).",
|
|
33063
|
+
args: {
|
|
33064
|
+
artifact: tool.schema.string().min(1).describe("Path to the artifact, or its inline content (multi-line)."),
|
|
33065
|
+
goal: tool.schema.string().min(1).describe("The question the audit must answer.")
|
|
33066
|
+
},
|
|
33067
|
+
execute: async (args, context) => {
|
|
33068
|
+
try {
|
|
33069
|
+
return await auditExecute(deps, args, context);
|
|
33070
|
+
} catch (err) {
|
|
33071
|
+
return internalError(AUDIT_TOOL_NAME, err);
|
|
33072
|
+
}
|
|
33073
|
+
}
|
|
33074
|
+
});
|
|
33075
|
+
}
|
|
33076
|
+
async function auditExecute(deps, args, ctx) {
|
|
33077
|
+
const start = await readArtifact(args.artifact, ctx.directory);
|
|
33078
|
+
if (!start.ok) {
|
|
33079
|
+
return `SIBYL AUDIT: refused \u2014 artifact unreadable: ${start.error}`;
|
|
33080
|
+
}
|
|
33081
|
+
const shaStart = sha2562(start.text);
|
|
33082
|
+
const created = await deps.client.session.create({
|
|
33083
|
+
body: { title: `sibyl:audit ${(/* @__PURE__ */ new Date()).toISOString()}` },
|
|
33084
|
+
query: { directory: ctx.directory }
|
|
33085
|
+
});
|
|
33086
|
+
const auditor = created.data?.id ?? "";
|
|
33087
|
+
if (auditor.length === 0) {
|
|
33088
|
+
const why = created.error === void 0 ? "no id in response" : JSON.stringify(created.error).slice(0, 140);
|
|
33089
|
+
return `SIBYL AUDIT: refused \u2014 engine did not provision the fresh root convener (${why}); an audit without a resolvable auditor session is not an audit`;
|
|
33090
|
+
}
|
|
33091
|
+
const caller = ctx.sessionID.length > 0 ? ctx.sessionID : "headless";
|
|
33092
|
+
const ballot = await consultExecute(deps, args, { ...ctx, sessionID: auditor });
|
|
33093
|
+
const runId = RUN_ID_RE.exec(ballot)?.[1] ?? "";
|
|
33094
|
+
const runs = await deps.store.load();
|
|
33095
|
+
const rec = runs.find((r) => r.runId === runId);
|
|
33096
|
+
const ind = rec?.independence;
|
|
33097
|
+
const independence = ind?.status ?? "MISSING-RECORD";
|
|
33098
|
+
if (independence !== "INDEPENDENT") {
|
|
33099
|
+
const ev = ind?.evidence ?? `run ${runId.length > 0 ? runId : "(unresolvable)"} not found in the store`;
|
|
33100
|
+
return `SIBYL AUDIT: REFUSED \u2014 the fresh convener ${auditor} resolved to independence=${independence}: ${ev}
|
|
33101
|
+
the ballot ${runId.length > 0 ? runId : "(no run id)"} is archived as DATA and carries no audit receipt; requested-by=` + caller + " (DATA only)";
|
|
33102
|
+
}
|
|
33103
|
+
const shaEnd = await artifactHash(args.artifact, ctx.directory);
|
|
33104
|
+
if (shaEnd !== shaStart) {
|
|
33105
|
+
return `SIBYL AUDIT: VOID \u2014 artifact bytes moved while the ballot was in flight (start sha256 ${shaStart.slice(0, 16)}\u2026, end ${shaEnd === null ? "unreadable" : `${shaEnd.slice(0, 16)}\u2026`}); run ${runId} archived as DATA; re-audit the current bytes`;
|
|
33106
|
+
}
|
|
33107
|
+
const v = rec?.verdict;
|
|
33108
|
+
const counts = v === void 0 ? "no verdict" : `${String(v.approvals)}A/${String(v.rejects)}R/${String(v.errors)}E/${String(v.missing)}M`;
|
|
33109
|
+
const rules = rec?.instrument?.rulesHash.slice(0, 12) ?? "no-instrument-face";
|
|
33110
|
+
return [
|
|
33111
|
+
`SIBYL AUDIT RECEIPT run=${runId}`,
|
|
33112
|
+
` verdict=${v?.verdict ?? "unknown"} (${counts}) rules=${rules}`,
|
|
33113
|
+
` auditor-convener=${auditor} (fresh engine-provisioned root; session row RETAINED for DB reconciliation)`,
|
|
33114
|
+
` requested-by=${caller} \u2014 recorded as ledger data only; it neither convenes nor votes`,
|
|
33115
|
+
` artifact-sha256=${shaStart} (re-hashed after the ballot, identical \u2014 the receipt binds to these bytes)`,
|
|
33116
|
+
"",
|
|
33117
|
+
"--- ballot face ---",
|
|
33118
|
+
ballot
|
|
33119
|
+
].join("\n");
|
|
33120
|
+
}
|
|
33121
|
+
|
|
33034
33122
|
// src/tools/probes.ts
|
|
33035
33123
|
var DEFAULT_TIME_POOL = [
|
|
33036
33124
|
{ url: "https://www.cloudflare.com", ownerDept: "cloudflare" },
|
|
@@ -33991,6 +34079,7 @@ async function SibylPlugin(input2, options) {
|
|
|
33991
34079
|
return {
|
|
33992
34080
|
tool: {
|
|
33993
34081
|
sibyl_consult: buildConsultTool(deps),
|
|
34082
|
+
sibyl_audit: buildAuditTool(deps),
|
|
33994
34083
|
sibyl_swarm: buildSwarmTool(deps),
|
|
33995
34084
|
sibyl_status: buildStatusTool(deps),
|
|
33996
34085
|
sibyl_review: buildReviewTool(deps),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sibyl-system",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
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",
|