sibyl-system 1.1.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 (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +211 -0
  3. package/dist/cli.js +20348 -0
  4. package/dist/index.js +33194 -0
  5. package/package.json +49 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 TachikomaGundam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # Sibyl-System
2
+
3
+ [![npm version](https://img.shields.io/npm/v/sibyl-system.svg)](https://www.npmjs.com/package/sibyl-system) ![license MIT](https://img.shields.io/badge/license-MIT-green.svg)
4
+
5
+ A standalone [opencode](https://opencode.ai) plugin with two capabilities:
6
+
7
+ - **`sibyl_consult`** — a three-voter review council (MELCHIOR / BALTHASAR / CASPER)
8
+ that audits an artifact against a goal and returns a **fail-closed 2/3 verdict**.
9
+ - **`sibyl_swarm`** — a lightweight workflow swarm: an ARCHITECT persona plans a
10
+ dependency-ordered task graph, deterministic workers execute it in waves, and
11
+ the result is aggregated into a verdict.
12
+
13
+ Zero coupling to team-mode or fleet orchestration: no `team_*` tools, no
14
+ cross-agent message bus. Everything runs through plain opencode child sessions.
15
+
16
+ ## v1.1 — the review chamber (民主集中制)
17
+
18
+ `sibyl_review` + `sibyl-chamber` is a GENERAL review apparatus: any artifact
19
+ (doc, plan, code, agent-behavior exam pack) goes through broad evidence
20
+ gathering -> blind PRO/CON clash with cross-critique -> an independently-drawn
21
+ judge (E2 pool+seed committed before launch) -> exactly ONE conclusion spoken
22
+ in ONE voice (APPROVE / REJECT / NEEDS_HUMAN). Internal dissent is preserved
23
+ by path+hash in the sealed run dir, never dumped outward. All role sessions
24
+ run isolated under /tmp (zero sessions in your main list); runs are
25
+ spotcheckable (`sha256sum -c CHECKSUMS.txt`) and append-only-ledgered.
26
+ Laws: docs/isolation-laws.md. Mechanism: docs/democratic-centralism.md.
27
+
28
+ ```
29
+ node dist/cli.js run --target doc.md --goal "Is this sound?" --model local-qwen/qwen3.8-flash-next
30
+ node dist/cli.js status --tail 5
31
+ node dist/cli.js spotcheck <runId>
32
+ ```
33
+
34
+ ## Install
35
+
36
+ Register the published npm package by name (opencode resolves it from npm):
37
+
38
+ ```jsonc
39
+ {
40
+ "plugin": ["sibyl-system"]
41
+ }
42
+ ```
43
+
44
+ Alternatively, register a local checkout by path. One registration line in
45
+ `~/.config/opencode/opencode.jsonc`
46
+ (Windows: `%USERPROFILE%\.config\opencode\opencode.jsonc`). This is a
47
+ **documented example only** — the plugin never edits your config:
48
+
49
+ ```jsonc
50
+ {
51
+ "plugin": [
52
+ [
53
+ "/abs/path/to/sibyl-system/src/index.ts",
54
+ {
55
+ "modelPool": {
56
+ "default": { "providerID": "your-provider", "modelID": "your-model" }
57
+ },
58
+ "concurrencyK": 4
59
+ }
60
+ ]
61
+ ]
62
+ }
63
+ ```
64
+
65
+ Registering the TS entry directly is supported (opencode transpiles plugin
66
+ sources with Bun). The tuple's second element is passed **verbatim** as the
67
+ options object, so option fields sit at its **top level** (as in the example);
68
+ unknown keys are rejected loudly, never silently ignored. The options object is
69
+ the **only** config surface — there are no other config files (the
70
+ `SIBYL_STATE_FILE` env var exists solely as a test/CI isolation seam, see
71
+ State).
72
+
73
+ Invalid options never crash the host: the plugin prints
74
+ `[sibyl] SIBYL plugin DISABLED — fix options (no sibyl_* tools registered)` to
75
+ stderr, registers nothing, and returns empty hooks.
76
+
77
+ ## Tools
78
+
79
+ | Tool | Arguments | Behavior |
80
+ |------|-----------|----------|
81
+ | `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. |
82
+ | `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`. With `judge: true`, one extra judge pass may replace the derived verdict — an unrecognized or failed judge reply keeps the derived one. |
83
+ | `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. |
84
+ | `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`. |
85
+
86
+ ## Options
87
+
88
+ All keys optional; a type error or a missing required entry (such as
89
+ `modelPool.default`) disables the plugin with per-field `[sibyl] config error: …`
90
+ lines. Unknown keys are rejected the same loud way — so a typo'd option key or a
91
+ misplaced nesting level (e.g. wrapping everything in `{ "options": { … } }`)
92
+ disables the plugin with a named-key error instead of silently running on
93
+ defaults.
94
+
95
+ | Key | Default | Meaning |
96
+ |-----|---------|---------|
97
+ | `modelPool` | `{ default: { providerID: "", modelID: "" } }` | Named `{providerID, modelID}` slots. The `default` entry is **required**. Empty-string provider/model is a sentinel meaning "host default". Resolution per persona: caller override slot → persona's own slot (`melchior`/`balthasar`/`casper`/`architect`) → `pool.default`; a missing named slot is never fatal. |
98
+ | `voters` | all `"default"` | `{ MELCHIOR, BALTHASAR, CASPER }` → pool slot names, letting each councilor run on a different model. |
99
+ | `swarm` | all `"default"` | `{ judge, pro, con }` → pool slot names for the swarm roles. |
100
+ | `maxRounds` | `4` | Swarm round budget (wave cycles). Integer 1–16. |
101
+ | `timeoutMs` | `240000` | Per-child-session timeout (create + prompt share the budget). Integer ≥ 1. |
102
+ | `concurrencyK` | `4` | Cap on parallel workers per wave (also capped by the schema's own `concurrency`). Integer 1–8. |
103
+ | `staggerMs` | `2000` | Delay between worker launches within a wave, to avoid rate-limit bursts. Integer ≥ 0. |
104
+
105
+ ## State layout
106
+
107
+ - **Runs file**: `~/.sibyl/runs.json` (Windows: `%USERPROFILE%\.sibyl\runs.json`) —
108
+ one record per run (id, kind, status, verdict tally, operator notes), co-located
109
+ with `spaces/` under the one `~/.sibyl` state root, so npm upgrades (which ship a
110
+ fresh, versioned package directory) never wipe your run history. Written
111
+ atomically (tmp + rename; the parent directory is created on demand).
112
+ *Note: 1.0.x-era runs recorded under the old package-local `<repo>/.state` path
113
+ are not migrated (pre-adoption by design) — the store starts empty.*
114
+ - **Per-run space**: `~/.sibyl/spaces/<runId>/` (Windows: `%USERPROFILE%\.sibyl\spaces`) — full voter replies
115
+ (`MELCHIOR.md`, …) for consults, worker drafts (`<workerId>.draft.md`) for swarms.
116
+ - **Test seam**: `SIBYL_STATE_FILE` env var overrides the runs-file path.
117
+ - `load()` never throws: a missing or corrupt file recovers to an empty list
118
+ (with a stderr warning); malformed individual entries are dropped, not fatal.
119
+
120
+ ## Fail-closed policy
121
+
122
+ Plainly stated:
123
+
124
+ - Approval needs **≥ 2 of 3 approvals and zero error/missing votes**.
125
+ Anything else — including 2A + 1 error or 2A + 1 missing — is **REJECT**.
126
+ - Error votes, missing votes, and malformed verdicts all count against approval.
127
+ - A reply that still doesn't parse after its single repair shot becomes a
128
+ 0-confidence REJECT ballot with reason `verdict-unparseable: …`.
129
+ - There is no lenient mode, by design.
130
+
131
+ ## Security notes
132
+
133
+ - Consult/swarm send the artifact text to your configured model pool providers.
134
+ - The plugin writes only under its state paths: the runs file and the per-run space.
135
+ - Voter/worker turns run with `bash`, `edit`, and `write` disabled per prompt.
136
+
137
+ ## Architecture (`src/`)
138
+
139
+ Dependencies point downward only:
140
+
141
+ ```
142
+ index.ts plugin entry: parse options → share one RunStore + client
143
+ adapter → register the three tools
144
+ ├── engine/ runPersona(): create + prompt one child session through a
145
+ │ structural client seam; per-stage timeouts; never throws —
146
+ │ every failure is a structured PersonaRunResult
147
+ ├── verdict/ strict JSON verdict contract: fence-tolerant extraction →
148
+ │ validation → exactly one repair → fail-closed REJECT
149
+ ├── council/ councilor personas + tallyVotes (majority2of3 default,
150
+ │ unanimous exported); pure aggregation, zero IO
151
+ ├── state/ RunStore: atomic RMW runs.json, per-run space dirs
152
+ ├── swarm/ planner (ARCHITECT schema) → minter (deterministic worker
153
+ │ roster) → dispatcher (dependency waves, stagger,
154
+ │ suspend-on-rate-limit, resume) → aggregate (report +
155
+ │ verdict derivation; full drafts never inlined)
156
+ ├── lane/ v1.1 E1/E4: isolated opencode-run launcher (L1 env pinning,
157
+ │ argv-only spawn, pidfile-only kill) + seating policy prefilter
158
+ ├── chamber/ v1.1 民主 protocol (evidence -> blind clash -> cross-critique
159
+ │ -> drawn judge, bounded rounds) + 集中 synthesis (ONE voice,
160
+ │ fail-closed, dissent sealed by path+hash)
161
+ ├── exam/ v1.1 E3: scenario-as-data behavioral exams, mechanical
162
+ │ transcript/disk signal grading, canary veto
163
+ ├── state/ RunStore + v1.1 chamber records: EOF-append ledger (A5),
164
+ │ face-last regeneration, CHECKSUMS + spotcheck (A4)
165
+ ├── personas.ts registry: 3 councilors + ARCHITECT, model slots
166
+ ├── options.ts zod v4 schema + parseOptions (never throws)
167
+ ├── cli.ts v1.1 sibyl-chamber bin: run | status | spotcheck | kill
168
+ └── tools/ sibyl_consult / sibyl_swarm / sibyl_status / sibyl_review
169
+ glue + shared helpers (model-slot chain, artifact reader)
170
+ ```
171
+
172
+ ## vs. swarm
173
+
174
+ This is **not** the oh-my-openagent swarm. It has no `team_*` tools, no
175
+ cross-agent message bus, and no fleet-orchestration dependency of any kind —
176
+ it is a clean-room implementation (~3.5K lines of product code, comments excluded) that happens to
177
+ share the problem space. `sibyl_swarm` is a single tool call driving a
178
+ PLAN→MINT→DISPATCH→AGGREGATE pipeline over ordinary child sessions.
179
+
180
+ ## Development
181
+
182
+ ```bash
183
+ npm run typecheck # tsc --noEmit, strict + noUncheckedIndexedAccess + exactOptionalPropertyTypes
184
+ npm run test # 309 unit tests, fully offline (no network, no LLM)
185
+ npm run build # esbuild bundle → dist/index.js (ESM)
186
+ node smoke/run-smoke.mjs # offline smoke of the shipped surface (see smoke/README.md)
187
+ ```
188
+
189
+ Live end-to-end evidence (real opencode, real model sessions):
190
+
191
+ - `.omo/evidence/t10/` (internal working evidence, not shipped) — happy path: 3 real voters audited an artifact with
192
+ planted defects and returned fail-closed **REJECT 0A/3R**, citing all 3
193
+ planted defect classes; council wall time 95.6 s ≈ slowest single voter
194
+ (Σ 168.2 s), proving true parallel fan-out.
195
+ - `.omo/evidence/t11/` (internal working evidence, not shipped) — failure paths, 31/31 assertions: a bad model slot
196
+ produced 3 error votes → fail-closed **REJECT 0A/0R/3E**; SIGINT mid-run left
197
+ the store valid with the interrupted run honestly frozen at `running`.
198
+ - `smoke/` ships a deterministic offline re-check of the build + entry +
199
+ status surface for CI use.
200
+
201
+ ## Naming
202
+
203
+ The product is named after the **Sibyl System** from *Psycho-Pass*: a distributed,
204
+ fail-closed deliberation network that renders verdicts — a fitting namesake for a
205
+ voting council. The councilor names **Melchior / Balthasar / Casper** are retained
206
+ as an Evangelion MAGI tribute to the original three-voter design. The project was
207
+ developed under the name MAGI and renamed to Sibyl-System before its first release.
208
+
209
+ ## License
210
+
211
+ MIT